【Unity输入系统】自定义与双击不冲突的单击Interaction

发布于:2025-08-08 ⋅ 阅读:(16) ⋅ 点赞:(0)

一、背景

unity自带的两种默认interaction——单击(tap)、多击(multip tap)在实际使用时可以发现,假设有多个action,action A绑定了按键A单击,action B绑定了按键A双击,则双击按键A时会导致action A也触发。为了规避这种情况,自定义了一个与双击不冲突的单击Interaction

二、思路及代码

  • 在检测到点击后延迟一小段时间(clickDelay)再判断(确认是否有第二次点击)是否进入perform状态

  • 如果在 clickDelay 期间没有再次点击,并且按住的时间不超过 holdThreshold,就判定为 单击

  • 如果超过 holdThreshold 就是 长按(取消单击逻辑)。

  • 如果在 clickDelay 时间内再次按下,则判断为 双击,取消单击逻辑。

using System.Diagnostics;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Utilities;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SingleClickOnlyIfNotDouble : IInputInteraction
{
    public float clickDelay = 0.3f;      // 点击后的等待时间,用于检测是否为双击(单位:秒)
    public float holdThreshold = 0.3f;   // 判定为长按的最小时间(单位:秒)
    private double m_TapStartTime;       // 记录第一次按下的时间点
    bool hasClicked = false;            // 标记是否已经处理过一次点击

#if UNITY_EDITOR
    [UnityEditor.InitializeOnLoadMethod]
#else
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
#endif
    public static void Initialize()
    {
        InputSystem.RegisterInteraction<SingleClickOnlyIfNotDouble>();
    }
    public void Process(ref InputInteractionContext context)
    {
        switch (context.phase)
        {
            case InputActionPhase.Waiting:
                if (context.control is ButtonControl button && button.wasPressedThisFrame)
                {
                    m_TapStartTime = context.time;
                    context.Started();
                    context.SetTimeout(clickDelay);

                }
                break;

            case InputActionPhase.Started:
                if (context.control is ButtonControl button2)
                {

                    if (button2.wasReleasedThisFrame && !hasClicked)
                    {
                        // UnityEngine.Debug.Log("按住时间" + (context.time - m_TapStartTime) + hasClicked);
                        if (context.time - m_TapStartTime <= holdThreshold)//松手且未超时才判定为单击
                        {
                            // UnityEngine.Debug.Log("按住时间" + (context.time - m_TapStartTime) + hasClicked);
                            hasClicked = true;
                            // UnityEngine.Debug.Log("单击检测");
                        }
                        else
                        {
                            context.Canceled();
                            // UnityEngine.Debug.Log("Hold detected → canceled");
                        }
                    }
                    if (hasClicked)
                    {
                        if (context.time - m_TapStartTime >= clickDelay)
                        {
                            context.Performed();
                            // UnityEngine.Debug.Log("Single click performed");
                        }
                        else if (button2.wasPressedThisFrame)
                        {
                            context.Canceled();
                            // UnityEngine.Debug.Log("Double click detected → canceled");
                        }
                    }
                }
                break;


        }
    }

    public void Reset()
    {
        hasClicked = false;
    }
}

需要注意的是,

  •  m_TapStartTime不能被context.startTime替代,context.startTime和context.time并不会在每次触发该interaction时重置,startTime是一个固定值,time是一个累计值,这会导致算出的时间差越来越长
  • clickDelay和holdThreshold的时间设置无关,因为一个是针对第一次按住检测,一个是针对第一次按下后检测。
  • holdThreshold设置应短于Hold的HoldTime,否则会导致其单击操作无法与Hold区分
  • clickDelay设置应长于MultiTap的Max Tap Spacing,否则会导致其单击操作无法与双击区分

三、参考

【Unity】Input Systemでシングルタップとマルチタップを判別する | ねこじゃらシティ

【Unity】Input SystemのInteractionの仕組みと使い方 | ねこじゃらシティ

https://docs.unity3d.com/Packages/com.unity.inputsystem@1.7/manual/Interactions.html#default-interaction


网站公告

今日签到

点亮在社区的每一天
去签到