ProjectTiny包里是有UI模块的,当然功能肯定没GameObject的UI强大,常用的Text、Button、Image都有,还没仔细用过,先不用它。
点击了哪个按钮,然后需要执行对应的什么事件,这么点逻辑用ECS来写是很方便的,甚至可以比MonoBehavior写的简单。大致流程是,在运行时动态创建Mesh作为一个按钮,然后设置好Mesh的UV来显示纹理指定部分的字母作为按钮的label(这两个要点可以看前面几篇文章来进行实现),最后写个System来监听点击了哪个按钮,其他System就可以调用该System的接口来判断并执行点击事件了。
为了简单起见,就不动态创建Mesh并设置对应UV了,直接在Unity编辑器里创建4个Quad作为W、A、S、D按钮,也就是上下左右。Mesh也不用设置纹理,直接用空白的,按钮label并不影响点击事件。上代码:
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Tiny.Input;
using Unity.Tiny.Rendering;
using Unity.Tiny;
namespace ECSButton {
[GenerateAuthoringComponent]
public struct Button : IComponentData
{
public enum Type : byte {
None,
W, A, S, D
}
public enum State : byte {
None,
Down,
Up
}
public Type type;
public State state;
}
[UpdateInGroup(typeof(InitializationSystemGroup))]
public class ButtonSystem : SystemBase
{
private InputSystem Input;
private ScreenToWorld s2w;
protected override void OnCreate() {
base.OnCreate();
Input = World.GetExistingSystem<InputSystem>();
s2w = World.GetExistingSystem<ScreenToWorld>();
}
protected override void OnUpdate()
{
Dependency = Entities.WithAll<Button>().ForEach((Entity entity, ref Button button) => {
if (button.state == Button.State.Down) {
button.state = Button.State.None;
}
}).Schedule(Dependency);
if (Input.IsTouchSupported()) { // 触屏点击了按钮
if (Input.TouchCount() == 1) {
var touch = Input.GetTouch(0);
if (touch.phase == TouchState.Began) {
OnClickDown(touch.x, touch.y);
}
}
} else { // 鼠标点击了按钮
if (Input.GetMouseButtonDown(0)) {
var clickPos = Input.GetInputPosition();
OnClickDown(clickPos);
}
}
}
void OnClickDown(float x, float y) {
var clickPosWS = s2w.ScreenSpaceToWorldSpacePos(new float2(x, y), 75);
Dependency = Entities.WithAll<Button>().ForEach((Entity entity, ref Button button, in Translation translation) => {
if (math.distance(new float3(clickPosWS.xy, 0), translation.Value) < 4) { // 这个数字4是写死的,可以根据按钮的Mesh大小计算出来
button.state = Button.State.Down;
}
}).ScheduleParallel(Dependency);
}
void OnClickDown(float2 pos) {
OnClickDown(pos.x, pos.y);
}
public bool IsButtonDown(Button.Type type) {
bool result = false;
Entities.WithAll<Button>().ForEach((Entity entity, in Button button) => {
if (button.state == Button.State.Down) {
if (type == button.type) {
result = true;
}
}
}).Run();
return result;
}
}
// 测试。其它System也是同样的用法
public class TestButtonDownSystem : SystemBase
{
ButtonSystem buttonSystem;
protected override void OnCreate() {
base.OnCreate();
buttonSystem = World.GetExistingSystem<ButtonSystem>();
}
protected override void OnUpdate()
{
if (buttonSystem.IsButtonDown(Button.Type.W)) {
Debug.Log("Button W down");
}
else if (buttonSystem.IsButtonDown(Button.Type.A)) {
Debug.Log("Button A down");
}
else if (buttonSystem.IsButtonDown(Button.Type.S)) {
Debug.Log("Button S down");
}
else if (buttonSystem.IsButtonDown(Button.Type.D)) {
Debug.Log("Button D down");
}
}
}
}
Unity编辑器里的设置如下:
这个按钮很简单,就只有点击事件,不能设置按钮的锚点(没有分辨率适配),也不能设置按钮高亮跟按下状态,也没有点击后的震动反馈。按钮点击状态还是可以实现的,这里先不实现,目前只需要用到点击事件。
再贴个运行截图(因为要看打印日志,所以打包的是Web,安卓要看Log太麻烦了):