Unity 2D 输入操作未释放

Unity 2D Input Action not releasing

提问人:Sewiwey 提问时间:9/3/2023 最后编辑:derHugoSewiwey 更新时间:9/4/2023 访问量:42

问:

我正在尝试使用 Unity 的新输入系统为平台游戏添加可变跳跃高度。由于我正在尝试集成有限状态机框架,因此我正在使用 Send Messages 行为读取输入。若要添加可变跳转高度,请在按下跳转操作时以空闲状态调用以下函数:

public void Jump(InputAction jumpAct)
    {
        float jumpCount = 10f;
        if (jumpAct.WasPressedThisFrame())
        {
            rb.velocity = new Vector2(rb.velocity.x, jumpPower);
        }
        while (jumpCount > 0f)
        {
            if (jumpAct.WasReleasedThisFrame() && rb.velocity.y > 0f)
            {
                Debug.Log("Yay");
                rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f);
            } else
            {
                Debug.Log(jumpAct.ReadValueAsObject());
            }
            jumpCount -= Time.deltaTime;
        }
    }

似乎即使我释放按钮,InputValue 仍注册为 On(第二个 if 语句总是失败,调试器为 jumpAct 值打印值 1)。尽管查看了文档和不同的论坛,但不知道为什么会发生这种情况。一些帮助将不胜感激。

额外说明:我知道一个事实,这可以通过使用调用 Unity Events 行为中的回调上下文来修复。但是,理想情况下,有一个使用“发送消息”行为的解决方案,否则我需要重写我编码的 FSM 框架的很大一部分。

试过 WasReleasedThisFrame(), !IsPressed() 和 !WasPressedThisFrame()。尝试过在字符组件上的 InputAction 中添加和删除触发器。尝试直接使用 ReadValueAsObject() 读取 InputValue,而不是检查上述方法。结果保持不变。有一个跳跃,但无论何时松开按钮,高度都是均匀的。

额外的更新。我找到了一种方法来读取操作的回调上下文阶段。在整个过程中,此阶段被读取为“已执行”,即使释放按钮也是如此。

C# Unity-Game-Engine 输入 2D

评论

0赞 BugFinder 9/3/2023
我承认我没有尝试过这样做,我已经使用了这个动作。IsPressed,以及已执行和取消的事件,但不是这样的。鉴于 while 循环,我不确定帧部分如何与它一起工作。看起来只要在 10 秒内松开按钮,跳跃速度就会减半。
0赞 derHugo 9/4/2023
@BugFinder整个循环都在单个帧内执行;)while

答:

0赞 derHugo 9/4/2023 #1

你的整个循环将在一帧内执行,你的释放条件永远无法满足,除非你真的如此快速地粉碎和释放按钮,以至于它在同一帧内被按下和释放。while

假设你无论如何都在每一帧调用这个方法,例如 from - 否则你只会使用事件驱动的方法 - 你一次只需要处理一个帧,例如Update

private float jumpCount = 10f;

public void Jump(InputAction jumpAct)
{
    if (jumpAct.WasPressedThisFrame())
    {
        jumpCount = 10f;
        rb.velocity = new Vector2(rb.velocity.x, jumpPower);
    }

    // NOTE: "if" instead of "while" -> only one single execution at a time
    if(jumpCount > 0f)
    {
        jumpCount -= Time.deltaTime;

        if (jumpAct.WasReleasedThisFrame() && rb.velocity.y > 0f)
        {
            Debug.Log("Yay");
            rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f);
        } 
        else
        {
            Debug.Log(jumpAct.ReadValueAsObject());
        }
    }
}

无论如何,我可能仍然会使用事件驱动的方法,并做一些类似的事情,例如

// in your state enter method
YourActionMap.jump.performed += StartJump;
YourActionMap.jump.canceled += StopJump;


// accordingly in your state exit method
YourActionMap.jump.performed -= StartJump;
YourActionMap.jump.canceled -= StopJump;

然后

private float jumpStartTime;

private void StartJump(InputAction.CallbackContext context)
{
    rb.velocity = new Vector2(rb.velocity.x, jumpPower);
    jumpStartTime = Time.time;
}

private void StopJump(InputAction.CallbackContext context)
{
    if(Time.time - jumpStartTime <= 10f)
    {
        rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f);
    }
}

评论

0赞 Sewiwey 9/4/2023
遗憾的是,该方法不会在 Update() 方法中重复调用。当在空闲状态下识别出跳转按下时,调用一次,然后退出该状态并立即进入跳转状态。即使情况并非如此,每次调用该方法时,jumpCount 也会不断重置。while 循环是尝试强制方法存在于多个帧上;有没有更好的方法来强制这种行为?
0赞 Sewiwey 9/4/2023
哦,哎呀。我没有注意到事件驱动的方法。我给这个实现一个旋转,我遇到了同样的问题。该操作被注册为“已执行”,但从未“取消”
0赞 Sewiwey 9/12/2023
我已经设法解决了这个问题。基本上,在 InputAction 可以注册为已取消之前,跳转脚本就被退出了,因为空闲状态转换到该帧的跳转状态。我所要做的就是将方法拆分为已执行方法和已取消方法,然后将逻辑移动到跳转状态,并将已执行部分保留在状态 Enter() 方法中,而已取消的部分可以作为事件方法存在