MLAPIを使って弾を撃って同期する (Unity)

Unity

前回の記事でPlayer同士は同期できました。
このままでは面白くないので、シューティングゲームのように弾を飛ばしたいと思います。

とりあえずシングルプレイ環境で弾を飛ばす

まずマルチプレイを考えずに弾を飛ばす機構を考えます。
Spaceキーを押したら単純なCubeが飛ぶとしましょう。

Playerクラス側はInput.GetKey(KeyCode.Space)でスペースキー監視します。
スペースが押されたら弾をinstantiateするだけ。
生成された弾がどう動くかはPlayerとは直接関係がないため別途Bulletクラスに記載します。
また連射間隔を制御するためにShot後に次回Shot可能になる時間を設定します。

PlayerController もしくは GunControllerなどの弾を発射するクラス

private Bullet bullet;
private Transform spawner;
private float nextShotTime;
private float betweenShotsMs = 200;

void Update()
{
    if (Input.GetKey(KeyCode.Space))
    {
        this.Shot();
    }
}

void Shot()
{
    if (Time.time < nextShotTime)
    {
        return;
    }

    var bullet = Instantiate(this.bullet, spawner.position, spawner.rotation) as bullet ;
    this.nextShotTime = Time.time + this.betweenShotsMs / 1000;
}

Bulletクラスでは毎フレームごとに弾を前に進めること、
そして一定時間後に自動的にオブジェクトを破棄することを記載します。

Bulletクラス

private float speed = 10;
private float lifetime = 3;

void Start()
{
    Destroy(this.gameObject, lifetime);
}

private void Update()
{
    var moveDistance = speed * Time.deltaTime;
    this.transform.Translate(Vector3.forward * moveDistance);
}

あとは適当にCubeを作成し、BulletクラスをComponentに追加します。
そしてPrefabとして保存します。
とりあえずはこれで弾が出るようになりました。

マルチプレイ対応で弾を飛ばす

ではこれをマルチプレイヤー対応に変化させていきます。
まずBullet PrefabにNetworkedObjectコンポーネントを追加します。
NetworkingManagerにも忘れずにBullet Prefabを登録します。

ここからはShotの際の処理を書き換えます。
要点は1つ。
シングルプレイでは弾の生成は各自が行えばよかったのですが、マルチプレイの状態で各Clientが好き勝手に弾を生成すると状態の管理が難しくなります。
なので基本的にネットワーク上で同期するオブジェクトの生成は全てサーバーで行い、Clientはサーバーからの生成を受けてClient内で描画する形にします。

流れとしては
Clientが弾を撃つコマンドをサーバーに送る
→ サーバーが弾を生成する。
→ サーバーが各Clientに通知する。
→ 各Clientで弾を描画する。
となります。

ではそのように処理を書き換えましょう。

PlayerController もしくは GunControllerなどの弾を発射するクラス

private Bullet bullet;
private Transform spawner;
private float nextShotTime;
private float betweenShotsMs = 200;

void Update()
{
    if (Input.GetKey(KeyCode.Space))
    {
        if (Time.time < nextShotTime)
        {
            return;
        }

        InvokeServerRpc(Shot);
        this.nextShotTime = Time.time + this.betweenShotsMs / 1000;
    }
}

[ServerRPC]
private void Shot()
{
    var bullet = Instantiate(this.projectile, spawner.position, spawner.rotation) as bullet;
    bullet.GetComponent<NetworkedObject>().Spawn();
}

少し書き方を変えたので見にくいかもしれません。
違うところは[ServerRPC]のアトリビュートがついたことと、Instantiate部分だけが分離したことです。

先ほど説明したようにオブジェクトの生成はサーバーのみで実行しなければなりません。
その際に使うのが[ServerRPC]のアトリビュートです。
省略せずに書くとMLAPI.Messaging.ServerRPCです。

RPCというのはRemote Procedure Callと呼ばれるもので、別端末のメソッドを実行する取り決めです。今回はServerRPCですのでサーバーで実行するメソッドという理解で問題ありません。

サーバーでオブジェクトの生成を行うこととクライアントで行うことの違いが1つだけあり、
それがbullet.GetComponent<NetworkedObject>().Spawn();の行です。
NetworkedObjectのSpawnメソッドを実行するとサーバーでのオブジェクトの生成が各Clientに伝播します。
ということでかなり簡単にマルチプレイで弾を飛ばすことができましたね。

https://mlapi.network/wiki/messaging-system/
https://mlapi.network/wiki/object-spawning/

コメント