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,一经查实,立即删除!

相关文章

Go常见语法题目解析

1、写出下面代码输出内容。 package mainimport ("fmt" )func main() {defer_call() }func defer_call() {defer func() { fmt.Println("打印前") }()defer func() { fmt.Println("打印中") }()defer func() { fmt.Println("打印后")…

快速幂

a^b % q 给定整数 a b q&#xff0c; 求 a 的 b 次方 mod q 根据题目数字取值范围&#xff0c;不能暴力处理。 会有两个问题&#xff1a; 1、计算 a 的次方会超出范围 2、不能循环 b 次计算 a 的乘积&#xff0c;会超时 处理问题1&#xff1a; 每计算一次 a 的乘积&#xf…

视频汇聚平台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…

修改hostname导致RabbitMQ数据丢失

背景介绍 公司的很多关键服务都使用了RabbitMQ来作为消息队列服务, 可以说是非常地关键的一个环节, 最近由于业务量的上升, 导致RabbitMQ的CPU持续走高, 所以抽空研究了一下RabbitMQ的扩容, 利用我们自己运维平台使用的一个单节点的RabbitMQ来作为测试吧.看到这个单节点的Rabbi…

第十七节 huggingface的trainner的断点续训的Demo(resume)

文章目录 前言一、参数决定权重保存1、model.safetensors保存2、scaler.pt保存3、optimizer.pt与scheduler.pt保存4、self.state状态保存(trainer_state.json)5、rng_state.pth保存6、权重相关保存位置(huggingface)二、Resume的Demo1、Demo构建2、实现Resume方法三、Resume训…

005 CentOS 7.9 RabbitMQ安装及配置

https://github.com/rabbitmq/rabbitmq-server/releases https://www.rabbitmq.com/docs/download https://packagecloud.io/rabbitmq/rabbitmq-server https://www.erlang-solutions.com/downloads/ https://www.erlang.org/ 文章目录 卸载erlerl版本安装与下载版本不匹配正…

AI技术的深度探索:重塑未来的智能引擎

随着科技的迅猛进步&#xff0c;人工智能&#xff08;AI&#xff09;技术已经逐渐渗透到我们生活的每一个角落&#xff0c;从简单的智能助手到复杂的决策支持系统&#xff0c;AI技术以其独特的方式和前所未有的速度改变着我们的世界。本文将对AI技术进行深入探讨&#xff0c;从…

开源贡献 | 基于长安链去中心化数字身份合约标准协议(CMDID-1)的DID

DID为每个实体&#xff08;人、组织、物品等&#xff09;提供了一个唯一的全球身份标识符&#xff0c;让用户可以控制和管理的自己的数字身份&#xff0c;并在使用时以最小化的方式出示&#xff0c;将数据所有权归还用户的同时以区块链技术保证了身份的不可篡改性&#xff0c;以…

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…

Redis到底是AP还是CP?

这个问题差评&#xff0c;没问清楚。当然&#xff0c;网上一搜&#xff0c;各种各样的狗屁答案都有&#xff0c;有时候是AP的&#xff0c;有时候是CP的&#xff0c;薛定谔的Redis。 好的&#xff0c;那应该怎么问呢&#xff1f;Q1.Redis Cluster集群是AP还是CP&#xff1f; A…

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…