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

通过前面两篇的介绍,基本上完成了虚拟摇杆对物体移动旋转的控制。这篇主要是介绍如何采用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));
        }
        
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值