热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

用Swoole来写个联机对战游戏呀!(六)游戏匹配机制

联机逻辑开发进度:■■□□□□□□□□□□本章结束开发进度:■■■■■□□□□□□□

联机逻辑开发进度:■■□□□□□□□□□□

本章结束开发进度:■■■■■□□□□□□□

上一章的答案:

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 开发者模式。

用Swoole来写个联机对战游戏呀!(六)游戏匹配机制

前端成功地发出了数据,服务端返回的数据也都接收到了,我们成功打通了游戏前后端。

游戏数据管理

游戏的数据主要有两个存储方式:

Redis

我们首先在 DataCenter 类中新增一个静态变量 $global ,所有的对战数据都将存储在这个变量中。

DataCenter 类:

class DataCenter
{
    public static $global;
    ...
}

目前我们的游戏还没有 Redis 连接方式,需要编写一个 Redis 驱动。

  1. app 目录下新建 Lib 目录,并新建一个 Redis.php 文件。
  2. 使用单例模式编写 Redis 驱动。
  3. DataCenter 类中,新增一个 redis() 方法返回 Redis 实例。

Redis 类:

 '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 来进一步处理。

是不是看上去还挺好理解呢?我们先从前端入手。

  1. 新增输入框,绑定 Vue 数据属性 playerId ,并默认生成一个随机ID。
  2. 新增按钮,绑定 Vue 方法 matchPlayer ,点击按钮时发送 code600 的数据。
  3. WebSocket 连接的时候,发送玩家 playerId 到服务端。

index.html

...
...

前端代码不多,下面轮到服务端实现。

服务端将会涉及到三个类,也就是 ServerLogicDataCenter ,这三个类的调用顺序是:

  • Server 将用户信息如 playerId 保存到 DataCenter
  • Server 接收到数据,调用 Logic 中的逻辑。
  • Logic 中的逻辑调用 DataCenter 进行数据操作。
Server    →    Logic

↓            ↓

→        DataCenter

Server

我们采用自顶向下的方法来编写试试,不存在的方法也可以先写出来调用。

先从 Server 开始。从前端代码可以看到,在 WebSocket 建立连接的时候,前端会发送 player_id 到服务端,这个时候我们需要把 player_id 和连接 fd 保存在 DataCenter 中。普通的消息会带有一个 code 用来标识协议码,我们需要根据 code600 时,调用 Logic 匹配。

  1. Server 类初始化的时候,初始化 Logic 对象保存在私有变量 $logic ,用于调用 Logic 类中的方法。
  2. onOpen 事件中,保存用户的 player_idfdDataCentersetPlayerInfo() 方法中。
  3. onMessage 事件中,根据当前连接的 fd 获取 player_id ,当前端发送的消息中的 code600 时,调用 Logic 中的 matchPlayer() 方法
  • 记得多多阅读官方文档哦: https://wiki.swoole.com/wiki/...

Server 类:

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 传递的消息并执行具体的逻辑。

  1. 新增 matchPlayer() 方法,将 Server 传递过来的 player_id 放入 DataCenter 的匹配队列中。
 
 

DataCenter

有了上述两个类的调用,我们的 DataCenter 需求就清晰很多了,需要实现用户信息的存取,需要实现一个队列的进出和长度查询,用于玩家匹配。

  1. 新增常量 PREFIX_KEY ,作为所有 Rediskey 前缀,区别于其他应用缓存值。
  2. playerIdfd 编写 settergetterdelete 方法,需要实现 playerIdfd 可以互相查找。
  3. 实现匹配队列的 pushpopgetLength 方法。
  4. 完成 Server 调用的 setPlayerInfo() 方法,保存 player_idfd

本章作为第一个游戏功能开发,请童鞋们尽量完成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来写个联机对战游戏呀!(六)游戏匹配机制


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 我们


推荐阅读
author-avatar
沉醉不知归路1222
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有