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;
}
}
}



浙公网安备 33010602011771号