LoginSignup
8
5

More than 3 years have passed since last update.

WebGLでビルドされたUnityとコンソールアプリケーション間で通信したい

Last updated at Posted at 2019-12-24

WebGLのUnityとコンソールアプリを通信したい

ブラウザ上に表示されるWebGLビルドされたUnityのアプリケーションと,ローカルで起動したコンソールアプリケーションを通信できるようにしたいときってありますよね.

スタンドアロンなUnityアプリケーションとコンソールアプリケーションを通信するだけであれば,TCPソケットはもちろん,Websocketでも接続できます.それから推察するに,WebGLビルドされたUnityのアプリケーションとコンソールアプリケーションもWebsocketを使えば通信できそうですが,なぜかそうはいかないようです(参考).

当エントリーでは,WebGLビルドされたUnityのアプリケーションとコンソールアプリケーションを通信できるように頑張った結果を報告します.もしかしたらよりエレガントな解決策があるかもしれません.

以下環境設定.
- Unity: 2019.1.8f1
- chrome: 79.0
- Windows: 10 pro 1903

解決策

*.jslibにWebsocketで通信するjavascriptを書き,Unityのスクリプト(C#)ではjavascriptが受け取ったメッセージをUnityの空間に持ち上げるようにする.

具体的には?

1. ファイルの用意

まずUnityのAssetsディレクトリ以下に,ファイルを二つ作ります.ファイル名はなんでもいいのですが,便宜上以下のものに決めます.階層構造は一応順守で.
- Assets/Plugins/Connector.jslib
- Assets/Scripts/Main.cs

2. Connector.jslibの編集

WebGLビルドされたUnityはこのファイルを通じてjavascriptの空間にアクセスできるようです.詳細は以下をURLを.
- https://qiita.com/gtk2k/items/1c7aa7a202d5f96ebdbf
- https://docs.unity3d.com/jp/540/Manual/webgl-interactingwithbrowserscripting.html

このファイルを以下のように編集し,まずはjavascript上でWebsocketのクライアントを作成します.

Connector.jslib
var ws;
var messages;

mergeInto(LibraryManager.library, {

  Initialize: function (server) {
    ws = new WebSocket( Pointer_stringify(server) );

    messages = new Array();

    ws.onmessage = function (evt) {
        var message = evt.data;

        messages.push( message );
    };

    ws.onopen = function () {
        console.log("system::open")
    };

    ws.onclose = function () {
        console.log("system::close")
    };
  },

  GetMessage: function () {
    var msg = "";

    if( messages.length > 0 ) {
        msg = messages.shift();
    }

    var buffer = _malloc( lengthBytesUTF8(msg) + 1 );
    writeStringToMemory(msg, buffer);
    return buffer;
  },

  SendMessage: function (message) {
    if (ws) {
        ws.send( Pointer_stringify(message) );
    }
  },

  Close: function () {
    if (ws) {
        ws.close();
    }
  }

});

3. Main.csの編集

次にMain.csを以下のように編集します.

Main.cs
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using UnityEngine;

public class Main : MonoBehaviour
{
  private GameObject logger;
  private Queue<string> ReceivedMessages = null;
  private static string server = @"ws://localhost:2333/ConnectionTest";

  [DllImport( "__Internal" )]
  private static extern void Initialize( string server );
  [DllImport( "__Internal" )]
  private static extern string GetMessage();
  [DllImport( "__Internal" )]
  private static extern void Send( string message );
  [DllImport( "__Internal" )]
  private static extern void Close();


  private void OnDestroy() {
    Close();
  }

  void Start() {
    logger = GameObject.Find( "Logger" );

    ReceivedMessages = new Queue<string>();

    Initialize( server );
  }


  private static string Formatter( string message ) => $"message from server:\n{message}";

  void Update() {

    string message = GetMessage();

    if( message != "" ) {
      ReceivedMessages.Enqueue( message );

      Debug.Log( $"Received -> {message}" );
    }

    if( ReceivedMessages.Any() ) {

      string line = ReceivedMessages.Dequeue();

      logger.GetComponent<TextMesh>().text = Formatter( line );
    }


  }
}

UnityのScene上にテキストを表示できるGameObjectを配置し,受け取ったメッセージを表示するようにしておきました.下図参照.
unity.png

ちなみにこのスクリプトは,javascript上に定義された関数をインポートして呼び出しているので,WebGLビルド以外ではそれら関数が呼び出せずエラーを吐くはずです.つまり,WebGLビルドしないと動作確認できません.

変数serverには,Websocketサーバーが起動しているPCのIPアドレスを入力します.今はlocalhostとしていますが,LAN内の別PC (192.168.11.*)を指定しても接続できていました.

4. UnityをWebGLビルドし実行,接続確認

ソースコードの編集が終わったのち,UnityをWebGLビルドします.方法は適当に調べてください.

作成されたindex.htmlをchromeで開くと,下図のようにUnityのプレーヤーの初期化時にエラーが生じて実行できない(Firefoxでは実行できるとかなんとか)ようです.エラーメッセージ見ると,ウェブサーバーにアップロードしろ的な感じなので,ビルドにより生成されたすべてのファイルをウェブサーバーにアップロードします.
error.png

そして,Websocketサーバーとしての機能を持つコンソールアプリケーションをあらかじめ作成しておき,起動します.Websocketサーバーはnode.jsで作る人が多いようですが,筆者はC#信者なのでC#で作りました.ライブラリとしてwebsocket-sharpにお世話になりました.

ローカルでWebsocketサーバーを立ち上げたのち,ブラウザから先ほどのindex.htmlにアクセスします.その後ローカルのサーバーからメッセージを送信すると,ブラウザ上のUnityにメッセージが表示されました(下GIF参照).

setuzoku_dekita.gif

このGIFの上半分はindex.htmlにアクセスしているブラウザの様子で,下半分はローカルに起動しているWebsocketサーバーの様子です.Websocketサーバーのコンソールに文字を入力し,エンターキーを入力すると,それまでの文字列がブラウザ上のUnityに送信されていることが確認できます.ブラウザ上のUnity上に表示されている文字が多少見切れていますが,全画面表示にするといい感じに表示されています.

おわりに

エレガントな方法かどうかはわかりませんが,ブラウザのUnityアプリケーションとローカルのコンソールアプリケーションが通信できるようになりました.

そういえば,ここによればjavascriptの空間からC#のスクリプト上に定義されたメソッドを呼ぶこともできるようなので,うまくやればメッセージがあるかどうかを無意味にビジーウェイトしなくてもよくなりそうです.

後日暇を見つけて,作成したUnityのスクリプトなどをGithub上にアップロードしてようと思います.Unityのエディター上でも動作確認できるような工夫もしていたり,Websocketのサーバーアプリケーションもアップロードしておこうかなあとも思っていたり.

これでなんか面白いアプリケーション作れないかな...

8
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
8
5