This document is about: FUSION 1
SWITCH TO

This page is a work in progress and could be pending updates.

Animations

Level 4

概述

Fusion動畫範例展示了六個不同的方法,來使用Fusion處理動畫網路。

overview
對於在Fusion中的動畫,以及轉譯準確的動畫及刷新準確的動畫之間的不同的介紹,請參照Fusion操作手冊中的動畫文檔頁面

示例1到5使用轉譯準確的方法,而示例6代表一個刷新準確的方法。各個示例以大約一秒鐘的時間間隔來繪製腿和手臂的命中框,來展示伺服器角色及代理角色之間的結果精確度。在遊戲使用延遲補償時,動畫精確度特別重要。

hitboxes

下載

版本 發佈日期 下載
1.1.6 Jul 11, 2023 Fusion Animations 1.1.6 Build 211

資料夾架構

以6個小的獨立示例的集合來架構專案。

/01_AnimatorSimple 示例1——附有動畫工具的已連線狀態
/02_AnimatorInterpolated 示例2——附有動畫工具的已內插補點已連線狀態
/03_AnimatorStateSync 示例3——附有動畫工具的狀態同步
/04_NetworkMecanimAnimator 示例4——網路Mecanim動畫工具
/05_AnimancerFSM 示例5——附有Animancer的網路FSM
/06_FusionAnimationController 示例6——Fusion動畫控制器
/Common 所有示例通用的預製件、指令碼及材料
/ThirdParty 第三方資產(模型、動畫、Animancer Lite)

如何進行

開始遊戲

各個示例有其自己的場景檔案,其可以被開啟及遊玩。另一種方法是,可以從 開始 場景(/Common/Start)來開始所有示例。

為了展示的目的,建議使用附有一個主機端加上2個客戶端的多重同儕節點模式(開始主機端+ 2個客戶端 按鈕)。這允許您在編輯器中來看見及比較狀態授權、輸入授權及代理結果。

start

控制

所有示例只使用三個動畫狀態。預設播放閒置的動畫,按下 向上箭頭 鍵來開始角色跑步動畫,並且按下 空白 鍵來觸發跳躍動畫。

當以同儕節點模式遊玩時,可使用數字012,及3鍵以在它們之間切換。另一種方法是,可以從 跑者可見度控制 視窗(頂層選單 Fusion > 視窗 > 跑者可見度控制)來切換同儕節點。

理解動畫精確度

對於特定遊戲應用程式而言,在同儕節點之間的準確的動畫同步可能非常重要,比如準確的延遲補償。當動畫完全同步時,它確保在一個客戶端上識別的命中也將在伺服器上識別,從而獲得良好的玩家體驗。當然,這只適用於角色的命中框被動畫影響的情形。請查看Fusion操作手冊的動畫頁面,其中詳細說明了重要概念。

所有示例都顯示了本機玩家看到的(代理*角色的命中框顯示為藍色),以及在伺服器上出現的(顯示為綠色),腿和手臂命中框的預期位置。動畫的精確度越高,框就對齊的更緊密。

hitboxes
注意事項:使用刷新準確的解決方案(示例6)可以達成完美及最可靠的動畫精確度,但是它的一個大的缺點是在正確設定及維護上最為複雜。請確保您在踏上這條路之前理解轉譯及刷新準確的動畫解決方案之間的不同。

命中框繪製由位於玩家預製件的HitboxDraw指令碼來執行。為了更好地比較不同,以50個刷新的間隔(大約一秒)來繪製命中框。

hitboxdraw

*代理物件 = 網路物件執行個體,其沒有輸入授權(不是由本機玩家控制),也沒有狀態授權(執行個體沒有關於它們的網路狀態的授權,通常是在伺服器上的執行個體)。簡單來說,當以一個客戶端遊玩時,代理角色是玩家可以看到的其他玩家。

模擬網路情況

在不佳的網路情況下,可以更好地觀察到已增加的動畫精確度的某些改變(比如使用已內插補點資料)。為了快速地模擬這樣的網路情況,可以使用像是Clumsy這樣的工具。請參見模擬網路情況章節以取得更多資訊。

clumsy

示例1——附有動畫工具的已連線狀態

轉譯準確的方法

這個示例展示了最簡單的解決方案,其使用最新的網路狀態以控制動畫工具參數。

通常可以使用一個現有的網路狀態(比如_controller.Speed)來控制動畫。基於在Render方法中或來自OnChanged回調的已連線資料,來設定轉譯準確的動畫。

C#

private CharacterController _controller;
private Animator _animator;

public override void Render()
{
    if (_lastVisibleJump < _controller.JumpCount)
    {
        _animator.SetTrigger("Jump");
    }

    _lastVisibleJump = _controller.JumpCount;

    _animator.SetFloat("Speed", _controller.Speed);
}
example
注意事項:請注意,在其他兩個客戶端加入之後,在開始時運動動畫可能會非常不同步。這是因為動畫時間未同步,所以即便在狀態授權上的角色已經播放動畫幾秒鐘了,在所有客戶端上都從零開始播放動畫。這將在下一個動畫動作(比如跳躍)時獲得自動校正,但如果這樣的行為是不可接受的,則需要同步動畫時間——請參見示例3。

示例2——附有動畫工具的已內插補點已連線狀態

轉譯準確的方法

這個示例使用已內插補點網路資料,而不是僅使用最新的網路資料,因此比上一個示例更上一層樓。使用已內插補點資料可以使動畫更加精確,並且即使在網路情況不理想的情況下,也可以保持這樣的精確度(請參見模擬網路情況章節)。

C#

private CharacterController _controller;
private Animator _animator;

public override void Render()
{
    int jumpCount = _useInterpolation == true ? _controller.InterpolatedJumpCount : _controller.JumpCount;

    if (_lastVisibleJump < jumpCount)
    {
        _animator.SetTrigger("Jump");
    }

    _lastVisibleJump = jumpCount;

    float speed = _useInterpolation == true ? _controller.InterpolatedSpeed : _controller.Speed;
    _animator.SetFloat("Speed", speed);
}
example
請確保您的遊戲受益於已增加的精確度,因為對於許多專案而言,使用內插運算可能會帶來不必要的複雜性,而且示例1的解決方案已將產生足夠好的結果。

為了比較已內插補點資料及最新的資料的使用,在玩家元件上有一個簡單的核取方塊。找到 Player_AnimatorInterpolated 預製件並且關閉 使用內插補點。以兩個額外的客戶端來開始遊戲,模擬一個不佳的網路(Clumsy——延遲100毫秒,下降20%)並且比較差異之處。

示例3——附有動畫工具的狀態同步

轉譯準確的方法

這個示例新增一個特殊的元件AnimatorStateSync,其定期地同步目前的玩家動畫工具狀態及它們的時間。狀態同步可確保代理動畫將同步,即便是在玩家加入遊戲後或代理物件進入興趣區域時也將同步。

example

狀態同步對於長時間運行的動畫(比如運動),或是對於校正可能在轉譯準確的方法下發生的錯誤而言,可能特別重要。

示例:當一位本機玩家加入遊戲,遠端玩家已經執行了10秒鐘的跑步動畫。然而當沒有同步動畫時間的話,在本機玩家機器上的代理角色將從一開始來開始跑步動畫,從而導致不同的動畫時刻。這個時刻將隨著下一個動畫動作(比如跳躍)來自行校正,但在此之前,動畫將是不同步的。

示例4——網路Mecanim動畫工具

轉譯準確的方法

透過與Fusion SDK搭售的NetworkMecanimAnimator(NMA)元件來同步動畫的一個簡單的解決方案。

網路Mecanim動畫工具不使用已內插補點的已連線資料,因此動畫精確度低於示例3的解決方案所能達到的精確度。請記得,NMA不使用現有的網路資料,而是複製目前的動畫工具參數到其自己的已連線資料架構,因此它將會使用更多頻寬,尤其是在已同步參數變動很大的情況。

請參見動畫文檔以了解更多關於網路Mecanim動畫工具的資訊。

C#

private CharacterController _controller;
private NetworkMecanimAnimator _networkAnimator;

public override void FixedUpdateNetwork()
{
    if (IsProxy == true)
        return;

    if (Runner.IsForward == false)
        return;

    if (_controller.HasJumped == true)
    {
        _networkAnimator.SetTrigger("Jump", true);
    }

    _networkAnimator.Animator.SetFloat("Speed", _controller.Speed);
}
example

示例5——附有Animancer的網路FSM

轉譯準確的方法

這個示例使用一個熱門的第三方動畫資產Animancer及網路FSM附加元件,該附加元件目前只在這個範例中預覽。請記得,Animancer也可以在沒有FSM的情況下使用,其使用方法與在第一個示例中的情況類似。

免責聲明:Animancer是一個由Kybernetik開發、維護及支援的第三方Unity資產。在撰寫本文的時候,它可以與Fusion結合使用。Photon不提供一個開箱即用的Animancer-Fusion整合。
example

網路FSM需要StateMachineController元件以放在遊戲物件上,以及一個使用者指令碼(比如玩家),其持有狀態機器並執行IStateMachineOwner介面。 StateMachineController之後將自動地同步必要的資料到所有同儕節點,並且更新已註冊的機器。

C#

public class Player : NetworkBehaviour, IStateMachineOwner
{
    private PlayerBehaviourMachine _fullBodyMachine;

    void IStateMachineOwner.CollectStateMachines(List<IStateMachine> stateMachines)
    {
        var states = GetComponentsInChildren<PlayerStateBehaviour>();
        var animancer = GetComponentInChildren<AnimancerComponent>();

        _fullBodyMachine = new PlayerBehaviourMachine("Full Body", _controller, animancer, states);
        stateMachines.Add(_fullBodyMachine);
    }
}

基於狀態的變化來播放動畫。一個狀態代表一個單一玩家行為,其是在玩家階層中附加了StateBehaviour指令碼的一個物件(比如跳躍狀態、運動狀態),或是繼承於State指令碼的一個單純類別(在此示例中沒有展示)。

C#

public override void FixedUpdateNetwork()
{
    if (IsProxy == true)
        return;

    if (_controller.HasJumped == true)
    {
        _fullBodyMachine.TryActivateState<PlayerJumpState>();
    }
}

C#

public class PlayerJumpState : PlayerStateBehaviour
{
    [SerializeField]
    private ClipTransition _jumpClip;

    protected override void OnEnterStateRender()
    {
        Animancer.Play(_jumpClip);
    }

    protected override void OnFixedUpdate()
    {
        if (Machine.StateTime >= _jumpClip.Length * _jumpClip.Speed)
        {
            // Jump animation should be finished, let's leave this state
            Machine.TryDeactivateState(StateId);
        }
    }
}

可以建立由一個上層狀態控制的子狀態機器(下層機器),這有效地建立一個HFSM(階層式有限狀態機器)。舉例而言,一個下層機器可以是一個空中機器,其附有比如 跳躍落下著陸 等由一個 空中 上層狀態所控制的狀態。當控制有許多狀態的複雜動畫設定時,這樣的分離是有用的。

注意事項:在這個示例中沒有展示下層機器,但是在所需的上層狀態的OnCollectChildStateMachines方法中提供一個下層機器,然後如同往常一般控制狀態機器,基本上就足夠了。

值得一提的是,預設網路FSM基於已內插補點資料來切換在代理上的狀態,並且提供目前狀態時間(Machine.StateTime),從轉譯調用中檢查該時間時,該時間也會被內插補點。這確保了精細的動畫精確度,且無需付出過多的努力。

C#

public class PlayerLocomotionState : PlayerStateBehaviour
{
    [SerializeField]
    private LinearMixerTransition _moveMixer;

    protected override void OnEnterStateRender()
    {
        Animancer.Play(_moveMixer);

        // Update the animation time based on the state time
        _moveMixer.State.Time = Machine.StateTime;
    }

    protected override void OnRender()
    {
        _moveMixer.State.Parameter = Controller.InterpolatedSpeed;
    }
}

示例6——Fusion動畫控制器

刷新準確的方法

Fusion動畫控制器正在試驗狀態,並且缺少網頁文檔。理解的前提是知道中級的編碼技能。

Fusion動畫控制器是一個 刷新準確的 動畫解決方案,直接在Unity的下層可遊玩的API的頂層上組建。

在您的遊戲中使用自訂動畫解決方案之前,請確保您理解自訂動畫解決方案的限制。請閱讀動畫文檔,以了解更多關於刷新準確的動畫的資訊。
example

類似於 Mecanim,動畫控制器使用 動畫圖層 來運作。每個圖層含有一個或多個 動畫狀態。圖層及狀態都簡單地由物件階層中的物件所代表(請參見Player_FusionAnimationController預製件)。透過AnimationController元件,從程式碼來控制動畫。

C#

private CharacterController _controller;
private PlayerLocomotionState _locomotionState;
private PlayerJumpState _jumpState;

public override void FixedUpdateNetwork()
{
    if (IsProxy == true)
        return;

    if (_controller.HasJumped == true)
    {
        _jumpState.Activate(0.15f);
    }
    else if (_jumpState.IsPlaying() == false || _jumpState.IsFinished(-0.15f, false) == true)
    {
        _locomotionState.Activate(0.15f);
    }
}

Fusion動畫控制器是一個在BR200中使用的動畫控制器的精緻版本。因此,如需取得更詳細的動畫示例,請參見BR200

第三方資產

動畫範例包含了由各自的創作者提供的多個資產。您可以在它們各自的網站上為您自己的專案取得完整的套件:

重要事項:為了在商業專案中使用它們,需要從各自的創作者購買一個授權。

Back to top