Unity新Input System触摸screenPosition偶现返回Infinity异常
Unity EnhancedTouch 偶现screenPosition为无穷大值解决方案
问题场景
项目基于Unity新Input System的EnhancedTouch模块实现单指滑动交互,初始实现逻辑为按下手指后注册move/up事件,滑动达到阈值后触发移动事件,抬起手指后复位事件监听。
初始实现代码:
void Start() { EnhancedTouchSupport.Enable(); UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerDown += onFingerDown; } public void onMoveAction(InputAction.CallbackContext ctx) { // if (ctx.started) // EmmitMoveEvent((int)ctx.ReadValue<Vector2>().x); } void onFingerDown(Finger controllFinger) { void onFingerMove(Finger finger) { if (finger.index != controllFinger.index) return; int delta = (int)((finger.screenPosition.x - controllFinger.screenPosition.x) / 100); if (delta != 0) { UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerMove -= onFingerMove; EmmitMoveEvent(delta); } } void onFingerUp(Finger finger) { if (finger.index != controllFinger.index) return; UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerDown += onFingerDown; UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerMove -= onFingerMove; UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerUp -= onFingerUp; } UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerDown -= onFingerDown; UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerMove += onFingerMove; UnityEngine.InputSystem.EnhancedTouch.Touch.onFingerUp += onFingerUp; } void EmmitMoveEvent(int direction) { EventHandler<MoveEventArgs> moveEvent = MoveEvent; if (moveEvent != null) moveEvent(this, new MoveEventArgs(direction)); }
异常表现
运行过程中偶发逻辑异常,排查发现同一次触摸流程中,finger.screenPosition取值偶尔返回(Infinity, -Infinity):上一帧调用还能获取到正常的屏幕坐标,下一帧同个finger对象的位置就变为无穷大值,无稳定复现路径。
根因分析
- 事件注册注销逻辑存在时序漏洞:初始实现在
onFingerDown触发时立刻注销全局down监听,同时在回调内部嵌套定义move、up闭包函数。当手指按下后快速滑动/抬起时,onFingerUp可能先于move事件的有效帧触发,此时Input System已经回收了对应触摸的结构体数据,闭包持有的controllFinger引用指向的内存已经被重置为脏数据,访问screenPosition就会拿到无穷大/NaN这类无效值。 - 缺少数据有效性校验:代码逻辑默认所有从Touch API拿到的坐标都是合法值,没有对无效浮点值做拦截。
- 低版本Input System本身存在已知bug:1.3.0及更早版本的EnhancedTouch模块,在120Hz及以上高刷新率安卓设备上,确实存在触摸帧数据更新不及时偶发返回无效坐标的问题。
修复方案
- 重构事件注册逻辑:不要在回调内嵌套定义事件处理函数,也不要反复注销/注册全局触摸事件,在初始化阶段一次性注册所有监听,通过手指ID标记当前交互的手指即可。
- 增加坐标有效性校验:所有读取
screenPosition的位置,先判断值是否为无穷大/NaN,无效值直接跳过本次逻辑。 - 升级Input System包版本:将包版本升级到1.4.4及以上稳定版,修复高刷设备下的触摸数据bug。
- 生命周期统一管理:在脚本销毁时主动注销所有触摸事件监听,避免内存泄漏。
修复后完整代码:
private int _activeFingerId = -1; private Vector2 _touchStartPos; void Start() { EnhancedTouchSupport.Enable(); // 初始化时一次性注册所有全局触摸事件 Touch.onFingerDown += OnFingerDown; Touch.onFingerMove += OnFingerMove; Touch.onFingerUp += OnFingerUp; } private void OnFingerDown(Finger finger) { // 仅响应第一根按下的手指,忽略多指操作干扰 if (_activeFingerId != -1) return; _activeFingerId = finger.index; _touchStartPos = finger.screenPosition; } private void OnFingerMove(Finger finger) { if (finger.index != _activeFingerId) return; Vector2 currentPos = finger.screenPosition; // 拦截所有无效坐标值 if (float.IsInfinity(currentPos.x) || float.IsInfinity(currentPos.y) || float.IsNaN(currentPos.x) || float.IsNaN(currentPos.y)) return; int moveDelta = (int)((currentPos.x - _touchStartPos.x) / 100); if (moveDelta == 0) return; EmmitMoveEvent(moveDelta); // 触发一次滑动后重置激活手指标记,避免单次触摸重复触发事件 _activeFingerId = -1; } private void OnFingerUp(Finger finger) { if (finger.index != _activeFingerId) return; _activeFingerId = -1; } void EmmitMoveEvent(int direction) { MoveEvent?.Invoke(this, new MoveEventArgs(direction)); } private void OnDestroy() { // 销毁时注销所有事件 Touch.onFingerDown -= OnFingerDown; Touch.onFingerMove -= OnFingerMove; Touch.onFingerUp -= OnFingerUp; }
注意:如果是编辑器下测试出现该问题,大概率是鼠标模拟触摸时切窗口、改分辨率导致的单次数据异常,加完有效性校验后不会影响实际真机运行逻辑。
内容的提问来源于stack exchange,提问作者Denis
相关产品推荐
相关产品推荐

