유니티 + Nodejs를 활용한
멀티플레이어 게임 개발하기
-최대한 간단하게-
KIYOUNG MOON
2017-07-10
Node.js
 Node.js는 서버사이드에서 자바스크립트로 응용 프로그램을 만들 수 있게
해준다.
 Module 기능을 사용해 이미 만들어진/다른 사람이 만든 모듈을 재사용할
수 있다.
 Express
 Forever
 Socket.io
 등등
 매우 짧은 코드로 서비스 가능한 응용 프로그램을 만들 수 있다.
Node.js를 활용한 프로젝트들
 Yammer
 Linkedin
 http://browserquest.mozilla.org/
 그 외에 모바일 앱을 위한 서버들…
Nodejs 다운로드
 https://nodejs.org/ko/
Nodejs의 REPL
 REPL(Read Eval Print Loop)
Nodejs의 핵심은 비동기
(Asynchronization aka Async)
 Blocking, Non-blocking 이해 필수.
 자세한 설명은 구글링!
웹서버 만들기
 웹서버는 다양한 형태로 만들 수 있다.
C#으로 만든 초간단 웹서버
C로도 만들 수 있지!
Nodejs로 만들면…
 http를 사용하기 위해 모듈을 가져온다.
 var http = require(‘http’);
var server = http.createServer(
function(request, response) {}
);
파일 읽기
var fs = require(‘fs’);
fs.readFile(‘image.png’,
function(error, data) { }
);
읽혀진 데이터는 data로 넘어온다.
오늘의 짤 서비스를 만들어 보자.
웹브라우저로 접근하면 오늘의 짤을 무작위로 보여주는 웹서비스 만들기.
무작위로 이미지를 보여주는 서비스를 Nodejs로 구현
var url = require('url'); // import url module.
var fs = require('fs'); // file system
var http = require('http');
var server = http.createServer(function(request, response){
response.writeHead(200, { 'Content-Type': 'image/png'});
var i = parseInt(Math.random() * 4 + 1);
fs.readFile('images/' + i + '.png', function(error, data){
response.end(data);
});
});
server.listen(3000);
데디케이티드 서버(Dedicated Server)
 하나의 서버 프로세스에서 게임 룸에 따라 모든 처리를 하는 것이 아니라 게
임 룸 하나당 하나의 프로세스를 생성해 처리하는 것. 이러한 서버를
Dedicated Server라 한다.
 결론적으로 로비에서 방1, 방2, …, 방n과 같이 방하나가 생길 때 데디케이트
서버 프로세스가 생성되고 이 프로세스에서 클라이언트와 게임 처리를 한
다.
 퀘이크(Quake), 언리얼 토너먼트(Unreal Tournament), 기어즈 오브 워
(Gears of War), LOL과 같은 게임들이 이러한 데디케이트 서버의 형태를 가
지고 있다.
유니티로 데디케이트 서버 만들기
 유니티에 –batchmode 커맨드 옵션을 주면 화면을 띄우지 않고 프로세스만
띄울 수 있다. 보통 서버 포트를 설정하는데 이것은 –port와 같이 문자열을
커맨드 옵션으로 주고 포트 번호만 파싱하면 된다.
 무식한 방법
if (commandLineOptions.Contains("-port")) {
string[] args = System.Environment.GetCommandLineArgs();
for (int i = 0; i < args.Length; ++i) {
if (args[i].Equals("-port")) {
serverPort = Convert.ToInt32(args[i + 1]);
break;
}
}
netManager.networkPort = serverPort;
}
데디케이티드 서버 배포
 포트를 커맨드 옵션으로 주면 서버가 생성되는 서버를 만든다.
그리고 이 파일들을 nodejs 서버가 실행되는 경로에 저장한다.
Nodejs에서 외부 실행파일 실행하기
var exec = require('child_process').exec;
exec('game.exe -batchmode -port ' + dedicateServer['port'] +
' -dedicateServer ' + i, function(error, stdout, stderr)
{
});
Game.exe를 실행할 때 –batchmode 옵션을 주고 –port 7777과 같이 포트 번호를
넘겨 준다. 그 뒤에 있는 옵션은 데디 서버의 인덱스 번호다.(nodejs 서버에서 관리
되는 데디케이티드 서버 리스트 인덱스)
참고로 Unet은 udp를 사용한다! 방화벽에서 udp포트를 열어 놓아야 한다.
Nodejs로 서버 생성하기
var server = http.createServer(function(request, response) {
}).listen(6666, function() {
console.log('"socket io server is running at 6666');
});
http 모듈을 사용해서 서버를 만든다. 서버의 포트 번호는 6666이다.
Scoket.io
 웹 소켓 프로그래밍을 하려면 socket.io를 사용하면 된다.
var io = socketio.listen(server);
생성된 서버로 Listen하고 클라이언트에서 웹 소켓으로 연결하면 된다.
유니티에서 socket.io 사용하기
using SocketIOClient;
https://github.com/NetEase/UnitySocketIO
에서 bin에 있는 모든 파일들을 유니티
Plugins 폴더 안에 넣으면 된다.
유니티에서 Nodejs 서버로 웹소켓 연결
하기using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SocketIOClient;
public class SocketManager : MonoBehaviour {
string url = "http://127.0.0.1:6666/";
public static Client Socket
{
get; private set;
}
// Use this for initialization
void Awake()
{
GameObject.DontDestroyOnLoad(gameObject);
Socket = new Client(url);
Socket.Opened += SocketOpened;
Socket.Connect();
}
void OnDisable()
{
Socket.Close();
}
}
Nodejs에서 웹소켓 연결 처리
클라이언트가 연결되면 connection이 호
출 된다.
다음에 socket.on에서 이벤트를 등록해 클
라이언트에서 이벤트가 날아오면 원하는
동작을 할 수 있다.
데디케이티드 서버가 만들어지고 난 후
클라이언트 연결 처리
 데디 서버가 만들어지고 나면 클라이언트들이 해당 데디케이티드 서버로
접속해야 한다. 클라이언트는 서버에서 만들어진 데디케이티드 서버의 주소
와 포트 번호를 모른다.
 Nodejs에서 데디 서버의 프로세스를 생성하고 포트 번호를 모든 클라이언
트에 전달한다.
데디케이티드 서버가 생성이 된 후에 D2SReadyToPlay를 nodejs로 이벤트를 날린다. 그 이후
에 모든 클라이언트에 데디 서버의 포트 번호를 알려준다. 이때 사용되는 이벤트는
S2CDedicateServerPort다.
Nodejs 서버에서 데디케이티드 서버의 포트를 S2CDedicateServerPort로 알려
주면 클라이언트는 해당 포트로 접속하여 게임 플레이 진행 가능하다.
실제 연결된 모습
데디서버 <-> 클라이언트 통신
 ClientRpc
 서버에서 호출되며 클라이언트에서 실행된다.
 Command
 클라이언트에서 호출되며 서버에서 실행된다.
 SyncVar
 연결된 모든 프로세스의 값이 동기화된다.
Network Identity 컴포넌트
 통신하는 오브젝트들은 Network Identity 컴포넌트를 가지고 있어야 한다.
이동, 회전, RigidBody
 유니티에서 이동, 회전, RigidBody와 같은 속성들을 동기화 시키는 컴포넌트
를 제공.
NetworkTransform 컴포넌트
 게임 오브젝트의 트랜스폼을 동기화 시킨다.
 품질은 매우 낮음(끊겨서 보임)
 그래서… 결국은 껯, Command를 사용해 직접 구현해서 사용하는 편이 좋다.
Rigidbody 동기화
 Transform Sync Mode를 통해 동기화 시킬 타입을 설정할 수 있다.
 RigidBody의 경우 Sync Rigidbody 3D를 선택해 RigidBody를 동기화 시킬
수 있다.
 품질은 Sync Transform보다는 괜찮음.
위치 보간 직접 구현해버리기
 SyncPlayerPos 스크립트 사용.
 각 클라이언트가 현재 위치를 서버에 알린다. 서버는 해당 클라이언트에 붙
어 있는 SyncPos를 업데이트 한다. 이렇게 하면 클라이언트의 SyncPos가 업
데이트 된다.
 이후에 클라이언트가 이 SyncPos로 현재 위치에서 보간 한다.
연습 : 직접 회전 보간도 해보도록 하자.
질문
끝

유니티 + Nodejs를 활용한 멀티플레이어 게임 개발하기

  • 1.
    유니티 + Nodejs를활용한 멀티플레이어 게임 개발하기 -최대한 간단하게- KIYOUNG MOON 2017-07-10
  • 2.
    Node.js  Node.js는 서버사이드에서자바스크립트로 응용 프로그램을 만들 수 있게 해준다.  Module 기능을 사용해 이미 만들어진/다른 사람이 만든 모듈을 재사용할 수 있다.  Express  Forever  Socket.io  등등  매우 짧은 코드로 서비스 가능한 응용 프로그램을 만들 수 있다.
  • 3.
    Node.js를 활용한 프로젝트들 Yammer  Linkedin  http://browserquest.mozilla.org/  그 외에 모바일 앱을 위한 서버들…
  • 4.
  • 5.
  • 7.
    Nodejs의 핵심은 비동기 (Asynchronizationaka Async)  Blocking, Non-blocking 이해 필수.  자세한 설명은 구글링!
  • 8.
    웹서버 만들기  웹서버는다양한 형태로 만들 수 있다.
  • 9.
  • 10.
  • 11.
    Nodejs로 만들면…  http를사용하기 위해 모듈을 가져온다.  var http = require(‘http’); var server = http.createServer( function(request, response) {} );
  • 12.
    파일 읽기 var fs= require(‘fs’); fs.readFile(‘image.png’, function(error, data) { } ); 읽혀진 데이터는 data로 넘어온다.
  • 13.
    오늘의 짤 서비스를만들어 보자. 웹브라우저로 접근하면 오늘의 짤을 무작위로 보여주는 웹서비스 만들기.
  • 14.
    무작위로 이미지를 보여주는서비스를 Nodejs로 구현 var url = require('url'); // import url module. var fs = require('fs'); // file system var http = require('http'); var server = http.createServer(function(request, response){ response.writeHead(200, { 'Content-Type': 'image/png'}); var i = parseInt(Math.random() * 4 + 1); fs.readFile('images/' + i + '.png', function(error, data){ response.end(data); }); }); server.listen(3000);
  • 15.
    데디케이티드 서버(Dedicated Server) 하나의 서버 프로세스에서 게임 룸에 따라 모든 처리를 하는 것이 아니라 게 임 룸 하나당 하나의 프로세스를 생성해 처리하는 것. 이러한 서버를 Dedicated Server라 한다.  결론적으로 로비에서 방1, 방2, …, 방n과 같이 방하나가 생길 때 데디케이트 서버 프로세스가 생성되고 이 프로세스에서 클라이언트와 게임 처리를 한 다.  퀘이크(Quake), 언리얼 토너먼트(Unreal Tournament), 기어즈 오브 워 (Gears of War), LOL과 같은 게임들이 이러한 데디케이트 서버의 형태를 가 지고 있다.
  • 16.
    유니티로 데디케이트 서버만들기  유니티에 –batchmode 커맨드 옵션을 주면 화면을 띄우지 않고 프로세스만 띄울 수 있다. 보통 서버 포트를 설정하는데 이것은 –port와 같이 문자열을 커맨드 옵션으로 주고 포트 번호만 파싱하면 된다.  무식한 방법 if (commandLineOptions.Contains("-port")) { string[] args = System.Environment.GetCommandLineArgs(); for (int i = 0; i < args.Length; ++i) { if (args[i].Equals("-port")) { serverPort = Convert.ToInt32(args[i + 1]); break; } } netManager.networkPort = serverPort; }
  • 17.
    데디케이티드 서버 배포 포트를 커맨드 옵션으로 주면 서버가 생성되는 서버를 만든다. 그리고 이 파일들을 nodejs 서버가 실행되는 경로에 저장한다.
  • 18.
    Nodejs에서 외부 실행파일실행하기 var exec = require('child_process').exec; exec('game.exe -batchmode -port ' + dedicateServer['port'] + ' -dedicateServer ' + i, function(error, stdout, stderr) { }); Game.exe를 실행할 때 –batchmode 옵션을 주고 –port 7777과 같이 포트 번호를 넘겨 준다. 그 뒤에 있는 옵션은 데디 서버의 인덱스 번호다.(nodejs 서버에서 관리 되는 데디케이티드 서버 리스트 인덱스) 참고로 Unet은 udp를 사용한다! 방화벽에서 udp포트를 열어 놓아야 한다.
  • 19.
    Nodejs로 서버 생성하기 varserver = http.createServer(function(request, response) { }).listen(6666, function() { console.log('"socket io server is running at 6666'); }); http 모듈을 사용해서 서버를 만든다. 서버의 포트 번호는 6666이다.
  • 20.
    Scoket.io  웹 소켓프로그래밍을 하려면 socket.io를 사용하면 된다. var io = socketio.listen(server); 생성된 서버로 Listen하고 클라이언트에서 웹 소켓으로 연결하면 된다.
  • 21.
    유니티에서 socket.io 사용하기 usingSocketIOClient; https://github.com/NetEase/UnitySocketIO 에서 bin에 있는 모든 파일들을 유니티 Plugins 폴더 안에 넣으면 된다.
  • 22.
    유니티에서 Nodejs 서버로웹소켓 연결 하기using System.Collections; using System.Collections.Generic; using UnityEngine; using SocketIOClient; public class SocketManager : MonoBehaviour { string url = "http://127.0.0.1:6666/"; public static Client Socket { get; private set; } // Use this for initialization void Awake() { GameObject.DontDestroyOnLoad(gameObject); Socket = new Client(url); Socket.Opened += SocketOpened; Socket.Connect(); } void OnDisable() { Socket.Close(); } }
  • 23.
    Nodejs에서 웹소켓 연결처리 클라이언트가 연결되면 connection이 호 출 된다. 다음에 socket.on에서 이벤트를 등록해 클 라이언트에서 이벤트가 날아오면 원하는 동작을 할 수 있다.
  • 24.
    데디케이티드 서버가 만들어지고난 후 클라이언트 연결 처리  데디 서버가 만들어지고 나면 클라이언트들이 해당 데디케이티드 서버로 접속해야 한다. 클라이언트는 서버에서 만들어진 데디케이티드 서버의 주소 와 포트 번호를 모른다.  Nodejs에서 데디 서버의 프로세스를 생성하고 포트 번호를 모든 클라이언 트에 전달한다.
  • 25.
    데디케이티드 서버가 생성이된 후에 D2SReadyToPlay를 nodejs로 이벤트를 날린다. 그 이후 에 모든 클라이언트에 데디 서버의 포트 번호를 알려준다. 이때 사용되는 이벤트는 S2CDedicateServerPort다.
  • 26.
    Nodejs 서버에서 데디케이티드서버의 포트를 S2CDedicateServerPort로 알려 주면 클라이언트는 해당 포트로 접속하여 게임 플레이 진행 가능하다.
  • 27.
  • 29.
    데디서버 <-> 클라이언트통신  ClientRpc  서버에서 호출되며 클라이언트에서 실행된다.  Command  클라이언트에서 호출되며 서버에서 실행된다.  SyncVar  연결된 모든 프로세스의 값이 동기화된다.
  • 30.
    Network Identity 컴포넌트 통신하는 오브젝트들은 Network Identity 컴포넌트를 가지고 있어야 한다.
  • 31.
    이동, 회전, RigidBody 유니티에서 이동, 회전, RigidBody와 같은 속성들을 동기화 시키는 컴포넌트 를 제공.
  • 32.
    NetworkTransform 컴포넌트  게임오브젝트의 트랜스폼을 동기화 시킨다.  품질은 매우 낮음(끊겨서 보임)  그래서… 결국은 껯, Command를 사용해 직접 구현해서 사용하는 편이 좋다.
  • 33.
    Rigidbody 동기화  TransformSync Mode를 통해 동기화 시킬 타입을 설정할 수 있다.  RigidBody의 경우 Sync Rigidbody 3D를 선택해 RigidBody를 동기화 시킬 수 있다.  품질은 Sync Transform보다는 괜찮음.
  • 34.
    위치 보간 직접구현해버리기  SyncPlayerPos 스크립트 사용.  각 클라이언트가 현재 위치를 서버에 알린다. 서버는 해당 클라이언트에 붙 어 있는 SyncPos를 업데이트 한다. 이렇게 하면 클라이언트의 SyncPos가 업 데이트 된다.  이후에 클라이언트가 이 SyncPos로 현재 위치에서 보간 한다.
  • 35.
    연습 : 직접회전 보간도 해보도록 하자.
  • 36.
  • 37.