You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 05:48:42