みかづきブログ・カスタム

基本的にはちょちょいのほいです。

WebSocketを使って、Unityアプリとブラウザでメッセージのやりとりを行う ⚡️

  1. Cubeがクリックされたことを検知
  2. HTTPリクエスト(GET)でJSONの取得
  3. HTTPリクエスト(POST)でJSONの送信
  4. WebSocketによる通信 👈 今回はここ

blog.kimizuka.org

blog.kimizuka.org

blog.kimizuka.org

今回のモックを作りたくて色々と記事を書いてきましたが、直接関係あるのは初回の記事だけで、Networking.UnityWebRequestを使ったGETPOSTは飛ばしても大丈夫です。

WebSocketは、websocket-sharpを使って実現します。

websocket-sharpの準備

websocket-sharpをダウンロードし、解凍します。

github.com

websocket-sharp.slnをダブルクリックし、Visual Studioで開きます。

Example、Example2、Example3を削除します。

リリースビルドに切り替え、ビルドすると、websocket-sharp/bin/Release/websocket-sharp.dllが生成されます。

Unityの修正

websocket-sharp.dllの読み込み

Assets/Plugins以下に、websocket-sharp.dllを設置します。

スクリプトの差し替え

Assets/Scripts/WebSocketClient.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using WebSocketSharp;

public class WebSocketClient : MonoBehaviour
{
    [SerializeField] private string url = "ws://localhost:3000/";
    [SerializeField] private string message = "Ya-Ha-!";

    private WebSocket ws;

    void Start()
    {
        GetComponent<Renderer>().material.color = Color.white;
        ws = new WebSocket(url);

        ws.OnOpen += (sender, evt) =>
        {
            Debug.Log("open.");
        };

        ws.OnMessage += (sender, evt) =>
        {
            Message.message = evt.Data;
            Debug.Log(Message.message);
        };

        ws.OnError += (sender, evt) =>
        {
            Debug.Log(evt);
        };

        ws.OnClose += (sender, evt) =>
        {
            Debug.Log("close.");
        };

        ws.Connect();
    }

    void OnDestroy()
    {
        ws.Close();
        ws = null;
    }

    public void OnClick()
    {
        ws.Send(message);
    }
}

スクリプトを作成し、CubeのPointer Clickに「WebSocketClient.OnClick」を設定します。

サーバの作成

package.json

{
  "name": "unity-network",
  "version": "1.0.0",
  "main": "app.mjs",
  "scripts": {
    "start": "node app.mjs"
  },
  "dependencies": {
    "express": "^4.19.2",
    "ws": "^8.18.0"
  }
}

app.mjs

import express from 'express';
import { createServer } from 'node:http';
import { WebSocketServer } from 'ws';

const __dirname = import.meta.dirname;
const app = express();
const http = createServer(app);
const wss = new WebSocketServer({ port: 3000 });

const wsList = [];

app.use(express.json());
app.use('/', express.static(`${ __dirname }/public`));

wss.on('connection', (ws) => {
  wsList.push(ws);

  ws.on('message', (message) => {
    for (const websocket of wsList) {
      if (ws !== websocket) {
        websocket.send(String(message));
      }
    }
  });
});

http.listen(8000);

public/index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>WebSocket</title>
  </head>
  <body>
    <button>click</button>
    <script>
      const socket = new WebSocket("ws://localhost:3000");

      socket.addEventListener('open', () => {
        console.log('open.');
      });

      socket.addEventListener('message', (evt) => {
        console.log(evt);
      });

      document.querySelector('button').addEventListener('click', () => {
        socket.send('click!');
      });
    </script>
  </body>
</html>
  • Expressでサーバを立てる
  • WebSocketでメッセージを受信した際に送信元以外にそのメッセージを送る
  • Webページをホスティングする(WebSocketでメッセージ受信時にログを表示する)

を実装しました。

npm start

で起動し、ブラウザで、localhost:8000にアクセスします。



これで準備OKです。

挙動確認

Unity側でCubeをクリックすると、ブラウザにログが表示されます。

逆にブラウザのボタンをクリックすると、Unity側に「click!」と表示されます。

これが、WebSocketの力です。
今回は簡易的に、「自分が発信したメッセージ以外を他の相手に送る」という実装になっているため、ブラウザでlocalhost:8000をたくさん開くと、ボタンをクリックした際に他のWindowには「click!」が送信されます。本当はUnityから送信されたメッセージか、ブラウザから送信されたメッセージかを判別するための工夫を実装した方が便利なのですが、今回は簡易版ということで、そこには触れません。

せっかく、メッセージのやり取りができるようになったので、ブラウザからCubeの色を変更できるようにしてみます。

Unityの修正

Assets/Scripts/WebSocketClient.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using WebSocketSharp;

public class WebSocketClient : MonoBehaviour
{
    [SerializeField] private string url = "ws://localhost:3000/";
    [SerializeField] private string message = "Ya-Ha-!";

    private WebSocket ws;

    void Start()
    {
        GetComponent<Renderer>().material.color = Color.white;
        ws = new WebSocket(url);

        ws.OnOpen += (sender, evt) =>
        {
            Debug.Log("open.");
        };

        ws.OnMessage += (sender, evt) =>
        {
            Message.message = evt.Data;
            Debug.Log(Message.message);
        };

        ws.OnError += (sender, evt) =>
        {
            Debug.Log(evt);
        };

        ws.OnClose += (sender, evt) =>
        {
            Debug.Log("close.");
        };

        ws.Connect();
    }

    void Update() {
        switch (Message.message) {
            case "red":
                GetComponent<Renderer>().material.color = Color.red;
                break;
            case "blue":
                GetComponent<Renderer>().material.color = Color.blue;
                break;
            case "green":
                GetComponent<Renderer>().material.color = Color.green;
                break;
            default:
                GetComponent<Renderer>().material.color = Color.white;
                break;
        }
    }

    void OnDestroy()
    {
        ws.Close();
        ws = null;
    }

    public void OnClick()
    {
        ws.Send(message);
    }
}

public class Message : MonoBehaviour
{
    public static string message = "";
}

HTMLの修正

public/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>WebSocket</title>
  </head>
  <body>
    <button id="click">click</button>
    <button id="red">red</button>
    <button id="green">green</button>
    <button id="blue">blue</button>
    <script>
      const socket = new WebSocket("ws://localhost:3000");

      socket.addEventListener('open', () => {
        console.log('open.');
      });

      socket.addEventListener('message', (evt) => {
        console.log(evt);
      });

      document.getElementById('click').addEventListener('click', () => {
        socket.send('click!');
      });
      
      document.getElementById('red').addEventListener('click', () => {
        socket.send('red');
      });
      
      document.getElementById('green').addEventListener('click', () => {
        socket.send('green');
      });
      
      document.getElementById('blue').addEventListener('click', () => {
        socket.send('blue');
      });
    </script>
  </body>
</html>

挙動確認

ブラウザ上のボタンを押すと、Cubeの色が変わるようになりました。
今回は以上です。