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 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBootWeb 篇-深入了解 Spring 异常处理、事务管理和配置文件参数配置化、yml 配置文件

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 配置文件 1.1 yml 配置文件 1.2 参数配置化 1.2.1 使用 Value 注解注入单个配置参数 1.2.2 使用 ConfigurationProperties 注解将一组相关配置参数注入到一个类中…

大型企业用什么文件加密软件,五款适合企业的文件加密软件

大型企业在选择文件加密软件时&#xff0c;通常会倾向于那些能够提供全面数据保护、具有高度可定制性、易于管理且能适应复杂组织结构的解决方案。以下是一些适合大型企业使用的文件加密软件&#xff1a; 1.域智盾软件&#xff1a; 作为一款企业级文件加密软件&#xff0c;支持…

Linux系统Mysql 8.0版本的安装

一、MySQL介绍 1.1 MySQL简介 1.2 MySQL特点 二、卸载mariadb数据库 2.1 卸载mariadb数据库 2.2 卸载mysql数据库 三、配置yum仓库 3.1 下载rpm文件 3.2 配置yum仓库 3.3 启动mysql服务 3.4 检查mysql服务状态 四、mysql的初始配置 4.1 获取登录密码 4.2 本地登录…

二叉树链式结构的前序、中序、后序、层序遍历

文章目录 一、二叉树创建二、前序遍历概念以及解释代码 三、中序遍历概念及解释代码 四、后序遍历概念及解释代码 五、层序遍历概念及解释代码 一、二叉树创建 &mesp; 实现二叉树的遍历&#xff0c;我们要先手搓出一个二叉树&#xff0c;在次基础上实现二叉树的前序、中序…

清洁力强的洗地机前十名排行榜:2024十大洗地机热销款式好用不踩雷

如今&#xff0c;洗地机行业竞争激烈&#xff0c;各品牌紧紧抓住用户对智能化和深度清洁的需求&#xff0c;深入研究创新。经过几轮行业内部的激烈竞争后&#xff0c;许多厂商在宣传中各说各的&#xff0c;对洗地机的重要参数描述不一&#xff0c;给消费者的选择带来了不少困惑…

深度学习-02-创建变量的函数

深度学习-02-创建变量的函数 本文是《深度学习入门2-自製框架》 的学习笔记&#xff0c;记录自己学习心得&#xff0c;以及对重点知识的理解。如果内容对你有帮助&#xff0c;请支持正版&#xff0c;去购买正版书籍&#xff0c;支持正版书籍不仅是尊重作者的辛勤劳动&#xff0…

手机离线翻译哪个好?断网翻译也能超丝滑

有时在异国他乡&#xff0c;面对语言不通的窘境&#xff0c;即便是简单的对话也变得异常困难&#xff0c;真是挑战满满&#xff01; 然而&#xff0c;能离线翻译的软件让语言障碍不再是问题&#xff0c;不必依赖网络也能轻松进行翻译啦~ 只需下载所需的语言包&#xff0c;选择…

牛客ONT45 距离是K的二叉树节点【中等 宽度优先遍历 Java/Go/PHP/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/e280b9b5aabd42c9b36831e522485622 思路 图&#xff0c;队列 构件图&#xff0c;直接从target出发&#xff0c;扩展到第k层就是答案Java代码 import java.util.*;/** public class TreeNode {* int val 0;* …

Anthropic公司CEO谈AI发展:Cluade安全超过商业利益

Anthropic公司今年3月发布的超越GPT-4模型Claude3 opus&#xff0c;成功吸引了大量GPT-4用户“叛变”。 作为OpenAI的头号劲敌&#xff0c;Claude3发布方Anthropic公司的联合创始人兼CEO&#xff0c;达里奥阿莫迪&#xff08;DarioAmodei&#xff09;承诺&#xff1a;在能够制…

激光焊接机作为一种高效、精密的焊接设备

激光焊接机是一种用于材料加工时激光焊接的机器&#xff0c;以下是对其的详细介绍&#xff1a; 1. 定义与别称&#xff1a; 激光焊接机&#xff0c;又常称为激光焊机、镭射焊机&#xff0c;是材料加工激光焊接时用的机器。 2. 工作原理&#xff1a; 激光焊接是利用高能量…

【贪心算法题目练习】

1. 分发饼干 这道题目和我们之前讲到的田忌赛马的问题很相似&#xff0c;只不过这这里不需要劣等马去抵消掉优等马&#xff0c;直接上贪心策略&#xff1a; 先将两个数组排序。针对胃口较小的孩子&#xff0c;从小到大挑选饼干: i. 如果当前饼干能满足&#xff0c;直接喂(最小…

【CPP】栈简介及简化模拟实现

CPP栈和队列简单模拟实现 目录 1. 栈的简介2. 栈简化模拟实现3. 栈练习题 1. 栈的简介 栈 是一种 特殊的线性表&#xff0c;具有数据 先进后出 特点。 具体参考&#xff1a;【数据结构】栈 CPP库参考文档&#xff1a;stl_stack 注意&#xff1a; 1.stack本身 不支持迭代器操…

C++之构造函数总结

1、构造函数定义 在C中&#xff0c;构造函数是一种特殊的成员函数&#xff0c;它在创建一个类的对象时自动被调用。构造函数的主要目的是初始化类对象的成员变量&#xff0c;为对象分配资源&#xff0c;以及执行任何其他必要的初始化任务。 构造函数具有以下特点&#xff1a; …

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

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

python-双胞胎字符串

[问题描述]&#xff1a;给定两个字符串s和t&#xff0c;每次可以任意交换s的奇数位和偶数位的字符&#xff0c;即奇数位的字符可以与任意其它奇数位的字符交换&#xff0c;偶数位的字符同样也可以与任意偶数位的字符的字符交换&#xff0c;问能否在有限的次数的交换下使s变为t?…

0基础学习Elasticsearch-Quick start

文章目录 1 背景2 前言3 快速部署ES4 快速部署Kibana5 发送请求给ES5.1 打开Kibana控制台5.2 通过REST API发送请求5.3 通过curl发送请求5.4 添加数据5.4.1 添加单个document5.4.2 添加多个document 5.5 搜索数据5.5.1 搜索所有documents5.5.2 match查询 6 总结 1 背景 因电商项…

【算法】模拟算法——外观数组(medium)

题解&#xff1a;模拟算法——外观数组(medium) 目录 1.题目2.题解3.参考代码4.总结 1.题目 题目链接&#xff1a;LINK 2.题解 首先应该理解题意&#xff1a; 就是开始给你一个字符串&#xff0c;然后你对其进行描述。 描述规则是&#xff1a;连续的数字为一组&#xff0c;…

大学生社团活动平台系统基于springboot+vue的社团管理系统java项目sprignboot项目

文章目录 大学生社团活动平台一、项目介绍二、部分功能截图三、部分代码展示四、底部获取项目源码&#xff08;9.9&#xffe5;带走&#xff09; 大学生社团活动平台 一、项目介绍 基于springbootvue的前后端分离大学生社团活动平台 系统角色 : 学生、社长、管理员 1、学生…

自学 Java 怎么入门?

关于自学 Java 如何入门这一重要课题&#xff0c;在此为大家进行详细阐述。 在此之前&#xff0c;如果大家有兴趣的话&#xff0c;可以看看我自己精心整理的嵌入式入门资料&#xff0c;这些资料将全部免费送给大家。其中包含了编程教学内容、详细的视频讲解、实用的数据库资料…

Java项目:92 基于SSM的办公管理系统

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 基于SSM的办公管理系统 1、项目介绍 基于SSM的办公管理系统主要是对于办公用品的申领进行管理&#xff0c;系统分为三种角色&#xff0c;超级管理员、企业 职…