Unity DOTS纯ECS实现虚拟摇杆Joystick控制角色移动

news2024/9/22 11:16:23

上篇已经实现了ECS框架下的IBeginDragHandlerIDragHandlerIEndDragHandler这几个拖动事件,使得可以任意给ECS框架下的UI(2D entity)响应拖动事件。本篇分享下在前篇实现的功能的基础上再实现一个常用的摇杆控制角色移动的功能。

需要注意的一点,目前Unity最新的Entities包是无法纯ECS写代码的,还有很多GameObject和组件无法转成Entity和IComponentData,所以要体验纯ECS写代码,得下载那个很久没更新过的ProjectTiny包,它的Entities版本还停留在0.17阶段。

代码不多,直接上代码:

using Unity.Entities;
using Unity.Mathematics;
using Unity.Tiny.UI;
using wangtal.EventSystem;
using TinyUI;

namespace wangtal.Joystick {
    public struct Joystick : IComponentData {
        public readonly struct Settings : IComponentData {
            // 可拖动的范围,可以大于背景的圆形
            public readonly float Radius;

            public Settings(float radius) {
                Radius = radius;
            }
        }

        public Entity Entity {get; private set;}

        public Entity Knob {get; private set;}

        public Entity Highlight {get; private set;}

        // 拖动距离
        public float Distance;

        public float2 Direction;

        public Joystick(Entity joystickEntity, Entity knob, Entity highlight) {
            Entity = joystickEntity;
            Knob = knob;
            Highlight = highlight;
            Distance = 0;
            Direction = float2.zero;
        }
    }

    public abstract class JoystickSystem : SystemBase, IBeginDragHandler, IDragHandler, IEndDragHandler
    {
    	// 上上篇实现的ECS下的UI分辨率适配系统-用来实现分辨率变化后,UI大小跟着变
        private CanvasScalerSystem canvasScalerSystem;

        protected override void OnCreate()
        {
            base.OnCreate();
            RequireSingletonForUpdate<Joystick>();
            RequireSingletonForUpdate<Joystick.Settings>();
            canvasScalerSystem = World.GetExistingSystem<CanvasScalerSystem>();
        }

        protected override void OnUpdate()
        {
            var joystick = GetSingleton<Joystick>();
            var settings = GetSingleton<Joystick.Settings>();

            var entity = joystick.Entity;
            var stickRectTransform = GetComponent<RectTransform>(entity);
            var highlight = joystick.Highlight;
            var higtlightRectTransform = GetComponent<RectTransform>(highlight);
            var knob = joystick.Knob;
            var knobRectTransform = GetComponent<RectTransform>(knob);

            if (this.BeginDrag(entity)) {
                higtlightRectTransform.Hidden = false;
                OnBeginDrag(joystick);
            }
            if (this.EndDrag(entity)) {
                higtlightRectTransform.Hidden = true;
                knobRectTransform.AnchoredPosition = float2.zero;

                joystick.Direction = float2.zero;
                joystick.Distance = 0;

                OnEndDrag(joystick);
            }

            if (this.Drag(entity, out DragEventData dragEventData)) {
                // Debug.Log("drag position:" + dragEventData.Position.ToString() + " pressPosition:" + dragEventData.PressPosition.ToString());
                var pressPosition = dragEventData.PressPosition;
                var position = dragEventData.Position;
                // 减去的是joystick的中心位置,所以要根据它的pivot来计算,如果是(0,0)的话,就要加上它宽高的一半
                var dstPos = position - (stickRectTransform.AnchoredPosition + stickRectTransform.SizeDelta * (-(stickRectTransform.Pivot - 0.5f)));
                // Debug.Log("距离:" + math.length(dstPos));
                var direction = math.normalize(dstPos);
                float distance = 0;
                float radius = canvasScalerSystem.WithScale(settings.Radius); // 这个半径要根据分辨率来,不然分辨率小了,可拖动距离就会变大

                if (math.length(dstPos) <= radius) {
                    knobRectTransform.AnchoredPosition = dstPos;
                    distance = math.length(dstPos);
                } else {
                    var maxPos = direction * radius;
                    knobRectTransform.AnchoredPosition = maxPos;
                    distance = math.length(maxPos);
                }
                joystick.Direction = direction;
                joystick.Distance = distance;

                OnDrag(joystick);
            }

            SetComponent<RectTransform>(highlight, higtlightRectTransform);
            SetComponent<RectTransform>(knob, knobRectTransform);
            SetSingleton<Joystick>(joystick);
        }

        protected abstract void OnBeginDrag(in Joystick joystick);
        protected abstract void OnDrag(in Joystick joystick);
        protected abstract void OnEndDrag(in Joystick joystick);
    }
}

主要功能就是上面那些,代码不多,还有一个Authoring脚本,用来挂在场景中的遥感GameObject上,并赋值好所需要的数据。

using UnityEngine;
using Unity.Entities;
// using Joystick;

namespace Authoring {
    [DisallowMultipleComponent]
    public class JoystickAuthoring : MonoBehaviour, IConvertGameObjectToEntity
    {
        public float radius;

        public GameObject Joy;

        public GameObject Knob;

        public GameObject Highlight;

        public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
        {
            var joy = conversionSystem.GetPrimaryEntity(Joy);
            var knob = conversionSystem.GetPrimaryEntity(Knob);
            var highlight = conversionSystem.GetPrimaryEntity(Highlight);
            dstManager.AddComponentData<Joystick.Joystick>(entity, new Joystick.Joystick(joy, knob, highlight));
            dstManager.AddComponentData<Joystick.Joystick.Settings>(entity, new Joystick.Joystick.Settings(radius));
        }
    }
}

这是Unity编辑器里的节点设置。场景会在打包后,转成纯ECS的Entity和ComponentData的,在编辑器里运行无法看到效果,必须打包后才能正确执行DOTSruntime代码。
在这里插入图片描述
具体用法的示例代码:

using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using wangtal.Joystick;
using wangtal.EventSystem;

namespace Tiny3D {
    [UpdateInGroup(typeof(InputSystemGroup))]
    [UpdateAfter(typeof(EventSystem))]
    public class HeroJoystickSystem : JoystickSystem
    {
        protected override void OnBeginDrag(in Joystick.Joystick joystick)
        {
            // 这里可以设置遥感到点击的位置,这样就不会一点击,角色就移动了,而是在拖动后才移动
        }

        protected override void OnDrag(in Joystick.Joystick joystick)
        {
            var direction = joystick.Direction;
            // Unity.Tiny.Debug.Log("onDrag direction:" + direction);

            Entities.WithAll<Player>().ForEach((ref Moveable moveable) =>
            {
                if (math.all(direction == float2.zero)) {
                    return;
                }
                moveable.moveDirection = new float3(direction.x, 0, direction.y);
            }).Run();
        }

        protected override void OnEndDrag(in Joystick.Joystick joystick)
        {
            // Unity.Tiny.Debug.Log("endDrag direction:" + joystick.Direction);
            Entities.WithAll<Player>().ForEach((ref Moveable moveable) =>
            {
                moveable.moveDirection = float3.zero;
            }).Run();
        }
    }
}

打包WASM后到浏览器上的运行效果图:
在这里插入图片描述

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

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

相关文章

[数据分析与可视化] 基于matplotlib和plottable库绘制精美表格

plottable是一个Python库&#xff0c;用于在matplotlib中绘制精美定制的图形表格。plottable的官方仓库地址为&#xff1a;plottable。本文主要参考其官方文档&#xff0c;plottable的官方文档地址为&#xff1a;plottable-doc。plottable安装命令如下&#xff1a; pip install…

猿人web学刷题18

1.第十八题 jsvmp - 猿人学 问题: 1.第一页请求正常能返回数据 2.第二页开始之后出现{"error": "Unexpected token/Validation failed"} 分析&#xff1a; 1.第二页开始&#xff0c;有带加密参数&#xff0c;直接重发请求无果&#xff0c;应该带了时间戳…

尚硅谷Docker实战教程-笔记11【高级篇,Docker网络】

尚硅谷大数据技术-教程-学习路线-笔记汇总表【课程资料下载】视频地址&#xff1a;尚硅谷Docker实战教程&#xff08;docker教程天花板&#xff09;_哔哩哔哩_bilibili 尚硅谷Docker实战教程-笔记01【基础篇&#xff0c;Docker理念简介、官网介绍、平台入门图解、平台架构图解】…

盛格塾暑期公益课程《学活LINUX》

学习LINUX有很多种方法&#xff0c;本系列课程以动手试验为主&#xff0c;取一个活的LINUX系统&#xff08;GDK8&#xff09;作为目标&#xff0c;使用内核调试器&#xff08;挥码枪&#xff09;将其中断到调试器&#xff0c;在调试器的帮助下&#xff0c;观察调用过程、执行现…

【1++的Linux】之基础开发工具

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的Linux】 文章目录 一&#xff0c;Linux软件包管理管理器二&#xff0c;Linux编辑器--vim2.1 什么是vim2.2 vim的基本操作 三&#xff0c;gcc的使用四&#xff0c;gdb的使用五&#xff0c;项目…

课时7:Trustzone基础知识

快速链接: . 👉👉👉 个人博客笔记导读目录(全部) 👈👈👈 付费专栏-付费课程 【购买须知】:Secureboot从入门到精通-[目录] 👈👈👈目录 Trustzone安全扩展双系统架构Trustone架构多方位支持的安全

探索Gradio库中的Textbox模块及其强大功能

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

作用域、垃圾回收机制、闭包、构造函数

作用域 作用域规定了变量能够被访问的 ‘范围’&#xff0c;离开了这个范围变量便不能被访问 分为&#xff1a; 局部作用域 函数作用域块级作用域 let/const 全局作用域 作用域链 嵌套关系的作用域串联起来形成了作用域链 作用:作用域链本质上是底层的变量的查找机制 函…

简写MKL库windows安装以及python如何调用dll库

MKL安装: 最新MKL库下载地址 Donwload: Accelerate Fast Math with Intel oneAPI Math Kernel Library 64位以及32位我直接都安装了 之后配置各种包含目录以及环境变量&#xff1a;网上有很多配置vs的配置教程&#xff0c;这里就不贴了。 &#xff08;ps: 2023 在vs2019上&a…

nodejs高级编程-核心模块

一、path 1 获取路径中的基础名称 const path require(path)// console.log(__filename) // /Users/liuchongyang/Desktop/分享/网页读取本地文件/node.js// 1 获取路径中的基础名称 /*** 01 返回的就是接收路径当中的最后一部分 * 02 第二个参数表示扩展名&#xff0c;如果…

手把手教-单片机stm32基于w25q128使用文件系统

一、开发测试环境 ①野火stm32f407开发板 ②rtthread操作系统 W25Q128的电路原理图&#xff1a; 二、开发步骤 ①使能spi驱动。 ②使能spi bus/device 驱动&#xff0c;选择sfud驱动。 ③开启dfs功能&#xff0c;选择elm文件系统。 ④保存&#xff0c;重新生成工程。 ⑤下载到…

VueCli 脚手架使用

VueCli 脚手架 到目前为止&#xff0c;已经会了Vue基本使用&#xff08;去创建vue实例&#xff0c;创建之后再去挂载&#xff0c;挂载之后就去使用各种功能&#xff0c;挂载之后就可以使用其各种功能&#xff0c;data methods compute 以及各个生命周期&#xff0c;常用的属性以…

779. 最长公共字符串后缀

题面&#xff1a; 给出若干个字符串&#xff0c;输出这些字符串的最长公共后缀。 输入格式 由若干组输入组成。 每组输入的第一行是一个整数 NN。 NN 为 00 时表示输入结束&#xff0c;否则后面会继续有 NN 行输入&#xff0c;每行是一个字符串&#xff08;字符串内不含空白符&…

Redis深入 —— 持久化和事务

前言 最近的学习中&#xff0c;荔枝深入了解了Redis的持久化、Redis事务相关的知识点并整理相应的学习笔记&#xff0c;在这篇文章中荔枝也主要梳理了相应的笔记和基本知识&#xff0c;小伙伴们如果需要的话可以看看哈。 文章目录 前言 一、Redis持久化 1.1 RDB 1.1.1 Redi…

掌握驱动之道:L298N模块多方式驱动电机的优劣分析

L298N模块是一种常用的直流电机驱动模块&#xff0c;它可以通过控制输入端口来实现对电机的速度和方向的控制。L298N模块有3个输入端口&#xff1a;IN1、IN2和EN。 方法一&#xff1a;使用高级定时器输出通道和互补输出通道控制电机 将模块的IN1和IN2分别连接到STM32高级定时器…

Python GUI编程利器:Tkinker中的事件处理(11)

​ 小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 和猫妹学Python&#xff0c;一起趣味学编程。 今日目标 学习下事件处理的相关知识点&#xff1a; 事件处理四要素 事件序列 事件绑定 今天要实现如下效果&#xff1…

Java在Excel中进行数据分析

摘要&#xff1a;本文由葡萄城技术团队于CSDN原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前一段时间淘宝出了一个“淘宝人生”的模块&#xff0c;可以看从注册淘宝账号至今的消…

k8s实战3-使用Helm在AKS上发布应用

AKS(Azure Kubenetes Service)是微软云azure上的K8s服务。 主要分为三步 1 连接到AKS 2 用kubectl发布应用 3 用Helm发布应用 1 登录 az login 2 连接dp-npr-dsm-aks&#xff08;Dsm项目的AKS&#xff09; az account set --subscription {{subID}} az aks get-credent…

指针的进阶(一)

目录 1. 字符指针 方法一 方法二 字符指针面试题 2. 指针数组 3. 数组指针 3.1 数组指针的定义 3.2 &数组名VS数组名 3.3 数组指针的使用 4. 数组传参和指针传参 4.1 一维数组传参 4.2 二维数组传参 4.3 一级指针传参 4.4 二级指针传参 5. 函数指针 代码一 代…

Windows用户怎么取消访问共享文件夹的密码

许多Windows系统用户在访问共享文件夹的时候却提示需要输入密码才可访问。这一步给很多人造成了困扰&#xff0c;其实我们可以取消访问共享文件夹密码。请看下面的两个方法。 方法一&#xff1a; 搜索 网络和共享中心。点击 更改高级共享设置。在最底下密码保护的共享那项&…