这篇文章主要展示下在Unity的ProjectTiny环境中用纯ECS代码实现UGUI里的EventSystem,包括常用的IPointerDownHandler、IPointerUpHandler、IBeginDragHandler、IDragHandler、IEndDragHandler、IPointerEnterHandler、IPointerExitHandler、IPointerClickHandler等功能。
虽然ProjectTiny里有自带的Button等UI,但是它的点击事件并不是通用的,无法随便给想要的2D entity加上点击事件。不过ProjectTiny提供了最基本的InputSystem,可以获得输入数据。根据这些数据,再处理下,就可以实现UGUI里的那几个Handler了,然后就可以让任意的2D entity(带RectTransform的entity)响应按下抬起点击滑动等事件,还有游戏中常用的鼠标滑动到物品icon上就弹出一个tip,滑出icon后tip消失(也就是IPointerEnter/IPointerExit的功能)。
需要注意一点,由于IComponentData中不能存放NativeList这种Container,所以用的是IBufferElementData,跟数组用法差不多,可以动态增长,但是增删比较麻烦。也可以直接在System里用一个NativeList来存放数据,但是这样不够ECS,而且数据不多,遍历几遍数组对性能影响不会太大。
看代码。代码只是展示一下实现方法,可以继续优化。
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Tiny.UI;
using Unity.Tiny;
using Unity.Tiny.Input;
using Unity.Tiny.Rendering;
using Unity.Collections;
namespace wangtal.EventSystem {
public interface IPointerDownHandler {
}
public interface IPointerUpHandler {
}
public interface IPointerClickHandler {
}
public interface IPointerEnterHandler {
}
public interface IPointerExitHandler {
}
public interface IBeginDragHandler {
}
public interface IDragHandler {
}
public interface IEndDragHandler {
}
public interface ICancelHandler {
}
internal struct PointerEventData : IBufferElementData {
internal enum TriggerState : byte {
None = 0 << 0,
PointerDown = 1 << 0,
PointerUp = 1 << 1,
PointerClick = 1 << 2,
BeginDrag = 1 << 3,
Drag = 1 << 4,
EndDrag = 1 << 5,
PointerEnter = 1 << 6,
PointerExit = 1 << 7
}
public TriggerState State;
public int PointerId;
public Entity Target;
// 当前位置
public float2 Position;
// 滑动间隔
public float2 Delta;
// 按下时的位置
public float2 PressPosition;
}
public class InputSystemGroup : ComponentSystemGroup
{
const int MaxTriggerEvent = 10;
protected override void OnCreate()
{
base.OnCreate();
var entity = EntityManager.CreateEntity();
var buffer = EntityManager.AddBuffer<PointerEventData>(entity);
for (int i = 0; i < MaxTriggerEvent; i++) {
PointerEventData data = new PointerEventData {
State = PointerEventData.TriggerState.None, PointerId = -1, Target = Entity.Null, Position = float2.zero, Delta = float2.zero, PressPosition = float2.zero
};
buffer.Add(data);
}
}
}
[UpdateInGroup(typeof(InputSystemGroup))]
unsafe public class EventSystem : SystemBase
{
private ScreenToWorld screenToWorld;
private InputSystem inputSystem;
protected override void OnCreate()
{
base.OnCreate();
screenToWorld = World.GetExistingSystem<ScreenToWorld>();
inputSystem = World.GetExistingSystem<InputSystem>();
}
protected override void OnUpdate()
{
var input = inputSystem;
Entities.ForEach((Entity entity, ref DynamicBuffer<PointerEventData> eventDatas) => {
PointerEventData* p = (PointerEventData*)eventDatas.GetUnsafePtr();
for (int i = 0; i < eventDatas.Length; i++, p++) {
ref var eventData = ref UnsafeUtility.AsRef<PointerEventData>(p);//eventDatas[i];
if (eventData.State == PointerEventData.TriggerState.PointerDown ||
eventData.State == PointerEventData.TriggerState.PointerUp ||
eventData.State == PointerEventData.TriggerState.BeginDrag ||
eventData.State == PointerEventData.TriggerState