前往
大廳
主題

【Unity】連續動作的處理

微笑的貘 | 2023-04-20 12:32:47 | 巴幣 12 | 人氣 794

我覺得這是一個很難發現卻很重要的問題。
很多人試玩了我的遊戲後反應動作有延遲。
本來以為是動作或碰撞判定的問題,後來才發現是 Animator 對連續輸入的反應所致。

一般來說,玩家輸入幾次,Animator Trigger 就會觸發幾次。
舉例來說,如果玩家按三下攻擊,Animator 就會演出三次攻擊動畫。
這聽起來很合理。
但是假如攻擊動畫有一秒,玩家手速超快,在一秒之內按了十次,你就會看到在接下來的十秒角色都在攻擊。
然而玩家心理預期的是手一放開,角色就該停止動作。
那這要怎辦咧?

首先先做一張表
以這張表為例,移動之後可以接任何動作。
普攻之後只能接移動,其他的都不行。
然後把它寫成txt,或是任何程式可以讀的檔案。
然後是程式的部分

protected override void Update()
{
    base.Update();

    //將輸入的 keycode 塞進 queue 中
    KeyCode keyCode = KeyCode.Escape;
    if (Input.GetKeyDown(KeyCode.X))
    {
        keyCode = KeyCode.X;
    }
    if (Input.GetKeyDown(KeyCode.A))
    {
        keyCode = KeyCode.A;
    }
    if (Input.GetKeyDown(KeyCode.C))
    {
        keyCode = KeyCode.C;
    }
    if (Input.GetKeyDown(KeyCode.Z))
    {
        keyCode = KeyCode.Z;
    }

    if (keyCode != KeyCode.Escape)
    {
        _actionQueue.Enqueue(keyCode);

        //如果 queue 中只有一個 keycode,就立刻執行動畫
        if (_actionQueue.Count == 1)
        {
            _lastKeyCode = keyCode;
            SetAnimation(keyCode);
        }
    }
}

private void SetAnimation(KeyCode keyCode)
{
    //X是普攻,會根據目前是否有輸入向上或向下來判斷是向上、向下、或向前的攻擊
    if (keyCode == KeyCode.X)
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            AttackUp();
        }
        else if (Input.GetKey(KeyCode.DownArrow))
        {
            AttackDown();
        }
        else
        {
            Attack();
        }
    }
    
    //其他的動作指令
    if (keyCode == KeyCode.A)
    {
        SkillA();
    }
    if (keyCode == KeyCode.C)
    {
        Dash();
    }
    if (keyCode == KeyCode.Z)
    {
        Jump();
    }
}

void Attack()
{
    Animator.SetTrigger("Attack");
}

在這些動作的動畫的最後一幀加入 Event,執行 OnActionEnd

OnAnimationEnd 的程式

public void OnActionEnd() //動作結束的時候
{
    if (_actionQueue.Count > 0)
    {
        _actionQueue.Dequeue();
        if (_actionQueue.Count > 0)
        {
            KeyCode currentKeyCode = _actionQueue.Peek();
            ContinueActionData.ActionEnum currentActionEnum = KeyCodeToActionEnum(currentKeyCode);
            ContinueActionData.ActionEnum lastActionEnum = KeyCodeToActionEnum(_lastKeyCode);

            //判斷是否為可連續的動作
            if (ContinueActionData.Data[(int)lastActionEnum, (int)currentActionEnum])
            {
                _lastKeyCode = currentKeyCode;
                SetAnimation(currentKeyCode);
            }
            else
            {
                _actionQueue.Clear();
            }
        }
    }
}
舉例來說,玩家在攻擊結束前輸入了衝刺,這個衝刺的指令就會被塞進 queue 中
攻擊動作結束時 dequeue
根據上面那張表來判斷攻擊之後可不可以接衝刺,答案是不行。
所以衝刺的指令會被忽略。
反之,如果是可連續的動作就會執行該動作。

還有一個問題是 Animatio Exit Time
舉例來說,角色的普通攻擊有兩段動作。
第一下普通攻擊結束後一段時間內再次進行的普通攻擊會不一樣。
這段時間就是 Exit Time
以我的遊戲為例,X之後0.5內再次按X就會進行 SecondAttack
這個 0.5 的單位好像不是秒,而是動畫長度的 normalized time

大概就是這樣...
這個問題超坑爹的。我就是玩了幾十年的遊戲都沒發現這個問題。
卻會一直讓人覺得"手感怪怪的"
多虧了專精動作遊戲的小夥伴才終於發現了這個問題。
希望這篇文章可以讓其他人避免被坑。

創作回應

更多創作