12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Unityアセット AnySync を使ってネットワークを介したゲームオブジェクトの位置のシンクロを簡単に

Posted at

はじめに

ネットワークゲームでは、ネットワーク上の各マシン上で共有されているゲームオブジェクトの位置が常にシンクロ(同期)していないと困りますが、それをきっちり作るのに意外と苦労しています、、、。最初はシンクロしているようでも、時間がたつと段々ズレてしまったりして。

そこで便利なのがAnySyncというUnity用アセットです(残念ながら有料。15ドル)

これは、ネットワーク上のマシン間でゲームオブジェクトのPosition, Rotationなどを共有するためのアセットです。UNET、Photonなど幅広い種類のライブラリと一緒に使うことができます。ただ、使用の際には少しコーディングが必要なので、その方法について解説します。なお、今回はLAN内P2P(peer to peer)に限定してお話するのでご注意ください。

準備

UnityのネットワークライブラリはUNET(2018.4LTSを最後に提供終了予定)が
最も身近ですが、Izmさんのこちらの記事を参考にした結果、UNETと近い感覚で使えて動作が安定している Mirror という無料アセットを使うことにしました。

Unityプロジェクトを作ったら、MirrorとAnySyncをダウンロード、インポートしてください。

シンクロさせるゲームオブジェクトの設定

今回は、2つのPCで一方にHost,もう一方にClientを実行し、Host側でスペースキーを押すとBallというプレハブがインスタンス化され、それをHost側で動かすとClient側でも動くようにします。

image.png

まず、メニューからGameObject/3D Object/Sphereを選んでゲームオブジェクトを作り、Ballという名前を付けてください。

次に、BallにNetwork Identityコンポーネントをアタッチします。Server Only,Local Player Authorityのチェックは不要です。更に、Sync Bufferもアタッチします。これには、シンクロさせたい位置情報などが保存されます。

image.png

それでは、シンクロに関する処理のためのスクリプトBallSync.csを準備しましょう。AnySyncのサンプルプロジェクトに付属のものを基にしています。準備できたらBallにアタッチしてください。

BallSync.cs

using UnityEngine;
using Mirror;

public class BallSync : NetworkBehaviour
{

    private const float MinimumSendInterval = 0.05f; 

    private float _timeSinceLastSync;
    private Vector3 _lastSentPosition;
    private bool _idle;

    private SyncBuffer BallSyncBuffer;

    private void Awake()
    {
        BallSyncBuffer = GetComponent<SyncBuffer>();
    }

    private void Update()
    {
        if (hasAuthority)
        {
            _timeSinceLastSync += Time.deltaTime;
            if (_timeSinceLastSync >= MinimumSendInterval)
            {
                if (_lastSentPosition != transform.position)
                    _idle = false;

                if (!_idle)
                {
                    CmdSync(_timeSinceLastSync, transform.position);
                    if (_lastSentPosition == transform.position)
                        _idle = true;

                    _lastSentPosition = transform.position;
                    _timeSinceLastSync = 0f;
                }
            }
        }
        else
        {
            if (BallSyncBuffer.HasKeyframes)
            {
                BallSyncBuffer.UpdatePlayback(Time.deltaTime);
                transform.position = BallSyncBuffer.Position;
            }
        }
    }

    [Command]
    private void CmdSync(float interpolationTime, Vector2 position)
    {
        BallSyncBuffer.AddKeyframe(interpolationTime, position);
        RpcSync(interpolationTime, position);
    }

    [ClientRpc]
    private void RpcSync(float interpolationTime, Vector2 position)
    {
        if (isLocalPlayer || isServer)
            return;

        BallSyncBuffer.AddKeyframe(interpolationTime, position);

    }
}

この中で、[Command]アトリビュートのついているCmdSyncはホスト側で、[ClientRpc]アトリビュートのついているRpcSyncはクライアント側で実行されます。

Update関数内はhasAuthorityによって分岐しています。hasAuthorityがTrue、つまりAuthorityを持っている場合(今回はホスト側)はCmdSyncを実行して現在の位置情報をSync Bufferに送ります。更にその中でRpcSyncを実行して、クライアント側のSyncBufferにも位置情報を送ります。これでホスト、クライアント両方で同じ位置情報がバッファーに保存されます。hasAuthorityがFalseの場合(今回はクライアント側)はSync Bufferに保存されているデータを読み取ってクライアント側のBallの位置として代入しています。

これでBallの準備はできたので、Ballをプロジェクトウィンドウにドラッグして、プレハブ化してください。ヒエラルキー内のBallは消しましょう。

image.png

他のゲームオブジェクトの設定

次に、BallSpawnerという空のゲームオブジェクトを作ってください。Network Identityコンポーネントをアタッチして、Server Onlyにチェックをいれます。これにより、BallSpawnerはホスト側だけで動作するようになります。

image.png

次に、スクリプト BallSpawner.csを作ってBallSpawnerオブジェクトにアタッチしてください。

BallSpawner.cs
using UnityEngine;
using Mirror;

public class BallSpawner : NetworkBehaviour {

    public GameObject Ball;
	
	void Update () {
		if (Input.GetKeyDown(KeyCode.Space))
        {
                Fire();
        }
	}

    void Fire()
    {
        var go = Instantiate(Ball);
        NetworkServer.Spawn(go);
    }
}

インスペクタ上で変数BallにBallプレハブをアサインするのを忘れずに。

これで、スペースキーを押すとホスト側でBallがインスタンス化されて、さらに、NetworkServer.Spawn(go)によりクライアント側でもBallオブジェクトが生成されます。

最後に、NetworkManagerという空のゲームオブジェクトを作ってください。そちらに、Network Managerコンポーネント、Network Manager HUDコンポーネントをアタッチしてください。さらに、Network Managerコンポーネントの中のSpawn Info中のRegistered Spawnable PrefabsにBallプレハブをアサインしてください。

image.png

完成

これで完成です。スタンドアロンとしてビルドして、エディターと一緒に実行してみましょう。

image.png

この画面になったら、Editor側ではLAN Host, ビルドしたアプリ側ではLAN Clientをクリックしてください。次に、スペースバーを押せばBallが出てきて、シーンビューか何かでBallを移動させれば、Client(スタンドアロンアプリ)側でも同じように移動するはずです。

AnySync.gif

12
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?