Unity跨平台新输入系统
新输入系统指的是区别于旧的轮询式的
Input
系统,基于事件订阅,仅在输入发生时触发逻辑的系统。
前语
Unity
中的输入,可以大致的分为两种:在UI
上的输入和来自外来设备的输入。其中UI
上的输入,一般指的是以UGUI
为框架,使用射线以及相关EventSystem
系统,BaseEventData
和相关类BaseInputModule
、PointerInputModule
,StandalonbeInputModule
,TouchInputModule
组成的部分;另外的就是指以鼠标、键盘、触控、主机控制器和VR等设备。
新旧对比
相比较于旧的输入系统,具有以下优点:
事件驱动性质
- 旧系统:依赖
Input.GetKey
轮询,效率低。 - 新系统:基于事件订阅,仅在输入发生时触发逻辑,减少CPU开销。
- 旧系统:依赖
多设备统一管理
- 旧系统:需要手动处理不同设备(如键鼠、触摸)的输入差异。
- 新系统:通过
InputService
集中管理,支持动态添加/移除设备。
复杂手势支持
- 旧系统:需手动实现缩放、旋转等手势。
- 新系统:集成
DigitalRubyShared
库,直接支持多指手势(如双指缩放、双击)。
解耦与扩展性
- 旧系统:业务逻辑与输入检测强耦合。
- 新系统:通过
InputMessage
传递事件,新增输入类型只需扩展消息类,无需修改现有代码。
跨平台兼容性
- 旧系统:需为不同平台编写条件分支(如
#if UNITY_ANDROID
)。 - 新系统:抽象层自动适配不同输入源(如触摸屏映射为鼠标事件)。
- 旧系统:需为不同平台编写条件分支(如
模块结构
服务层(InputService)
职责:全局单例管理所有输入设备,统一分发消息。
关键接口:
public void AddDevice<TDevice>() where TDevice : MonoBehaviour, IInputDevice; public void PushMessage(InputMessage msg); // 消息推送入口
设备抽象层(InputDevice)
职责:所有输入设备的基类,实现订阅机制与消息处理。
核心能力:
public abstract bool HandleMessage(InputMessage msg); // 消息处理 public void SubscribeOn<TMessage>(Action<TMessage> callback); // 事件订阅
设备实现层
- InputDevice_NewSystem:基于Unity新输入系统(InputSystem)的键盘/鼠标/触摸实现。
- InputDevice_Legacy:兼容传统InputManager的输入处理。
- InputDevice_Virtual:虚拟控件(如虚拟摇杆、按钮)的事件转发。
消息协议(InputMessage)
所有输入事件的基类,通过派生类定义具体行为:
public class KeyDown : InputMessage { ... } // 按键按下 public class TouchMove : InputMessage { ... } // 触摸移动 public class AxisChange : InputMessage { ... } // 摇杆输入
模块协作流程
以下以虚拟按钮点击事件为例,展示系统内部协作:
用户点击虚拟按钮
// VirtualButton.cs public void OnPointerDown(PointerEventData eventData) { InputService.Instance.PushMessage(new ButtonDown(btnName)); }
消息路由至设备
// InputService.cs public void PushMessage(InputMessage msg) { foreach (var device in deviceList) { if (device.HandleMessage(msg)) break; } }
设备处理订阅回调
// 业务代码订阅 InputService.Instance.SubscribeOn<ButtonDown>(msg => { if (msg.ButtonName == "Jump") Player.Jump(); });
跨平台
先思考一个问题,对于游戏中的
UI
,不管在PC端还是移动端,我们都没有预先对此进行适配,但是为什么我们在点击或者触摸的时候,游戏好像已经知道我们要干什么了?这是因为Unity
已经在底层上给我们做了适配,UGUI
的源码里面有TouchInputModule
这个就是触摸的部分逻辑,这里推荐可以看看《Unity3D 高级编程 主程手记》这本书,里面有对UGUI
较为详细的解析。
然后是思考输入设备的跨平台是如何做到的,其实
Unity
一开始对主机层面的跨平台做的不好,需要一个个特殊处理写过去,直到从2019版本开始推新的输入系统,才开始有改善。
图上的输入就能兼容触摸Touch
和MouseMove
的操作,然后只要在代码中发送同样的消息事件就能实现一样的操作,这一部分属于Unity
本身的新输入系统自带的配置文件。
点击生成对应的C#文件后,就能在框架中使用,具体使用可以看框架代码。本文不详细解释关于Unity
中自身输入框架的内容。
- GitHub 源码库:https://github.com/Unity-Technologies/InputSystem
代码示例
InputSystem.onDeviceChange += OnDeviceChange;
private void OnDeviceChange(InputDevice inputDevice, InputDeviceChange inputDeviceChange)
{
if (inputDeviceChange == InputDeviceChange.Added)
{
SetupDevice(inputDevice);
Debug.Log("设备接入:" + inputDevice.device.name);
}
else if (inputDeviceChange == InputDeviceChange.Removed)
{
RemoveDevice(inputDevice);
Debug.Log("设备移除:" + inputDevice.device.name);
}
}
private void SetupDevice(InputDevice inputDevice)
{
// 根据设备类型进行相应的设置
if (inputDevice is Touchscreen touchscreen)
{
inputMap.Player.Touch.started += OnTouchStarted;
inputMap.Player.Touch.performed += OnTouchPerformed;
inputMap.Player.Touch.canceled += OnTouchCanceled;
}
else if (inputDevice is Keyboard keyboard)
{
inputMap.Player.Move.performed += OnMovePerformed;
inputMap.Player.Move.canceled += OnMoveCancle;
inputMap.Player.Jump.performed += OnKeyboardPerformed;
inputMap.Player.Jump.canceled += OnKeyboardCancle;
inputMap.Player.MouseButton.started += OnMouseButtonStarted;
inputMap.Player.MouseButton.performed += OnMouseButtonPerformed;
inputMap.Player.MouseButton.canceled += OnMouseButtonCanceled;
}
else if (inputDevice is Mouse mouse)
{
inputMap.Player.MouseMove.performed += OnMouseMove;
inputMap.Player.MouseScroll.performed += OnMouseScroll;
}
}