点击上方“青年码农”关注

回复“源码”可获取各种资料

Colyseus 是一个独特的多人游戏框架,被用于许多 H5 小游戏和手机游戏中,使用容易,且选项众多,可满足开发者多样化的需求。如果你在制作多人联网游戏时遇到过各种扩展性需求和细节问题造成的项目阻碍,那么推荐你使用这个免费的开源解决方案。

这个框架也是最近接触,自己在摸索过程中遇到很多坑,因此记录下。文章基于 Express + TS 演示及说明。

在使用 Colyseus 框架之前,我们要满足 Colyseus 的运行环境。

  1. 下载并安装 Node.js V14.0 或更高版本(使用 nvm 或者 n 来管理 Node 版本)

  2. 下载并安装 Git SCM

  3. 下载并安装 Visual Studio Code (或者您喜欢的其他编辑器)

安装

安装过程和其他 npm 库一样,用 npm、yarn 或者其他都可以,但是要注意,npm 库中有 colyseus 和 colyseus.js,这两个有区别,一个是给服务端用的,一个是给客户端用的。

  • colyseus:Node 环境,也就是服务端

  • colyseus.js:前端环境,也就是客户端

npm i colyseus
# or
yarn add colyseus

配置

在 app.ts 文件中引入 colyseus ,

var express = require("express");
import { Server } from "colyseus";
import { WebSocketTransport } from "@colyseus/ws-transport";
import { createServer } from "http";
const port = 3300;

var app = express();
const server = createServer(app);
const gameServer = new Server({
  transport: new WebSocketTransport({
    server
  })
});

gameServer.listen(port);

启动项目,如果没有报错,colyseus 服务就启动成功了,地址就是 IP+3300,客户端创建 client 就可以链接到这个服务了。

var client = new Colyseus.Client("ws://127.0.0.1:3300");

通过上面我们只是简单的有了服务可以使用,游戏,那应该有房间和状态

房间和状态

先说说我对房间的理解,Colyseus 内置房间有两个,大厅房间和中继房间,大厅房间应该是类似我们进入游戏时的大厅,所有人都在一起,然后再找自己喜欢类型的房间,中继房间,我猜测类似吃鸡游戏中,我们选择开始游戏,在出生岛等待游戏开始的房间,以上是个人理解,如有不对,请指正。

大厅房间和中继房间已经帮我们初始了一些状态,但是这些可能不满足我们游戏的需求,因此,我们就需要定义我们自己的房间,使用 Room 类定义

import http from "http";
import { Room, Client } from "colyseus";

export class MyRoom extends Room {
    // 房间初始化时
    onCreate (options: any) { }

    // 在 WebSocket 握手完成前, 客户端基于其提供的 options 进行验证
    onAuth (client: Client, options: any, request: http.IncomingMessage) { }

    // 当客户端成功加入房间时
    onJoin (client: Client, options: any, auth: any) { }

    // 当客户端离开房间时
    onLeave (client: Client, consented: boolean) { }

    // 析构函数, 当房间里没有客户端时被调用. (参考 `autoDispose`)
    onDispose () { }
}

有了房间,肯定要有一些初始状态,使用 Schema 初始化状态

// MyState.ts
import { Client } from "colyseus";
import { Schema, MapSchema, type } from "@colyseus/schema";

// 一个抽象玩家对象, 表达其在3D世界的位置
export class Player extends Schema {
  @type("number")
  x: number = 0;

  @type("number")
  y: number = 0;

  @type("number")
  z: number = 0;
}

export class MyState extends Schema {
  @type({ map: Player })
  players = new MapSchema<Player>();

  @type("string")
  direction: string = "up";
  /**
   * 添加新用户的方法
   *
   * @param {Client} client
   * @memberof PlayerState
   */
  addPlayer(client: Client) {
    let player = new Player(0, 0, 1);
    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,
    z: number = 0,
    direction: string = "up"
  ) {
    let player = this.players.get(client.sessionId);
    console.log(player);
    if (player != undefined) {
      if (direction == "down" || direction == "right") {
        (<Player>player).x += x;
        (<Player>player).y += y;
        (<Player>player).z += z;
      } else {
        (<Player>player).x -= x;
        (<Player>player).y -= y;
        (<Player>player).z -= z;
        if ((<Player>player).x <= 0) {
          (<Player>player).x = 0;
        }
        if ((<Player>player).y <= 0) {
          (<Player>player).y = 0;
        }
        if ((<Player>player).z <= 0) {
          (<Player>player).z = 0;
        }
      }
    } else {
      // 当前用户不存在
      console.log("client sessionId not exist!");
    }
  }
}

状态有了,稍微修改下房间,在房间初始化时把状态加上

import { MyState } from "./myState";

onCreate(options: any) {
    this.setState(new MyState());
}

房间和状态都定义好了,通过 define 公开房间(修改文件 app.ts)

gameServer.define("game", MyRoom);

再重新启动项目,就可以使用新的房间了

通过@colyseus/monitor 实时监察服务器生成的房间列表

@colyseus/monitor 是一个方便易用的工具,可以实时监察服务器生成的房间列表.

安装模块:

npm install --save @colyseus/monitor

在项目中引入:

import { monitor } from "@colyseus/monitor";

app.use("/colyseus", monitor());

1864586251408741142f187ddd98eff6.png

接收消息和广播

怎么接收客户端发送的消息和怎么发送广播?通过 onMessage 接收消息,通过 broadcast 发送广播,在 onCreate 定义

onCreate(options: any) {
    this.setState(new MyState());
    // 活动消息
    this.onMessage("action", (client, message) => {
      console.log(client.sessionId, "sent 'action' message: ", message);
    });

    // 消息保存
    this.onMessage("save", (client, data) => {
      // 广播
      this.broadcast("action-taken", "an action has been taken!");
    });
  }

客户端通过 send 触发。

this.room.send("action", { direction: "left" });
// 触发广播
this.room.send("save", { direction: "left" });

fc513dc24d070444dbbedf4a3efb26df.png

一个用户加入

8ed17d6bc0d1601f7f305fb53542072e.png

两个用户加入,并且一个用户位置发生了改变

dd0778c79000ede8f0af3f60a3ef8691.png

监测有人进入房间

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐