정식 서버로 페이저 3에서 간단한 멀티 플레이어 게임 만들기 - 파트 3

다른 플레이어 표시

마지막 튜토리얼에서 우리는 게임에서 플레이어를 표시하는 논리를 추가하여 포장을 풀었습니다. 이제 우리는 게임에서 다른 플레이어를 표시하는 작업을 할 것입니다. 이 튜토리얼의 두 번째 파트에서는 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를 사용했습니다.

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

댓글 없음:

댓글 쓰기