Bolt 103 - プロパティとコールバック
Bolt 101
とBolt 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
スクリプトを添付する必要があります。
ゲームをビルドして2つのインスタンス、つまりサーバー1台とクライアント1台(またはそれ以上)を開始すると、Cubeを回転させたり、ネットワーク上で正常に複製できるようになります。
リモートのキューブの動きが滑らかでない点に気づくかもしません。円滑に補間されておらず、新しい位置に「スナップ」しています。
Boltにはこの処理をおこなう組み込み機能がありますので、有効化しましょう!
最初に、'CubeState'上の'CubeTransform'プロパティに対する Interpolation を有効にします。
まだ開いていない場合は、Window/Bolt/Assets からBolt Assetsウィンドウを開き、CubeState
をクリックして編集を開始してください。
Smoothing Algorithm
の欄が、CubeTransform
プロパティに関する設定の下にあることを確認してください。これをNone
からInterpolation
に変更してください。変更後、Boltを再度コンパイルして変更を認識させてください。これはBolt Assets
ウィンドウにある緑の矢印をクリックするか、Assets/Bolt/Compile Assembly
コマンドを実行することで行うことができます。
その後、ゲームの組み立てと開始を再度行うことができます。他のプレイヤーに対して、キューブがスムーズに内挿されていることが分かります。
Boltには、通常'micro stutter'と呼ばれるものを除去するための高度機能もありますが、開始時にリクエストされるものではないため、これについては現時点では触れません。これは、ゲームの終了時に最後のタッチとして加えるものです。
重要 キューブの動作が既にカクカクしている場合、Assets/Bolt/Compile Assembly
のコマンドを実行していることを確認してください。
現時点では、このゲームであなたがコントロールしているキューブがどれなのかを判別するのは少々困難です。どのキューブも見た目が全く同じであり、互いに見分けることができないためです。色分けすることでこれを解決してみましょう。
Bolt Editor
の中のCubeState
を開き、New Property
をクリックして、プロパティに CubeColor
と名前をつけ、そのタイプをColor
に割り当ててください。
その後、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;
}
}
以上の結果、ゲームのビルドと実行をおこなうと、以下の画像のような外観となります。
次章 >>に続きます。
Back to top