10分钟开发一个游戏服务器

前言

  安利一个游戏服务器框架colyseus,使用起来十分简单,只需要一丢丢的代码就可以实现一个状态同步的服务器,10分钟主题,废话不多说,Let’s Rock!

安装&项目设置

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

编程ING

先码一遍代码比看着纸想有用. —–鲁迅

  • 入口文件 在主目录下新建一个index.ts文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { GameRoom } from './room/GameRoom';
import { Server } from 'colyseus';
import express from 'express';
import http from 'http';

const app = express();

// 初始化游戏服务器
const gameServer = new Server({
server: http.createServer(app)
});

// 注册房间服务器
gameServer.register('game', GameRoom);

// 开始监听端口
gameServer.listen(3000);
console.log('server is on');
  • 新建一个文件夹room 新建一个GameRoom.ts文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { Room, Client } from 'colyseus';
import { PlayerState } from '../entity/PlayerState';

export class GameRoom extends Room<PlayerState> {
// 房间内的最大人数
maxClients: number = 2;

// 房间初始化方法
onInit(options: any) {
console.log('ChatRoom onInit');
// 设置需要更新的状态
this.setState(new PlayerState());
// 设置发送频率
this.setPatchRate(50);
}

// 消息收取方法
onJoin(client: Client) {
this.state.addPlayer(client);
}

// 消息收取方法
onLeave(client: Client) {
this.state.removePlayer(client);
}

// 消息收取方法
onMessage(client: Client, data: any): void {
this.state.movePlayer(client, data.x, data.y);
}

}
  • 新建一个文件夹entity 新建文件PlayerState.ts和Player.ts

  • PlayerState.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import { EntityMap, Client } from 'colyseus';
import { Player } from './Player';

export class PlayerState {
// EntityMap是colyseus的对象实体模板
players: EntityMap<Player> = {};

/**
* 添加新用户的方法
*
* @param {Client} client
* @memberof PlayerState
*/
addPlayer(client: Client) {
let player = new Player(0, 0);
this.players[client.sessionId] = player;
}

/**
* 删除一个用户的方法
*
* @param {Client} client
* @memberof PlayerState
*/
removePlayer (client: Client) {
delete this.players[ client.sessionId ];
}

/**
* 移动用户的方法
*
* @param {Client} client
* @param {number} [x=0]
* @param {number} [y=0]
* @memberof PlayerState
*/
movePlayer(client: Client, x: number = 0, y: number = 0) {
this.players[client.sessionId].x += x;
this.players[client.sessionId].y += y;
if(x > 0){
this.players[client.sessionId].dir = true;
} else {
this.players[client.sessionId].dir = false;
}
}
}
  • Player.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { randomChineseName } from '../Utils'
export class Player {
public name: string; // 名称
public x: number; // x轴的位置
public y: number; // y轴的位置
public dir: boolean; // 玩家的方向(左 false 右 true) 简单定义
constructor( x: number, y: number,name?: string) {
this.x = x;
this.y = y;
this.name = name || randomChineseName();
this.dir = true;
}
}
  • 根目录新建一个Utils.ts的文件
  • 一些基础工具方法写在这里
  • 现在又一个随机返回一个中文名称的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const NAMES: Array<string> = [
'断笔画墨',
'默然相爱',
'旅人不扰',
'多余温情',
'云中谁忆',
'残雪冰心',
'末世岛屿',
'桑榆非晚',
'扉匣与桔',
'木槿暖夏',
'空城旧梦',
];

/**
* 返回随机的中文名
*
* @export
* @returns {string}
*/
export function randomChineseName(): string {
return NAMES[~~(NAMES.length * Math.random())];
}

简单分析

  • 不正确的图像架构

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

启动服务器

1
tsc && node ./dist/index.js

简单的客户端

源码地址:https://gitee.com/limo/simple_server