BoltEntity
BoltEntityは、Boltによりネットワーク上で表されるUnityのゲームオブジェクトです。
GameObjectをネットワークとしてマークするには、 BoltEntityコンポーネントを使用する必要があります。
このコンポーネントを使用すると、通常のUnityのPrefabをネットワーク要素に変換し、Boltがデータを抽出、同期、管理できるようにします。
また、エンティティの状態を操作して、ゲームの状態を反映するように変更できるようにします。
BoltEntityは、UnityまたはuLinkのNetworkViewまたはPUNのPhotonViewに類似しています。これはネットワーク・アウェアのオブジェクトを表すものであり、BoltがUnity内の実際のゲームオブジェクトを管理・複製するための基盤となるものです。
エンティティの所有権
BoltEntityはサーバーまたはクライアントからのものではありません。Boltの観点では、エンティティの所有権はOwnerと Controllerの2つの別々のカテゴリに分類されます。代わりに、以下のものである可能性があります。
- Owned、entity.isOwner == trueの場合。
- Controlled、entity.hasControl == trueの場合。
- Proxy、entity.isOwner == false && entity.hasControl == falseの場合。
Owner
Owner は、 BoltNetwork.Instantiate呼び出しが発行されるピアに割り当てられる変更不可能なプロパティです。
このピアは、BoltEntity.isOwnerがtrueを返す唯一のピアです。
エンティティの所有権を他人に譲渡することはできません。
エンティティの所有者は、すべて(状態、変換など)を完全に制御できます。
Controller
エンティティのControllerは割り当て、剥奪、他のピアへ転送することができるものです。
エンティティの制御を割り当てたり、奪うことができるのはOwnerのみです。
Owner は、 entity.AssignControl(otherConnection)を呼び出して他のピアへの接続を渡すことにより、エンティティの制御を別のピアに割り当てることができます。
制御は、 entity.RevokeControl()を呼び出して削除することもできます。
このピアは、このエンティティのControllerと見なされます。
Ownerは自身でエンティティ自身を制御することもでき、その場合、OwnerとController の両方と見なされます。
あなたがOwnerであるエンティティを制御するには、 entity.TakeControl()を呼び出します。制御を解放するにはentity.ReleaseControl()を使用します。
entity.hasControlプロパティがtrueを返すのはControllerのみです。
どちらでもない場合
エンティティのOwnerでもControllerでもない場合、 entity.isOwnerと entity.hasControlはどちらもfalseを返します。
問題のエンティティはあまり操作できません。通常、これはゲーム内で制御できないものです(たとえば、他のプレイヤーのアバターなど)。
BoltEntity コンポーネントの設定
BoltEntityコンポーネントで利用可能なすべての設定の内訳は次のとおりです。
また、MiscellaneousセクションのBolt SettingsウィンドウでShow BoltEntity Settings hintsを有効にすると、Unityの各フィールドの説明が表示されます。
プレハブと状態
- Type: 現在のゲームオブジェクトのUnityのPrefabType値を示すユーティリティフィールド。
- Scene Id: このEntityの一意のID。Boltが初期設定のためにネットワーク経由で参照するために使用するScene Entityの場合。
- Id: Bolt内のこのプレハブに割り当てられた内部ID。 シーンオブジェクトの場合は常に0です。
- State: このBolt Entityに使用される状態。
設定
- Replication Rate: Boltがこのエンティティの更新を送信する頻度を制御します。(i)1 =すべてのパケット、2 =1パケットおきなど。
- Persistent: Boltが BoltNetwork.LoadSceneで読み込まれたシーン間でこのオブジェクトを保持する必要がある場合に使用。
- Always Replicate: この設定により、Boltがゲームの現在の読み込み状態を無視するかどうかを制御し、シーンの読み込み中であっても常にこのエンティティをプロキシすることができます。
 これはUnityのGameObject.DontDestroyOnLoadのようなものです。
 これは、特定のマップやキャラクターではなく、ゲーム全体に関する情報を含む「メタ」オブジェクトに役立ちます。
- Proxy When Frozen: 有効にすると、Boltは、凍結されていても、このエンティティが最初の複製を実行できるようにします。
- Detach On Disable: 有効にすると、このエンティティは無効になったときにネットワークから切り離されます。
- Auto Attach On Load: 有効にすると、シーンエンティティがマップの読み込み時に自動的にアタッチされます。
- Auto Freeze Frames: 0以上に設定すると、このエンティティは、指定されたフレーム数のネットワーク更新を受信していない場合、所有者以外のこのエンティティはBoltによって自動的に凍結されます。
- Controller Predicted Movement: この設定では、エンティティのコントローラーでローカル予測を使用している場合にBoltを指定できます。つまり、ローカル予測と修正を実行できるキャラクターモーターで SimulateControllerとExecuteCommandを使用しています。
- Remove Parent On Detach: 有効にすると、デタッチされるエンティティの変換階層全体でネストされたエンティティを検索し、transform.parentをnullに設定するようにBoltに指示します。
- Allow Client Instantiate: Bolt SettingsウィンドウでInstantiate ModeをIndividual On Each Prefabに切り替えた場合にのみ使用できます。
 クライアントがこのプレハブのBoltNetwork.Instantiateを呼び出すことができるかどうかを制御できます。
- IEntityBehaviour Query: Boltは、新しいエンティティをインスタンス化するときに、 IBoltEntityBehaviourインプリメンターを動的にクエリします。 デフォルトでは、GetComponentsInChildrenを実行するグローバル設定を使用しますが、この動作はここで変更できます。
- IPriorityCalculator Query: Boltは新しいエンティティをインスタンス化するときに、IPriorityCalculatorインプリメンターを動的にクエリします。 デフォルトでは、GetComponentsInChildrenを実行するグローバル設定を使用しますが、この動作はここで変更できます。 注:ほとんどのユーザーはこれを実装しません。その場合、パフォーマンス上の理由からグローバルまたはプレハブのいずれかでNoneを選択します。
- IEntityReplicationFilter Query: Boltは、新しいエンティティをインスタンス化するときに、 IEntityReplicationFilterインプリメンターを動的にクエリします。 デフォルトでは、GetComponentsInChildrenを実行するグローバル設定を使用しますが、この動作はここで変更できます。 注:ほとんどのユーザーはこれを実装しません。その場合、パフォーマンス上の理由からグローバルまたはプレハブのいずれかでNoneを選択します。
Bolt エンティティの詳細
BoltEntityを他のものに同期する
状態をOwnerから他のクライアントに同期させるには、Stateの作成が必要です。
これはBoltのBolt/Assetsから行うことができます。
新しい Stateを作成することで、ネットワークに接続する必要があり、特定の BoltEntityにアタッチされるすべてのプロパティの説明を作成します。
利用可能なプロパティタイプは次のとおりです。
- Array: 配列形式に編成された、同じタイプの値のコレクション。インデックスによって個別のアイテムにアクセスできます。
- Bool: ブール値。
- Color: Unity Color インスタンス。
- Color32: Unity Color32 インスタンス。
- Entity: 他の BoltEntityへの参照。
- Float: float値。
- Guid: System.Guidのインスタンス。
- Integer: 整数値。
- Matrix4x4: Unity Matrix4x4 インスタンス。
- NetworkId: Bolt.NetworkIdへの参照。
- Object: Object Bolt Assetタイプのインスタンス。
- PrefabId: Bolt.PrefabIdへの参照。
- ProtocolToken: Bolt.IProtocolTokenを実装するカスタムトークンへの参照。
- Quaternion: Unity Quaternion インスタンス。
- String: 文字列値。
- Transform: Unity Transform インスタンス。
- Trigger: 特別なワンファイア状態。
- Vector: Unity Vector3 インスタンス。
状態を作成/変更した後は、必ずボルトアセットを再コンパイルしてください
BoltEntityの管理
BoltEntityの管理について設定するには、そのエンティティのExecuteCommand()とSimulateController()メソッドをオーバーライドする必要があります。
これは通常、クライアント側の予測 により行われます。
ただし、BoltEntityの実際のStateについてオーナーは完全な管理を所有し続けます。
オーソリテーティブな移動、およびクライアントに予測された移動を用いる場合には変換コンポーネントを直接用いるか、Character Controllerコンポーネントを用いる必要があります。Mecanimのルートモーションまたはrigidbodiesは、キャラクターの管理に使用できません。
メソッド: BoltEntity.SimulateController()
エンティティの管理を割り当てられている人物上でのみ動作します。
これは、 entity.TakeControl()をエンティティに対して呼び出すことで自身に管理を割り当てたオーナーか、あるいはオブジェクトのリモートプロキシを与えられ、オーナーがentity.AssignControl(BoltConnection connection)を呼び出すことでコントロールを付与した人物である可能性もあります。
実行用のコマンドを整列するために、SimulateControllerのentity.QueueCommandを呼び出すこともできます。
メソッド: BoltEntity.ExecuteCommand(BoltCommand cmd, bool resetState)
この機能はオーナーとコントローラーの両方で動作しますが、それぞれでの動作は異なります。
オーナー上ではOwnerがコントローラーかどうかに関わらず、それぞれのコマンドについて 一度のみ動作します。
コマンドがローカルで作成されたかどうかや、オーナー自身に作成されたかどうか、管理権を与えられたリモート接続により発せられたものかどうかは無関係です。2番目のパラメータである resetStateは、オーナー上では決してtrueにはなりません。
コントローラー上では、そのコントローラーがオーナーでない場合 ExecuteCommandが少々複雑になります。
移動のローカルな予測と、Ownerからの状態の訂正の両方を扱うことができる必要があります。
前者はフレームごとに起こります。Boltは、Ownerにより実証された状態を持っていた最新のコマンドに渡します。このコマンドが resetStateにおいて渡されたとき、パラメータはtrueとなります。
実証された最新のコマンドがresetStateにより実行されたら、BoltはまだOwnerから実証されていないその他全てのコマンドを実行します。
つまり、ExecuteCommandはOwnerによって実証されるまで、連続した複数のフレームでリモートのコントローラー上の1つのコマンドに対して複数回呼び出されます。
resetStateを含むtrueに設定されたコマンドが実行された場合、Boltはキャラクターの動力のローカルの状態の設定をコマンドによって表される状態にしようとします。
ここではBoltCommand.Inputではなく、BoltCommand.Resultのみを用いて下さい。
これによりBoltは、リモートのコントローラー(多くの場合はクライアント)のローカルの動きを、オーナー(多くの場合はサーバー)からの正しい状態に合わせて修正することができます。
リセットが必要な状態は、何らかの形であなたのエンティティの移動に影響を及ぼす状態全てです。通常、位置や回転、立っているか匍匐前進しているかを示すような状態の変数などがこれに含まれます。また、複雑なコントローラーがある場合には速度や加速度、外力などをリセットする必要もあります。
状態リセットが正しくない、または完全でない場合にはコントローラー側の「ぎくしゃくした動き」の主な原因となります。
BoltCommand.isFirstExecutionの用途
コマンドからの直接的なアクションを行うコードです。例として、武器の発砲やアニメーションの状態設定があります。次のようなブロックに包括されていなくてはなりません:
C#
public override void ExecuteCommand(Bolt.Command cmd, bool resetState)
{
    if (resetState)
    {
        // reset code
    }
    else
    {
        if (cmd.isFirstExecution)
        {
            // First Execution code ...
        }
    }
}
リモートのコントローラー上では、ExecuteCommandは同一のコマンドのために複数回呼び出されることとなります。このため、アニメーションなどの直接的なアクションが最初の実行時にのみ起こるようにチェックしていない場合、リモートのコントローラー上でのキャラクターの行動は非常に奇妙なものとなってしまいます。
コントローラーでない通常プロキシについて
位置や回転、アニメーション、状態の同期は、これらのエンティティに対するBoltの状態オブジェクトを通じて行われます。
つまり、あらゆるタイプの状態やアクション、イベントを接続しているすべての人に対して可視化させたい場合、Bolt State Mecanimによって送信するか、Bolt Event上で送信する必要があります。Bolt Commandsはコントローラーかオーナー上のみで実行されるためです。
エンティティプーリング
内部的にBoltは IPrefabPoolインターフェースを実装します。デフォルトのBolt実装は、要求されたときにのみにプレハブを動的に割り当てて破棄します(もちろん、デフォルトの内部Bolt実装は内部的にプールしません)。この動作をオーバーライドしたい場合は、独自のカスタムクラスにIPrefabPoolを実装し、BoltNetwork.SetPrefabPool(IPrefabPool pool)を使用してBoltの実装をオーバーライドできます。 これを行うと、Boltプレハブの作成方法と破棄方法を完全に制御できます。 次に、独自のカスタムプーリングソリューションへの呼び出しを実装して、Boltを動的インスタンス化の使用からプーリングパターンに変換できます。必要に応じて、プレハブを別のプレハブに置き換えることもできます。たとえば、サーバープレハブとクライアントプレハブが必要な場合は、このメカニズムを使用して行うことができます。
これは、Boltがデフォルトで使用する単純なプール実装です。 実際には何もプールしないことに注意してください(エンティティを動的にインスタンス化して破棄するだけです)。
C#
using UnityEngine;
using Bolt;
public class DefaultPrefabPool : IPrefabPool
{
    public GameObject Instantiate(PrefabId prefabId, Vector3 position, Quaternion rotation)
    {
        GameObject go;
        go = GameObject.Instantiate(LoadPrefab(prefabId), position, rotation);
        go.GetComponent<BoltEntity>().enabled = true;
        return go;
    }
    public void Destroy(GameObject gameObject)
    {
        GameObject.Destroy(gameObject);
    }
    public GameObject LoadPrefab(PrefabId prefabId)
    {
        return PrefabDatabase.Find(prefabId);
    }
}
実装クラスを使用するには、Boltの起動が完了したときにクラスを登録する必要があります。
C#
public class MyGlobalListener : Bolt.GlobalEventListener
{
    public override void BoltStartDone()
    {
        BoltNetwork.SetPrefabPool(new MyPool());
    }
}
コントローラーでない通常プロキシについて
エンティティの直接的な管理下になく、エンティティのオーナーでもない通常プロキシ上において施行される唯一のコールバックは SimulateProxy() であり、SimulateController() や ExecuteCommand() は稼働しません。
位置や回転、アニメーション、状態の同期は、これらのエンティティに対するBoltの状態オブジェクトを通じて行われます。
つまり、あらゆるタイプの状態やアクション、イベントを接続しているすべての人に対して可視化させたい場合、Bolt State Mecanimによって送信するか、Bolt Event上で送信する必要があります。Bolt Commandsはコントローラーかオーナー上のみで実行されるためです。