UnityからNode.jsサーバにWebSocketを使ってデータ送信

## Pref.
UnityからNode.jsで動かしてるサーバにオブジェクトの座標を送ってみたかったんです．
[こちら](https://qiita.com/oishihiroaki/items/bb2977c72052f5dd5bd9)の @oishihiroaki さんの記事を参考にwebsocket-sharpをUnityに入れてやってみたのでまとめます．ありがとうございます(๑•̀ㅂ•́)و✧

以下に今回作ったサンプルプロジェクトを置いておきました．

[https://github.com/nmxi/UnityWebSocketTest](https://github.com/nmxi/UnityWebSocketTest)

## Env.
Windows10 64bit
Unity 20181.5f1
Node v8.11.3

## Method
### 1.Unityに2つのライブラリをインポート
[こちら](https://qiita.com/oishihiroaki/items/bb2977c72052f5dd5bd9)の記事を参考にまず[websocket-sharp](https://github.com/sta/websocket-sharp)をUnityに入れます．

上記の記事の中ではMonoDeveropを利用してビルドし，dllファイルを生成しているのですが，Unity2018からエディタがVisual Studioになったので（？）今回はそちらの方法を簡単に書き残しておきます．簡単ですがｗ

まずGitHubからCloneかZipで保存しまして，<font color="Green">websocket-sharp.sln</font>をVisual Studioで開きます．

![アセット 1@.png](https://qiita-image-store.s3.amazonaws.com/0/181807/d2816f74-2356-ad7f-7b18-004044813880.png)

Visual Studioが開かれると思いますので，以下の画像のようにソリューションエクスプローラーの中からwebsocket-sharpを右クリックし，ビルドを選択します．このときソリューションエクスプローラーの中に存在しているExample*は必要ないので私は消しました．

![アセット 2@.png](https://qiita-image-store.s3.amazonaws.com/0/181807/9167a359-cd68-2c01-e378-4375fc42aef2.png)

ビルドをすると<font color="Green">websocket-sharp-master/websocket-sharp/bin/Debug</font>の中に
<font color="red">websocket-sharp.dll</font>が生成されます．

![image.png](https://qiita-image-store.s3.amazonaws.com/0/181807/5248dd52-3fc6-5772-3788-2589c51478b3.png)

このdllをUnityのプロジェクト側のAssets/Pluginsに入れてください．
これで，erbsocket-sharpのUnityへのインポートは完了です．

今回<font color="red">UniRX</font>もインポートしました．
UniRxは非同期処理においてはちゃめちゃ便利なライブラリです．
アセットストアからインポートインポートして下さい．

[https://assetstore.unity.com/packages/tools/integration/unirx-reactive-extensions-for-unity-17276](https://assetstore.unity.com/packages/tools/integration/unirx-reactive-extensions-for-unity-17276)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/181807/aea6b19b-0a76-4c04-253a-109766f31318.png)

2つともインポートが完了したら，UnityのAssets/Pluginsの中身は以下のようになっていると思います．

![image.png](https://qiita-image-store.s3.amazonaws.com/0/181807/8804e424-ab04-07cf-96f3-782bee367ae2.png)

### 2.UnityでWebSocketクライアントを作る
- Unity上のオブジェクトの3次元座標情報をNode.jsで動かしているサーバに送信
- オブジェクトの座標に変化があったときだけサーバに情報を送信

これらができるクライアントを作ります．

まずUnityの空間上に

- Box
- Button 2つ

を配置しました．こんな感じ．

![image.png](https://qiita-image-store.s3.amazonaws.com/0/181807/ea50bc30-a093-d867-b508-db1fd7331920.png)

スクリプトをひとつだけ書きました．

```PositionSync.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using WebSocketSharp;
using UniRx;

public class PositionSync : MonoBehaviour {

    [SerializeField] private string _serverAddress;
    [SerializeField] private int _port;

    [SerializeField] private Transform _syncObjTransform;   //Share transform

    [SerializeField] private SyncPhase _nowPhase;

    private WebSocket ws;

    public enum SyncPhase {
        Idling,
        Syncing
    }

    private void Awake() {
        _nowPhase = SyncPhase.Idling;

        var cTransformValue = gameObject.ObserveEveryValueChanged(_ => _syncObjTransform.position);
        cTransformValue.Subscribe(x => OnChangedTargetTransformValue(x));
    }

    /// <summary>
    /// Get Down Start Sync Button
    /// </summary>
    public void OnSyncStartButtonDown() {
        var ca = "ws://" + _serverAddress + ":" + _port.ToString();
        Debug.Log("Connect to " + ca);
        ws = new WebSocket(ca);

        //Add Events
        //On catch message event
        ws.OnMessage += (object sender, MessageEventArgs e) => {
            print(e.Data);
        };

        //On error event
        ws.OnError += (sender, e) => {
            Debug.Log("WebSocket Error Message: " + e.Message);
            _nowPhase = SyncPhase.Idling;
        };

        //On WebSocket close event
        ws.OnClose += (sender, e) => {
            Debug.Log("Disconnected Server");
        };

        ws.Connect();

        _nowPhase = SyncPhase.Syncing;
    }

    /// <summary>
    /// Get Down Stop Sync Button
    /// </summary>
    public void OnSyncStopButtonDown() {
        ws.Close(); //Disconnect
    }

    public void OnChangedTargetTransformValue(Vector3 pos) {
        if (_nowPhase == SyncPhase.Syncing) {
            Debug.Log(pos);
            ws.Send(pos.ToString());
        }
    }
}
```

本当に数分で書いたのではちゃめちゃですが...
以上のスクリプトでは
<b>
オブジェクトの<font color="green">Transform.position</font>に変化があったとき
&
Enumで定義した<font color="green">SyncPhase _nowPhase</font>が<font color="green">Syncing</font>のとき
</b>

サーバ側にVector3をstring型にして送信しています．
余談ですが「この値が変化したとき」といったときの処理にUniRxを使っています．

このスクリプトをEmptyObjectなどをMasterとかに名前を変えて空のオブジェクトを作成して，コンポーネントとして追加します．
コンポーネントを追加したら，サーバアドレスとポート，座標情報をサーバに送りたいオブジェクトのTransformを変数SyncObjTransformにセットします．サーバアドレスとポートは特に問題がなければ"localhost"と8080を入れておきます．

![image.png](https://qiita-image-store.s3.amazonaws.com/0/181807/8198869a-2767-9d85-caba-9dfaa5036b4c.png)

スタートボタンとストップボタンのOnClickには

- PositionSync.OnSyncStartButtonDowns
- PositionSync.OnSyncStopButtonDown

をセットしておきます．以下はStartButtonの例

![image.png](https://qiita-image-store.s3.amazonaws.com/0/181807/e28faf95-1a0b-12cb-d003-21946ae3fc52.png)

### 3.Node.jsでWebSocketサーバを作る
Node.jsを予めインストールしておいて下さい．
今回私の環境ではv8.11.3を利用しました．

適当なディレクトリに以下のserver.jsを作ります．

```server.js
var WebSocketServer = require('ws').Server
var wss = new WebSocketServer({
	port: 8080
});

wss.on('connection', function(ws) {
	ws.on('message', function(message) {
		console.log('received: %s', message);
	});
	ws.send('This is server');
});
```

これでポート番号8080でサーバが立ち，クライアントが接続したときに"This is server"とメッセージを送るようになります．

### 4.動作確認
まずサーバ側を以下コマンドで起動させます．

```.sh
$ node server.js
```

次にUnity側をPlayにし，Sync Startボタンを押して
箱を移動させると...

![ecWc.gif](https://qiita-image-store.s3.amazonaws.com/0/181807/9a8e197f-c811-60de-7b50-a9594873651b.gif)

できました．
上の様子ではマウスでドラッグするとボックスが動いていますが，以下の記事を参考させて頂きました．

## Ref.
[http://neareal.com/1230/](http://neareal.com/1230/)
[https://qiita.com/oishihiroaki/items/bb2977c72052f5dd5bd9](https://qiita.com/oishihiroaki/items/bb2977c72052f5dd5bd9)
[http://hwks.hatenadiary.jp/entry/2014/07/20/022229](http://hwks.hatenadiary.jp/entry/2014/07/20/022229)
[http://blog.katty.in/2206](http://blog.katty.in/2206)