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