This document is about: FUSION 2
SWITCH TO

Animations

Level 4

概要

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

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

サンプル1~5はレンダーアキュレートアプローチを使い、サンプル6はティックアキュレートアプローチを使っています。それぞれのサンプルでは、およそ1秒間隔で脚と腕のヒットボックスを描画することで、サーバーキャラクターとプロキシキャラクターの間の結果の精度を示しています。アニメーションの精度は、ゲームがラグ補償を使っている場合に特に重要です。

Hitboxes

ダウンロード

バージョン リリース日 ダウンロード
2.0.10 Dec 19, 2024 Fusion Animations 2.0.10 Build 742

フォルダ構造

プロジェクトは、6つの小さな独立したサンプルで構成されています。

/01_AnimatorSimple サンプル 1 - アニメーションを伴うネットワーク状態
/02_AnimatorInterpolated サンプル 2 - アニメーションを伴う内挿ネットワーク状態
/03_AnimatorStateSync サンプル 3 - アニメーターでの状態同期
/04_NetworkMecanimAnimator サンプル 4 - ネットワークメカニムアニメーター
/05_AnimancerFSM サンプル 5 - AでのnimancerでのネットワークFSM
/06_FusionAnimationController サンプル 6 - Fusionアニメーションコントローラー
/Common 全てのサンプルに共通するプレハブ、スクリプト、マテリアル
/ThirdParty サードパーティアセット(モデル、アニメーション、Animancer Lite)

方法

ゲームを開始する

それぞれのサンプルは、開いて再生することができる独自のシーンファイルを持っています。または、すべてのサンプルは、Start シーン(/Common/Start)から開始することができます。

デモには、ホストとクライアント2件を有するマルチピアモードを推奨します(Start Host + 2 Clients ボタン)。これにより、状態権限、入力権限、プロキシの結果をエディタで確認し、比較することができます。

Start

コントロール

すべてのサンプルでは、3つのアニメーション状態だけを使用しています。デフォルトではアイドルアニメーションが再生され、Up Arrow キーを押すとキャラクターランアニメーションが開始され、Space キーを押すとジャンプアニメーションが開始されます。

複数のピアとプレイしているときは、Num 0123 キーで切り替えることができます。また、Runner Visibility Controls ウィンドウ(トップメニュー Fusion > Windows > Runner Visibility Controls)からも切り替えられます。

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

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

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

Hitboxes
注意: 完璧で最も信頼できるアニメーションの精度は、ティックアキュレートソリューション (サンプル 6) で実現できますが、このアプローチは正しく設定し、維持するのが最も複雑であるという大きな欠点があります。レンダーアキュレートソリューションとティックアキュレートアニメーションソリューションの違いを理解してから、この道を進んでください。

ヒットボックスの描画は、プレイヤーのプレハブに置かれた HitboxDraw スクリプトによって行われます。違いの比較のため、ヒットボックスは50ティック間隔(およそ1秒)で描画されます。

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
注意: 他の 2 件のクライアントの参加後、開始時にロコモーションアニメーションが非常にずれていることに注意してください。これは、アニメーションの時間が同期していないため、状態権限のあるキャラクターがすでに数秒間アニメーションを再生しているにもかかわらず、すべてのクライアントでアニメーションがゼロから再生されるためです。これは次のアニメーションアクション(ジャンプなど)で自動的に修正されますが、このような動作が許容できない場合は、アニメーション時間の同期が必要です。サンプル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 コンポーネントに簡単なチェックボックスがあります。Player_AnimatorInterpolated*プレハブを見つけ、Use Interpolationをオフにします。2つのクライアントを追加してゲームを開始し、ネットワークの悪さ(Clumsy - Lag 100 ms, Drop 20%) をシミュレートし、その違いを比較してください。

サンプル 3 - アニメーターを伴う状態同期

レンダーアキュレートアプローチ

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

Example

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

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

サンプル4 - ネットワークメカニムアニメーター

レンダーアキュレートアプローチ

Fusion SDKに搭載されているNetworkMecanimAnimator(NMA)コンポーネントを使ってアニメーションを同期するシンプルなソリューションです。

ネットワークメカニムアニメーター(NMA)は内挿されたネットワークデータを使用しないため、アニメーションの精度はサンプル3のソリューションによるものよりも低くなります。NMAは既存のネットワークデータを使用せず、現在のAnimatorパラメータを独自のネットワークデータ構造にコピーするため、特に同期パラメータが大量に変更される場合は、帯域幅を消費します。

ネットワークメカニムアニメーターについて詳細はアニメーション のドキュメントを参照してください。

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インターフェイスを実装するユーザースクリプト(Playerなど)が必要です。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);
    }
}

アニメーションは、ステートの変化に基づいて再生されます。ステートはプレーヤーの1つの動作を表し、プレーヤー階層内のオブジェクトに StateBehaviour スクリプトがアタッチされているか (例: Jump State、Locomotion State)、または 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(階層型有限ステートマシン)を効果的に作成することができます。例えば、子マシンは、Airborneの親ステートから制御される、ジャンプ転ぶ着陸 などのステートを持つAirborneマシンになります。このような分離は、多くのステートを持つ複雑なアニメーション設定を制御するときに便利です。

注意: 子マシンはこのサンプルでは紹介されていませんが、基本的には、希望する親ステートのOnCollectChildStateMachinesメソッドで子マシンを提供し、通常通りステートマシンを制御すれば十分です。

ネットワークFSMはデフォルトで、内挿されたデータに基づいてプロキシのステートを切り替え、Renderコールからチェックされたときに内挿された現在のステートの時間(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 Animation Controllerはまだ実験的な段階であり、ウェブドキュメントが不足しています。理解するには中級のコーディングスキルが必要です。

Fusion Animation Controllerは、Unityの低レベルのPlayables API上に直接構築されたティックアキュレートアニメーションソリューションです。

ゲームで使用する前に、カスタムアニメーションソリューションの制限について理解しておいてください。ティックアキュレートなアニメーションについては、アニメーションドキュメントを参照してください。
Example

Similarly to Mecanim, Fusion Animation Controller operates with Animation Layers. Every layer contains one or more Animation States. Both layers and states are simply represented by objects in the object hierarchy (see Player_FusionAnimationController prefab). Animations are controlled from code via the AnimationController component.
メカニム と同様に、Fusionアニメーションコントローラーは、アニメーションレイヤー で動作します。各レイヤーには、1つ以上の アニメーションステート が含まれます。レイヤーとステートは、オブジェクト階層のオブジェクトで表現されます(プレハブ 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で使用されたアニメーションコントローラーの改善版です。

サードパーティアセット

アニメーションサンプルには、それぞれのクリエイターのご好意により提供されたいくつかのアセットが使用されています。完全なパッケージは、それぞれのサイトで皆さんのプロジェクト用に入手することができます。

重要:商用プロジェクトで使用するには、それぞれのクリエイターからライセンスを購入する必要があります。

Back to top