PUN Classic (v1)、PUN 2、Boltはメンテナンスモードとなっております。Unity2022についてはPUN 2でサポートいたしますが、新機能が追加されることはありません。お客様のPUNプロジェクトおよびBoltプロジェクトが停止することはなく、将来にわたってパフォーマンス性能が落ちることはありません。 今後の新しいプロジェクトについては、Photon FusionまたはQuantumへ切り替えていただくようよろしくお願いいたします。

Bolt 103 - プロパティとコールバック

Bolt 101Bolt 102では、Boltの実行方法、プロパティの取得、ネットワーク上でのゲームオブジェクトの複製について説明しました。このページではネットワーク上でプロパティを複製する(シリアル化する)方法について説明します。

Cube とステート CubeState を作成した際に、 CubeTransformという名前のプロパティを追加しました。
ここでは、このプロパティを使用して Bolt エンティティのコーディングをおこないます。

Tutorial/Scripts フォルダ内に CubeBehaviour という名前のスクリプトを作成し、任意のテキストエディタで開きます。
デフォルトのUnityメソッドを削除し、通常のMonoBehaviourの代わりにBolt.EntityBehaviour<ICubeState>からクラスを継承するように設定してください。

C#

using System.Collections;
using UnityEngine;

public class CubeBehaviour : Bolt.EntityBehaviour<ICubeState>
{
    // Your code here...
}

CubeBehaviour 内にコードを追加する前に、 Bolt.EntityBehaviour<ICubeState> が具体的に何をおこなうのかについて見ておきましょう。
ICubeState は、Assets/Bolt Engine/Compile Assembly を実行した際にBoltが自動で作成するC# interfaceで、そのステートで定義した全てのプロパティを露出します。
これにより、ステートのすべてのプロパティへの静的に型付けされた簡易的なアクセスが得られます。

継承するクラスはBoltソースコードでBolt.EntityBehaviour<T>として定義されており、このクラスは総体的なパラメーターとして用いたいステートのタイプを取ります。これによって、 CubeBehaviour スクリプト内でアクセスしたいステートのタイプをBoltに伝えることができます。

'Bolt 101'からの Bolt.GlobalEventListenerクラスと同様、Bolt.EntityBehaviour<T>クラスはMonoBehaviourから継承します。ここでも、通常のUnityのメソッドを用いることができます。

Attachedという名前の特殊なBoltメソッドを実装します。このメソッドはUnityに存在するStart メソッドに相当するものとみなされますが、ゲームオブジェクトがBolt内にセットアップされてネットワーク上に存在する場合にのみ呼び出されます。

C#

using UnityEngine;
using System.Collections;

public class CubeBehaviour : Bolt.EntityBehaviour<ICubeState>
{
    public override void Attached()
    {
        // Your code here...
    }
}

重要: Bolt 101 からの SceneLoadLocalDone メソッドのように、メソッド実装のpublic override ... バージョンを使用します。このメソッドは、Unityがおこなうものとは異なります。

Attached メソッド内にコードを1行追加します:state.SetTransforms(state.CubeTransform, transform); 以下に詳述します:

  • state. - この部分はエンティティのステート、すなわちICubeStateにアクセスします。
  • transform - GameObjectの変換。
  • CubeTransform. - ここで、Bolt AssetsおよびBolt Editorウィンドウのステートで定義したCubeTransformプロパティにアクセスします。
  • SetTransforms - ここでBoltに、CubeBehaviourスクリプトが添付された最新のゲームオブジェクトの転換を使用するよう、またネットワーク上で複製するように伝えます。

サンプルコードの全体は以下のとおりです。

C#

using UnityEngine;
using System.Collections;

public class CubeBehaviour : Bolt.EntityBehaviour<ICubeState>
{
    public override void Attached()
    {
        state.SetTransforms(state.CubeTransform, transform);
    }
}

CubeBehaviourに非常にシンプルなムーブメントコードを追加します。通常の void Update() ...を用いることもできますが、
Boltには SimulateOwnerと呼ばれるメソッドもあります。SimulateOwnerの実装は Attached と同様におこない、その中にムーブメントコードを追加します。

C#

using UnityEngine;
using System.Collections;

public class CubeBehaviour : Bolt.EntityBehaviour<ICubeState>
{
    public override void Attached()
    {
        state.SetTransforms(state.CubeTransform, transform);
    }

    public override void SimulateOwner()
    {
        var speed = 4f;
        var movement = Vector3.zero;

        if (Input.GetKey(KeyCode.W)) { movement.z += 1; }
        if (Input.GetKey(KeyCode.S)) { movement.z -= 1; }
        if (Input.GetKey(KeyCode.A)) { movement.x -= 1; }
        if (Input.GetKey(KeyCode.D)) { movement.x += 1; }

        if (movement != Vector3.zero)
        {
            transform.position = transform.position + (movement.normalized * speed * BoltNetwork.frameDeltaTime);
        }
    }
}

SimulateOwner 内のコードは標準のUnityに準拠しており、非常にシンプルです。

唯一Boltに固有な点は、BoltNetwork.frameDeltaTime プロパティです。
現在、これには単に組み込み式のUnity Time.fixedDeltaTime プロパティが含まれていますが、将来的な互換性のためにBoltのバージョンの使用を推奨します。

先へ進む前に、少し戻ってSimulateOwnerが何であるか見てみましょう。
BoltNetwork.Instantiate を呼び出したコンピュータは常に オーナー(詳細は 用語集を参照してください)
として認識されます。オーナーにおいて SimulateOwner が呼び出され、これはこのコンピュータ 以外で呼び出されることはありません。
つまり、プレハブをインスタンス化したコンピューター上でのみ実行する移動またはその他に対する特定のコードを networkView.isMine の確認なしで書くことができます。

最後に、ゲームを開始する前にCubeプレハブにCubeBehaviourスクリプトを添付する必要があります。

Attach Cube Behavior to Cube Prefab
Cubeの挙動をCubeプレハブに添付

ゲームをビルドして2つのインスタンス、つまりサーバー1台とクライアント1台(またはそれ以上)を開始すると、Cubeを回転させたり、ネットワーク上で正常に複製できるようになります。

Cubes Gameplay
Cubeのゲームプレイ

リモートのキューブの動きが滑らかでない点に気づくかもしません。円滑に補間されておらず、新しい位置に「スナップ」しています。
Boltにはこの処理をおこなう組み込み機能がありますので、有効化しましょう!

最初に、'CubeState'上の'CubeTransform'プロパティに対する Interpolation を有効にします。
まだ開いていない場合は、Window/Bolt/Assets からBolt Assetsウィンドウを開き、CubeStateをクリックして編集を開始してください。

Smoothing Algorithmの欄が、CubeTransform プロパティに関する設定の下にあることを確認してください。これをNone からInterpolationに変更してください。変更後、Boltを再度コンパイルして変更を認識させてください。これはBolt Assets ウィンドウにある緑の矢印をクリックするか、Assets/Bolt/Compile Assembly コマンドを実行することで行うことができます。

Cube Transform Smoothing Algorithm
Cube Transform Smoothing Algorithm

その後、ゲームの組み立てと開始を再度行うことができます。他のプレイヤーに対して、キューブがスムーズに内挿されていることが分かります。
Boltには、通常'micro stutter'と呼ばれるものを除去するための高度機能もありますが、開始時にリクエストされるものではないため、これについては現時点では触れません。これは、ゲームの終了時に最後のタッチとして加えるものです。

重要 キューブの動作が既にカクカクしている場合、Assets/Bolt/Compile Assembly のコマンドを実行していることを確認してください。

現時点では、このゲームであなたがコントロールしているキューブがどれなのかを判別するのは少々困難です。どのキューブも見た目が全く同じであり、互いに見分けることができないためです。色分けすることでこれを解決してみましょう。

Bolt Editor の中のCubeStateを開き、New Propertyをクリックして、プロパティに CubeColor と名前をつけ、そのタイプをColorに割り当ててください。

Cube Color Asset
Cube Color Asset.

その後、Compile Assembly コマンドをもう一度実行してください。
CubeBehaviour クラスとAttached メソッドにコードを数行追加し、これによりそれぞれのキューブの色をランダムに割り当てます。

C#

// ...
public override void Attached()
{
    state.SetTransforms(state.CubeTransform, transform);

    if (entity.isOwner)
    {
        state.CubeColor = new Color(Random.value, Random.value, Random.value);
    }
}
// ...

新たなコードはif文とその中にあるコードの行です。entity.isOwnerをチェックするのは、そのオブジェクトのインスタンスを作成した人物についてのみこれを行うためです。全員に対してではありません。

SimulateOwner を使わないのはなぜか、と思っている人もいるかもしれません。確かに SimulateOwner で、オーナーでのみ動作するコードを書くことは可能です。
しかしこの場合、毎フレームごとに実行されることになり、今回はこれは不要です。
AttachedOwner の特殊なコールバックはありません。Attached内のコードのほとんどは、全員にとって全く同じだからです。

オーナーについて色を設定した後は、関連した設定を行っていきましょう。
プロパティコールバック について見てみましょう。

プロパティコールバックを使用すると、プロパティが値を変更した際に常に呼び出されるように、メソッドに接続することができます。
まずは、新たなメソッドとしてColorChangedを作成します。これにより、 state.CubeColor から色を選び、
それをレンダラーのmaterial.color プロパティに割り当てます。

C#

// ...
void ColorChanged()
{
    GetComponent<Renderer>().material.color = state.CubeColor;
}
// ...

以上の設定で、Attached メソッドの終了時 Attached メソッドに接続し、state.CubeColor プロパティが変化したら常に呼び出されるようになります。

C#

// ...
public override void Attached()
{
    state.SetTransforms(state.CubeTransform, transform);

    if (entity.isOwner)
    {
        state.CubeColor = new Color(Random.value, Random.value, Random.value);
    }

    state.AddCallback("CubeColor", ColorChanged);
}
// ...

Attachedの最後の行には、 state.AddCallback("CubeColor", ColorChanged);とあります。これは鍵です。この時点でプロパティ名を入力する必要があります。
今回の場合は、文字列内の "CubeColor" がプロパティ名となります(今回はこれを静的に型付けしています)。

ゲームを再実行する前に、最後にCubeBehaviourの中に、以下のようなUnityのOnGUIメソッドを追加します。

C#

void OnGUI()
{
    if (entity.isOwner)
    {
        GUI.color = state.CubeColor;
        GUILayout.Label("@@@");
        GUI.color = Color.white;
    }
}

以下が CubeBehaviourに対する全体のソースコードです。

C#

using UnityEngine;
using System.Collections;

public class CubeBehaviour : Bolt.EntityBehaviour<ICubeState>
{
    public override void Attached()
    {
        state.SetTransforms(state.CubeTransform, transform);

        if (entity.isOwner)
        {
            state.CubeColor = new Color(Random.value, Random.value, Random.value);
        }

        state.AddCallback("CubeColor", ColorChanged);
    }

    void OnGUI()
    {
        if (entity.isOwner)
        {
            GUI.color = state.CubeColor;
            GUILayout.Label("@@@");
            GUI.color = Color.white;
        }
    }

    public override void SimulateOwner()
    {
        var speed = 4f;
        var movement = Vector3.zero;

        if (Input.GetKey(KeyCode.W)) { movement.z += 1; }
        if (Input.GetKey(KeyCode.S)) { movement.z -= 1; }
        if (Input.GetKey(KeyCode.A)) { movement.x -= 1; }
        if (Input.GetKey(KeyCode.D)) { movement.x += 1; }

        if (movement != Vector3.zero)
        {
            transform.position = transform.position + (movement.normalized * speed * BoltNetwork.frameDeltaTime);
        }
    }

    void ColorChanged()
    {
        GetComponent<Renderer>().material.color = state.CubeColor;
    }
}

以上の結果、ゲームのビルドと実行をおこなうと、以下の画像のような外観となります。

Cubes with colors
色付けされたキューブ

次章 >>に続きます。

Back to top