クイックスタートガイド

Preview

Fusion Godot SDK 3は開発プレビュー版で、まだ製品での利用を想定していません

はじめに

このガイドでは、Photon Fusion GDExtension for Godot 4.6を使用して、シンプルなマルチプレイヤーゲーム/アプリケーションを作成する方法を解説します。ここでは、AppIDの作成・接続・スポーン・移動のレプリケーション・カスタムプロパティ・RPCについて扱います。最終的に、2クライアントが同じルーム内を動き回る姿をお互いにリアルタイムで確認できるようになります。

前提として、Godot 4.6がインストール済みで、GodotとGDScriptについての基本的な知識が必要です。

ステップ 1 - PhotonアカウントとAppID

FusionはPhoton Cloud上で動作します。AppIDは、接続・認証・マッチメイキング・ステート配信を処理するバックエンドとプロジェクトを紐づけます。このガイドで使用できる新しいAppIDを作成するには、以下の手順に従ってください。

  1. Photonダッシュボードでアカウントを作成してログインします。
  2. アプリの新規作成をクリックし、SDKはFusion、バージョンはFusion 3(UnrealとGodotの両方で使用可能)を選択します。
  3. アプリ作成後にAppIDをコピーします。

AppIDの作成のためにログインしている場合は、次のステップに進んでSDKをダウンロードできます。

ステップ 2 - SDKのダウンロードとインストール

Photon Fusion GodotはGDExtensionアドオンで、コンパイル済みライブラリ(マルチプラットフォーム対応)と.gdextension記述子で構成されています。これをプロジェクトに入れるだけで、Godotで自動的に認識されるため、エンジンの再ビルド等は不要です。

  1. SDKのダウンロードからSDKをダウンロードします。
  2. SDKを展開して、プロジェクトのaddons/fusion/フォルダーをコピーします。
  3. プロジェクトを再読み込みすると、GodotはGDExtensionを自動的にロードします。

これでアドオンが登録され、設定を変更できるようになります。次のステップに進みましょう。

ステップ 3 - Fusion GDExtensionの設定

Photon Fusionでは、initialize_from_settings()を呼び出すだけで、起動時に定義済みの値を簡単に利用できます。認証情報はプロジェクト設定 > Fusion > Connection(外部GDExtensionの設定は、検索フィールドを使用するか、「高度な設定」を有効にした場合のみ表示されることに注意してください)から設定できます。

  • app_id - ステップ1で作成したAppIDを入力します。
  • app_version - マッチメイキングを分離するために使用できるバージョン文字列(例:"1.0")です。
  • default_region - Photonのリージョン(例:"us""eu")です。
プロジェクト設定 - Fusion
プロジェクト設定 - Fusion

基本的な認証情報が設定できたら、メインシーンの準備に取り掛かりましょう。

ステップ 4 - メインシーン

Godotでは、すべての要素はシーンで構成され、シーンはノードツリーで構成されています。メインシーンは、他のシーンの読み込みやゲームを開始するためのエントリーポイントです。

このガイドにおけるメインシーンの役割は、Photon Fusionのインフラストラクチャへの接続、ルームの作成/参加、クライアントが制御するオブジェクト(「プレイヤー」を表すGodotの小さなシーン)のスポーンです。そのため、このシーンには、接続ロジックに加えて、FusionSpawnerノード(ネットワークオブジェクトのファクトリー)インスタンスを含める必要があります。

以下はNode2Dをルートとしてシーンを構成する例です。このシーンには、接続をトリガーするButtonと、FusionSpawner(後でプレイヤーシーンを生成するために使用)が子ノードとして含まれています。

Main Scene Structure
Main Scene Structure

以下はGDScriptの最初の部分です。

  • FusionSpawnerノード・接続ボタン・プレイヤーとしてスポーンするシーンの参照の保持
  • 接続ボタン(接続してルームへ参加する)と、ルーム参加時のシグナル(プレイヤーシーンをスポーンする)の登録
  • スポーン可能なシーンをFusionSpawnerノードに登録(これはインスペクターからも登録可能)

GDScript

extends Node2D

const PlayerScene = preload("res://player.tscn")

@onready var spawner: FusionSpawner = $FusionSpawner
@onready var connect_button: UIBUtton = $UIConnectButton

func _ready():
    FusionClient.room_joined.connect(_on_room_joined)
    connect_button.pressed.connect(_connect_and_join_room)
    spawner.add_spawnable_scene(PlayerScene)

上の画像のような構成のシーンの場合、このスクリプトをシーンのルートノードにアタッチできます。シグナルは両方とも対応する関数を実装する必要があります。また、Godotエディターは、プレイヤーシーンのリソースが無いことを警告するはずです。それでは、これらを一つずつ解決していきましょう。

ステップ 5 - 接続と参加

接続フローは2ステップです。まずPhotonのマスターサーバーに接続し、その後にルームの作成/参加を行います。ルームは、レプリケーションやRPCが実際に実行される場所です。以下の関数は、接続ボタンが押された際に呼び出されます。connected_to_serverシグナルに登録されたラムダ関数では、ルームへの参加を試みて、ルームが存在しない場合にはルームを作成します。

GDScript

func _connect_and_join_room():
    print("clicked connect")
    FusionClient.initialize_from_settings()
    var user_id = "user_%d" % randi()
    FusionClient.connect_to_server(user_id)
    FusionClient.connected_to_server.connect(func():
        print("trying to join/create room as user: ", user_id)
        FusionClient.join_or_create_room()
    )

ルームに入ると、他のノードツリーをスポーンして制御できるようになります。残りは、room_joinedシグナルに接続された関数を実装するだけです。

ステップ 6 - プレイヤーシーン

プレイヤーは、FusionReplicator子ノードを持つ(小さな)シーンです。FusionReplicatorは、ネットワーク上で何を同期するかを定義するために使用されるノードです。現時点では、Root Replication ModeAutoに設定しておくと、ルートノード型に適した基本的なTransformプロパティが自動的に選択されます。

Player Scene Setup
Player Scene Setup
Player FusionReplicator Settings
Player FusionReplicator Settings

Autoによって、位置と回転(ルートノードがRigidbodyCharacterの場合は、さらに速度)が自動的に同期されて、ルートノード型に基づいてスムージングが適用されます。

これでクライアントがルームに入った際に、プレイヤーのスポーンをトリガーできるようになります。

ステップ 7 - 参加時のスポーン

FusionClientシングルトンからroom_joinedシグナル(ステップ4で登録済み)が発火すると、プレイヤーインスタンスがスポーンされます。FusionSpawnerはネットワーク側の処理として、ユニークIDの割り当て・クラウドサーバーへスポーンメッセージの送信を行います。このメッセージは他のクライアントに伝搬し、各クライアントは対応するインスタンスを作成します。これは途中参加者に対しても自動的に処理されます。サーバーは、スポーン/デスポーンだけでなく、ネットワーク上のすべてのシーン/オブジェクトの現在の状態をキャッシュして配信する役割も担っているためです。

GDScript

func _on_room_joined():
    var pos = Vector2(randf_range(100, 700), randf_range(100, 500))
    var player = spawner.spawn()
    player.position = pos
    print("joined room and spawned player scene at: ", pos)

スクリプトは正常に動作し、Photonへの接続やルームの作成/参加が可能になっています。プレイヤーシーンもスポーンするようになりましたが、まだ動きません。これを動かすために入力を追加しましょう。

ステップ 8 - 移動

権限を持つクライアント(ここでは、シーンをスポーンしたクライアント)のみが入力を処理できます。それ以外のクライアントは、レプリケーションによって位置の更新を自動的に受信し、FusionReplicatorによって自動的にスムージングが行われます。以下のGDScriptコードを、プレイヤーシーンにアタッチしてください。

GDScript

extends Node2D

const SPEED := 200.0
@onready var sync: FusionReplicator = $FusionReplicator

func _process(delta):
    if sync.has_authority():
        var dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
        position += dir * SPEED * delta

func _ready():
    $Sprite2D.modulate = Color.GREEN if sync.has_authority() else Color.RED

テストとして、このスクリプトをプレイヤーのルートノードに追加してください。

_readyシグナルが(作成元のクライアントとリモートクライアントの両方で)呼び出されると、Fusionはオブジェクトがローカルで制御可能かどうかを正しく指示できます。上記のスクリプトはそれに基づいて色を割り当てるため、ローカルでスポーンしたオブジェクトとリモートでスポーンしたオブジェクトを区別できます。

また、このスクリプトは、権限情報に基づいてオブジェクトの移動を制御します。つまり、このオブジェクトを「所有」しているクライアントのみが、オブジェクトに変更を適用することが可能です。(リモートクライアントは、所有していないオブジェクトに対して書き込んではいけません。書き込みを行っても、クライアント側でもサーバー側でも変更は反映されません。)

ステップ 9 - テスト

Godotは同じエディターから複数のインスタンスを簡単に起動できるため、ローカルでのテストも容易です。

  1. デバッグ > 実行インスタンスのカスタマイズ... > 複数インスタンスを有効にチェックを入れて2に設定します。
  2. 再生ボタンを押すと、両方のインスタンスが接続されます(接続ボタンを使用している場合は、両方のウィンドウでボタンをクリックしてください)。

どちらのクライアントも同じルームに参加し、互いのプレイヤーシーン/オブジェクトが確認できるはずです。これでコアループは完成です。次のステップでは、ゲームプレイ機能を追加していきます。

ステップ 10 - カスタムプロパティ

Autoモードを使用すれば位置は簡単に複製できますが、ゲームプレイデータ(体力・スコア・その他のデータ)は手動で追加する必要があります。FusionReplicatorの下部パネルを開き、シーン内の任意の箇所からカスタムプロパティを登録してください。それらはルートプロパティと一緒に自動的に複製されます。

Player FusionReplicator Settings
Player FusionReplicator Settings

Godotのsetterセマンティクスを使用することで、所有権/権限を持つクライアントとリモートクライアントの両方で、任意のプロパティの変更を簡単に捕捉することができます。

GDScript

var player_name: String = "":
    set(value):
        player_name = value
        $Label.text = value

このコードは、カスタムプロパティが変更された際に、ラベルのテキストを自動的に設定します。

これをテストするには、Playerノードの移動スクリプトにこの変数を追加し、FusionReplicatorPlayer:player_nameを追加してください。

そして例えば、player_nameは、同じ移動スクリプトの_ready()内で次のように設定できます。

GDScript

if sync.has_authority():
    player_name = "Player" + str(FusionClient.get_local_player_id())

他のプロパティの型や配列がどのように複製されるかは、レプリケーションをご覧ください。

ステップ 11 - RPC

ゲーム要素すべてが継続的な状態とは限りません。チャットメッセージ・ヒット通知・スキル発動など、一度きりのイベント(例:ローカルクライアントが「所有」していないオブジェクトへの変更を必要とするもの)は、RPCとして送信する方が適している場合があります。RPCは信頼性がありますが、発火は一度のみで、サーバーの状態バッファには保存されないため、途中参加者には届かないことに注意してください。

GDScript

@rpc("any_peer", "call_local")
func show_message(text: String):
    $MessageLabel.text = text

func send_message():
    FusionClient.rpc(show_message, "Hello!")

RPCをテストするには、上記コードを移動スクリプトに追加し、さらに_process関数のhas_authority()内に次のコードを追加します。

GDScript

if Input.is_key_pressed(KEY_E):
    send_message()

再生ボタンを押してEキーを押すと、両方のクライアントのMessageLabelHello!に更新されます。

ブロードキャストRPCや特定対象への配信などのバリエーションについては、RPCをご覧ください。

次のステップ

このガイドでは、接続・スポーン・複製・RPCの基礎について解説しました。これらのトピックについては、より詳細な専用マニュアルページも存在します。以下は次の学習ステップとしての推奨リストです。

Back to top