見出し画像

Multiplayer drawing with Unity WebGL

Hi, I am an interaction engineer/designer MAO (@rainage) of The Designium.

In this article, I'm going to introduce a prototype project, "MultiDraw".
You could try the demo on the below link: (use 2 browser tab could simulate 2 players)

We start to think of ideas to make some projects which are allowing people to enjoy our creativity while staying home.

In this project. I start to try Unity WebGL, then making a simple online playground to let people leave some messages.

UnityWebGL with Socket.IO 

After some testing, I choose the asset called "Socket.IO for Native and WebGL builds" for data communication tool of Unity WebGL.
- multiple platform: I could use the same messages for WebGL version and desktop version.
- package data with JsonUtility  

#Use Socke.IO in Unity

multi-draw_工作區域 1

- create socketIOController and set up URL & port (local server testing setting)

画像2

- register socket.on event

void Start() {
	// socketIO event register
	socket.On("play", OnPlay);
	socket.On("drawClick", OnDrawClick);
    socket.On("drawDrag", OnDrawDrag);
    socket.On("changeColor", OnChangeColor);
	socket.On("connect", (SocketIOEvent e) => {
     Debug.Log("SocketIO connected");
     socket.Emit("player linked");
 });
 ...
}

- send a command with socket.emit()

void Start() {
 ...
	// send out command
    socket.Emit("player connect"); 
}

# Node JS Server need to handle the coming data format

- the socket.emit() is telling the currently connected user something.
- the socket.braodcast.emit() is telling other connected users something.

var port = process.env.PORT || 3000,
   io =  require("socket.io")(port);

...

gameSocket = io.on("connection", function(socket) {
 var currentPlayer = {};
 currentPlayer.name = "unknown";

 console.log("socket connected: " + socket.id);

 socket.on("disconnect", function() {
   console.log("socket disconnected: " + socket.id);
 });
 
 //---- player event ----//
 socket.on("player linked", function() {
   console.log(" recv: player linked");
 });
 socket.on('play', function(data) {
		...
		currentPlayer = {
			name:data.name,
			position: data.position,
			rotation: data.rotation,
     color: data.color
		};
		// tell you that you have joined
		clients.push(currentPlayer);
		console.log(currentPlayer.name + ' emit: play: ' + JSON.stringify(currentPlayer));
		socket.emit('play', currentPlayer);
		// tell the other players about you.
		socket.broadcast.emit('other player connected', currentPlayer);
	});

	...

});

Share Drawing with socket.IO 

If we need to share more information about the user, we will need to build a class and use JsonUtility to package and un-package it.
I use GameObject to store each drawing user's data. If a new user jointed, I will create a new GameObject for it. When getting onDrawDrag event, I would check if this user already exists, then updating the data and drawing.

<Unity>

[Serializable]
public class UserDrawJSON
{
   public string name;
   public float[] position;
   public int color;

   public static UserDrawJSON CreateFromJSON(string data)
   {
       return JsonUtility.FromJson<UserDrawJSON>(data);
   }
}
[Serializable]
public class PtDragJSON
{
   public float[] position;

   public PtDragJSON(Vector2 _fromposition, Vector2 _toposition)
   {
       position = new float[] { _fromposition.x, _fromposition.y, _toposition.x, _toposition.y };
   }
}
...
public void CommandDrawDrag(Vector2 fromVec2, Vector2 vector2)
{
   socket.Emit("drawDrag", JsonUtility.ToJson(new PtDragJSON(fromVec2, vector2)));
}
void OnDrawDrag(SocketIOEvent socketIOEvent)
{
   string data = socketIOEvent.data.ToString();
   UserDrawJSON userJSON = UserDrawJSON.CreateFromJSON(data);
   Vector2 fromposition = new Vector2(userJSON.position[0], userJSON.position[1]);
   Vector2 toposition = new Vector2(userJSON.position[2], userJSON.position[3]);

   // if it is the current player exit
   if (userJSON.name == playerName)
   {
       return;
   }
   GameObject p = GameObject.Find(userJSON.name + "_drawing") as GameObject;
   if (p != null)
   {
       socketDraw.PointDragAction(fromposition, toposition, socketDraw.GetColor(p.GetComponent<DrawPlayer>().color));
   }
}

<Node JS Server>

socket.on('drawDrag', function(data) {
	var data = {
		name: currentPlayer.name,
   position: data.position
	};
	socket.emit('drawDrag', data);
	socket.broadcast.emit('drawDrag', data);
});


Online Node JS Server

After building the prototype demo, I need to test it online. I use glitch as my online node server, it is very easy to set up a Socket.IO server.

#Set pacakge.json:
setup your start script & dependencies to run your Socket.IO server script in server.js

{
	...
 "description": "A simple socket-io multiplayer server for unity game.",
 "main": "server.js",
 "scripts": {
   "start": "node server.js"
 },
 "dependencies": {
		"socket.io": "*"
 },
 "engines": {
   "node": "12.x"
 },
	...
}

#Change my SocketIOController setting
- change URL as glitch server
- change port as 0 (because glitch will redirect the port to socket.IO)

画像3

SSL problem

Original I also want put WebGL publish webpage on glitch but put folders and images on glitch is not convenience (the glitch do not support adding more folders under asset folder).

So I put my published webpage on my GitHub page. When user connects the webpage, it will connect to my glitch server to send message.

However, the GitHub, amazon s3(AWS), or some web service are using HTTPS protocol ("https://xxx.xxx.xxx"). If you put your WebGL page on such server then using Socket.IO to send out message, it will be a failure.

So I need to change my Socket.IO Controller setting again.

画像4

Resolution issue on a different browser

During the testing of multi-player drawing, I also meet an issue that the 2D pixel position will be a little different on different browser.

画像5

It will happened if you use fixed or fullscreen on your gameContainer. If you need to force the resolution of your WebGL game. You could make a template to force resolution and disable the fullscreen button.

<!DOCTYPE html>
<html lang="en-us">
 <head>
   <meta charset="utf-8">
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   <title>Unity WebGL Player | %UNITY_WEB_NAME%</title>
   <link rel="shortcut icon" href="TemplateData/favicon.ico">
   <link rel="stylesheet" href="TemplateData/style.css">
   <script src="%UNITY_WEBGL_LOADER_URL%"></script>
   <script>
     var gameInstance = UnityLoader.instantiate("gameContainer", "%UNITY_WEBGL_BUILD_URL%")
   </script>
   <style>
     #gameContainer { width: 960px; height: 600px; }
     canvas { width: 960; height: 600; }
   </style>
 </head>
 <body>
   <div class="webgl-content">
     <div id="gameContainer"></div>
   </div>
 </body>
</html>

編集後記

広報のマリコです。今回はMaoが作った、ステイホームな今の時期にぴったりのお絵かきアプリの記事でした😊UnityのWebGLを使って書き出しているのでウェブブラウザ上で遊ぶことができ、しかも複数人が一緒に遊べるようにSocket.IOという通信システムもつかっています。異なるブラウザに対応させるのに苦労したみたいですが、なんだか凄く面白そうですよね❗離れた場所でクリエイティビティあふれる遊びをみんなでできるのはとても楽しそうなので今後が楽しみです✨

The Designium.inc
Interactive website
Twitter
Facebook

この記事が参加している募集

この記事が気に入ったらサポートをしてみませんか?