Unity와 Socket.io 이용해서 Multiplayer를 구현하는 방법에 대해 소개하려고 함. 대략 이정도 수준의 결과물이 나올 예정.
Unity 5, Node.js v6.5.0, Atom Editor v1.10.2
먼저 공굴리기 유니티 프로젝트 준비하기.
공에 콘트롤러 붙여서 맵을 돌아다니기만 하는게 끝임.
그냥 다운받아도 되고 직접 따라가면서 해봐도 됨.
그냥 다운받기 (권장)
직접 따라가기
(2-2 Setting up the play area 까지.)
다운받았으면, 압축을 풀고 Unity에서 불러옴.
npm install socket.io로 socket.io 패키지 설치. 아래와 같이 결과가 나오면 성공한 것임.E:\Users\ㅁㅁㅁ\Documents\Unity-Multiplayer\Server>npm install socket.io
E:\Users\ㅁㅁㅁ\Documents\Unity-Multiplayer\Server
`-- socket.io@1.4.8
+-- debug@2.2.0
| `-- ms@0.7.1
+-- engine.io@1.6.11
| +-- accepts@1.1.4
| | +-- mime-types@2.0.14
| | | `-- mime-db@1.12.0
| | `-- negotiator@0.4.9
| +-- base64id@0.1.0
| +-- engine.io-parser@1.2.4
| | +-- after@0.8.1
| | +-- arraybuffer.slice@0.0.6
| | +-- base64-arraybuffer@0.1.2
| | +-- blob@0.0.4
| | +-- has-binary@0.1.6
| | `-- utf8@2.1.0
| `-- ws@1.1.0
| +-- options@0.0.6
| `-- ultron@1.0.2
+-- has-binary@0.1.7
| `-- isarray@0.0.1
+-- socket.io-adapter@0.4.0
| `-- socket.io-parser@2.2.2
| +-- debug@0.7.4
| `-- json3@3.2.6
+-- socket.io-client@1.4.8
| +-- backo2@1.0.2
| +-- component-bind@1.0.0
| +-- component-emitter@1.2.0
| +-- engine.io-client@1.6.11
| | +-- component-inherit@0.0.3
| | +-- has-cors@1.1.0
| | +-- parsejson@0.0.1
| | +-- parseqs@0.0.2
| | +-- ws@1.0.1
| | +-- xmlhttprequest-ssl@1.5.1
| | `-- yeast@0.1.2
| +-- indexof@0.0.1
| +-- object-component@0.0.3
| +-- parseuri@0.0.4
| | `-- better-assert@1.0.2
| | `-- callsite@1.0.0
| `-- to-array@0.1.4
`-- socket.io-parser@2.2.6
+-- benchmark@1.0.0
+-- component-emitter@1.1.2
`-- json3@3.3.2
npm WARN enoent ENOENT: no such file or directory, open 'E:\Users\ㅁㅁㅁ\Documents\Unity-Multiplayer\Server\package.json'
npm WARN Server No description
npm WARN Server No repository field.
npm WARN Server No README data
npm WARN Server No license field.
E:\Users\ㅁㅁㅁ\Documents\Unity-Multiplayer\Server>cd ..
E:\Users\ㅁㅁㅁ\Documents\Unity-Multiplayer>atom .
var io = require('socket.io')(80)
io.on('connection', function(socket){
console.log('a user connected')
socket.on('disconnect', function() {
console.log('user disconnect')
})
})
E:\Users\ㅁㅁㅁ\Documents\Unity-Multiplayer\Server>node app.js
SocketIO 애셋 다운로드
Project - Assets-SocketIO-Prefabs 에 있는 SocketIO Scene에 복사
SocketIO Prefab의 Inspector에서 URL을 수정
실행 및 유저가 접속하고 나가는 것을 서버에서 확인.
E:\Users\ㅁㅁㅁ\Documents\Unity-Multiplayer\Server>node app.js
a user connected
user disconnect
/*
Server version 2
It supports syncing players.
*/
var io = require('socket.io')(80)
var players = {}
io.on('connection', function(socket){
console.log('a user connected')
socket.on('disconnect', function() {
console.log('user disconnect')
if (socket.id in players) delete players[socket.id]
})
socket.on('sync', function(player) {
/*
player:
id: String, but empty
position: [float, float, float]
rotation: [float, float, float]
*/
player.id = socket.id
// update server's player list
players[player.id] = player
// broadcast to other clients
socket.broadcast.emit('sync', player)
// Print current players for debugging
printPlayers()
})
})
function printPlayers() {
console.log(players)
}
using UnityEngine;
using SocketIO;
using System;
using System.Collections;
public class PlayerSyncer : MonoBehaviour {
public static SocketIOComponent socket = null;
// Use this for initialization
void Start () {
GameObject go = GameObject.Find("SocketIO");
socket = go.GetComponent<SocketIOComponent>();
}
// Update is called once per frame
void Update () {
PlayerData player_data = new PlayerData(transform);
socket.Emit("sync", player_data.ToJSONObject());
}
}
[Serializable]
public class PlayerData {
public string id = "undefined";
public Vector3 position;
public Quaternion rotation;
public PlayerData(Transform transform) {
position = transform.position;
rotation = transform.rotation;
}
public PlayerData(JSONObject json_data) {
PlayerData data = JsonUtility.FromJson<PlayerData>(json_data.ToString());
id = data.id;
position = data.position;
rotation = data.rotation;
}
public JSONObject ToJSONObject() {
return new JSONObject(JsonUtility.ToJson(this));
}
}using UnityEngine;
using SocketIO;
using System.Collections;
using System.Collections.Generic;
public class OtherPlayersSyncer : MonoBehaviour {
public GameObject otherPlayerPrefab;
private static SocketIOComponent socket = null;
private Dictionary<string, GameObject> otherPlayersDict = new Dictionary<string, GameObject>();
// Use this for initialization
void Start () {
GameObject go = GameObject.Find("SocketIO");
socket = go.GetComponent<SocketIOComponent>();
socket.On("sync", OnSync);
socket.On("remove", OnRemove);
}
GameObject FindOrCreatePlayer(PlayerData player_data) {
if (!otherPlayersDict.ContainsKey(player_data.id)) {
GameObject newPlayer = Instantiate(
otherPlayerPrefab,
player_data.position,
player_data.rotation) as GameObject;
newPlayer.transform.parent = GameObject.Find("다른플레이어들").transform;
otherPlayersDict.Add(player_data.id, newPlayer);
}
return otherPlayersDict[player_data.id];
}
void OnSync(SocketIOEvent e) {
PlayerData player_data = new PlayerData(e.data);
// Create other player object if it does not exist.
GameObject player = FindOrCreatePlayer(player_data);
player.transform.position = player_data.position;
player.transform.rotation = player_data.rotation;
}
void OnRemove(SocketIOEvent e) {
PlayerData player_data = new PlayerData(e.data);
if (otherPlayersDict.ContainsKey(player_data.id)) {
Destroy(otherPlayersDict[player_data.id]);
otherPlayersDict.Remove(player_data.id);
}
}
}이상으로 멀티플레이어 당구공 만들었음
숙제로 Transform의 position, rotation만 보낼 것이 아니라 Rigidbody의 position, rotation, velocity 를 동기화하는 걸 구현해 보시길.