Jason_lvx 2025-11-28 14:41 采纳率: 0%
浏览 4

Unity safearea外多指触控异常

在我的2D Unity手游项目里,我已经把MultiTouchEnabled设为false了,并且有用SafeArea来做异形屏的适配。
当在SafeArea范围内的时候,手机点击下去且不松开,然后把该手指移出safearea 范围后,可以用另一个手指来点击,并且会记录并反映点击事件。(应该来说怎么样都不应该记录第二个手指点击)
游戏是一款合成游戏,棋盘上的棋子是用Input的touch的,而UI用的UGUI用的是EventSystem的点击事件来触发的。只要手指拖动一个棋子到safe area之外,就可以继续用另一根手机拖动其他棋子或者点击UI。
在SafeArea的范围之内一切正常。
并且目前只在刘海屏和灵动道的iOS设备上有发现该问题,在安卓设备上(不管是不是异形屏)都没有该问题。

  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2025-11-28 14:42
    关注

    下午好🌅🌅🌅
    本答案参考通义千问

    这个问题在Unity中确实是一个比较复杂且容易被忽视的多点触控行为异常问题,尤其是在iOS设备上(尤其是刘海屏和灵动岛设备),由于系统对触控事件的处理机制与安卓不同,导致在SafeArea之外的触控事件仍然被Unity引擎捕获,从而引发非预期的输入行为


    一、问题分析

    你提到的问题现象是:

    • SafeArea范围内 正常;
    • 当手指移出 SafeArea范围后,仍能用另一个手指进行点击或拖动操作;
    • 这种行为在 安卓设备上不存在,但在 iOS设备上存在
    • 游戏使用的是 Input.touchesUGUI的EventSystem 来处理触摸事件。

    根本原因:

    Unity 的 Input.touches 并不自动考虑 SafeArea 的边界。即使你在屏幕外(比如刘海区域),只要触控点还在屏幕内,Unity 就会继续将其视为一个有效的触控点。

    而 iOS 系统在某些情况下(如灵动岛、刘海屏)允许触控事件超出 SafeArea 的边界,这会导致 Unity 捕获到这些“无效”的触控点,从而引发 多指触控异常


    二、解决方案(详细步骤)

    1. 自定义触控过滤逻辑

    你需要在每一帧中手动过滤掉那些不在 SafeArea 内的触控点。

    关键代码实现:

    using UnityEngine;
    using UnityEngine.UI;
    
    public class TouchFilter : MonoBehaviour
    {
        private Rect safeAreaRect;
    
        void Start()
        {
            // 获取 SafeArea 的矩形区域
            safeAreaRect = Screen.safeArea;
        }
    
        void Update()
        {
            for (int i = 0; i < Input.touchCount; i++)
            {
                Touch touch = Input.GetTouch(i);
    
                // 如果触控点不在 SafeArea 内,则忽略该触控
                if (!safeAreaRect.Contains(touch.position))
                {
                    // 重置该触控状态
                    if (touch.phase == TouchPhase.Began)
                    {
                        touch.phase = TouchPhase.Canceled;
                    }
                    else if (touch.phase == TouchPhase.Moved || touch.phase == TouchPhase.Stationary)
                    {
                        touch.phase = TouchPhase.Canceled;
                    }
                }
            }
        }
    }
    

    注意: 上述代码直接修改了 Input.touches 中的触控信息,不推荐直接操作原生的 Input.touches,因为 Unity 可能会忽略这种修改。

    更安全的方式:创建自定义的触控列表

    public class CustomTouchManager : MonoBehaviour
    {
        public static List<Touch> filteredTouches = new List<Touch>();
    
        void Update()
        {
            filteredTouches.Clear();
    
            Rect safeArea = Screen.safeArea;
    
            for (int i = 0; i < Input.touchCount; i++)
            {
                Touch touch = Input.GetTouch(i);
    
                if (safeArea.Contains(touch.position))
                {
                    filteredTouches.Add(touch);
                }
            }
        }
    }
    

    然后在你的游戏逻辑中使用 CustomTouchManager.filteredTouches 来替代 Input.touches


    2. 使用 UGUI 的 EventSystem 控制触控

    如果你的 UI 是通过 UGUI 的 EventSystem 处理的,你可以通过以下方式限制触控只在 SafeArea 内生效。

    自定义 EventSystem 的射线检测逻辑

    using UnityEngine;
    using UnityEngine.EventSystems;
    
    public class SafeAreaUIHandler : MonoBehaviour, IPointerClickHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
    {
        private RectTransform canvasRect;
        private Rect safeArea;
    
        void Start()
        {
            canvasRect = GetComponentInParent<Canvas>().GetComponent<RectTransform>();
            safeArea = Screen.safeArea;
        }
    
        public void OnPointerClick(PointerEventData eventData)
        {
            if (safeArea.Contains(eventData.pointerCurrentRaycast.screenPosition))
            {
                // 正常处理点击事件
                Debug.Log("Pointer click inside SafeArea");
            }
        }
    
        public void OnBeginDrag(PointerEventData eventData)
        {
            if (safeArea.Contains(eventData.pointerCurrentRaycast.screenPosition))
            {
                // 开始拖拽
                Debug.Log("Begin drag inside SafeArea");
            }
        }
    
        public void OnDrag(PointerEventData eventData)
        {
            if (safeArea.Contains(eventData.pointerCurrentRaycast.screenPosition))
            {
                // 拖拽中
                Debug.Log("Dragging inside SafeArea");
            }
        }
    
        public void OnEndDrag(PointerEventData eventData)
        {
            if (safeArea.Contains(eventData.pointerCurrentRaycast.screenPosition))
            {
                // 结束拖拽
                Debug.Log("End drag inside SafeArea");
            }
        }
    }
    

    注意: 需要将此脚本挂载到需要限制触控的 UI 元素上。


    3. 禁用多点触控(可选)

    虽然你已经设置了 MultiTouchEnabled = false,但为了确保,可以在 Player Settings 中确认:

    • Project Settings > Player > Other Settings > Multi Touch Enabled 设置为 False

    注意: 这个设置可能不会完全阻止所有触控事件,特别是在 iOS 上。


    三、总结

    | 问题 | 原因 | 解决方案 | |------|------|----------| | 触控事件在 SafeArea 外被触发 | iOS 系统允许触控点超出 SafeArea | 自定义触控过滤逻辑(推荐) | | UGUI 事件在 SafeArea 外被触发 | EventSystem 不受 SafeArea 限制 | 使用自定义 UI 事件处理逻辑 | | 多指触控异常 | Unity 默认未过滤 SafeArea 范围 | 使用 filteredTouches 替代 Input.touches |


    四、建议

    • 优先使用 filteredTouches 方式,避免直接修改 Unity 内部的 Input.touches
    • 针对 iOS 设备做特殊处理,例如:
      #if UNITY_IOS
          // iOS 特殊逻辑
      #endif
      
    • 测试时务必使用真实 iOS 设备(如 iPhone 14 Pro、15 Pro 等)来验证效果。

    如有需要,我可以提供完整的 Unity 项目结构示例或进一步优化触控逻辑。

    评论

报告相同问题?

问题事件

  • 创建了问题 11月28日