WEBGL :: '분류 전체보기' 카테고리의 글 목록 (7 Page)

달력

22025  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

꾸르

 

꾸르

 

꾸르

 

꾸르

 

꾸르

 

꾸르

 

꾸르

 

 

꾸르

 

꾸르

 

꾸르

 

꾸르

 

꾸르

 

꾸르

 

꾸르

Posted by HammerOh
|

꾸르

 

꾸르

 

꾸르

 

꾸르

 

꾸르

 

꾸르

 

꾸르

 

꾸르

 

꾸르

 

꾸르

 

 

꾸르

 

꾸르

 

꾸르

 

꾸르

Posted by HammerOh
|

꾸르

꾸르

꾸르

Posted by HammerOh
|


Posted by HammerOh
|

꾸르

Posted by HammerOh
|

Posted by HammerOh
|

다른 플레이어 표시

마지막 튜토리얼에서 우리는 게임에서 플레이어를 표시하는 논리를 추가하여 포장을 풀었습니다. 이제 우리는 게임에서 다른 플레이어를 표시하는 작업을 할 것입니다. 이 튜토리얼의 두 번째 파트에서는 newPlayer와 disconnect 이벤트를 내보내는 Socket.IO를 설정합니다. 이 두 가지 이벤트와 currentPlayers 이벤트에 대한 현재 논리를 사용하여 다른 플레이어를 추가하거나 제거합니다. 이렇게하려면 server / public / js / game.js를 열고 create 함수를 다음과 일치하도록 업데이트하십시오.

function create() {
var self = this;
this.socket = io();
this.players = this.add.group();

this.socket.on('currentPlayers', function (players) {
Object.keys(players).forEach(function (id) {
if (players[id].playerId === self.socket.id) {
displayPlayers(self, players[id], 'ship');
} else {
displayPlayers(self, players[id], 'otherPlayer');
}
});
});

this.socket.on('newPlayer', function (playerInfo) {
displayPlayers(self, playerInfo, 'otherPlayer');
});

this.socket.on('disconnect', function (playerId) {
self.players.getChildren().forEach(function (player) {
if (playerId === player.playerId) {
player.destroy();
}
});
});
}

방금 추가 한 코드를 살펴 보겠습니다.


currentPlayers 이벤트가 발생할 때 호출되는 함수를 업데이트하여 해당 플레이어가 현재 플레이어가 아닌 경우 플레이어 객체를 반복 할 때 displayPlayers 함수를 호출합니다.

우리는 socket.on ()을 사용하여 newPlayer를 수신하고 이벤트 연결을 끊습니다.

newPlayer 이벤트가 발생하면 displayPlayers 함수를 호출하여 새 플레이어를 게임에 추가합니다.

연결 해제 이벤트가 발생하면 해당 플레이어의 ID를 가져와 해당 플레이어의 배를 게임에서 제거합니다. 우리는 플레이어 그룹에서 getChildren () 메서드를 호출하여이를 수행합니다. getChildren () 메서드는 해당 그룹에있는 모든 게임 객체의 배열을 반환하며 여기에서 forEach () 메서드를 사용하여 해당 배열을 반복합니다.

마지막으로 destroy () 메서드를 사용하여 해당 게임 객체를 게임에서 제거합니다.


다른 플레이어를 우리 게임에 추가하는 새로운 논리를 테스트하기 전에이 플레이어를 위해 자산을로드해야합니다. 그 자산을 여기서 찾을 수 있습니다.


이 이미지는 public / assets 폴더에 저장해야합니다. 이미지가 있으면 이미지를 게임에로드 할 수 있습니다. 프리로드 함수에 다음 행을 추가하십시오.

this.load.image('otherPlayer', 'assets/enemyBlack5.png');

이제 브라우저에서 게임을 새로 고침하면 플레이어의 배가 계속 표시됩니다. 다른 탭이나 브라우저를 열고 게임으로 이동하면 게임 창에 여러 개의 스프라이트가 나타나야 하며, 해당 게임 중 하나를 닫으면 다른 게임에서 스프라이트가 사라지는 것을 볼 수 있습니다.


플레이어 입력 처리

게임에서 모든 플레이어를 표시하기 위한 논리를 사용하여 플레이어 입력을 처리하고 플레이어가 움직 이도록 할 것입니다. Phaser의 내장 키보드 관리자를 사용하여 플레이어 입력을 처리하고 플레이어를 클라이언트 측에서 직접 이동하는 대신 Socket.IO를 사용하여 플레이어 입력을 서버에 보냅니다. 이렇게 하려면 public / js / game.js의 create 함수 맨 아래에 다음 코드를 추가하십시오.

this.cursors = this.input.keyboard.createCursorKeys();
this.leftKeyPressed = false;
this.rightKeyPressed = false;
this.upKeyPressed = false;

이렇게하면 커서 객체를 네 개의 기본 Key 객체 (위쪽, 아래쪽, 왼쪽 및 오른쪽)로 채우고 키보드의 해당 화살표에 바인딩합니다. 그런 다음, 업데이트 기능에서 이러한 키가 눌러져 있는지 확인하면됩니다. 우리는 또한 어떤 키가 현재 눌려지고 있는지 추적하는 세 가지 새로운 변수를 만들었습니다.이 변수는 업데이트 기능에 사용될 것입니다.


Socket.IO를 사용하여 플레이어의 입력을 서버에 전송할 것이므로 이 키 중 하나를 누르고 있으면 언제든지 서버에 메시지를 보낼 수 있습니다. 그러나 이렇게 하면 필요하지 않은 많은 수의 호출이 서버에 발생합니다. 그 이유는 플레이어가 처음 키를 눌렀을 때와 키를 놓을 때를 인식하기 만하면 되기 때문입니다. 이것을 추적하기 위해 위에서 만든 세 변수를 사용하여 키가 눌려져있는 상태를 저장하고 상태가 변경되면 서버에 메시지를 보냅니다.


이제 public / js / game.js의 업데이트 함수에 다음 코드를 추가합니다.

const left = this.leftKeyPressed;
const right = this.rightKeyPressed;
const up = this.upKeyPressed;

if (this.cursors.left.isDown) {
this.leftKeyPressed = true;
} else if (this.cursors.right.isDown) {
this.rightKeyPressed = true;
} else {
this.leftKeyPressed = false;
this.rightKeyPressed = false;
}

if (this.cursors.up.isDown) {
this.upKeyPressed = true;
} else {
this.upKeyPressed = false;
}

if (left !== this.leftKeyPressed || right !== this.rightKeyPressed || up !== this.upKeyPressed) {
this.socket.emit('playerInput', { left: this.leftKeyPressed, right: this.rightKeyPressed, up: this.upKeyPressed });
}

방금 추가 한 코드를 살펴 보겠습니다.


먼저 왼쪽, 오른쪽 및 위로 세 가지 변수를 새로 만들었습니다. 이 변수는 이전에 누른 키의 상태를 저장하는 데 사용됩니다.

그런 다음 위로, 왼쪽 또는 오른쪽 키를 눌렀는지 확인합니다. 그런 경우 keyPressed 변수를 새 상태로 업데이트하고 키가 눌러지지 않은 경우 해당 변수를 false로 설정합니다.

마지막으로, 플레이어가 업 키를 누르지 않고 현재 키가 눌려 있지 않은 경우와 같이 키 중 하나의 상태가 변경되었는지 확인합니다. 상태가 변경되면 각 키의 상태를 전달하는 playerInput 메시지를 내 보냅니다.


다음으로 새로운 playerInput 메시지를 처리하기 위해 서버의 로직을 업데이트해야합니다. 이렇게하려면 authoritative_server / js / game.js를 열고 socket.on ( 'disconnect', function () {} 밑에 다음 코드를 추가합니다.

socket.on('playerInput', function (inputData) {
handlePlayerInput(self, socket.id, inputData);
});


그런 다음 update 함수 아래에 다음 코드를 추가합니다.

function handlePlayerInput(self, playerId, input) {
self.players.getChildren().forEach((player) => {
if (playerId === player.playerId) {
players[player.playerId].input = input;
}
});
}

위의 코드에서 우리는 다음을 수행했습니다.


먼저 playerInput 메시지를 듣고 이 메시지가 수신되면 handlePlayerInput이라는 새로운 함수를 호출하고 현재 장면에 대한 참조, 메시지를 전달한 플레이어의 소켓 ID 및 플레이어의 입력 키를 전달합니다

handlePlayerInput 함수에서 우리는 players 그룹의 getChildren 메소드를 호출하여 모든 게임 객체의 배열을 가져 왔습니다. 그런 다음 배열을 반복하여 해당 게임 객체의 playerId가 메시지를 전달한 플레이어의 소켓 ID와 일치하는지 확인합니다.

해당 playerId가 일치하면 플레이어 객체의 해당 플레이어 데이터를 업데이트하고 해당 플레이어의 입력을 저장합니다. 플레이어의 입력을 업데이트 기능에서 사용할 수 있도록 저장하고 있습니다.


다음으로 플레이어의 입력을 플레이어 객체의 새 속성에 저장하므로 초기에 이 객체에 기본값을 추가합니다. io.on ( 'connection') 콜백 함수에서 플레이어 [socket.id] 객체에 다음 코드를 추가합니다.

input: {
left: false,
right: false,
up: false
}

이 객체는 다음과 같이 보일 것입니다 :

players[socket.id] = {
rotation: 0,
x: Math.floor(Math.random() * 700) + 50,
y: Math.floor(Math.random() * 500) + 50,
playerId: socket.id,
team: (Math.floor(Math.random() * 2) == 0) ? 'red' : 'blue',
input: {
left: false,
right: false,
up: false
}
};

이제 우리는 각 플레이어의 게임 객체를 이동시키는 업데이트 기능에 로직을 추가 할 수 있습니다.


업데이트 함수에 다음 코드를 추가합니다.

this.players.getChildren().forEach((player) => {
const input = players[player.playerId].input;
if (input.left) {
player.setAngularVelocity(-300);
} else if (input.right) {
player.setAngularVelocity(300);
} else {
player.setAngularVelocity(0);
}

if (input.up) {
this.physics.velocityFromRotation(player.rotation + 1.5, 200, player.body.acceleration);
} else {
player.setAcceleration(0);
}

players[player.playerId].x = player.x;
players[player.playerId].y = player.y;
players[player.playerId].rotation = player.rotation;
});
this.physics.world.wrap(this.players, 5);
io.emit('playerUpdates', players);

방금 추가 한 코드를 살펴 보겠습니다.


먼저 플레이어 그룹의 getChildren 메서드를 호출하여 플레이어의 게임 개체 배열을 가져온 다음 forEach 메서드를 사용하여이 배열을 반복합니다.

이 루프에서는 먼저 input이라는 새 변수를 만들고 거기에 플레이어의 입력 데이터를 저장합니다. 그런 다음 왼쪽, 오른쪽 또는 위로 키를 눌렀는지 확인합니다.

왼쪽 또는 오른쪽 키를 누르면 setAngularVelocity ()를 호출하여 플레이어의 각 속도를 업데이트합니다. 각 속도는 선박이 좌우로 회전하도록합니다.

왼쪽 또는 오른쪽 키를 누르지 않으면 각 속도가 다시 0으로 재설정됩니다.

위로 키를 누르면 배의 속도가 업데이트되고 그렇지 않으면 0으로 설정됩니다.

마지막으로 우리는 플레이어 객체에 플레이어 게임 객체의 x, y 및 회전 속성을 저장합니다. 우리는 클라이언트 측에 다시 전달할 수 있도록 이러한 속성을 저장하고 있으며 해당 데이터를 사용하여 플레이어의 위치를 ​​업데이트합니다.

그런 다음 physics.world.wrap ()을 호출하고 플레이어 그룹과 오프셋 5를 전달합니다. 플레이어의 배가 화면에서 벗어나면 플레이어의 배가 다른쪽에 강제로 표시됩니다. 화면.

마지막으로 우리는 모든 플레이어에게 playerUpdates 메시지를 내고 플레이어에게이 메시지를 전달합니다.


이제 서버에서 플레이어의 입력을 처리하기 위한 코드를 만들었으므로 마지막으로 해야 할 일은 클라이언트 측을 업데이트하여 새 playerUpdates 메시지를 처리하는 것입니다. 클라이언트 쪽에서 이 메시지를 받으면 우리는 그 데이터를 사용하여 각 플레이어의 게임 개체의 위치와 회전을 업데이트합니다.


이렇게하려면 public / js / game.js를 열고 this.cursors = this.input.keyboard.createCursorKeys (); 위에 다음 코드를 추가하십시오.

this.socket.on('playerUpdates', function (players) {
Object.keys(players).forEach(function (id) {
self.players.getChildren().forEach(function (player) {
if (players[id].playerId === player.playerId) {
player.setRotation(players[id].rotation);
player.setPosition(players[id].x, players[id].y);
}
});
});
});

위의 코드에서 우리는 다음을 수행했습니다.


먼저 playerUpdates 메시지와 함께 전달 된 플레이어 객체를 반복 한 다음 플레이어 그룹에있는 모든 게임 객체를 반복합니다.

그런 다음 플레이어 게임 개체 인 playerId가 플레이어 개체의 playerId와 일치하는지 확인합니다.

playerId가 일치하면 setRotation 및 setPosition 메소드를 호출하여 해당 게임 객체의 회전 및 위치를 업데이트합니다.


저장하고 서버를 다시 시작한 다음 게임을 새로 고침하면 우주선을 화면에서 움직일 수있게되었습니다.


별 수집하기

현재 플레이어의 입력을 다루는 게임으로 플레이어에게 목표를 제공해야합니다. 이 튜토리얼에서는 플레이어가 수집 할 수 있도록 게임에 수집 할 수있는 별표를 추가하고 팀이 10 점을 얻게됩니다. 이렇게하려면 몇 가지 새로운 게임 개체와 몇 가지 새로운 Socket.IO 이벤트를 만들어야합니다. 첫째, 스타 게임 객체에 집중할 것입니다.


이 소장품의 자산은 여기에서 다운로드 할 수 있습니다. public / assets 및 authoritative_server / assets 폴더에 star_gold.png 복사본을 놓습니다.


이제 authoritative_server / js / game.js의 preload 함수에 다음 코드를 추가합니다.

this.load.image('star', 'assets/star_gold.png');


그런 다음 create 함수에 다음 코드를 추가하십시오.

this.scores = {
blue: 0,
red: 0
};

this.star = this.physics.add.image(randomPosition(700), randomPosition(500), 'star');
this.physics.add.collider(this.players);

this.physics.add.overlap(this.players, this.star, function (star, player) {
if (players[player.playerId].team === 'red') {
self.scores.red += 10;
} else {
self.scores.blue += 10;
}
self.star.setPosition(randomPosition(700), randomPosition(500));
io.emit('updateScore', self.scores);
io.emit('starLocation', { x: self.star.x, y: self.star.y });
});

위의 코드에서 우리는 다음을 수행했습니다.


프리로드 기능으로 새로운 스타 이미지에로드됩니다.

빨간색과 파란색 팀 모두 점수를 저장하는 데 사용할 객체 인 scores라는 새 변수를 만듭니다.

star collectible에 대한 새로운 게임 객체를 생성하고, randomPosition이라는 새 함수를 호출하여 x 및 y 위치에 대한 임의의 위치를 ​​만들었습니다.

플레이어 Phaser 그룹에 콜리더를 추가했습니다. Phaser 그룹을 physics.add.collider () 메소드에 전달하면 Phaser가 모든 하위 게임 객체 간의 충돌을 자동으로 확인합니다.

플레이어 Phaser 그룹과 별 게임 개체 사이에 중복을 추가했으며, 게임 개체 중 하나가 다른 개체와 겹칠 때 호출되는 콜백 함수를 제공했습니다. Phaser 그룹과 하나의 게임 개체를 전달하면 Phaser는 모든 자식 게임 개체와 단일 게임 개체 사이의 충돌을 자동으로 확인합니다.

콜백 함수에서 플레이어 게임 개체가 속한 팀을 확인하고 해당 팀의 점수를 업데이트했습니다. 그런 다음 새로운 임의의 위치를 ​​제공하여 스타 게임 개체의 위치를 ​​업데이트합니다.

마지막으로 updateScore와 starLocation이라는 두 개의 새로운 Socket.IO 메시지를 생성했습니다. updateScore 메시지를 내 보내면 점수 변수도 클라이언트 측에 보냅니다. starLocation 메시지를 내 보내면 별 게임 개체의 x 및 y 위치도 보냅니다.


다음으로, io.on ( 'connection') 콜백 함수에서 socket.broadcast.emit ( 'newPlayer', players [socket.id]) 아래에 다음 코드를 추가하십시오.

// send the star object to the new player
socket.emit('starLocation', { x: self.star.x, y: self.star.y });
// send the current scores
socket.emit('updateScore', self.scores);


마지막으로 update 함수 아래에 다음 코드를 추가합니다.

function randomPosition(max) {
return Math.floor(Math.random() * max) + 50;
}

위 코드에서 우리는 :


별 게임 개체의 위치와 현재 점수를 우리 게임에 참여하는 새로운 플레이어에게 보냅니다.

위의 코드에서 호출 된 randomPosition 함수가 추가되었습니다.


서버의 코드가 변경되면 클라이언트 측으로 전환합니다. 우리가 할 첫 번째 일은 스타 애셋을 클라이언트 측 코드에 로드하는 것입니다. public / js / game.js에서 preload 함수 맨 아래에 다음 행을 추가하십시오.

this.load.image('star', 'assets/star_gold.png');


다음으로, 현재 점수가 무엇인지 알리는 방법이 필요합니다. Phaser의 텍스트 게임 개체를 사용하여이를 수행 할 수 있습니다. create 함수에서 this.players = this.add.group (); 아래에 다음 코드를 추가하십시오.

this.blueScoreText = this.add.text(16, 16, '', { fontSize: '32px', fill: '#0000FF' });
this.redScoreText = this.add.text(584, 16, '', { fontSize: '32px', fill: '#FF0000' });

위의 코드에서 this.add.text ()를 호출하여 두 개의 새로운 텍스트 게임 객체를 만들었습니다. 이 두 객체를 만들 때 객체 배치 위치, 객체의 기본 텍스트, 텍스트 객체에 사용할 글꼴 크기 및 채우기를 전달했습니다.


마지막으로 우리가 만든 두 개의 새로운 Socket.IO 이벤트에 대한 논리를 추가하면됩니다. create 함수에서 this.cursors = this.input.keyboard.createCursorKeys (); 위에 다음 코드를 추가하십시오.

this.socket.on('updateScore', function (scores) {
self.blueScoreText.setText('Blue: ' + scores.blue);
self.redScoreText.setText('Red: ' + scores.red);
});

this.socket.on('starLocation', function (starLocation) {
if (!self.star) {
self.star = self.add.image(starLocation.x, starLocation.y, 'star');
} else {
self.star.setPosition(starLocation.x, starLocation.y);
}
});

방금 추가 한 코드를 살펴 보겠습니다.


updateScore 이벤트가 수신되면 setText () 메서드를 호출하여 게임 개체의 텍스트를 업데이트하고 팀의 점수를 각 개체에 전달합니다.

starLocation 이벤트가 수신되면 먼저 스타 게임 개체가 존재하지 않는지 확인하고 그렇지 않은 경우 제공된 위치에 스타 게임 개체를 만듭니다. 별 게임 개체가 존재하면 setPosition 메서드를 호출하여 별 게임 개체를 업데이트합니다.


코드 변경 사항을 저장하고 서버를 다시 시작하고 브라우저를 새로 고침하면 새로운 스타 수집품과 팀 점수를 확인해야합니다.

우주선을 스타 수집품으로 옮기면 스타가 새로운 위치로 이동해야하며 팀 점수 업데이트가 표시되어야합니다. 또한 브라우저에서 새 탭을 열면 우주선을 다른 플레이어의 우주선으로 옮기고 충돌을 볼 수 있어야합니다.


결론

스타 수집품을 게임에 추가하면이 가이드가 끝납니다. 요약하면 Phaser의 신뢰할 수있는 서버로 간단한 멀티 플레이어 게임을 만드는 방법을 보여주었습니다. 주로 Socket.IO와 Node.js를 사용했습니다.


이 튜토리얼을 모두 즐겁게 사용하여 도움이 되었기를 바랍니다. 궁금한 점이 있거나 다음에 다루어야 할 사항에 대한 제안이 있으면 아래 의견에 알려주십시오.

Posted by HammerOh
|

이 튜토리얼의 제 1 부에서는 Node.js 서버를 만들고 기본적인 Phaser 게임을 설정하고 헤드리스 모드로 Phaser를 실행하도록 서버를 설정합니다.

이 튜토리얼에서는 게임에 Socket.IO 라이브러리를 추가하고 게임에 플레이어를 추가 및 제거하는 서버 로직을 추가하며 플레이어를 게임에 추가하기위한 클라이언트 측 로직을 추가하는 데 집중할 것입니다.


Socket.IO 추가하기

Phaser가 이제 서버에서 실행되면서 이제 Socket.IO를 게임에 추가 할 것입니다.

Socket.IO는 웹 클라이언트와 서버 간의 실시간 양방향 통신을 가능하게하는 JavaScript 라이브러리입니다.

Socket.IO를 사용하려면 클라이언트와 서버 코드를 업데이트하여 둘 사이의 통신을 가능하게해야합니다.


터미널에서 다음 명령을 실행하십시오.

npm install --save socket.io

서버가 계속 실행중인 경우 새 터미널 창을 열고 프로젝트 폴더에서 코드를 실행하거나 서버를 중지 한 다음 (Ctrl + C) 명령을 실행할 수 있습니다. 이렇게하면 Socket.IO 노드 패키지가 설치되어 package.json 파일에 저장됩니다.


이제 index.js에 var server = require ( 'http').Server (app); 아래에 다음 코드를 추가합니다.

const io = require('socket.io').listen(server);

그런 다음 dom.window.gameLoaded 코드 아래에 다음 행을 추가하십시오.

dom.window.io = io;

그러면 jsdom에 socket.io 인스턴스가 삽입되어 서버에서 실행중인 Phaser 코드에서 이 인스턴스에 액세스 할 수 있습니다. 이제 authoritative_server / js / game.js에서 create 함수에 다음 코드를 추가합니다.

io.on('connection', function(socket) {

console.log('a user connected');

socket.on('disconnect', function() {

console.log('user disconnected');

});

});

위 코드에서 우리는 :

socket.io 모듈을 참조하고 서버 객체를 수신하게했습니다.

연결 및 연결 끊김을 청취하는 논리가 추가되었습니다.


다음으로 Socket.IO 라이브러리를 포함하도록 클라이언트 측 코드를 업데이트합니다. public / index.html을 열고 <body> 요소의 맨 위에 다음 줄을 추가하십시오.

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>

그런 다음 public / js / game.js를 열고 create 함수에 다음 코드를 추가합니다.

this.socket = io();

이제 코드 변경 사항을 저장하고 서버를 다시 시작한 다음 브라우저에서 게임을 새로 고침하면 연결된 사용자에 대한 메시지가 표시됩니다. 브라우저에서 게임을 새로 고침하면 사용자 연결 해제에 대한 새 메시지와 사용자 연결에 대한 다른 메시지가 표시됩니다.

플레이어 추가 - 서버

소켓 연결 설정을 통해 플레이어를 게임에 추가 할 수 있습니다. 가장 먼저해야 할 일은 플레이어의 우주선에 사용될 자산을 적재하는 것입니다. 이 튜토리얼에서는 Kenny 's Space Shooter Redux 자산 팩의 일부 이미지를 사용합니다. 게임의 자산은 여기에서 다운로드 할 수 있습니다.


authoritative_server 폴더에서 assets이라는 새 폴더를 만들고 여기에 이미지를 저장합니다. 게임에서 이미지를로드하려면 authoritative_server / js / game.js의 사전로드 함수에 다음 행을 추가해야합니다.

this.load.image('ship', 'assets/spaceShips_001.png');


우주선 이미지가 로드되면 플레이어 추가를 위한 나머지 로직이 추가됩니다. 모든 플레이어의 게임을 동기화 상태로 유지하려면 사용자가 게임에 연결하거나 연결을 끊을 때 모든 플레이어에게 알릴 수있는 방법이 필요합니다. 또한 새 플레이어가 연결되면 새 플레이어가 게임의 다른 모든 플레이어를 알 수있는 방법이 필요합니다.


이렇게 하기 위해 소켓 연결을 사용하여 각 클라이언트에 메시지를 보낼 수 있습니다. 플레이어 데이터의 경우 연결하고 끊는 각 플레이어를 추적해야 합니다.


authoritative_server / js / game.js 파일의 맨 위에 다음 코드를 추가하십시오.

const players = {};


이 오브젝트를 사용하여 현재 게임중인 모든 플레이어를 추적합니다. 그런 다음 create 함수의 맨 위에 다음 코드를 추가합니다.

const self = this;

this.players = this.physics.add.group();


그런 다음 socket.io 연결 이벤트의 콜백 함수에서 console.log ( 'a user connected') 아래에 다음 코드를 추가합니다.

// create a new player and add it to our players object

players[socket.id] = {

rotation: 0,

x: Math.floor(Math.random() * 700) + 50,

y: Math.floor(Math.random() * 500) + 50,

playerId: socket.id,

team: (Math.floor(Math.random() * 2) == 0) ? 'red' : 'blue'

};

// add player to server

addPlayer(self, players[socket.id]);

// send the players object to the new player

socket.emit('currentPlayers', players);

// update all other players of the new player

socket.broadcast.emit('newPlayer', players[socket.id]);

방금 추가 한 코드를 살펴 보겠습니다.


- 우리는 self라고 불리는 새로운 변수를 만들었고이 변수를 사용하여 이 Phaser Scene에 대한 참조를 저장합니다.

- 우리는 우리 게임에서 모든 플레이어를 관리하는 데 사용될 새로운 Phaser 물리 그룹을 만들었습니다. Phaser의 그룹에 익숙하지 않은 경우, 유사한 게임 개체를 관리하고 하나의 단위로 제어 할 수있는 방법입니다. 한 가지 예는 각 게임 개체의 충돌을 개별적으로 확인하지 않고 그룹과 다른 게임 개체 간의 충돌을 검사 할 수 있다는 것입니다.

- 플레이어가 웹 소켓에 연결하면 플레이어에 대한 데이터로 플레이어 개체를 업데이트하고 이 데이터를 개체로 저장하고 소켓의 ID를 개체의 키로 사용합니다.

- 우리는 플레이어의 회전, x 및 y 위치를 저장하고 있으며 클라이언트 측에서 스프라이트를 작성하고 이 데이터를 사용하여 각 플레이어 게임을 업데이트하는 데 사용합니다.

- 우리는 또한 playerId를 저장하여 게임에서 참조 할 수 있으며 나중에 사용할 팀 속성을 추가했습니다.

- 우리는 socket.emit과 socket.broadcast.emit을 사용하여 클라이언트 측 소켓에 이벤트를 발생시켰다. socket.emit은 이 특정 소켓 (방금 연결 한 새 플레이어)으로 이벤트를 방출합니다. socket.broadcast.emit은 이벤트를 다른 모든 소켓 (기존 플레이어)에게 보냅니다.

- currentPlayers 이벤트에서 플레이어 개체를 새 플레이어로 전달합니다. 이 데이터는 새 플레이어의 게임에서 모든 플레이어 스프라이트를 채우는 데 사용됩니다.

- newPlayer 이벤트에서 우리는 새로운 플레이어의 데이터를 다른 모든 플레이어에게 전달하므로 새로운 스프라이트를 게임에 추가 할 수 있습니다.

- 마지막으로 addPlayer라는 새로운 함수를 호출했습니다.이 함수는 서버에서 플레이어를 만드는 데 사용됩니다.


이제 우리는 addPlayer 함수를 생성 할 것입니다. 이 함수는 새로운 플레이어 게임 개체를 만드는 데 사용되며 방금 만든 플레이어 그룹에 해당 게임 개체를 추가합니다. 업데이트 함수 아래에 다음 코드를 추가하십시오.

function addPlayer(self, playerInfo) {

const player = self.physics.add.image(playerInfo.x, playerInfo.y, 'ship').setOrigin(0.5, 0.5).setDisplaySize(53, 40);

player.setDrag(100);

player.setAngularDrag(100);

player.setMaxVelocity(200);

player.playerId = playerInfo.playerId;

self.players.add(player);

}

위 코드에서 우리는 :

- 이전에 생성 한 x와 y 좌표를 사용하여 새 플레이어의 배를 만들었습니다.

- 플레이어의 배를 만들기 위해 self.add.image를 사용하는 대신 self.physics.add.image를 사용하여 게임 객체가 아케이드 물리를 사용할 수있게했습니다.

- setOrigin ()을 사용하여 게임 객체의 원점을 왼쪽 상단 대신 객체의 중간에 설정했습니다. 우리가 게임 객체를 회전시킬 때 원점을 중심으로 회전하기 때문에 이 작업을 수행 한 이유입니다.

- setDisplaySize ()를 사용하여 게임 객체의 크기와 크기를 변경했습니다. 원래 우주선 이미지는 106 × 80 픽셀이었습니다. setDisplaySize ()를 호출 한 후 이미지가 이제 게임에서 53x40 픽셀입니다.

- 마지막으로 setDrag, setAngularDrag 및 setMaxVelocity를 사용하여 게임 객체가 아케이드 물리에 반응하는 방식을 수정했습니다. setDrag 및 setAngularDrag는 객체가 움직일 때 저항하는 양을 제어하는 ​​데 사용됩니다. setMaxVelocity는 게임 개체가 도달 할 수있는 최대 속도를 제어하는 ​​데 사용됩니다.


플레이어 제거 - 서버

서버에 플레이어를 추가하는 로직을 사용하여 서버에서 플레이어를 제거하는 로직을 추가합니다. 플레이어가 연결을 끊으면 플레이어 개체에서 해당 플레이어의 데이터를 제거해야하며,이 사용자에 대한 다른 모든 플레이어에게 메시지를 내 보내야합니다. 그러면 플레이어의 스프라이트가 클라이언트 게임에서 제거 될 수 있습니다.


socket.io 연결 해제 이벤트의 콜백 함수에서 console.log ( 'user disconnected') 아래에 다음 코드를 추가하십시오.

// remove player from server

removePlayer(self, socket.id);

// remove this player from our players object

delete players[socket.id];

// emit a message to all players to remove this player

io.emit('disconnect', socket.id);


그런 다음 addPlayer 함수 아래에 다음 코드를 추가합니다.

function removePlayer(self, playerId) {

self.players.getChildren().forEach((player) => {

if (playerId === player.playerId) {

player.destroy();

}

});

}

위 코드에서 우리는 :


- removePlayer라는 새 함수를 만들었습니다. 이 함수는 연결이 끊어진 플레이어의 socket.id를 가져오고 Phaser 그룹에서 플레이어의 게임 개체를 찾아 파괴합니다.

- 우리는 플레이어 그룹에서 getChildren () 메서드를 호출하여이를 수행합니다. getChildren () 메서드는 해당 그룹에있는 모든 게임 객체의 배열을 반환합니다. 일치하는 ID를 가진 게임 객체를 찾을 때까지 forEach () 메서드를 사용하여 배열을 반복합니다.

- 서버에서 게임 개체가 파괴되면 delete 개체를 사용하여 해당 개체를 제거하여 플레이어 개체에서 해당 개체를 삭제합니다.

- 마지막으로 io.emit을 사용하여 모든 소켓에 메시지를 보냈습니다. 이 경우 연결을 끊은 사용자의 socket.id를 전달하여 클라이언트 측 코드에서 해당 플레이어의 스프라이트를 제거 할 수 있습니다.


authoritative_server / js / game.js 파일은 다음과 같아야합니다.

const players = {};


const config = {

type: Phaser.HEADLESS,

parent: 'phaser-example',

width: 800,

height: 600,

physics: {

default: 'arcade',

arcade: {

debug: false,

gravity: { y: 0 }

}

},

scene: {

preload: preload,

create: create,

update: update

},

autoFocus: false

};


function preload() {

this.load.image('ship', 'assets/spaceShips_001.png');

}


function create() {

const self = this;

this.players = this.physics.add.group();


io.on('connection', function (socket) {

console.log('a user connected');

// create a new player and add it to our players object

players[socket.id] = {

rotation: 0,

x: Math.floor(Math.random() * 700) + 50,

y: Math.floor(Math.random() * 500) + 50,

playerId: socket.id,

team: (Math.floor(Math.random() * 2) == 0) ? 'red' : 'blue'

};

// add player to server

addPlayer(self, players[socket.id]);

// send the players object to the new player

socket.emit('currentPlayers', players);

// update all other players of the new player

socket.broadcast.emit('newPlayer', players[socket.id]);


socket.on('disconnect', function () {

console.log('user disconnected');

// remove player from server

removePlayer(self, socket.id);

// remove this player from our players object

delete players[socket.id];

// emit a message to all players to remove this player

io.emit('disconnect', socket.id);

});

});

}


function update() { }


function addPlayer(self, playerInfo) {

const player = self.physics.add.image(playerInfo.x, playerInfo.y, 'ship').setOrigin(0.5, 0.5).setDisplaySize(53, 40);

player.setDrag(100);

player.setAngularDrag(100);

player.setMaxVelocity(200);

player.playerId = playerInfo.playerId;

self.players.add(player);

}


function removePlayer(self, playerId) {

self.players.getChildren().forEach((player) => {

if (playerId === player.playerId) {

player.destroy();

}

});

}


const game = new Phaser.Game(config);

window.gameLoaded();


이제 코드 변경 사항을 저장하고 서버를 다시 시작한 다음 브라우저에서 게임을 새로 고침하면 콘솔에 오류 메시지가 표시됩니다.


오류 메시지의 모양에서 jsdom은 URL.createObjectURL 메소드를 지원하지 않는 것 같습니다. 이 정적 메서드는 지정된 소스 객체의 내용을 참조하는 데 사용할 수있는 객체 URL이 포함 된 DOMString을 만드는 데 사용됩니다. 이 오류를 해결하려면 비슷한 값을 반환하는 메서드를 구현해야합니다.


이를 위해 datauri 패키지를 사용하여 데이터 URI를 반환합니다. 이 패키지를 사용하려면 프로젝트에 추가해야합니다. 터미널에서 서버를 중지하고 다음 명령을 실행하십시오.

npm install -save datauri


그런 다음 서버 코드에 라이브러리를 포함시켜야합니다. server / index.js를 열고 const {JSDOM} = jsdom;

const Datauri = require('datauri');

const datauri = new Datauri()


setupAuthoritativePhaser 함수에서 다음 코드를 dom.window.gameLoaded = () => {line :

dom.window.URL.createObjectURL = (blob) => {

if (blob) {

return datauri.format(blob.type, blob[Object.getOwnPropertySymbols(blob)[0]]._buffer).content;

}

};

dom.window.URL.revokeObjectURL = (objectURL) => { };

방금 추가 한 코드를 살펴 보겠습니다.


- 첫째, 우리는 datauri 패키지를 포함 시켰고 새로운 인스턴스를 만들었습니다.

그런 다음 jsdom에 구현되어 있지 않은 createObjectURL 및 revokeObjectURL 함수를 만들었습니다.

- revokeObjecURL 함수의 경우 함수에 아무 것도하지 않습니다.

- createObjectURL 함수의 경우 datauri.format 메서드를 사용하여 blob을 필요한 형식으로 포맷합니다.

이제 코드 변경 사항을 저장하고 서버를 시작하면 모든 것이 잘 시작됩니다.


플레이어 추가 - 클라이언트

플레이어를 추가하는 서버 코드를 사용하여 클라이언트 측 코드를 작성합니다. 가장 먼저해야 할 일은 플레이어에게 사용될 자산을 로드하는 것입니다. 공용 폴더에서 assets이라는 새 폴더를 만들고 이 폴더에서 다른 assets 폴더의 spaceShips_001.png 이미지를 복사합니다.


게임에서 이미지를 로드하려면 public / js / game.js의 preload 함수에 다음 줄을 추가해야합니다.

this.load.image('ship', 'assets/spaceShips_001.png');

우주선 이미지가 로드되면 이제 게임에서 플레이어를 만들 수 있습니다. 이전에 새 플레이어가 게임에 연결할 때마다 currentPlayers 이벤트를 발생하도록 Socket.IO를 설정했으며 이 이벤트가 발생하면 현재 플레이어의 데이터가 포함 된 플레이어 개체도 전달했습니다.


public / js / game.js의 create 함수를 다음과 일치하도록 업데이트하십시오.

function create() {
var self = this;
this.socket = io();
this.players = this.add.group();

this.socket.on('currentPlayers', function (players) {
Object.keys(players).forEach(function (id) {
if (players[id].playerId === self.socket.id) {
displayPlayers(self, players[id], 'ship');
}
});
});
}

방금 추가 한 코드를 살펴 보겠습니다.


먼저 클라이언트 측의 모든 게임 개체를 관리하는 데 사용되는 새로운 Phaser 그룹을 만들었습니다.

우리는 currentPlayers 이벤트를 수신하기 위해 socket.on을 사용했으며, 이 이벤트가 트리거되면 우리가 제공 한 함수가 서버에서 전달한 players 객체로 호출됩니다.

이 함수가 호출되면 각 플레이어를 반복하고 해당 플레이어의 ID가 현재 플레이어의 소켓 ID와 일치하는지 확인합니다.

플레이어를 통해 반복하기 위해 우리는 Object.keys ()를 사용하여 전달 된 Object의 모든 키 배열을 만듭니다. 반환되는 배열의 경우 forEach () 메서드를 사용하여 배열의 각 항목을 반복합니다 .

마지막으로 displayPlayers () 함수를 호출하여 현재 플레이어의 정보와 현재 장면에 대한 참조를 전달했습니다.


이제 public / js / game.js에 displayPlayer 함수를 추가해 보겠습니다. 파일의 맨 아래에 다음 코드를 추가하십시오.

function displayPlayers(self, playerInfo, sprite) {
const player = self.add.sprite(playerInfo.x, playerInfo.y, sprite).setOrigin(0.5, 0.5).setDisplaySize(53, 40);
if (playerInfo.team === 'blue') player.setTint(0x0000ff);
else player.setTint(0xff0000);
player.playerId = playerInfo.playerId;
self.players.add(player);
}

위 코드에서 우리는 :


우리 서버 코드에서 생성 한 x와 y 좌표를 사용하여 플레이어의 배를 만들었습니다.

setOrigin ()을 사용하여 게임 객체의 원점을 왼쪽 상단 대신 객체의 중간에 설정했습니다.

setDisplaySize ()를 사용하여 게임 객체의 크기와 크기를 변경했습니다.

우리는 setTint ()를 사용하여 배 게임 객체의 색상을 변경했으며 서버에 플레이어 정보를 생성 할 때 생성 된 팀에 따라 색상을 선택합니다.

나중에 id로 게임 객체를 찾을 수 있도록 playerId를 저장했습니다.

마지막으로 플레이어의 게임 개체를 우리가 만든 Phaser 그룹에 추가했습니다.


브라우저를 새로 고침하면 플레이어의 배가 화면에 표시됩니다.


또한 게임을 새로 고침하면 우주선이 다른 위치에 나타나고 우주선이 빨간색이나 파란색으로 무작위로 표시되어야 합니다.


결론

플레이어를 표시하기위한 클라이언트 측 코드를 사용하면이 자습서 시리즈의 제 2 편이 끝납니다. 3 부에서는 멀티 플레이어 게임을 계속 진행합니다.


다른 플레이어를 게임에 추가하기위한 클라이언트 측 로직 추가.

플레이어 입력에 대한 논리 추가.

수집품에 대한 논리 추가.

나는 당신이 우리 두 번째 할부를 즐겁게하고 그것이 도움이되기를 바랐다. 우리가 다음에 다루어야 할 것에 대해 질문이나 제안이 있으면, 아래의 의견에 저희에게 알려주십시오.

Posted by HammerOh
|

이 멀티 파트 튜토리얼에서는 Phaser 및 Socket.io와 함께 간단한 멀티 플레이어 게임을 제작할 것입니다. 멀티 플레이어 게임의 경우 클라이언트 - 서버 게임 아키텍처를 따르고 서버에서 실행되도록 Phaser를 설정하고 이를 신뢰할 수있는 서버로 사용합니다. 클라이언트 - 서버 게임 아키텍처에 익숙하지 않은 경우 클라이언트가 플레이어에게 게임을 표시하고 플레이어의 입력을 처리하며 서버에 데이터를 전송해야합니다. 우리의 인증서버는 주요 Phaser 논리를 실행하는 책임이 있으며 각 클라이언트에 데이터를 전송할 책임이 있습니다.


이 튜토리얼의 목표는 멀티 플레이어 게임을 만드는 기본 사항을 가르치는 것입니다. 다음 방법을 배우게됩니다.


- 신뢰할 수있는 서버로 작동 할 Node.js 및 Express 서버를 설정합니다. 이 서버는 또한 클라이언트 측 파일 렌더링을 담당합니다.

- 서버에서 헤드리스 모드로 Phaser를 설정하고 실행하십시오.

- 우리의 클라이언트로 행동 할 기본 Phaser 3 게임을 설정하십시오.


- Socket.IO를 사용하면 서버와 클라이언트가 서로 통신 할 수 있습니다.


자습서 요구 사항

이 자습서에서는 Node.js와 npm을 사용하여 이 프로젝트에 필요한 필수 패키지를 설치합니다. 이 자습서를 수행하려면 Node.js 및 NPM을 로컬로 설치해야하며 그렇지 않으면 이미 설치되어있는 환경에 액세스해야 합니다. 또한 명령 프롬프트 (Windows) / 터미널 (Mac)을 사용하여 필수 패키지를 설치하고 노드 서버를 시작 / 중지합니다.


이러한 도구에 대한 사전 경험이 있으면 도움이되지만 이 자습서에서는 필요하지 않습니다. 이 튜토리얼의 초점이 Phaser로 게임을 만들고 있으므로 이 도구를 설치하는 방법은 다루지 않을 것입니다. 또한 이 자습서와 함께 Chrome 웹 브라우저에 액세스해야합니다. 마지막으로 필요한 것은 코드 편집을 위한 IDE 또는 텍스트 편집기입니다.


Node.js를 설치하려면 여기 링크를 클릭하고 LTS 버전을 선택하십시오. 이 자습서에서는 현재 버전을 다운로드하여 사용할 수 있지만 대부분의 사용자는 LTS 버전을 사용하는 것이 좋습니다. Node.js를 설치하면 NPM도 컴퓨터에 설치됩니다. 이러한 도구가 설치되면 다음 부분으로 넘어갈 수 있습니다.


서버 설정

우리가 할 첫 번째 일은 게임 파일을 제공 할 기본 Node.js 서버를 만드는 것입니다. 시작하려면 컴퓨터에 새 폴더를 만드십시오. 원하는 이름으로 부를 수 있습니다. 그런 다음 터미널에서 이 폴더로 이동하여 다음 명령을 실행합니다. npm init -f. 이렇게 하면 프로젝트 폴더에 package.json 파일이 만들어집니다. 이 파일을 사용하여 프로젝트가 의존하는 모든 패키지를 추적합니다.


프로젝트에 추가 할 첫 번째 패키지는 정적 인 클라이언트 측 파일을 렌더링하는 데 사용할 Node.js 웹 응용 프로그램 프레임 워크입니다. 터미널에서 다음 명령을 실행하십시오. npm install --save express 그러면 프로젝트의 node_modules 폴더에 해당 패키지와 필수 종속성이 설치됩니다. 이 명령은 express 모듈을 추가하여 package.json 파일을 업데이트합니다.


이제 프로젝트 폴더에 server라는 새 폴더를 만들고 이 폴더에 index.js라는 새 파일을 만듭니다. index.js를 열고 다음 코드를 추가하십시오.

const express = require('express');

const app = express();

const server = require('http').Server(app);


app.use(express.static(__dirname + '/public'));


app.get('/', function (req, res) {

res.sendFile(__dirname + '/index.html');

});


server.listen(8081, function () {

console.log(`Listening on ${server.address().port}`);

});

위의 코드에서 우리는 다음을 수행했습니다.


- Express 모듈을 참조했습니다. 이 모듈은 정적 파일을 렌더링하는 데 도움이 되는 웹 프레임 워크입니다.

- 표현의 새로운 인스턴스를 만들고 app이라고 했습니다.

- 익스프레스가 HTTP 요청을 처리 할 수 있도록 HTTP 서버에 앱을 제공했습니다.

- Express에서 기본 제공되는 express.static 미들웨어 기능을 사용하여 정적 파일을 렌더링하도록 서버를 업데이트했습니다.

- 루트 페이지로 index.html 파일을 제공하도록 서버에 알립니다.


- 서버가 포트 8081에서 청취를 시작 했어야 합니다.


클라이언트 설정

기본 서버 코드가 끝나면 클라이언트 측 코드 설정 작업을 시작합니다. 서버 폴더에서 public이라는 새 폴더를 만듭니다. 이 폴더에 넣은 파일은 우리가 설정 한 서버에서 렌더링됩니다. 따라서 모든 정적 클라이언트 측 파일을 이 폴더에 저장하려고 합니다. 이제 공용 폴더에서 index.html이라는 새 파일을 만듭니다. index.html을 열고 다음 코드를 추가하십시오.

<!DOCTYPE html>

<html>


<head>

<meta charset="utf-8">

</head>


<body>

<script src="https://cdn.jsdelivr.net/npm/phaser@3.18.0/dist/phaser.min.js"></script>

<script src="js/game.js"></script>

</body>


</html>

그런 다음 공용 폴더에서 js라는 새 폴더를 만들고, 이 폴더에 game.js.라는 새 파일을 만듭니다. 이 파일에 다음 코드를 추가하십시오.

var config = {

type: Phaser.AUTO,

parent: 'phaser-example',

width: 800,

height: 600,

scene: {

preload: preload,

create: create,

update: update

}

};


var game = new Phaser.Game(config);


function preload(){}

function create(){}

function update(){}

위의 코드에서 기본 HTML 파일을 만들고 Phaser 라이브러리를 참조했습니다. 마지막으로 새로운 Phaser Game 인스턴스를 만들었습니다.


기본 클라이언트 측 코드가 설정되면 서버를 테스트하고 모든 것이 올바르게 작동하는지 확인합니다. 터미널 / 명령 프롬프트로 돌아가서 node / index.js 명령을 실행하면 다음 줄이 표시됩니다. 8081에서 Listening이 표시됩니다. 이제 웹 브라우저를 열고 http : // localhost : 8081 /을 선택하면 웹 페이지에 검은 색 상자가 표시되고 개발자 도구에서 콘솔을 열면 게임이 실행중인 Phaser 버전의 로그 라인이 표시됩니다.


디버깅

다음 단계에서는 Headless 모드에서 Phaser를 실행하기 위해 서버를 설정합니다. 서버에서 가상 DOM을 실행해야 합니다. 그러나 먼저 서버에서 실행중인 Phaser 코드를 디버깅하는 것이 좋습니다. 코드를 디버깅하기 위해 노드의 --inspect 플래그와 Chrome의 개발자 도구를 사용하여 서버에서 실행중인 dom의 콘솔 출력을 볼 수 있습니다. 이 작업을 수행하려면 node --inspect  index.js 명령을 사용하여 서버를 중지했다가 다시 시작해야합니다. 그런 다음 Chrome을 열고 chrome : // inspect / # devices URL을 방문합니다. 다음과 유사한 화면이 나타납니다.

노드의 전용 DevTools 열기 링크를 클릭하면 dom의 출력을 표시하는 콘솔이있는 새 브라우저 창이 열립니다. 위의 링크를 클릭하면 서버를 중지했다가 다시 시작하더라도 콘솔은 노드 세션에 계속 연결되며 업데이트 된 출력이 표시됩니다.


권한있는 서버 설정

이제 클라이언트 측에서 Phaser를 실행 했으므로 Phaser를 서버에서 실행하는 작업을 진행할 것입니다. 서버에서 Phaser를 실행할 것이므로 제대로 작동하려면 몇 가지 추가 라이브러리를 프로젝트에 추가해야합니다. 우리가 필요로하는 첫 번째 패키지는 브라우저에서 DOM JavaScript API의 대부분을 재생성하는 데 사용되는 jsdom이며, HTML 파일을로드하고 상호 작용할 수 있습니다.


두 번째 패키지는 노드 캔버스입니다.이 캔버스는 Node.js에 캔버스 API를 구현 한 것입니다. 이 패키지가 필요한 이유는 헤드리스 모드로 실행 중일 때도 Phaser가 캔버스 API를 실행해야하기 때문입니다. 이 패키지를 설치하려면 터미널에서 다음 명령을 실행하십시오. 

npm install --save canvas 

npm install --save jsdom


이제 필요한 패키지가 설치되었으므로 서버에 Phaser를 실행하기위한 코드를 추가 할 수 있습니다. 서버 폴더에서 authoritative_server라는 새 폴더를 만듭니다. 이 폴더에서 index.html이라는 새 파일을 만들고 다음 코드를 추가합니다.

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

</head>

<body>

<script src="https://cdn.jsdelivr.net/npm/phaser@3.18.0/dist/phaser.min.js"></script>

<script src="js/game.js"></script>

</body>

</html>

그런 다음 js라는 새 폴더를 만들고 여기에 game.js라는 새 파일을 만들고 여기에 다음 코드를 추가합니다.

const config = {

type: Phaser.HEADLESS,

parent: 'phaser-example',

width: 800,

height: 600,

physics: {

default: 'arcade',

arcade: {

debug: false,

gravity: { y: 0 }

}

},

scene: {

preload: preload,

create: create,

update: update

}

};


function preload() { }

function create() { }

function update() { }


const game = new Phaser.Game(config);

이 코드는 이전에 추가 한 클라이언트 측 코드와 비슷해야합니다. 이 둘의 주된 차이점은 Phaser 구성에서 Phaser.HEADLESS로 유형을 설정한다는 것입니다.


페이저 서버 코드를 제자리에 두면 다음으로 해야 할 일은 서버에 이러한 파일을 로드하고 실행하는 것입니다. server 폴더에서 index.js를 열고 다음 코드를 파일의 맨 위에 추가하십시오.

const path = require('path');

const jsdom = require('jsdom');

그런 다음 서버 변수 아래에 다음 코드를 추가하십시오.

const { JSDOM } = jsdom;

마지막으로 파일 맨 아래에 다음 코드를 추가합니다.

function setupAuthoritativePhaser() {

JSDOM.fromFile(path.join(__dirname, 'authoritative_server/index.html'), {

// To run the scripts in the html file

runScripts: "dangerously",

// Also load supported external resources

resources: "usable",

// So requestAnimatinFrame events fire

pretendToBeVisual: true

});

}

setupAuthoritativePhaser();

위의 코드에서 우리는 다음을 수행했습니다.


- 먼저 jsdom 패키지를 포함 시켰습니다. jsdom 패키지를 사용하면 서버에서 DOM API를 사용할 수 있습니다.

- 그런 다음 setupAuthoritativePhaser라는 새 함수를 만들어 함수를 호출했습니다.

- 이 함수에서 JSDOM의 fromFile 메소드를 사용하여 앞서 만든 index.html을로드했습니다. 

- fromFile 메서드를 호출 할 때 우리는 로드하려는 파일과 이 파일을 실행할 때 필요한 옵션이 들어있는 객체를 전달합니다. 이러한 옵션에는 다음이 포함됩니다.

  JSDOM이 스크립트를 실행할 수 있도록 허용

  JSDOM이 외부 리소스를로드 할 수 있도록 허용


  일반적인 비주얼 브라우저처럼 동작하도록 JSDOM에 지시


이제 코드를 저장하고 서버를 다시 시작하면 Phaser가 서버에서 실행 중이며 헤드리스 모드로 실행되고 있음을 확인해야합니다.


그러나 구현되지 않은 window.focus 메서드에 대한 오류 메시지가 표시되어야 합니다. 이 오류가 표시되는 이유는 현재 이 메서드가 jsdom에 구현되어 있지 않으며 이 메서드가 호출 될 때마다 이 오류가 표시된다는 것입니다. 이 오류를 해결하려면 서버에서 실행중인 Phaser가로드 한 마이너 변경 구성 객체 만 만들어야합니다. authoritative_server / js / game.js에서 config 객체에 다음 속성을 추가합니다.

autoFocus: false

이렇게하면 게임이 처음 부팅 할 때 Phaser가 window.focus ()를 호출하지 않게됩니다. 기본적으로 이 값은 true로 설정됩니다. 다음 섹션으로 넘어 가기 전에 서버 코드를 약간 변경해야 합니다. 현재 우리 서버는 청취를 시작하고 서버상의 Phaser가 작동하기 전에 클라이언트가 서버에 연결할 수 있습니다. 대신 클라이언트가 연결하기 전에 Phaser가 서버에서 실행되고 있는지 확인하고자 합니다.


이렇게하려면 가상 DOM이 준비되고 Express 서버가 시작될 때까지 기다려야합니다. server / index.js를 열고 파일에서 다음 코드 행을 제거합니다.

server.listen(8081, function () {

console.log(`Listening on ${server.address().port}`);

});

그런 다음 setupAuthoritativePhaser 함수를 다음 코드로 바꿉니다.

function setupAuthoritativePhaser() {

JSDOM.fromFile(path.join(__dirname, 'authoritative_server/index.html'), {

// To run the scripts in the html file

runScripts: "dangerously",

// Also load supported external resources

resources: "usable",

// So requestAnimatinFrame events fire

pretendToBeVisual: true

}).then((dom) => {

dom.window.gameLoaded = () => {

server.listen(8081, function () {

console.log(`Listening on ${server.address().port}`);

});

};

}).catch((error) => {

console.log(error.message);

});

}

마지막으로 authoritative_server / js / game.js 파일을 열고 파일 맨 아래에 다음 코드를 추가합니다.

window.gameLoadded();

방금 추가 한 코드를 살펴 보겠습니다.


fromFile 메서드는 약속을 반환하므로 .then ()을 사용하여 약속이 해결 될 때까지 기다린 다음 콜백 함수를 호출하게 할 수 있습니다. 이 콜백 함수에서는 서버를 시작하기위한 논리를 추가했습니다.


game.js에서 우리는 위의 콜백 함수에서 정의한 window.gameLoaded ()를 호출했습니다. Phaser 라이브러리를 로드 한 다음 게임 객체를 생성하기 때문에 우리는 서버를 시작하기 전에 이러한 작업이 완료되었는지 확인해야합니다. 따라서 이 새로운 함수가 추가되었습니다.


이제 변경 사항을 저장하고 서버를 다시 시작하면 고속 서버를 시작하기 전에 Phaser 게임이 생성 된 것을 볼 수 있습니다.

결론

서버에서 헤드리스 모드로 실행되는 Phaser를 사용하면 Part 1이 끝납니다. 2 부에서는 멀티 플레이어 게임을 계속 진행합니다.


프로젝트에 Socket.IO 라이브러리 추가하기.

게임에 플레이어를 추가하기위한 서버 로직 추가.

게임에서 플레이어를 제거하기위한 서버 로직 추가.

게임에 플레이어를 추가하기위한 클라이언트 측 로직 추가.


이 튜토리얼 시리즈의 시작을 즐겁게하고 도움이되기를 바랍니다. 

우리가 다음에 다루어야 할 것에 관한 질문이나 제안이 있으면, 아래의 의견에 우리에게 알려주십시오.

Posted by HammerOh
|

꾸르

Posted by HammerOh
|