Fusion Introduction

概要

Fusionは、Unity用高性能ステート同期ネットワーキングライブラリです。 シングルAPIで、専用サーバーやリッスンサーバー、リレーなど多くのネットワークアーキテクチャに対応しています。

「シンプルさ」を大切に構築しており、Unityの代表的なワークフローとシームレスに統合できます。 また、アドバンスト機能も搭載しているため、さまざまなユースケースに対応します。

トップに戻る

最先端機能

FUSIONに搭載されている重要な機能の一部のまとめです。

  • Snapshot Delta Compression(スナップショットデルタ圧縮): FUSIONは、帯域幅を多く使わず、かつ低CPU負荷の、新しいデルタ圧縮技術を用いたフルステートまたはAoI(興味関心領域)マネージドステートチャンクです。
  • Made for Unity MonoBehaviours(Unity MonoBehaviours対応): 自分のUnityコンポーネントでゲームステートを定義します。シリアライゼーションなど複雑なコードを記述する必要はありません。ネスティングや引数などUnityの最新プレハブ機能ともシームレスに連動します。
  • Client side prediction + server reconciliation(クライアントサイド予測+サーバー照合): 内部データバッファを透過的に使用することで、Unityが持つデータが常にスムーズなものとなり、照合・内挿が自動的(またはカスタム通り)に処理されます。
  • RPC: APIの利便性のため、シンプルながら高性能なRPCも搭載されています。

トップに戻る

1つのライブラリで多くのアーキテクチャ

FUSIONは既存のPhotonステート転送製品(Bolt及びPUN)に置き換わるものとして開発されました。これらの製品が対応している全アーキテクチャに新機能が加えられています。

  • シングルプレイヤー: ローカルでは同一のコード。接続は必要ありません。
  • 専用サーバー: フルサーバー権限でUnityのヘッドレスインスタンスをデプロイします。
  • リッスンサーバー: ビルトインパンチスルー、フォールバックとしてのリレー搭載のプレイヤーホストのサーバーおよびクライアント(PhotonCloudによるフルホスト移行サポーを含む)。
  • リレー: データドリブンサーバーマネージドスナップショットおよびインタレスト管理(AoI:興味関心領域)を伴うクライアント権限。 Fusionでは、リレーのみのモードでもプレイヤー数が多くなればスケーリングします。
  • カスタムコードつきスマートリレー: Fusion Photon Serverプラグインには、Unityがなくてもゲームステートへのフルアクセス権限があります。 軽いサーバーゲームロジックであれば簡単に記述できます。

トップに戻る

コードスニペット

UnityでのFusionの使用例について、予備のコードサンプルでご紹介します。 APIが成長するしたがって、本ドキュメントもアップデートを行い、時間をかけてサンプル数も増えていく予定です。

トップに戻る

ネットワークステート

プロパティを使用してMonoBehaviorを作成し、ネットワークを越えて同期させるには、NetworkedBehaviorベースクラスを拡張します。 Fusionがその高性能データバッファとデルタ圧縮を使用して、内部的にプロパティを処理します。

public class PlayerBehaviour : NetworkedBehaviour {
  public float Health { get; set; }
}

適切な数値型に必要な精度は自分でコントロールできます。

public class PlayerBehaviour : NetworkedBehaviour {
  [Accuracy(0.001)]
  public float Health { get; set; }
}

トップに戻る

組み込みコンポーネント

FusionはUnityでの代表的なユースケース用にターンキーサブクラスNetworkedBehaviorを搭載しています。

  • RigidBody
  • Transform
  • Animation
  • NavmeshAgent
  • など

自分のGameObjectに対応するコンポーネントを追加し、Fusionで全てを同期させます。

トップに戻る

プレハブ

Fusionはすぐに使えるUnityの最新プレハブ機能のほぼすべてに対応しています:

  • Prefab Variants(プレハブ変数)
  • Nested Prefabs(ネスト状のプレハブ)
  • Nested Networked Objects (非プレハブ) (ネスト状のネットワークオブジェクト)

Unityからこれらのプレハブを直接インスタンス化できます。Fusionがそれをネットワークオブジェクトとして(プレハブには1つのまたは複数のNetworkedBehaviorコンポーネントがあると仮定して)自動的に検出します。

トップに戻る

Player Input(プレイヤーインプット) と Server Reconciliation(サーバー照合)

Fusionには組み込み済みのクライアントサイド予測とサーバー照合があります。 これらを余すところなく使用するには、独自のinput構造体をシミュレーションAPIで設定します。

全サンプル:

A - input構造体を定義する:

public struct MyGameInput : INetworkedInput {
  public const uint BUTTON_FORWARD  = 1 << 3;
  public const uint BUTTON_BACKWARD = 1 << 4;
  public const uint BUTTON_LEFT     = 1 << 5;
  public const uint BUTTON_RIGHT    = 1 << 6;

  public uint Buttons;

  public bool IsUp(uint button) {
    return IsDown(button) == false;
  }

  public bool IsDown(uint button) {
    return (Buttons & button) == button;
  }
}

B - ティックベースのゲームロジック実装時に使用する:

ネットワークステートに影響を与えるロジックを記述するには、FixedUpdateNetwork(UnityのFixedUpdateに類似)をオーバーライドします。以下を参照ください。

[OrderBefore(typeof(NetworkedPhysicsSimulation))]
public class PlayerBehaviour : SimulationBehaviour {
  public Rigidbody Rigidbody;
  public float     Speed = 5;

  public override void FixedUpdateNetwork() {
    if (GetInput(out MyGameInput input)) {
      if (input.IsDown(MyGameInput.BUTTON_FORWARD)) {
        Rigidbody.velocity += Vector3.forward * Speed * Time.fixedDeltaTime;
      }

      if (input.IsDown(MyGameInput.BUTTON_BACKWARD)) {
        Rigidbody.velocity += Vector3.back * Speed * Time.fixedDeltaTime;
      }

      if (input.IsDown(MyGameInput.BUTTON_LEFT)) {
        Rigidbody.velocity += Vector3.left * Speed * Time.fixedDeltaTime;
      }

      if (input.IsDown(MyGameInput.BUTTON_RIGHT)) {
        Rigidbody.velocity += Vector3.right * Speed * Time.fixedDeltaTime;
      }
    }
  }
}

C - インプットをポーリングする:

ポーリングコールバックを使用して、Fusionへのインプットをフィードします:

public class ClientInputBehaviour : MonoBehaviour {
  public void OnInput(NetworkRunner runner, NetworkedInput inputContainer) {
    var myInput = new MyGameInput();

    if (Input.GetKey(KeyCode.W)) {
      myInput.Buttons |= MyGameInput.BUTTON_FORWARD;
    }

    if (Input.GetKey(KeyCode.S)) {
      myInput.Buttons |= MyGameInput.BUTTON_BACKWARD;
    }

    if (Input.GetKey(KeyCode.A)) {
      myInput.Buttons |= MyGameInput.BUTTON_LEFT;
    }

    if (Input.GetKey(KeyCode.D)) {
      myInput.Buttons |= MyGameInput.BUTTON_RIGHT;
    }

    inputContainer.Set(myInput);
  }
}

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