cocos creator 3.x实现手机虚拟操作杆

简介

在许多移动游戏中,虚拟操纵杆是一个重要的用户界面元素,用于控制角色或物体的移动。本文将介绍如何在Unity中实现虚拟操纵杆,提供了一段用于移动控制的代码。我们将讨论不同类型的虚拟操纵杆,如固定和跟随,以及如何在实际游戏中使用这些操纵杆。

unity2022版本实现虚拟操作杆可以查看这篇文章 点击查看

界面节点设置


1. 添加一个Canvas节点

首先,我们需要创建一个画布节点,这是我们整个界面的基础。这个节点将允许我们绘制和排列其他元素。

2. 在Canvas节点下添加一个Camera节点

接下来,我们将在Canvas节点下创建一个Camera节点。这个Camera节点是查看操作按钮的摄像头

2. 在Canvas节点下添加一个Joystick节点

这个Joystick节点将充当容器,用于组织和管理我们的界面元素。

3. 在Joystick节点下添加两个Sprite节点

在Joystick节点中,我们将添加两个Sprite节点。这两个Image节点具有不同的用途:

a. 背景节点:第一个Sprite节点将用作背景,为整个界面提供背景图像或颜色。

b. 操作按钮节点:第二个Sprite节点将用于显示操作按钮或其他交互元素。

Canvas (画布)
│
└─ Camera (摄像机)
└─ Joystick (虚拟操作父节点)│├─ Bg(背景)│└─ Btn(操作按钮)

截图可以这样:

脚本编写


简要说明:


因为编写的是虚拟操作杆 需要添加三个事件:

触摸开始(touchStart),拖动(touchMove),触摸结束(touchEnd)

在触摸开始记录拖动的一些起始坐标。

在拖动中移动操作按钮节点如果是操作角色移动这里就可以操作移动角色

在触摸结束的时候重置坐标

        1.touchStart方法:描述touchStart方法,它处理当玩家触摸操纵杆时的行为。根据操纵杆类型(固定或跟随),它设置操纵杆的初始位置。

        2.touchMove方法:详细解释touchMove方法,这是当玩家拖动操纵杆时执行的代码。说明如何计算操纵杆输入的方向,以及如何限制操纵杆的移动范围。

        3.touchEnd方法:描述touchEnd方法,用于当玩家释放操纵杆时重置相关变量和位置,同时停止玩家的移动。在初始化引用的时候可以传入参数(JoystickType)控制虚拟操作杆是固定的还是跟随触摸点的

完整的脚本如下:

import { Component, assetManager, _decorator, Node, Prefab, instantiate, JsonAsset, UITransform, Vec3, Widget, Graphics, Enum, input, Input, EventTouch, RichText, Label, sys, CCString, Vec2, Camera } from "cc"
import utils from "../../../utils";
const { ccclass, property, type } = _decorator;// 定义操纵杆的类型
export enum JoystickType {FIXED,    // 固定类型的操纵杆FOLLOW    // 跟随类型的操纵杆
}// 定义操纵杆的显示类型
export enum JoystickShowType {ALWAYS_DISPLAY,         // 一直显示NOT_ALWAYS_DISPLAY      // 仅在操作时显示
}@ccclass('Joystick')
export default class Joystick extends Component {// 绑定Camera组件,用于获取屏幕触摸位置@property(Camera)public camera: Camera;// 绑定操纵杆节点@property(Node)public joystick: Node;// 绑定操纵杆背景节点@property(Node)public joystickBG: Node;// 绑定操纵杆的父节点@property(Node)public joystickParent: Node;// 存储操纵杆的移动向量public joystickVec: Vec3;// 指定操纵杆的类型(固定或跟随)@property({type: Enum(JoystickType),tooltip: "类型"})public joystickType: JoystickType = JoystickType.FIXED;// 指定操纵杆的显示类型(一直显示或仅操作时显示)@property({type: Enum(JoystickShowType),tooltip: "显示类型"})public joystickShowType: JoystickShowType = JoystickShowType.ALWAYS_DISPLAY;// 触摸开始时操纵杆的位置private joystickTouchPos: Vec3;// 操纵杆背景的原始位置private joystickOriginalPos: Vec3;// 操纵杆背景的半径private joystickRadius: number;// 标识是否开始拖动操纵杆public startDrop: boolean;// 初始化start() {// 根据操纵杆显示类型设置其初始显示状态if (this.joystickShowType === JoystickShowType.NOT_ALWAYS_DISPLAY) {this.joystickParent.active = false;} else {this.joystickParent.active = true;}// 保存操纵杆背景的原始位置this.joystickOriginalPos = this.joystickBG.position.clone();// 计算操纵杆背景的半径(背景节点宽度的一半)this.joystickRadius = utils.getNodeSize(this.joystickBG).width / 2;// 绑定触摸事件的处理函数this.node.on(Input.EventType.TOUCH_END, this.touchEnd, this);this.node.on(Input.EventType.TOUCH_START, this.touchStart, this);this.node.on(Input.EventType.TOUCH_MOVE, this.touchMove, this);}// 在组件销毁时,解除触摸事件的绑定protected onDestroy(): void {this.node.off(Input.EventType.TOUCH_END, this.touchEnd, this);this.node.off(Input.EventType.TOUCH_START, this.touchStart, this);this.node.off(Input.EventType.TOUCH_MOVE, this.touchMove, this);}/*** 获取触摸事件在节点坐标系中的位置** @param e 触摸事件对象* @returns 触摸事件在节点坐标系中的位置*/getEventPosInNodePos(e: EventTouch) {// 获取触摸点的屏幕坐标let p = e.getLocation();// 将屏幕坐标转换为世界坐标let word = this.camera.screenToWorld(utils.getVec3(p));// 获取UITransform组件,将世界坐标转换为节点本地坐标let uiTransform = this.node.getComponent(UITransform);let localPos = uiTransform.convertToNodeSpaceAR(new Vec3(word.x, word.y));return localPos;}// 触摸开始事件处理函数touchStart(e: EventTouch) {this.startDrop = true;this.joystickParent.active = true;// 根据操纵杆类型设置初始触摸位置if (this.joystickType == JoystickType.FIXED) {// 固定类型的操纵杆,设置触摸位置为操纵杆背景的原始位置this.joystickTouchPos = this.joystickOriginalPos.clone();} else if (this.joystickType == JoystickType.FOLLOW) {// 跟随类型的操纵杆,将操纵杆和背景设置为触摸位置this.joystick.setPosition(utils.nodePosToNodePos(this.getEventPosInNodePos(e), this.node, this.joystick.parent));this.joystickBG.setPosition(this.joystick.position);this.joystickTouchPos = this.joystick.position.clone();}// 处理触摸移动this.touchMove(e);}/*** 触摸移动事件处理函数** @param e 触摸事件对象*/touchMove(e: EventTouch) {// 如果没有开始拖动,则不处理if (this.startDrop === false) {return;}// 获取操纵杆触摸位置的副本let joystickTouchPos = this.joystickTouchPos.clone();// 获取触摸事件在节点坐标系中的位置let dragPos = this.getEventPosInNodePos(e);// 获取操纵杆背景相对于节点的本地坐标let nodePos = utils.nodePosToNodePos(joystickTouchPos, this.joystickBG.parent, this.node);// 计算操纵杆的移动向量,并归一化let joystickVec = dragPos.clone().subtract(nodePos).normalize();// 计算触摸点与操纵杆触摸位置之间的距离let joystickDist = Vec3.distance(dragPos, nodePos);// 限制操纵杆在指定半径范围内移动if (joystickDist < this.joystickRadius) {// 如果距离小于半径,直接设置操纵杆的位置let pos = joystickTouchPos.add(joystickVec.multiplyScalar(joystickDist));this.joystick.setPosition(pos);} else {// 如果距离大于半径,设置操纵杆到半径位置let pos = joystickTouchPos.add(joystickVec.multiplyScalar(this.joystickRadius));this.joystick.setPosition(pos);}}/*** 触摸结束事件处理函数** @param e 触摸事件对象*/touchEnd(e: EventTouch) {// 标识结束拖动this.startDrop = false;// 根据操纵杆显示类型设置其显示状态if (this.joystickShowType === JoystickShowType.NOT_ALWAYS_DISPLAY) {this.joystickParent.active = false;} else {this.joystickParent.active = true;}// 将操纵杆移动向量重置为零this.joystickVec = Vec3.ZERO;// 将操纵杆和背景位置重置为原始位置this.joystick.setPosition(this.joystickOriginalPos);this.joystickBG.setPosition(this.joystickOriginalPos);}
}

大致效果如下:

固定

跟随

社交:

QQ群:859055710 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/20559.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视频汇聚平台EasyCVR对接GA/T 1400视图库结构化数据:人员/人脸、非/机动车、物品

在信息化浪潮席卷全球的背景下&#xff0c;公安信息化建设日益成为提升社会治理能力和维护社会稳定的关键手段。其中&#xff0c;GA/T 1400标准作为公安视频图像信息应用系统的核心规范&#xff0c;以其结构化数据处理与应用能力&#xff0c;为公安信息化建设注入了强大的动力。…

【图解IO与Netty系列】Reactor模型

Reactor模型 Reactor模型简介三类事件与三类角色Reactor模型整体流程 各种Reactor模型单Reactor单线程模型单Reactor多线程模型主从Reactor模型 Reactor模型简介 Reactor模型是服务器端用于处理高并发网络IO请求的编程模型&#xff0c;与传统的一请求一线程的同步式编程模型不…

翼龙面板是什么,如何进行搭建

翼龙面板是一个开源的&#xff0c;用于游戏服务器管理的程序&#xff0c;可以方便地在网页界面中创建Minecraft&#xff0c;起源引擎游戏和Teamspeak3 服务器。 它使用前后端程序&#xff0c;因此可以创建多后端节点&#xff0c;对游戏服务器和服务器节点进行统一管理。 对游戏…

Vue进阶之Vue无代码可视化项目(二)

Vue无代码可视化项目 项目初始化路由子路由错误示范正确示范App.vuerouter/index.tsAboutView.vueAboutAboutview.vuerouter/index.ts项目路由router/index.tsApp.vueActionsView.vueDataSourceView.vueLayoutView.vue路由样式App.vue进一步的App.vue项目初始化 路由 router i…

synchronized 锁的到底是什么?

通过8种情况演示锁运行案例&#xff0c;看看我们到底锁的是什么 1锁相关的8种案例演示code package com.bilibili.juc.lock;import java.util.concurrent.TimeUnit;/*** 题目&#xff1a;谈谈你对多线程锁的理解&#xff0c;8锁案例说明* 口诀&#xff1a;线程 操作 资源类* 8…

LeetCode875爱吃香蕉的阿珂

题目描述 珂珂喜欢吃香蕉。这里有 n 堆香蕉&#xff0c;第 i 堆中有 piles[i] 根香蕉。警卫已经离开了&#xff0c;将在 h 小时后回来。珂珂可以决定她吃香蕉的速度 k &#xff08;单位&#xff1a;根/小时&#xff09;。每个小时&#xff0c;她将会选择一堆香蕉&#xff0c;从…

IntelliJ IDEA / Android Studio 方法显示Git提交人

显示方法&#xff1a; 设置 > 编辑器 > 嵌入提示 > Code Vision > 代码作者&#xff08;勾选&#xff09; IntelliJ IDEA Android Studio

springboot编写日志环境搭建过程

AOP记录日志 AOP记录日志的主要优点包括&#xff1a; 1、低侵入性&#xff1a;AOP记录日志不需要修改原有的业务逻辑代码&#xff0c;只需要新增一个切面即可。 2、统一管理&#xff1a;通过AOP记录日志可以将各个模块中需要记录日志的部分进行统一管理&#xff0c;降低了代…

神经网络的工程基础(二)——随机梯度下降法|文末送书

相关说明 这篇文章的大部分内容参考自我的新书《解构大语言模型&#xff1a;从线性回归到通用人工智能》&#xff0c;欢迎有兴趣的读者多多支持。 本文涉及到的代码链接如下&#xff1a;regression2chatgpt/ch06_optimizer/stochastic_gradient_descent.ipynb 本文将讨论利用…

WinApp自动化测试之辅助工具介绍

前篇文章中&#xff0c;我们简单介绍了部分WinApp自动化测试脚本常规操作&#xff0c;今天我们来讲剩余的部分。 文件批量上传 文件批量上传和文件单个上传原理是相同的&#xff0c;单个上传直接传入文件路径即可&#xff0c;批量上传需要进入批量上传的文件所在目录&#xf…

uniapp创建支付密码实现(初始密码,第二次密码)

示例&#xff1a; 插件地址&#xff1a;自定义数字/身份证/密码输入框&#xff0c;键盘密码框可分离使 - DCloud 插件市场 1.下载插件并导入HBuilderX&#xff0c;找到文件夹&#xff0c;copy number-keyboard.vue一份为number-keyboard2.vue&#xff08;number-keyboard.vue是…

C++ STL map容器erase操作避坑

map容器的erase方法有三种重载形式&#xff1a; //1.删除迭代器所指向的元素 //返回值是指向下一个节点的迭代器 iterator erase(iterator it); //2.区间删除 iterator erase(iterator first, iterator last); //3.根据键值删除 //返回值为删除的元素个数 size_type erase(con…

民国漫画杂志《时代漫画》第37期.PDF

时代漫画37.PDF: https://url03.ctfile.com/f/1779803-1248636302-c017ee?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了&#xff0c;截止1937年6月战争来临被迫停刊共发行了39期。 ps: 资源来源网络!

C++基础编程100题-002 OpenJudge-1.1-04 输出保留3位小数的浮点数

更多资源请关注纽扣编程微信公众号 002 OpenJudge-1.1-04 输出保留3位小数的浮点数 http://noi.openjudge.cn/ch0101/04/ 描述 读入一个单精度浮点数&#xff0c;保留3位小数输出这个浮点数。 输入 只有一行&#xff0c;一个单精度浮点数。 输出 也只有一行&#xff0c;…

07.爬虫---使用session发送请求

07.使用session发送请求 1.目标网站2.代码实现 1.目标网站 我们以这个网站作为目标网站 http://www.360doc.com/ 注册用户 注册后从登录界面获取到这些信息 2.代码实现 import requestssession requests.Session() url http://www.360doc.com/ajax/login/login.ashx u…

深入剖析Java线程池的核心概念与源码解析:从Executors、Executor、execute逐一揭秘

文章目录 文章导图前言Executors、Executor、execute对比剖析Executors生成的线程池&#xff1f;线程池中的 execute 方法execute 方法的作用execute的工作原理拒绝策略 源码分析工作原理基本知识线程的状态线程池的状态线程池状态和线程状态总结线程池的状态信息和线程数量信息…

RedisSearch与Elasticsearch:技术对比与选择指南

码到三十五 &#xff1a; 个人主页 数据时代&#xff0c;全文搜索已经成为许多应用程序中不可或缺的一部分。RedisSearch和Elasticsearch是两个流行的搜索解决方案&#xff0c;它们各自具有独特的特点和优势。本文简单探讨一些RedisSearch和Elasticsearch之间的技术差异。 目录…

9款实用而不为人知的小众软件推荐!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/ 在电脑软件的浩瀚海洋中&#xff0c;除了那些广为人知的流行软件外&#xff0c;还有许多简单、干净、功能强大且注重实用功能的小众软件等待我们…

[NISACTF 2022]sign_crypto(LATEX)

题目&#xff1a; 我们看出这是LATEX编码&#xff0c;破解之后&#xff1a; 看出每个“\”之后的第一个字母连起来即使&#xff1a;nss....&#xff0c;在大写即可得到flag。

Sui Nami Bags对NFT使用案例进行创新

在四月的Sui Basecamp活动中&#xff0c;与会者体验了一系列Sui技术&#xff0c;这些技术以Nami Bags的形式呈现&#xff0c;这些数字礼包里满是来自Sui生态的NFT和优惠券。通过Enoki&#xff08;Mysten Labs的新客户参与平台&#xff09;提供支持&#xff0c;即使没有加密钱包…