Unityのネットワーク機能 UNETを使ってみる


とりあえず最低限の機能だけ実装して、どんな感じかみてみる。

UNETの概要については下記の記事を参照
http://tsubakit1.hateblo.jp/entry/2015/06/13/233000
http://tips.hecomi.com/entry/2015/08/14/220030

こんな感じのものを作ってみる
---------------
マルチプレイで、ステージ上の標的を弾丸でうつというだけのもの。
![application.png](https://qiita-image-store.s3.amazonaws.com/0/20773/2cb31a39-eeba-d449-5030-ef9e367ca193.png "application.png")



下準備
---------------
まずは必要なオブジェクトをヒエラルキーに置いていく。

- 標的（Cube）と、弾丸（Sphere）、プレイヤー（Capsule）、床（Plane）を配置
制御用に空の GameObject を2つ、Main と UnetManager を置く
![objects.png](https://qiita-image-store.s3.amazonaws.com/0/20773/8b5425da-6f7a-8a22-3c5d-22a15b148c14.png "objects.png")



- ネットワーク共有させるオブジェクト、標的（Cube）と、弾丸（Sphere）、プレイヤー（Capsule）、それぞれに `NetworkTransform` をアタッチ、Sync Mode を `Sync transform` にする。(`NetworkTransform` をアタッチすると `NetworkIdentity` も自動的にアタッチされる）

- プレイヤー（Capsule）のインスペクタの `NetworkIdentity` にある `Local Players and Authority` にチェックをつける
![player.png](https://qiita-image-store.s3.amazonaws.com/0/20773/774172e6-13a7-842c-d121-f10c3b95e4c3.png "player.png")


- Cube には Rigidbody を付けておく

- 空の GameObject （Main）に `NetworkIdentity` をアタッチ

- UnetManager に、 `NetworkManager` および `NetworkManagerHUD` をアタッチ

- Cube と Sphere、 Player を Prefab 化し、ヒエラルキー上から削除する（`NetworkManager` が配置する）

- ここまでのプロジェクトファイルの構造はこんな感じ
![hierarchy.png](https://qiita-image-store.s3.amazonaws.com/0/20773/7e3041a8-bddd-0696-181c-835caa72e0ac.png "hierarchy.png")



スクリプトを実装する
----------------

### Cube を適当なタイミングで生成する、Main.cs
これは空の GameObject（Main）にアタッチする。
ネットワーク系の操作を行うので、`NetworkBehaviour` を継承させる。
オブジェクトの生成は `Instantiate` で行うのは通常どおり。ネットワークで共有するには `NetworkServer.Spawn` にオブジェクトを渡すが、これは Command attribute で行う必要がある。

```C#Main.cs
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class Main : NetworkBehaviour
{
    public GameObject Cube;

    void Start ()
    {
    }

    void Update ()
    {
        if (Time.frameCount % 300 == 0)
        {
            CmdCreateCube ();
        }
    }

    [Command]
    void CmdCreateCube()
    {
        GameObject obj = (GameObject) Instantiate (Cube, transform.position, transform.rotation);
        NetworkServer.Spawn (obj);
    }
}
```

アタッチしたら、インスペクタの Cube プロパティに、Cube Prefab を設定。

### 弾丸用クラス Sphere.cs
こちらはすごく単純にただ飛んで行くだけのオブジェクト。Sphere Prefab にアタッチする

```C#:Sphere.cs
using UnityEngine;
using System.Collections;

public class Sphere : MonoBehaviour
{
    void Start()
    {
        Destroy(gameObject, 5f);
    }

    void Update()
    {
        transform.position += transform.forward * 2.0f;
    }
}
```

### プレイヤーの制御を行う、Player.cs
次にプレイヤーの実装。Player Prefab にアタッチする。
アローキーで移動して、スペースキー押下で、Sphere を射出させるのだけのスクリプト。これも、`NetworkBehaviour` を継承させておく。
射出した弾丸（Sphere）は、Main.cs と同様に NetworkServer.Spawn で共有させる。

操作を反映させたいのは、自分自身のオブジェクト（local）だけなので、 `isLocalPlayer` で判定する。

```C#Player.cs
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class Player : NetworkBehaviour
{
    public GameObject Sphere;

    void Start ()
    {
    }

    void Update ()
    {
        if (isLocalPlayer == true)
        {
            if (Input.GetKey (KeyCode.Space))
            {
                CmdSphere ();
            }

            Move ();
        }
    }

    void Move()
    {
        if (Input.GetKey (KeyCode.UpArrow))
        {
            transform.position += transform.forward * 0.2f;
        }

        if (Input.GetKey (KeyCode.DownArrow))
        {
            transform.position += transform.forward * -0.2f;
        }

        if (Input.GetKey (KeyCode.LeftArrow))
        {
            transform.position += transform.right * -0.2f;
        }

        if (Input.GetKey (KeyCode.RightArrow))
        {
            transform.position += transform.right * 0.2f;
        }
    }

    [Command]
    void CmdSphere()
    {
        GameObject obj = (GameObject) Instantiate (Sphere, transform.position, transform.rotation);
        NetworkServer.Spawn (obj);
    }
}
```

アタッチしたら、インスペクタの Sphere プロパティに、Sphere Prefab を設定。

ネットワーク系を設定する
----------------
`NetworkManager` のインスペクタに Player と Spawn オブジェクトを登録する。

![spawn.png](https://qiita-image-store.s3.amazonaws.com/0/20773/9b2c5d15-f1cd-7453-01b0-2bf53e45a8ea.png "spawn.png")




実行する
----------------

- エディター側を、LAN Host 、ビルドしたアプリ側の LAN Client を押下すると同期が開始される
※ Run in Background は、`NetworkManager` がデフォルトでオンにしてくれる
![application.png](https://qiita-image-store.s3.amazonaws.com/0/20773/2cb31a39-eeba-d449-5030-ef9e367ca193.png "application.png")

ついでにパケットキャプチャみてみる（BSDname はローカルIPのインターフェイスを選ぶ）

```
sudo tcpdump -i [BSDname] port 7777 -X
```
_スクショのログはこれ_

ネットワーク上の別PCと通信するには、ビルドしたアプリを配布して行う。LAN Client にホスト側のIPを設定するのを忘れずに。