このページは編集中です。更新が保留になっている可能性があります。

Fusion アニメーション

Level Advanced

概要

Fusion Animationsサンプルでは、Fusionを使ったアニメーションネットワーキングを扱う6つの異なるアプローチを紹介しています。

Overview

Fusionのアニメーションの紹介、レンダーアキュレートアニメーションとティックアキュレートアニメーションの違いについては、Fusionマニュアルのアニメーションに関するドキュメントページを参照してください。

例 1 から例 5 は render accurate approach を使用し、例 6 は tick accurate approach を表しています。各例では、脚と腕のヒットボックスをおよそ1秒間隔で描画することで、サーバーキャラクターとプロキシキャラクターの間の精度を実証しています。アニメーションの精度は、ゲームが 遅延補償 を使用しているときに特に重要です。

Hitboxes

ダウンロード

バージョン リリース日 ダウンロード
1.1.3 Dec 14, 2022 Fusion Animations 1.1.3 Build 86

フォルダー構造

プロジェクトは、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)

ハウツー

ゲームを開始する

各サンプルには独自のシーンファイルがあり、それを開いてプレイすることができます。または、すべてのサンプルは、Startシーン(/Common/Start)から起動することができます。

デモの目的には、ホストと2クライアントのマルチピアモードをお勧めします(Start Host + 2 Clients ボタン)。これにより、State Authority、Input Authority、Proxyの結果をエディタで確認・比較することができます。

Start

コントロール

すべての例では、3つのアニメーションの状態のみを使用しています。デフォルトでは待機アニメーションが再生され、上矢印キーを押すとキャラクターランアニメーションが始まり、スペースキーを押すとジャンプアニメーションが始まります。

複数のピアでプレイするときは、Num 0123キーを使って、ピアを切り替えることができます。また、ランナー可視化コントロールウィンドウ(トップメニューのFusion > Windows > Runner Visibility Controls)からも切り替えが可能です。

アニメーションの精度を理解する

ピア間の正確なアニメーション同期は、正確な遅延補正など、特定のゲームアプリケーションにとって非常に重要です。アニメーションが完全に同期していると、あるクライアントで認識されたヒットがサーバーでも認識されることが保証され、結果として良いプレイ体験を提供することができます。もちろん、これはキャラクターのヒットボックスがアニメーションの影響を受けている場合のみ適用されます。FusionマニュアルのAnimationページで、重要なコンセプトが詳しく説明されていますので、ご確認ください。

全ての例は、ローカルプレイヤーから見た足と腕のヒットボックスの予想位置(プロキシ*キャラクターのヒットボックスは青色で表示)と、サーバーに表示される位置(緑色で表示)を示しています。アニメーションの精度が高いほど、ボックスの位置がより近くなります。

Hitboxes

補足: 最も信頼できる完璧なアニメーション精度はtick accurateソリューションで達成されますが(例6)、正しく設定して維持するのが複雑であるという大きな欠点があります。この方法を取る前に、レンダーアキュレートとティックアキュレートのアニメーションソリューションの違いを理解しておいてください。

Hitbox drawing is performed by HitboxDraw script placed on the Player prefabs. To better compare the difference, hitboxes are drawn in 50 ticks intervals (roughly a second).

HitboxDraw

プロキシオブジェクト = 入力権限(ローカルプレイヤーによって制御されていない)および状態権限(そのネットワーク状態に対する権限を持たないインスタンス、通常はサーバー上のインスタンス)を持たないネットワークオブジェクトインスタンスです。クライアントとしてプレイする場合、プロキシキャラクターはプレイヤーに見える他のプレイヤーのことです。

ネットワーク状態のシミュレーション

アニメーションの精度を上げるためのいくつかの変更(補間されたデータの使用など)は、悪いネットワーク条件で観察するのがよいでしょう。そのような状態を素早くシミュレートするために、Clumsyのようなツールを使用することができます。詳しくは Simulating Network Conditions のセクションをご覧ください。

Clumsy

例1 - アニメーターとネットワーク状態

レンダリング精度の高いアプローチ

この例では、最新のネットワーク状態を使用してアニメータのパラメータを制御す る、最もシンプルなソリューションを示します。

通常、既存のネットワーク状態(例:_controller.Speed)を使用してアニメーショ ンを制御することができます。レンダリング精度の高いアニメーションは、RenderメソッドやOnChangedコールバックでネットワーク上のデータに基づいて設定されます。

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

備考: 他の 2 つのクライアントが参加した後、開始時に運動アニメーションの同期が大きくずれていることに注目してください。これはアニメーションの時間が同期していないためで、状態権限のあるキャラクターがすでに数秒間アニメーションをプレイしているにもかかわらず、すべてのクライアントでアニメーションがゼロからプレイ開始さ れているためです。これは次のアニメーション動作(ジャンプなど)で自動的に修正されますが、このような動作が許容できない場合は、アニメーション時間の同期が必要です - 例3参照。

例2 - アニメーターと補間されたネットワーク状態

レンダリング精度の高いアプローチ

この例では、最新のネットワークデータだけでなく、補間されたネットワークデータを使用することで、前の例から一歩前進しています。補間されたデータを使用することで、アニメーションがより正確になり、ネットワークの状態が理想的でない場合でもこの正確さが保たれます(ネットワーク状態のシミュレーションセクションを参照)。

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 プレハブを探し、Use Interpolation をオフにしてください。2つのクライアントを追加してゲームを開始し、質の悪いネットワーク(Clumsy - Lag 100 ms, Drop 20%)をシミュレートし、その違いを比較します。

例3 - アニメーターとの状態同期

レンダリング精度の高いアプローチ

この例では、特別なコンポーネント AnimatorStateSync を追加し、現在のプレイヤーアニメーターの状態とその時間を定期的に同期させます。状態の同期は、プレイヤーがゲームに参加した後や、プロキシオブジェクトが関心領域 に入ったときでも、プロキシのアニメーションが同期していることを保証します。

Example

状態同期は、長時間のアニメーション(ロコモーションなど)や、レンダーアキュレートアプローチで起こりうるミスを修正するために特に重要です。

例:ローカルプレイヤーがゲームに参加したとき、リモートプレイヤーはすでに10秒間のランニングアニメーションを実行しています。しかし、アニメーションの時間が同期されていない場合、ローカルプレイヤーのマシンのプロキシキャラクターが最初からランニングアニメーションを開始するため、アニメーションのタイミングが異なってしまいます。このタイミングは、次のアニメーション動作(例:ジャンプ)で自動的に修正されますが、それまではアニメーションが同期していないことになります。

例4 - ネットワークMecanimアニメーター

レンダリング精度の高いアプローチ

Fusion SDKに同梱されているNetworkMecanimAnimator (NMA)コンポーネントを使用してアニメーションを同期させる簡単な方法です。

Network Mecanim Animatorは、補間されたネットワークデータを使用しないため、例3のソリューションよりもアニメーションの精度は低くなります。NMAは、既存のネットワークデータを使用せず、現在のアニメーターパラメーターを独自のネットワークデータ構造にコピーするため、特に同期されたパラメーターが大きく変化する場合、より多くの帯域幅を消費することに注意してください。

Network Mecanim Animator についての詳細は Animation のドキュメントを参照してください。

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 と、このサンプルでプレビュー中の Network FSM アドオンを使用しています。Animancer は、最初の例と同様に、FSM なしでも使用できることに留意してください。

免責事項:AnimancerはKybernetikによって開発、保守、サポートされているサードパーティのUnityアセットです。執筆時点では、Fusionと組み合わせて使用することができます。Photonは、AnimancerとFusionの統合をすぐに提供するものではありません。

Example

ネットワーク FSM は、ゲームオブジェクトに StateMachineController コンポーネントを配置し、ステートマシンを保持し IStateMachineOwner インターフェイスを実装したユーザスクリプト(Player など)を必要とします。その後、StateMachineController が自動的に必要なデータを全てのピアに同期し、登録されたマシンを更新します。

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 スクリプトを付加したもの (例: Jump State, Locomotion State) か、State スクリプトを継承したクラス (この例では紹介していません) である、単一のプレイヤー挙動を表します。

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

    if (_controller.HasJumped == true)
    {
        _fullBodyMachine.TryActivateState<PlayerJumpState>();
    }
}
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(階層型有限状態マシン)を効果的に作成することが可能です。たとえば、子マシンは、JumpFallLand などの状態を持つ Airborne マシンで、親状態である Airborne から制御することができます。このような分離は、多くのステートを持つ複雑なアニメーションのセットアップを制御するときに便利で す。

N補足: この例では子マシンを紹介していないが、基本的には目的の親状態のOnCollectChildStateMachinesメソッドで子マシンを用意し、通常通りステートマシンを制御すれば十分です。

Network FSM はデフォルトで補間されたデータに基づいてプロキシの状態を切り替え、Render 呼び出しからチェックする際に補間された現在の状態時間 (Machine.StateTime) も提供することです。これにより、労力をかけずに細かいアニメーションの精度を確保することができます。

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 Animation Controllerは、まだ実験段階であり、Webドキュメントが不足しています。理解するには中級程度のコーディングスキルが必要です。

Fusion Animation Controllerは、Unityの低レベルなPlayables APIの上に直接構築されたtick accurateアニメーションソリューションです。

カスタムアニメーションソリューションをゲームに使用する前に、その制限を理解していることを確認してください。正確なアニメーションの作成については、Animationドキュメントをご覧ください。

Example

Mecanim と同様に、Animation Controller は Animation Layers で操作します。各レイヤーは、1つ以上のアニメーション状態を含みます。レイヤーと状態は、オブジェクト階層にあるオブジェクトで表現されます(Player_FusionAnimationControllerプレハブを参照)。アニメーションは、コードから AnimationController コンポーネントを介して制御されます。

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 Animation Controllerは、BR200で使用されたアニメーションコントローラを改良したものです。より精巧なアニメーション例については、BR200をご確認ください。


ドキュメントのトップへ戻る