联机游戏结束
联机逻辑开发进度:■■■■■■■■■■■□
本章结束开发进度:■■■■■■■■■■■■■
上一章的答案
服务端只需要获取玩家本房间的Game
对象,再根据前端发送的方向,调用Game
对象的playerMove()
方法,最后发送游戏地图数据就完成了。
<?php
...
class Server
{
...
const CLIENT_CODE_MOVE_PLAYER = 602;
...
public function onMessage($server, $request)
{
...
switch ($data['code']) {
...
case self::CLIENT_CODE_MOVE_PLAYER:
$this->logic->movePlayer($data['direction'], $playerId);
break;
}
}
...
}
...
但是获取房间Game
对象前,需要拿到玩家的room_id
,我们先来解决这个问题。在DataCenter
中新增玩家room_id
的增删查方法,在bindRoomWorker()
的时候把player_id
和room_id
进行一次绑定。
DataCenter
类:
<?php
...
class DataCenter
{
...
public static function setPlayerRoomId($playerId, $roomId)
{
$key = self::PREFIX_KEY . ':player_room_id:' . $playerId;
self::redis()->set($key, $roomId);
}
public static function getPlayerRoomId($playerId)
{
$key = self::PREFIX_KEY . ':player_room_id:' . $playerId;
return self::redis()->get($key);
}
public static function delPlayerRoomId($playerId)
{
$key = self::PREFIX_KEY . ':player_room_id:' . $playerId;
self::redis()->del($key);
}
...
public static function initDataCenter()
{
...
//清空玩家房间ID
$key = self::PREFIX_KEY . ':player_room_id*';
$values = self::redis()->keys($key);
foreach ($values as $value) {
self::redis()->del($value);
}
}
...
}
Logic
类:
<?php
...
class Logic
{
...
public function movePlayer($direction, $playerId)
{
if (!in_array($direction, Player::DIRECTION)) {
echo $direction;
return;
}
$roomId = DataCenter::getPlayerRoomId($playerId);
if (isset(DataCenter::$global['rooms'][$roomId])) {
/**
* @var Game $gameManager
*/
$gameManager = DataCenter::$global['rooms'][$roomId]['manager'];
$gameManager->playerMove($playerId, $direction);
$this->sendGameInfo($roomId);
}
}
...
private function bindRoomWorker($playerId, $roomId)
{
...
DataCenter::setPlayerRoomId($playerId, $roomId);
...
}
}
这下移动功能应该就可以用了,我们赶紧来重启一下服务器,尝试玩一下。
如果代码没毛病的话,相信童鞋们已经可以玩起来了。
但是我们的游戏还没结束,还差了最后一个基础功能:联机结束判断。
联机结束判断
做题时间
- 玩家每次移动后,调用
Game
对象的isGameOver()
方法,检测是否已结束。 - 满足结束条件的游戏房间则通知客户端并发送胜利玩家ID。
- 关闭游戏房间。
Sender
类:
<?php
...
class Sender
{
...
const MSG_GAME_OVER = 1005;
const CODE_MSG = [
...
self::MSG_GAME_OVER => '游戏结束啦~'
];
...
}
Logic
类:
<?php
...
class Logic
{
public function movePlayer($direction, $playerId)
{
...
if (isset(DataCenter::$global['rooms'][$roomId])) {
...
$this->checkGameOver($roomId);
}
}
...
private function checkGameOver($roomId)
{
/**
* @var Game $gameManager
* @var Player $player
*/
$gameManager = DataCenter::$global['rooms'][$roomId]['manager'];
if ($gameManager->isGameOver()) {
$players = $gameManager->getPlayers();
$winner = current($players)->getId();
foreach ($players as $player) {
Sender::sendMessage($player->getId(), Sender::MSG_GAME_OVER, ['winner' => $winner]);
DataCenter::delPlayerRoomId($player->getId());
}
unset(DataCenter::$global['rooms'][$roomId]);
}
}
}
index.html
:
...
<script>
var app = new Vue({
...
methods: {
...
websocketonmessage(e) { //数据接收
...
switch (message.code) {
...
case 1005://游戏结束
this.winner = responseData.winner
setTimeout(function () {
alert('游戏结束~胜者是:' + responseData.winner)
}, 200)
break;
}
},
...
}
})
</script>
...
重启游戏,我们再试一次。
童鞋们发现了吗?胜利者是player_161
,但是地图上却留下了player_355
的大名,没错,这是一个bug
,请童鞋们找到并修复它,答案在本章结束处。
结语
恭喜你走到了课程的最后,如果有认真做完每章后面的Homework,有认真去思考每一个做题时间
的实现方法,应该还是有一丢丢收获的吧?~
基本功能虽然已经开发完毕,但是游戏的可扩展性还是很强的。例如可以加入聊天系统,或者加入一些地雷之类的陷阱。还有许多的功能在第一章中已列举出来,并附上了难度等级。后面的路就需要各位童鞋自己去开拓了。
小册编写过程中部分代码有可能会产生master
分支和teach
分支不一致的情况,如果会影响到主流程的Bug请直接告诉我,如果只是游戏体验方面的Bug请童鞋们自行修复,尝试开发额外功能遇到问题时,也可以直接跑来问我要怎么解决。
如果觉得阅读本小册收获异常丰富,仅仅请赵童鞋喝一杯奶茶远远不够,欢迎给我发红包,你的赞赏会让我更有动力写下一篇๑乛◡乛๑
小册资料
本小册所有出现过的资料链接都在这里,大家有需要的可尽情查阅。
PHP
- Windows一键安装包:www.apachefriends.org/index.html
- Linux一键安装包:lnmp.org/install.htm…
Composer
- 安装:pkg.phpcomposer.com/#how-to-ins…
- 国内镜像一:
composer config -g repo.packagist composer https://packagist.phpcomposer.com
- 国内镜像二:
composer config -g repo.packagist composer https://packagist.laravel-china.org
Swoole
- Swoole安装教程:wiki.swoole.com/wiki/page/6…
- Swoole Server:wiki.swoole.com/wiki/page/p…
- Swoole Task:wiki.swoole.com/wiki/page/1…
- Swoole Websocket Server:wiki.swoole.com/wiki/page/3…
- Swoole Client fd:wiki.swoole.com/wiki/page/5…
- 多进程共享数据:wiki.swoole.com/wiki/page/8…
- Server bind:wiki.swoole.com/wiki/page/3…
- Swoole WebSocket onMessage: wiki.swoole.com/wiki/page/4…
Redis
- Redis安装教程:redis.io/download
- php-redis扩展包:pecl.php.net/package/red…
Vue
- Vue官方文档:cn.vuejs.org/v2/guide/
- v-if:cn.vuejs.org/v2/guide/co…
- v-for:cn.vuejs.org/v2/guide/li…
<template>
:cn.vuejs.org/v2/guide/li…- 阮一峰WebSocket教程:www.ruanyifeng.com/blog/2017/0…
- Vue生命周期:cn.vuejs.org/v2/guide/in…
本章BUG答案:
Logic
类:
<?php
...
class Logic
{
...
private function sendGameInfo($roomId)
{
...
//必须倒序输出,因为游戏设定数组第一个是寻找者,第二个是躲藏者,叠加时赢的是寻找者。
foreach (array_reverse($players) as $player) {
$mapData[$player->getX()][$player->getY()] = $player->getId();
}
...
}
}