zoukankan      html  css  js  c++  java
  • COLYSEUS服务器框架实践Demo

    前言

      游戏服务器框架colyseus,使用起来十分简单,只需要一丢丢的代码就可以实现一个状态同步的服务器

    安装&项目设置

    • 使用npm初始化项目
    npm i -g typescript
    npm init -y
    tsc --init
    npm i colyseus
    npm i express @types/express @types/node @types/redis

      colyseus@0.14.23 版本

    • 入口文件 在主目录下新建一个index.ts文件
    /**
     * 官方文档
     * https://docs.colyseus.io/zh_cn/colyseus/server/api/
     */
    
    import { GameRoom } from './room/GameRoom';
    import { Server } from 'colyseus';
    import http from 'http';
    import express from 'express';
    const port = Number(process.env.port) || 3000;
    
    const app = express();
    app.use(express.json());
    
    // init game server
    const gameServer = new Server({
        server: http.createServer(app)
    });
    
    // Define 'game' room
    gameServer.define('game', GameRoom);
    
    // listen server port
    gameServer.listen(port);
    console.log('server is on');
    • 新建一个文件夹room 新建一个GameRoom.ts
    import { Room, Client } from 'colyseus';
    import { State } from '../entity/State';
    
    export class GameRoom extends Room<State> {
        // max clients
        maxClients: number = 3;
    
        // Colyseus will invoke when creating the room instance
        onCreate(options: any) {
            // initialize empty room state
            this.setState(new State());
            // set patch rate
            this.setPatchRate(50);
    
            // Called every time this room receives a "move" message
            this.onMessage("move", (client, data) => {
                this.state.movePlayer(client, data.x, data.y);
    
                // test log
                let player = this.state.players.get(client.sessionId);
                if (player != undefined) {
                    console.log(client.sessionId + " at, x: " + player.x, "y: " + player.y);
                }
    
            });
    
            // Triggers when any other type of message is sent,
            // excluding "move", which has its own specific handler defined above.
            this.onMessage("*", (client, type, message) => {
                console.log(client.sessionId, "sent", type, message);
            });
        }
    
        // Called every time a client joins
        onJoin(client: Client, options?: any, auth?: any) {
            this.state.addPlayer(client);
        }
    
        // Called every time a client leaves
        onLeave(client: Client, consented?: boolean) {
            this.state.removePlayer(client);
        }
    
    }
    • 新建一个文件夹entity 新建文件State.ts和Player.ts

    • State.ts

    import { Client } from 'colyseus';
    import { Schema, MapSchema, type } from '@colyseus/schema'
    import { Player } from './Player';
    
    export class State extends Schema {
        // MapSchema是colyseus的对象实体模板
        @type({ map: Player })
        players = new MapSchema<Player>();
    
        /**
         * 添加新用户的方法
         *
         * @param {Client} client
         * @memberof PlayerState
         */
        addPlayer(client: Client) {
            let player = new Player(0, 0);
            this.players.set(client.sessionId, player);
        }
    
        /**
         * 删除一个用户的方法
         *
         * @param {Client} client
         * @memberof PlayerState
         */
        removePlayer(client: Client) {
            this.players.delete(client.sessionId);
        }
    
        /**
         * 移动用户的方法
         *
         * @param {Client} client
         * @param {number} [x=0]
         * @param {number} [y=0]
         * @memberof PlayerState
         */
        movePlayer(client: Client, x: number = 0, y: number = 0) {
            let player = this.players.get(client.sessionId);
            if (player != undefined) {
                (<Player>player).x += x;
                (<Player>player).y += y;
                if (x > 0) {
                    (<Player>player).dir = true;
                } else {
                    (<Player>player).dir = false;
                }
            } else {
                // 当前用户不存在
                console.log('client sessionId not exist!');
            }
        }
    }

      Player.ts

    import { Schema,type } from '@colyseus/schema'
    import { randomChineseName } from '../Utils'
    export class Player extends Schema {
        @type("string")
        name: string;  // 名称
        @type("number")
        x: number;    // x轴的位置
        @type("number")
        y: number;   // y轴的位置
        @type("boolean")
        dir: boolean; // 玩家的方向(左 false 右 true) 简单定义
        constructor(x: number, y: number, name?: string) {
            super();
            this.x = x;
            this.y = y;
            this.name = name || randomChineseName();
            this.dir = true;
        }
    }
    • 根目录新建一个Utils.ts的文件
    • 一些基础工具方法写在这里
    • 现在又一个随机返回一个中文名称的方法
    const NAMES: Array<string> = [
        '断笔画墨',
        '默然相爱',
        '旅人不扰',
        '多余温情',
        '云中谁忆',
        '残雪冰心',
        '末世岛屿',
        '桑榆非晚',
        '扉匣与桔',
        '木槿暖夏',
        '空城旧梦',
    ];
    
    /**
     * 返回随机的中文名
     * 
     * @export
     * @returns {string}
     */
    export function randomChineseName(): string {
        return NAMES[~~(NAMES.length * Math.random())];
    }

    不正确的图像架构简单分析

    • 一个游戏服务器下面可以开N个房间Room
    • Room中存在一个state的对象,发生变化时候同步到Room下的客户端
    • 使得客户端的状态保持一致
    • 这个就是colyseus实现的状态同步服务器

    说明

    1、使用npm初始化项目
    	npm i -g typescript
    	npm init -y
    	tsc --init
    	npm i colyseus
    	npm i express @types/express @types/node @types/redis
    
    2、tsconfig.json 配置文件修改
    	添加"outDir": "./dist"
            取消注释"experimentalDecorators": true
    
    3、package.json 配置文件修改
    	在scripts下添加start的配置
    	"start": "tsc && cd dist && port=3001 node index.js"
    
    4、启动服务器
    	npm start

    启动服务器

    npm start

    简单的客户端效果

    colyseus框架文档:https://docs.colyseus.io/zh_cn/colyseus/

    本文参考出处:https://allknowboy.com/posts/a8be8288/

    客户端

    colyseus.js@0.14.13

    ccc客户端文档:https://docs.colyseus.io/zh_cn/colyseus/getting-started/cocos-creator/

    colyseus.js下载地址:https://github.com/colyseus/colyseus.js/releases

    客户端代码

    this.client = new Colyseus.Client('ws://10.250.8.127:3000');
    this.connect();

    connect 方法:

    async connect() {
            let _self = this;
            try {
                this.room = await this.client.joinOrCreate("game");
    
                // console.log("joined successfully!", this.room);
                // console.log("user's sessionId:", this.room.sessionId);
    
                this.room.state.players.onAdd = function (player, sessionId) {
                    let playerNode: cc.Node = cc.instantiate(_self.p_prefab);
                    playerNode.getChildByName('name').getComponent(cc.Label).string = player.name;
                    _self.m_gameMap.addChild(playerNode);
                    _self.players[sessionId] = playerNode;
                    if (sessionId === _self.room.sessionId) {
                        playerNode.getChildByName('name').color = cc.Color.RED;
                    }
    
                    player.onChange = function (changes) {
                        _self.players[sessionId].position = cc.v2(player.x, player.y);
                    }
                    // console.log('add', player)
                }
    
                this.room.state.players.onRemove = function (player, sessionId) {
                    if (this.players[sessionId]) {
                        this.players[sessionId].removeFromParent(true);
                        delete this.players[sessionId];
                    }
                }
    
                this.room.onStateChange((state) => {
                    console.log("onStateChange: ", state);
                    //console.log("players: ", state.players);
                });
    
                this.room.onLeave((code) => {
                    console.log("onLeave:", code);
                });
    
                this.room.onMessage("*", (type, message) => {
                    console.log("received message:", type, "=>", message);
                });
    
            } catch (e) {
                console.error(e);
            }
        }

    onKeyDown方法:

     onKeyDown(event: cc.Event.EventKeyboard) {
            switch (event.keyCode) {
                case cc.macro.KEY.up:
                case cc.macro.KEY.w:
                    {
                        this.room.send("move", { y: 5 });
                        break;
                    }
                case cc.macro.KEY.down:
                case cc.macro.KEY.s:
                    {
                        this.room.send("move", { y: -5 });
                        break;
                    }
                case cc.macro.KEY.left:
                case cc.macro.KEY.a:
                    {
                        this.room.send("move", { x: -5 });
                        break;
                    }
                case cc.macro.KEY.right:
                case cc.macro.KEY.d:
                    {
                        this.room.send("move", { x: 5 });
                        break;
                    }
            }
        }
    

    源码下载地址

    https://gitee.com/opendavid/colyseus_demo

  • 相关阅读:
    轮播图
    原生js实现分页效果(带实例)
    mint-ui Toast icon 图标
    阮小二买彩票
    js事件冒泡和事件捕捉
    html,css,js加载顺序
    单调栈-哈希表-768. 最多能完成排序的块 II
    同余问题-三整除系列
    动态规划-区间dp-单调栈-1130. 叶值的最小代价生成树
    动态规划-1Ddp-983. 最低票价
  • 原文地址:https://www.cnblogs.com/zhangdw/p/15598409.html
Copyright © 2011-2022 走看看