内容简介:联机逻辑开发进度:■■□□□□□□□□□□本章结束开发进度:■■■■■□□□□□□□
联机逻辑开发进度:■■□□□□□□□□□□
本章结束开发进度:■■■■■□□□□□□□
上一章的答案:
index.html :
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
websock: null,
},
created() {
this.initWebSocket();
},
destroyed() {
this.websock.close() //离开路由之后断开websocket连接
},
methods: {
initWebSocket() { //初始化websocket
const wsuri = "ws://192.168.3.41:8811";
this.websock = new WebSocket(wsuri);
this.websock.onmessage = this.websocketonmessage;
this.websock.onopen = this.websocketonopen;
this.websock.onerror = this.websocketonerror;
this.websock.onclose = this.websocketclose;
},
websocketonopen() { //连接建立之后执行send方法发送数据
let actions = {"test": "12345"};
this.websocketsend(actions);
},
websocketonerror() {//连接建立失败重连
this.initWebSocket();
},
websocketonmessage(e) { //数据接收
let message = JSON.parse(e.data);
},
websocketsend(Data) {//数据发送
this.websock.send(JSON.stringify(Data));
},
websocketclose(e) { //关闭
console.log('断开连接', e);
},
}
})
- 记得将
192.168.3.41改为你的IP地址哦。
Server 类:
public function onWorkerStart($server, $workerId)
{
echo "server: onWorkStart,worker_id:{$server->worker_id}\n";
}
public function onOpen($server, $request)
{
DataCenter::log(sprintf('client open fd:%d', $request->fd));
}
public function onMessage($server, $request)
{
DataCenter::log(sprintf('client open fd:%d,message:%s', $request->fd, $request->data));
$server->push($request->fd, 'test success');
}
public function onClose($server, $fd)
{
DataCenter::log(sprintf('client close fd:%d', $fd));
}
写完以上代码后,在虚拟机中重新运行 Server ,在浏览器访问前端页面并打开 F12 开发者模式。
前端成功地发出了数据,服务端返回的数据也都接收到了,我们成功打通了游戏前后端。
游戏数据管理
游戏的数据主要有两个存储方式:
Redis
我们首先在 DataCenter 类中新增一个静态变量 $global ,所有的对战数据都将存储在这个变量中。
DataCenter 类:
class DataCenter
{
public static $global;
...
}
目前我们的游戏还没有 Redis 连接方式,需要编写一个 Redis 驱动。
- 在
app目录下新建Lib目录,并新建一个Redis.php文件。 - 使用单例模式编写
Redis驱动。 - 在
DataCenter类中,新增一个redis()方法返回Redis实例。
Redis 类:
<?php
namespace App\Lib;
class Redis
{
protected static $instance;
protected static $config = [
'host' => '127.0.0.1',
'port' => 6379,
];
/**
* 获取 redis 实例
*
* @return \Redis|\RedisCluster
*/
public static function getInstance()
{
if (empty(self::$instance)) {
$instance = new \Redis();
$instance->connect(
self::$config['host'],
self::$config['port']
);
self::$instance = $instance;
}
return self::$instance;
}
}
DataCenter 类:
class DataCenter
{
...
public static function redis()
{
return Redis::getInstance();
}
...
}
到这里为止所有准备都做好了,下面开始正式进入到游戏功能开发。
进入匹配队列
我们首先要做的第一个功能就是游戏匹配。
赵童鞋的想法是:
- 前端发送一个匹配消息到服务端。
- 服务端将玩家的ID放入一个
Redis队列里。 - 当队列里人数满足条件时,创建一个游戏房间。
- 根据
player_id获取连接fd,发送游戏数据。
WebSocket 并不像普通的 HTTP 请求那样,一个功能对应一个接口,那我们要怎么区分发送和接收的消息呢?很简单,我们只要固定发送和接收数据的格式,其中加入一个参数 code 作为功能协议标识,所有的操作都根据发送和接收的 code 来进一步处理。
是不是看上去还挺好理解呢?我们先从前端入手。
- 新增输入框,绑定
Vue数据属性playerId,并默认生成一个随机ID。 - 新增按钮,绑定
Vue方法matchPlayer,点击按钮时发送code为600的数据。 - 在
WebSocket连接的时候,发送玩家playerId到服务端。
index.html
...
<div id="app">
<label>
玩家ID:
<input type="text" :value="playerId">
</label>
<button @click="matchPlayer">匹配</button>
</div>
<script>
var app = new Vue({
...
data: {
playerId: 'player_' + Math.round(Math.random() * 1000),
...
},
...
methods: {
//匹配玩家
matchPlayer() {
let actions = {"code": 600};
this.websocketsend(actions);
},
initWebSocket() { //初始化websocket
const wsuri = "ws://192.168.3.41:8811?player_id=" + this.playerId;
...
},
...
}
})
</script>
...
前端代码不多,下面轮到服务端实现。
服务端将会涉及到三个类,也就是 Server 、 Logic 、 DataCenter ,这三个类的调用顺序是:
-
Server将用户信息如playerId保存到DataCenter -
Server接收到数据,调用Logic中的逻辑。 -
Logic中的逻辑调用DataCenter进行数据操作。
Server → Logic ↓ ↓ → DataCenter
Server 类
我们采用自顶向下的方法来编写试试,不存在的方法也可以先写出来调用。
先从 Server 开始。从前端代码可以看到,在 WebSocket 建立连接的时候,前端会发送 player_id 到服务端,这个时候我们需要把 player_id 和连接 fd 保存在 DataCenter 中。普通的消息会带有一个 code 用来标识协议码,我们需要根据 code 为 600 时,调用 Logic 匹配。
- 在
Server类初始化的时候,初始化Logic对象保存在私有变量$logic,用于调用Logic类中的方法。 - 在
onOpen事件中,保存用户的player_id和fd在DataCenter的setPlayerInfo()方法中。 - 在
onMessage事件中,根据当前连接的fd获取player_id,当前端发送的消息中的code为600时,调用Logic中的matchPlayer()方法
- 记得多多阅读官方文档哦: https://wiki.swoole.com/wiki/...
Server 类:
<?php
...
class Server
{
const CLIENT_CODE_MATCH_PLAYER = 600;
...
private $logic;
public function __construct()
{
$this->logic = new Logic();
...
}
...
public function onOpen($server, $request)
{
DataCenter::log(sprintf('client open fd:%d', $request->fd));
$playerId = $request->get['player_id'];
DataCenter::setPlayerInfo($playerId, $request->fd);
}
public function onMessage($server, $request)
{
DataCenter::log(sprintf('client open fd:%d,message:%s', $request->fd, $request->data));
$data = json_decode($request->data, true);
$playerId = DataCenter::getPlayerId($request->fd);
switch ($data['code']) {
case self::CLIENT_CODE_MATCH_PLAYER:
$this->logic->matchPlayer($playerId);
break;
}
}
...
}
...
Logic 类
下面到 Logic 类, Logic 其实代码不多,他需要实现的就是接收 Server 传递的消息并执行具体的逻辑。
- 新增
matchPlayer()方法,将Server传递过来的player_id放入DataCenter的匹配队列中。
<?php
...
class Logic
{
public function matchPlayer($playerId)
{
DataCenter::pushPlayerToWaitList($playerId);
}
}
DataCenter 类
有了上述两个类的调用,我们的 DataCenter 需求就清晰很多了,需要实现用户信息的存取,需要实现一个队列的进出和长度查询,用于玩家匹配。
- 新增常量
PREFIX_KEY,作为所有Redis的key前缀,区别于其他应用缓存值。 - 为
playerId和fd编写setter、getter和delete方法,需要实现playerId和fd可以互相查找。 - 实现匹配队列的
push、pop、getLength方法。 - 完成
Server调用的setPlayerInfo()方法,保存player_id和fd。
本章作为第一个游戏功能开发,请童鞋们尽量完成Homework哦,其他功能如邀请、观战也将会是这个调用流程。
当前目录结构:
HideAndSeek
├── app
│ ├── Lib
│ │ └── Redis.php
│ ├── Manager
│ │ ├── DataCenter.php
│ │ ├── Game.php
│ │ └── Logic.php
│ ├── Model
│ │ ├── Map.php
│ │ └── Player.php
│ └── Server.php
├── composer.json
├── composer.lock
├── frontend
│ └── index.html
├── test.php
└── vendor
├── autoload.php
└── composer
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 用Swoole来写个联机对战游戏呀!(五)联机初始化
- 用Swoole来写个联机对战游戏呀!(三)完善游戏功能
- 用Swoole来写个联机对战游戏呀!(四)游戏结束判断
- 用Swoole来写个联机对战游戏呀!(八)创建游戏房间
- 从动物森友会聊主机游戏联机原理
- 用Swoole来写个联机对战游戏呀!(二)单机游戏架构
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
汇编语言(第2版)
王爽 / 清华大学出版社 / 2008-4 / 33.00元
《汇编语言(第2版)》是各种CPU提供的机器指令的助记符的集合,人们可以用汇编语言直接控制硬件系统进行工作。汇编语言是很多相关课程(如数据结构、操作系统、微机原理等)的重要基础。为了更好地引导、帮助读者学习汇编语言,作者以循序渐进的思想精心创作了《汇编语言(第2版)》。《汇编语言(第2版)》具有如下特点:采用了全新的结构对课程的内容进行组织,对知识进行最小化分割,为读者构造了循序渐进的学习线索;在......一起来看看 《汇编语言(第2版)》 这本书的介绍吧!