凹みTips

C++、JavaScript、Unity、ガジェット等の Tips について雑多に書いています。

Unity 5 の WebGL で WebSocket を利用した通信をする方法について調べてみた

はじめに

Unity の WebGL では現状ではネットワーク機能に制約がありますが、WebSocket は使えるようです。

以前の記事では外側の JavaScript で Socket.IO を通じて通信した内容を用いましたが、これだとビルドしてから色々と確認しなければならず大変でした。

そこで、WebSocket 機能の利用方法について調べてみました。

WebSockets.unitypackage のダウンロード

上記スレッドより、WebSockets.unitypackage がダウンロードできます。

使ってみる

Unity 側のコード

unitypackage を展開して見ると、EchoTest.cs というサンプルが入っており、これで実験することが出来ます。

EchoTest.cs
using UnityEngine;
using System.Collections;
using System;

public class EchoTest : MonoBehaviour {

    // Use this for initialization
    IEnumerator Start () {
        WebSocket w = new WebSocket(new Uri("ws://127.0.0.1:3000")); // <-- アドレスだけ変えます
        yield return StartCoroutine(w.Connect());
        w.SendString("Hi there");
        int i=0;
        while (true)
        {
            string reply = w.RecvString();
            if (reply != null)
            {
                Debug.Log ("Received: "+reply);
                w.SendString("Hi there"+i++);
            }
            if (w.Error != null)
            {
                Debug.LogError ("Error: "+w.Error);
                break;
            }
            yield return 0;
        }
        w.Close();
    }
}

これを適当なオブジェクトにアタッチします。

サーバ側のコード

Node.js で ws モジュールを使って以下のように書いてみます。

ws モジュールの導入
$ npm install ws
server.js
var ws = require('ws').Server;

var server = new ws({ port: 3000 });
server.on('connection', function(ws) {
    console.log('connected!');
    ws.on('message', function(message) {
        console.log(message.toString());
    });
    ws.on('close', function() {
        console.log('disconnected...');
    });
    setInterval(function() {
        ws.send("Hi!");
    }, 1000);
});

実行結果

Server

f:id:hecomi:20141228221814p:plain

Unity

f:id:hecomi:20141228221849p:plain

追記: 2014/12/29

書き忘れてましたが、ビルド後にブラウザ上でも問題なく動きます。

追記: 2014/12/31

もう一度確認してみたところ、どうやらビルド後にクライアントでメッセージを受ける側がうまく動いていないように思われます。追って調査し分かり次第報告します。

発展例

簡単に再接続機能をつけてみたサンプルがこちらです。

WebSocketTest.cs
using UnityEngine;
using System.Collections;
using System;

public class WebSocketTest : MonoBehaviour 
{
    public string serverUrl = "ws://127.0.0.1:3000";
    private bool isConnected_ = false;
    private WebSocket ws_;

    IEnumerator Start() 
    {
        for (;;) {
            ws_ = new WebSocket( new Uri(serverUrl) );
            yield return StartCoroutine( ws_.Connect() );
            isConnected_ = true;

            for (;;) {
                var message = ws_.RecvString();
                while (message != null) {
                    Log(message);
                    message = ws_.RecvString();
                }
                if (ws_.Error != null) {
                    LogError(ws_.Error);
                    isConnected_ = false;
                    yield return new WaitForSeconds(1);
                    break;
                }
                yield return new WaitForEndOfFrame();
            }
        }
    }

    void OnDestroy()
    {
        ws_.Close();
    }

    void Update()
    {
        if (Input.anyKeyDown && isConnected_) {
            ws_.SendString("Hello!");
        }
    }

    void Log(string message)
    {
        Debug.Log(message);
        Application.ExternalCall("console.log", message);
    }

    void LogError(string error)
    {
        Debug.LogError(error);
        Application.ExternalCall("console.error", error);
    }
}

今後の展開

公式で UNET(Unity 5 のネットワーク目玉機能)の WebGL サポートが言及されています。

@Evan: WebGL will never support .NET sockets to directly work on UDP or TCP sockets as that is not exposed to JavaScript. But we already have a working wrapper to use WebSockets and WebRTC would also be possible. I expect that our upcoming built-in networking solution, UNET, will support WebGL using one of these protocols.

WebSocket を使う場合は Unity 社が立ててくれたサーバを利用、WebRTC がサポートされればユーザ間で P2P な通信ができるようになる、というイメージでしょうか。とても楽しみです。

また、実績もあってドキュメントも豊富な Photon も対応中のようで、これまでの資産も活用できるようになるとブラウザ上で手軽にできるゲームが増えそうです。

おわりに

これで、Unity のエディタ上でもビルドしてブラウザ上で実行しても同じ結果が得られ、開発が簡単になりました。まだまだ情報が少ない WebGL x ネットワーク周りですが、色々な可能性を感じるので、ぜひ皆さんも遊んでみてください。