【问题标题】:How to repeat function while button is held down (New unity input system)按住按钮时如何重复功能(新的统一输入系统)
【发布时间】:2020-01-21 08:59:21
【问题描述】:

在按住按钮的同时尝试连续重复功能函数OnAttack()

基本上我正在寻找与Update() { GetKeyDown() {//code }} 等效的方法,但使用输入系统。

编辑:使用操纵杆,无法分辨正在按下哪个按钮。

【问题讨论】:

  • 你可以判断一个键是否按下,然后你可以使用一个正常的统一思想设置一个值 += 重复之间的延迟,如果 >= maxwait 然后将值设置为 0 并做事

标签: c# unity3d


【解决方案1】:

好的,我通过在交互中使用“按下”并给出触发行为“按下并释放”来解决它,然后做了

bool held = false;
Update()
{
    if(held)
    {
        //animation
    }
    else if(!held)
    {
        //idle animation
     }
}
OnAttack() {
    held = !held;
}

这样,如果我按下按住的按钮会变为 true,因此它会在每一帧重复动画,放手会使“按住”不真实并执行空闲动画

【讨论】:

    【解决方案2】:

    基本上,您分配给按钮的功能将在每次按下按钮时触发两次,一次是按下(执行),一次是释放(取消)。您可以在函数的开头传递此上下文,只需确保您使用的是该脚本顶部的库。 现在您可以打开和关闭 bool 来说明按钮是否被按下,然后在更新期间根据 bool 的状态执行操作

    using static UnityEngine.InputSystem.InputAction;
    
    bool held = false;
    Update()
    {
        if(held)
        {
            //Do hold action like shooting or whatever
        }
        else if(!held)
        {
            //do alternatice action. Not Else if required if no alternative action
         }
    }
    //switch the status of held based on whether the button is being pressed or released. OnAttack is called every time the button is pressed and every time it is released, the if statements are what determine which of those two is currently happening.
    OnAttack(CallbackContext ctx) {
        if (ctx.performed)
                held= true;
        if (ctx.canceled)
                held= false;
    } 
    

    【讨论】:

    【解决方案3】:

    这是对我为点击移动 arpg 机制创建的解决方案的解释。

    using System.Threading.Tasks;
    using UnityEngine;
    
    [SerializeField] private InputAction pointerClickAction;
    
    private bool pointerHeld;
    
    void Start()
    {
        
        pointerClickAction.canceled += ClickMouseMove;
        pointerClickAction.started += PointerHoldBegin;
        pointerClickAction.performed += ClickMouseMove;
        pointerClickAction.canceled += PointerHoldEnd;
        
    }
    
    private void OnEnable()
    {
        pointerClickAction.Enable();
        pointerPositionAction.Enable();
    }
    
    private void OnDisable()
    {
        pointerClickAction.Disable();
        pointerPositionAction.Disable();
    }
    
    public async void ClickMouseMove(InputAction.CallbackContext context)
    {
        while (pointerHeld)
        {
            DoSomething();
    
            await Task.Delay(500);
        }
    
    }
    
    public void PointerHoldBegin(InputAction.CallbackContext context)
    {
        pointerHeld = true;
    }
    
    public void PointerHoldEnd(InputAction.CallbackContext context)
    {
        pointerHeld = false;
    }
    
    public void DoSomething()
    {
        //Your Code
    }
    

    在 Task.Delay() 中,您可以以毫秒为单位插入自己的轮询速率,使用 Task.Yield() 似乎比 Update 更快,所以我不建议您这样做,您应该至少轮询与您的延迟相同的延迟物理/固定更新,如果您不需要每个循环的大量重复,则具有更高的延迟可以提高性能。我将我的设置为 500,因为我不需要我的角色经常绘制它的导航。对于 TC,您可以将延迟设置为合理的值,例如攻击的动画长度,或者每秒可以执行多少次攻击的延迟率。

    【讨论】:

      【解决方案4】:

      如果您希望您的项目可扩展,您可能希望在 Update/FixedUpdate/LateUpdate 函数中尽可能避免断言(即 if 函数),因为它们会不断执行。我建议你阅读这篇关于协程的文章https://gamedevbeginner.com/coroutines-in-unity-when-and-how-to-use-them/

      您可以构建协程作为本地 update() 函数,仅在需要时执行。这将引导您更好地组织代码,并且在某些情况下可能会提高性能。

      例如,在您的情况下,您可以使用类似的东西。

      bool held = false;
      
      Update()
      {
          /* Whatever you want but the least assertion possible */
      }
      
      IEnumerator RenderHeldAnimation()
      {
          while (held)
          {
              // held animation 
              yield return new WaitForFixedUpdate(); /* Will block until next fixed frame right after FixedUpdate() function */
              // yield return null /* Will block until next next frame right after Update() function */
          }
      }
      
      IEnumerator RenderIdleAnimation()
      {
          while (!held)
          {
              // idle animation 
              yield return new WaitForFixedUpdate(); /* Will block until next fixed frame right after FixedUpdate() function */
              // yield return null /* Will block until next next frame right after Update() function */
          }
      }
      
      OnAttack() {
          held = !held;
          if (held) {
              StartCoroutine(RenderHeldAnimation());
          } else {
              StartCoroutine(RenderIdleAnimation());
          }
      }
      

      【讨论】:

        【解决方案5】:

        正如另一个答案中提到的,context.canceled 在使用 Press And Release 交互时不会被调用。作为用于文档目的的后续行动,因为这是 Google 的最高结果,要正确使用 bool held 而不进行可能导致漂移的盲切换 (held = !held),您可以访问 context.control.IsPressed(),如下所示:

        void OnAttack(CallbackContext context)
        {
            held = context.control.IsPressed();
        }
        

        【讨论】:

          【解决方案6】:

          您可以为此目的使用计时器,并结合事件 KeyUp 和 KeyDown。 请查看以下链接。这与您的问题非常相似。 Link

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-05-17
            • 1970-01-01
            • 1970-01-01
            • 2022-01-16
            • 1970-01-01
            • 2018-11-05
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多