邀请玩家对战
这一章我们来新增一个对战类游戏很常见的功能:邀请其他玩家进行开房对战。
整个功能其实不太复杂,我们先来看一下流程图:
玩家A发起挑战,玩家B会有两种处理方式,分别是接受挑战和拒绝挑战,接受的时候就按正常游戏逻辑来创建房间,拒绝的时候则通知玩家A对手已拒绝。
我们可以把整个功能划分为两块:玩家发起挑战、对手处理挑战。
玩家发起挑战
发起挑战的逻辑很简单,在前端页面中输入对手ID,将对手ID发送至服务端。
做题时间
- 前端新增一个输入框,用于输入对手ID。
- 前端新增一个“挑战”按钮,绑定
makeChallenge()
方法,用于发送对手ID以及挑战消息到服务端 - 服务端接收到挑战消息后,发送消息给对手进行挑战确认。
小提示:如果玩家不在线怎么办?
index.html
:
...
<div id="app">
...
<div v-if="matching" style="display: inline">
匹配中……
</div>
<div v-else>
<div v-if="!roomId" style="padding-top: 5px;">
对手ID:
<input type="text" v-model="opponentId">
<button @click="makeChallenge">挑战</button>
</div>
</div>
...
</div>
<script>
var app = new Vue({
...
data: {
...
opponentId: '',
},
...
methods: {
makeChallenge() {
if (!this.opponentId) {
alert('请输入对手ID');
return;
}
let actions = {
"code": 603,
"opponent_id": this.opponentId
};
this.websocketsend(actions);
},
...
websocketonmessage(e) { //数据接收
...
switch (message.code) {
...
case 1007:
alert("对手不在线")
break;
}
},
...
}
})
</script>
...
Server
类:
<?php
...
class Server
{
...
const CLIENT_CODE_MAKE_CHALLENGE = 603;
...
public function onMessage($server, $request)
{
...
switch ($data['code']) {
...
case self::CLIENT_CODE_MAKE_CHALLENGE:
$this->logic->makeChallenge($data['opponent_id'], $playerId);
break;
}
}
...
}
...
Logic
类:
<?php
...
class Logic
{
...
public function makeChallenge($opponentId, $playerId)
{
if (empty(DataCenter::getOnlinePlayer($opponentId))) {
Sender::sendMessage($playerId, Sender::MSG_OPPONENT_OFFLINE);
} else {
$data = [
'challenger_id' => $playerId
];
Sender::sendMessage($opponentId, Sender::MSG_MAKE_CHALLENGE, $data);
}
}
...
}
Sender
类:
<?php
...
class Sender
{
...
const MSG_OPPONENT_OFFLINE = 1007;
const MSG_MAKE_CHALLENGE = 1008;
const CODE_MSG = [
...
self::MSG_OPPONENT_OFFLINE => '对手不在线',
self::MSG_MAKE_CHALLENGE => '发起挑战',
];
...
}
服务端接收到挑战消息后,要先判断玩家B是否在线状态,当玩家B离线时可以立即通知玩家A挑战失败,下面我们就来测试一下这部分代码是否正常。
成功发送了一个code
为603
的消息,并附带了对手ID:player_563
。
另一边也成功接收到了消息,并且附带了挑战者ID:player_939
。
对手处理挑战
像上面所说,对于挑战有两种处理方式,分别是接受挑战和拒绝挑战。简单点的方式只需要使用JavaScript
的confirm()
方法就能满足这一需求。
做题时间
- 收到挑战消息后,弹出选择框,让玩家选择接受与拒绝。
- 当玩家接受挑战后,服务端开始一局游戏。
- 当玩家拒绝挑战后,服务端通知发起挑战玩家消息:对手已拒绝。
index.html
:
...
<script>
var app = new Vue({
...
methods: {
...
websocketonmessage(e) { //数据接收
...
switch (message.code) {
...
case 1008:
var challengerId = responseData.challenger_id;
var msg = "玩家 " + challengerId + " 邀请你进行对战,是否接受";
let actions = {
"code": 604,
"challenger_id": challengerId
};
if (!confirm(msg)) {
actions = {
"code": 605,
"challenger_id": challengerId
};
}
this.websocketsend(actions);
break;
case 1009:
alert("对方拒绝了你的挑战");
break;
}
},
...
}
})
</script>
...
Server
类:
<?php
...
class Server
{
...
const CLIENT_CODE_ACCEPT_CHALLENGE = 604;
const CLIENT_CODE_REFUSE_CHALLENGE = 605;
...
public function onMessage($server, $request)
{
...
switch ($data['code']) {
...
case self::CLIENT_CODE_ACCEPT_CHALLENGE:
$this->logic->acceptChallenge($data['challenger_id'], $playerId);
break;
case self::CLIENT_CODE_REFUSE_CHALLENGE:
$this->logic->refuseChallenge($data['challenger_id']);
break;
}
}
...
}
...
Logic
类:
<?php
...
class Logic
{
...
public function acceptChallenge($challengerId, $playerId)
{
$this->createRoom($challengerId, $playerId);
}
public function refuseChallenge($challengerId)
{
Sender::sendMessage($challengerId, Sender::MSG_REFUSE_CHALLENGE);
}
...
}
Sender
类:
...
class Sender
{
...
const MSG_REFUSE_CHALLENGE = 1009;
const CODE_MSG = [
...
self::MSG_REFUSE_CHALLENGE => '对方拒绝了你的挑战',
];
...
}
得益于我们前面开发游戏时良好的封装,接受挑战仅仅需要一行代码:调用createRoom()
方法,就能让程序走回原本的创建房间逻辑。
下面我们来测试一下代码:
发起挑战后,对手成功接收到了消息。
选择拒绝挑战的时候:
发起挑战方成功收到了拒绝消息。
选择接受挑战的时候:
成功进入了游戏界面,功能正常。
Homework
本章节我们开发了一个邀请对战功能,虽然基本流程是走通了,但其实还是会出现不少Bug的。
如:
- 发起挑战时,对手在游戏中怎么办?
- 对手接受挑战后,如果发起方断线了怎么办?
- 对手接受挑战后,如果发起方在游戏中怎么办?
请同学们思考一下并尝试自行解决以上问题。
本章对应Github Commit
:扩展六:邀请玩家对战