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
相关文章:

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

【数据分享】中国电力年鉴(2004-2022)
大家好!今天我要向大家介绍一份重要的中国电力统计数据资源——《中国电力年鉴》。这份年鉴涵盖了从2004年到2022年中国电力统计全面数据,并提供限时免费下载。(无需分享朋友圈即可获取) 数据介绍 自1993年首次出版以来…...
两个数组的交集Ⅱ-力扣
想到的解法是使用两个map来进行记录,mp1用来统计num1中每个元素出现的次数。当nums2的元素能够在mp1中查找到时,将这个元素添加到mp2,按照这个规则统计得到nums2和nums1重复的元素,mp2中的value记录了nums2中这个元素出现的次数最…...

【TCP协议中104解析】wireshark抓取流量包工具,群殴协议解析基础
Tcp ,104 ,wireshark工具进行解析 IEC104 是用于监控和诊断工业控制网络的一种标准,而 Wireshark则是一款常用的网络协议分析工具,可以用干解析TEC104 报文。本文将介绍如何使用 Wireshark解析 IEC104报文,以及解析过 程中的注意事项。 一、安…...

[个人笔记] 记录docker-compose使用和Harbor的部署过程
容器技术 第三章 记录docker-compose使用和Harbor的部署过程 容器技术记录docker-compose使用和Harbor的部署过程Harborhttps方式部署:测试环境部署使用自签名SSL证书https方式部署:正式环境部署使用企业颁发的SSL证书给Docker守护进程添加Harbor的SSL证…...

详细介绍运算符重载函数,清晰明了
祝各位六一快乐~ 前言 1.为什么要进行运算符重载? C中预定义的运算符的操作对象只能是基本数据类型。但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作。这时就必须在C中重新定义这些运算符ÿ…...
国内外知名的低代码开发平台下载地址
以下是国内外几款低代码开发平台的列表,包含了下载地址、适应操作系统、是否可以独立部署、优点、缺点以及是否包含流程引擎的信息。 平台名称 下载地址 适应操作系统 是否可以独立部署 优点 缺点 是否包含流程引擎 国内平台 阿里云宜搭 阿里云官网 跨平台…...

【Pr学习】01新建项目起步
【Pr学习】01新建项目起步 1、新建项目2.序列设置2.1新建序列2.2序列参数讲解2.3自定义设置 3.PR窗口认识3.1 项目窗口3.2 源窗口2.4 保存面板 4.剪辑导入4.1 素材导入4.2 视图切换4.3 时间轴4.4轨道工具4.5 节目窗口素材导入 5.基础操作5.1 取消视频音频链接5.2 单独渲染&…...
【Redis延迟队列】redis中的阻塞队列和延迟队列
阻塞队列(RBlockingQueue) 作用和特点: 实时性:阻塞队列用于实时处理消息。生产者将消息放入队列,消费者可以立即从队列中取出并处理消息。阻塞特性:如果队列为空,消费者在尝试获取消息时会被…...
el-tree常用操作
一、定义 <el-treeclass"myTreeClass":data"dirTreeData":props"dirTreeProps":filter-node-method"filterDirTree":expand-on-click-node"false"node-key"id"node-click"dirTreeNodeClick":allow-…...
SQL 语言:存储过程和触发器
文章目录 基本概述创建触发器更改和删除触发器总结 基本概述 存储过程,类似于高阶语言的函数或者方法,包含SQL语句序列,是可复用的语句,保存在数据库中,在服务器中执行。特点是复用,提高了效率,…...
Ubuntu Linux 24.04 使用certbot生成ssl证书
设置域名 1. 将需要生成SSL证书的域名解析到IP地址 idealand.xyz <> 64.176.82.190 检查防火墙的设置 1. 首先查看防火墙的状态: # ufw status 2. 如果防火墙开启了,要开放80和443端口用于certbot验证 # ufw allow 80 # ufw allow 443 生…...

Vivado 比特流编译时间获取以及FPGA电压温度获取(实用)
Vivado 比特流编译时间获取以及FPGA电压温度获取 语言 :Verilg HDL 、VHDL EDA工具:ISE、Vivado Vivado 比特流编译时间获取以及FPGA电压温度获取一、引言二、 获取FPGA 当前程序的编译时间verilog中直接调用下面源语2. FPGA电压温度获取(1&a…...

Window下VS2019编译WebRTC通关版
这段时间需要实现这样一个功能,使用WebRTC实现语音通话功能,第一步要做的事情就是编译WebRTC源码,也是很多码友会遇到的问题。 经过我很多天的踩坑终于踩出来一条通往胜利的大路,下面就为大家详细介绍,编译步骤以及踩…...

【云原生 | 60】Docker中通过docker-compose部署kafka集群
🍁博主简介: 🏅云计算领域优质创作者 🏅2022年CSDN新星计划python赛道第一名 🏅2022年CSDN原力计划优质作者 🏅阿里云ACE认证高级工程师 🏅阿里云开发者社区专…...

allure测试报告用例数和 pytest执行用例数不相同问题
我出现的奇怪问题: pytest执行了9条用例,但是测试报告确只显示3条用例 我将其中的一个代码删除后,发现allure测试报告又正常了 我觉得很奇怪这个代码只是删除了二维数组的第一列,我检查了半天都找不到问题,只有降低版本…...
Ubuntu 离线安装 gcc、g++、make 等依赖包
前言 项目现场的服务器无法连接互联网,需要提前获取 gcc、g、make 等依赖包。 一、如何获取依赖包 需要准备一台可以连接互联网的电脑(如:个人电脑上的虚拟机安装一个与服务器一样的系统),用于下载依赖包。之后把通过…...

Vxe UI vxe-upload 上传组件,显示进度条的方法
vxe-upload 上传组件 查看官网 https://vxeui.com 显示进度条很简单,需要后台支持进度就可以了,后台实现逻辑具体可以百度,这里只介绍前端逻辑。 上传附件 相关参数说明,具体可以看文档: multiple 是否允许多选 li…...
探索API接口:技术深度解析与应用实践
在当今的软件开发和数据交换领域,API(应用程序编程接口)已经成为了一个不可或缺的工具。它允许不同的软件应用程序或组件之间进行交互和通信,从而实现了数据的共享和功能的扩展。本文将深入探讨API接口的技术原理、设计原则以及在…...
ARM-V9 RME(Realm Management Extension)系统架构之系统安全能力的系统隔离属性
安全之安全(security)博客目录导读 目录 一、系统隔离属性 1、系统配置完整性 1.1、时间隔离 2、关键错误的报告 一、系统隔离属性 1、系统配置完整性 MSD必须确保任何可能危及其安全保证的系统寄存器的正确性和完整性。例如,MSD必须确认内存控制器配置是一致…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...

听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...