应用Entitas、Ecs、JobSystem实现Unity虚拟摇杆功能

xiaoxiao2025-04-16  13

通过前面两篇的介绍,基本上完成了虚拟摇杆对物体移动旋转的控制。这篇主要是介绍如何采用ECS、JobSystem及Entitas框架实现虚拟摇杆对物体移动和旋转的控制。Entitas框架就已经实现了数据和行为的分离,那为什么还要结合Unity提供的Ecs框架呢,这里主要是要使用JoySystem实现多线程功能,提高性能。所以本文采用Entitas框架结合Unity的Ecs和JobSystem混用的方式。阅读本文之前,大家需要对Entitas、Ecs、JobSystem有个大致的掌握。本位不介绍这些框架的原理,只是大致介绍如何实现。具体原理请参阅其它文章。也可留言,一起沟通交流。

前期准备

Ecs环境搭建

Entitas框架接入

Entitas

首先定义数据结构,由于目标移动和旋转是通过目标向量完成。所以定义个Vector2的属性即可,InputType,预留方便扩展,本文中该属性未使用。由于是检测输入,所以这里我们定义的标签为[Input] [Input] public class InputData : IComponent { public Vector2 targetPos; public InputType inputType; } public enum InputType { Press, Release } System系统定义类InputSystem继承ReactiveSystem,这样如果Entity的InputData数据发生变化就会监测到。在InputSystem初始化的方法中,初始化Ecs框架中EntityManager和GameObjectEntity,并为Ecs的GameObjectEntity赋予PostitionData。每帧execute去更新数据。 using System.Collections.Generic; using Ecs; using Entitas; using Unity.Entities; using UnityEngine; namespace System { public class InputSystem : ReactiveSystem<InputEntity>,IInitializeSystem ,ICleanupSystem { private InputContext inputContext; private EntityManager mEntityManager; private Unity.Entities.Entity mEntity; public InputSystem(Contexts context) : base(context.input) { inputContext = context.input; } protected override ICollector<InputEntity> GetTrigger(IContext<InputEntity> context) { return context.CreateCollector(InputMatcher.InputData); } protected override bool Filter(InputEntity entity) { return entity.hasInputData; } protected override void Execute(List<InputEntity> entities) { foreach (var e in entities) { var posComponentData = new PositionComponent() { targetDir = e.inputData.targetPos }; mEntityManager.SetComponentData(mEntity, posComponentData); } } public void Cleanup() { var entities = inputContext.GetEntities(); foreach (var e in entities) { e.Destroy(); } } public void Initialize() { mEntityManager = World.Active.GetOrCreateManager<EntityManager>(); var player = GameObject.FindWithTag("Player"); if (player) { var gameObjectComponent = player.GetComponent<GameObjectEntity>(); if (!gameObjectComponent) { gameObjectComponent = player.AddComponent<GameObjectEntity>(); } mEntity = gameObjectComponent.Entity; var posComponentData = new PositionComponent() { targetDir = Vector2.zero }; mEntityManager.AddComponentData(mEntity, posComponentData); } } } } Ecs实体定义 public struct PositionComponent : IComponentData { public Vector2 targetDir; } JobSystem 这里要使用到Transform组件,故这里需继承IJobParallelForTransform 。在Execute方法中根据数据去设置旋转和位移。 using Unity.Entities; using Unity.Jobs; using Unity.Mathematics; using UnityEngine; using UnityEngine.Jobs; namespace Ecs { public struct PositionUpdateJob : IJobParallelForTransform { public ComponentDataArray<PositionComponent> positionComponentArray; public float deltaTime; public void Execute(int index, TransformAccess transform) { var targetPos = positionComponentArray[index].targetDir; var moveVector = new Vector3(targetPos.x,0f,targetPos.y); transform.rotation = Quaternion.LookRotation(moveVector); var moveSpeed = 0.8f; transform.position = Vector3.Lerp(transform.position,moveVector * moveSpeed,math.smoothstep(0f,1f,deltaTime)); } } public class PositionJobSystem : JobComponentSystem { struct PositionGroup { public TransformAccessArray transformAccessArray; public ComponentDataArray<PositionComponent> positionComponentArray; } [Inject] private PositionGroup positionGroup; protected override JobHandle OnUpdate(JobHandle inputDeps) { var positionUpdateJob = new PositionUpdateJob() { positionComponentArray = positionGroup.positionComponentArray, deltaTime = Time.deltaTime }; return positionUpdateJob.Schedule(positionGroup.transformAccessArray, inputDeps); } } } 在虚拟摇杆中,定义输入。 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using System; using Entity; [RequireComponent(typeof(RectTransform))] public class JoyStickHandle : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { public event Action OnBeginDragEvent = delegate { }; public event Action<Vector2> OnDragEvent = delegate(Vector2 vector2) { }; public event Action OnEndDragEvent = delegate { }; public RectTransform rtJoyStick; private Transform tfHandle; private float radius = 0f; private float limit = 0f; private Vector2 targetPos; private InputContext inputContext; public Vector2 TargetPos { get { return targetPos; } private set { } } // Use this for initialization void Start() { tfHandle = this.GetComponent<RectTransform>(); radius = (rtJoyStick.rect.xMax - rtJoyStick.rect.xMin) / 2; limit = radius * radius; inputContext = Contexts.sharedInstance.input; } public void OnBeginDrag(PointerEventData eventData) { if (OnBeginDragEvent != null) { OnBeginDragEvent(); } } public void OnDrag(PointerEventData eventData) { if (RectTransformUtility.ScreenPointToLocalPointInRectangle(rtJoyStick, eventData.position, eventData.pressEventCamera, out targetPos)) { DealDragEvent(); } } private void DealDragEvent() { if (targetPos.sqrMagnitude > limit) { targetPos = targetPos.normalized * radius; } tfHandle.localPosition = targetPos; if (OnDragEvent != null) { OnDragEvent(targetPos); } inputContext.CreateEntity().AddInputData(this.targetPos, InputType.Press); } public void OnEndDrag(PointerEventData eventData) { tfHandle.localPosition = Vector3.zero; targetPos = Vector2.zero; if (OnEndDragEvent != null) { OnEndDragEvent(); } inputContext.CreateEntity().AddInputData(Vector2.zero, InputType.Release); } } 创建System using System; using UnityEngine; using Entitas; public class GameMain : MonoBehaviour { private Systems systems; private void Start() { var contexts = Contexts.sharedInstance; systems = new Feature("RootSystem"); systems.Add(new InputFeature(contexts)); systems.Initialize(); } private void Update() { systems.Execute(); systems.Cleanup(); } private void OnDestroy() { systems.TearDown(); } } namespace System { public class InputFeature : Feature { public InputFeature(Contexts contexts) : base("InputSystem") { Add(new InputSystem(contexts)); } } }

资源下载

资源的代码跟文章代码会有出路,由于资源是先上传的,但写文章的过程中又临时作出了调整。 https://download.csdn.net/download/eie08027/10751431

转载请注明原文地址: https://www.6miu.com/read-5028373.html

最新回复(0)