简单的 MMO, 为 Scratch 中实现多人游戏而设计

文章正文
发布时间:2025-11-30 08:05

MMO(Massively Multiplayer Online,大型多人在线游戏)是一种非常多玩家在同一服务器上游玩的游戏。MMO游戏让玩家能够和其他玩家合作或者对抗,让游戏变得更加有趣。

为什么使用 Simple MMO?

在原版 Scratch 中制作 MMO 游戏是非常困难的,因为原版的云变量设计并不适合联机,你需要在原版云变量的限制上设计各种机制来实现联机。

同时,由于原版云变量为了方便使用,设计了“一人修改,所有在线用户同步”的功能,导致在大量玩家同时游玩同一作品时,大量耗费流量和服务器资源。

因此设计了 Simple MMO 拓展,既提供大量方便的积木实现联机匹配同步系统,同时优化了联机时的数据传输量,在保证高效联机的同时,节约玩家流量,降低服务器负担。

如何使用快速入门

Simple MMO 提供了一些用于快速制作简单多人游戏的积木。你可以用最少的积木实现一个最简单的多人联机游戏。

image

加入或者创建一个房间,并且等待连接成功。

加入模式

 

描述

 

加入或创建

 

遇到服务器 ID 相同且未锁定、未满员的房间就会加入,否则会创建新的房间。建议在创建或者加入广播房间的时候使用。

 

创建

 

创建新的房间,无论是否已经有服务器 ID 相同的房间存在。建议在创建游戏室的时候使用。 如果用创建来创建广播房间,可能会导致相同服务器 ID 的广播房间出现,这会导致之后加入房间时发生混乱。

 
 

对于游戏大厅来说,加入或创建 和 创建 没有区别。

 

房间类型

 

描述

 

广播 broadcast

 

最原始的房间,根据服务器 ID 区分。 虽然叫做广播房间,但它在支持广播的同时也支持玩家数据的同步。 不同的游戏室不应该共用同一个服务器 ID,这会导致加入的时候无法区分房间。

 

游戏室 match

 

比广播房间更加完善的房间,支持限制房间人数和锁定房间功能。 不同的游戏室可以共用同一个服务器 ID,可以通过加入同一服务器 ID 的游戏大厅的方式获得相同服务器 ID 的所有游戏室房间的房间 ID,通过房间 ID 即可加入特定的房间。

 

游戏大厅 lobby

 

特殊的房间,进入该房间后可以实时获取与该大厅同服务器 ID 的游戏室列表,以便选择游戏室。 在游戏大厅里无法获取同一大厅的玩家列表,查看其他玩家,或者接收广播。

 

额外数据:设定玩家的额外数据(不是房间的额外数据),额外数据可以用玩家相关积木获得。

这个积木会在连接成功之后运行后面的积木。连接失败了,后面的积木也会运行,可以用其他积木来判断房间是否连接成功。例如“我的 [ 会话 ID ]”积木为 NaN 时表示失败。

icon

不能同时连接到多个房间。在创建新的房间或者连接到其他房间之前,必须先断开与原有房间的连接。

image

断开与当前房间的连接。

image

获取当前连接的相关信息。

选项

 

描述

 

会话 ID

 

会话 ID,每个玩家的会话 ID 不同。 可以用来获取自己的相关属性,区分自己和其他玩家。 玩家每次连接到新的房间,会话 ID 会改变。 NaN 代表没有连接到任何房间

 

房间 ID

 

房间 ID,每个房间的 ID 不同。 在游戏大厅里获取同一服务器 ID 的游戏室列表后,可以用房间 ID 来区分不同的房间,以便加入。 NaN 代表没有连接到任何房间

 

房间类型

 

连接到的房间类型。 broadcast 代表广播房间 match 代表游戏室 lobby 代表游戏大厅 NaN 代表没有连接到任何房间

 

image

设置自己的 X, Y, 缩放等数据,这样其他玩家可以获得这些数据。

icon

注意:使用 X, Y, 缩放等名称只是为了编程的便捷,这个积木只存储和同步数据,并不会同时修改角色的实际位置、缩放、角度等属性。

属性名

 

描述

 

名字

 

名字,任意文本,通过设置积木设置,一般设为玩家的名字。默认是玩家的用户名。

 

X

 

X,最多保留两位小数的数字,通过设置积木设置,一般设为玩家的X坐标。默认是 0。

 

Y

 

Y,最多保留两位小数的数字,通过设置积木设置,一般设为玩家的Y坐标。默认是 0。

 

缩放

 

缩放,最多保留两位小数的数字,通过设置积木设置,一般设为玩家的大小。默认是 0。

 

方向

 

方向,最多保留两位小数的数字,通过设置积木设置,一般设为玩家的朝向。默认是 0。

 

额外数据

 

玩家的额外数据,任意文本,通过设置积木设置。任何东西都可以放这里。默认值通过加入房间的积木设定。

 

image

用一个积木同时设置X数据和Y数据。

icon

用循环实时设置当前角色的位置、大小和方向。

查看示例积木

image

获取房间里的玩家数量,如果没有连接房间,或者是连接到了游戏大厅,玩家数量将是0。

image

获取房间里玩家的详细信息,注意序号从 0 开始,第一个玩家的序号是 0,第二个玩家的序号是 1,依此类推。

属性名

 

描述

 

会话 ID sessionId

 

会话 ID,每个玩家每次连接的会话 ID 不同,可以用来区分不同的玩家。 在有玩家加入或者退出房间的时候,玩家的序号可能会改变,而会话 ID 不会变,因此应该用会话 ID 来区分玩家。 玩家每次连接到新的房间,会话 ID 会改变。

 

名字 name

 

名字,任意文本,通过设置积木设置,一般设为玩家的名字。 默认是玩家的用户名。

 

X x

 

X,最多保留两位小数的数字,通过设置积木设置,一般设为玩家的 X 坐标。 默认是 0。

 

Y y

 

Y,最多保留两位小数的数字,通过设置积木设置,一般设为玩家的 Y 坐标。 默认是 0。

 

缩放 scale

 

缩放,最多保留两位小数的数字,通过设置积木设置,一般设为玩家的大小。 默认是 0。

 

方向 direction

 

方向,最多保留两位小数的数字,通过设置积木设置,一般设为玩家的朝向。 默认是 0。

 

CCW UUID uuid

 

玩家的用户 ID,每个用户不同。也就是个人主页中的“共创世界 ID”。

 

在线? connected

 

玩家的在线状态。目前只能为 true。

 

额外数据 extra

 

玩家的额外数据,任意文本,通过设置积木设置,任何东西都可以放这里。默认值通过加入房间的积木设定。

 

所有 JSON 数据

 

用 JSON 格式表示的所有玩家相关数据。相关的属性名称在表格中用斜体标出。下面是 JSON 示例。

 

Copy{"name":"Alex","x":0,"y":0,"scale":100,"direction":0,"sessionId":"cTZEHuqKs","uuid":"123456789","extra":"Extra data","connected":true}

image

获取会话 ID 对应的玩家的详细信息。记得修改后面的[会话 ID]为你想要的属性。

image

和原版Scratch的克隆自己的积木相似,唯一的区别是这个克隆体会对应一位玩家的会话 ID

克隆体所在的位置和大小等和本体相同,不会自动移动到对应玩家的位置,需要添加额外的积木让它运动到对应玩家的位置。

image

获取这个克隆体对应的会话 ID 所对应的玩家的数据。属性名参考上面的“属性名”表格。

icon

现在可以根据在线的其他玩家的数据克隆克隆体,并且实时更新克隆体状态了。可以点击查看下面的示例积木。

查看示例积木

image

在有新玩家进入房间的时候触发此积木。需要注意的是,自己进入房间的时候也会触发。

sessionId 新玩家的会话 ID

name 新玩家的名字(加入房间时,这个名字只能是默认的用户名)

data 新玩家的初始额外数据(在加入房间的积木里设置)

icon

在新玩家加入的时候创建克隆体。

查看示例积木

image

在有玩家离开房间的时候触发此积木,自己退出房间的时候不会触发。

sessionId 离开房间的玩家的会话 ID

name 玩家离开时的名字(中途有可能通过设置积木设定了别的名字)

data 玩家离开时的额外数据

image

判断当前克隆体所对应的会话 ID 是不是对应的 ID。等价于

image

icon

在玩家离开的时候删除克隆体。

查看示例积木

image

额外数据:设定房间的额外数据(不是玩家的额外数据),额外数据可以是任何文本(不一定要是 JSON)。

注意:所有的玩家都能读取和修改房间的额外数据。从游戏大厅可以读取游戏室的额外数据

image

获得房间的额外数据

广播消息

image

给房间里的其他玩家发出广播。

icon

消息类型中不能包含有 syscmd: ,因为这是扩展保留的内部广播名称。

image

房间里的其他玩家发送广播时触发。自己不会收到自己发出的广播。

sender 代表其他玩家的会话 ID

senderUID 代表其他玩家的共创世界 ID

name 代表其他玩家设定的名字(默认是用户名)

type 代表发送的消息的类型

content 代表消息的内容。

游戏大厅与游戏室创建游戏室

image

创建游戏室的积木,功能和最开始的创建房间积木相似,不过只能创建游戏室,而且能够设定游戏室的最大人数。

不要给每一个房间分配不同的服务器 ID,同种类的游戏室服务器 ID 应该相同,这样才能通过相同的服务器 ID 查询到所有创建的游戏室。游戏室之间通过房间 ID 区分。

加入模式

 

描述

 

加入或创建

 

遇到服务器 ID 相同且未锁定、未满员的游戏室就会自动加入,否则会创建新的房间。非常适合用来做“玩家自动匹配”机制。

 

创建

 

创建新的游戏室,无论是否已经有服务器 ID 相同且可以加入的房间。可以在玩家主动创建新的游戏室的时候使用。

 

image

禁止新的玩家加入游戏室。一般在游戏已经开始的时候使用。

加入游戏大厅,搜索游戏室

使用创建或加入房间的积木加入到和游戏室服务器 ID 相同的游戏大厅后,即可搜索游戏室。

显示示例积木

image

获取和大厅服务器 ID 相同的游戏室的数量。

image

获取大厅服务器中的房间信息。编号从 0 开始。

属性

 

描述

 

房间 ID roomId

 

这个房间的房间 ID

 

房间类型 name

 

只可能是 match,代表游戏室

 

玩家数 clients

 

游戏室中玩家的数量

 

最大玩家数 maxClients

 

游戏室最多能接收的玩家数,或者 null 代表不限制玩家数量。

 

创建于 createdAt

 

房间创建的时间。使用 ISO 8601 时间表达 例子:2022-06-18T10:06:25.143Z

 

元数据 metadata

 

用 JSON 格式表示的房间数据,可以同时获取房间锁定状态、额外数据和房间的真实 ID(作品 ID 和 房间 ID 用 “-” 连接起来的结果)。 因为有更方便的方法获取这些数据,目前没有使用的必要。

 

额外数据 metadata extra

 

获得房间的额外数据。 如果没有设置,返回 undefined。在 JSON 数据中将会省略

 

锁了? metadata locked

 

房间是否锁定,true 代表锁定,false 代表没锁。

 

所有 JSON 数据

 

以上全部内容的 JSON 格式表达。相关的属性名称在表格中用斜体标出。下面是 JSON 示例。

 

Copy{"clients":1,"createdAt":"2022-06-18T10:06:25.143Z","maxClients":10,"metadata":{"locked":false,"gid":"63e066d11ba8060c1769b63f-serverId","extra":"Room data"},"name":"match","processId":"0b3kFmIru","roomId":"jFU7S--W0"}

icon

可以使用数据助手扩展或者 Not.js 扩展来处理 JSON 格式的文本,可以参考文章底部“其他问题”部分。

icon

注意创建时间后面的 Z 代表用的是协调世界时,需要考虑时区问题 (中国使用东八区的区时,时间会比协调世界时晚八个小时) 可以使用时间扩展处理,时间扩展会注意到 Z 字符并自动换算成不带 Z 的时间(即使用系统设置的时区的时间)。可以参考文章底部“其他问题”部分。

image

将指定列表的内容整个替换为所有游戏室的指定属性。属性和上一个积木相同。顺序保持一致。

关于额外数据里的 undefined:这个值非常特殊,在列表里看不到,但是能正常使用。

icon

建议填充数据的同时也填充一份对应的房间 ID,因为在处理数据的时候可能会有新的房间加入或者房间移出。

不建议的做法,在回答的时候可能房间加入或者移出,导致加入错误房间:

建议的做法,获取:

image

获取所有游戏室的 JSON 数据,是每个游戏室的“所有 JSON 数据”按顺序连接的数组。

修改选项,可以只获取已锁定或者未锁定的游戏室。

Copy[{"clients":1,"createdAt":"2023-04-05T15:22:48.302Z","maxClients":null,"metadata":{"locked":false,"gid":"63e066d11ba8060c1769b63f-serverId"},"name":"match","processId":"0b3kFmIru","roomId":"jFU7S--W0"},...]

image

在有相同服务器 ID 的游戏室被创建或者删除时触发。

选项

 

描述

 

发生任何变化

 

创建或者删除都触发

 

有新游戏室被创建

 

创建时触发

 

有游戏室被移出

 

删除时触发

 

rooms 格式:将改动的房间的消息连接在一起成为一个数组。

房间 JSON 的格式见下方“得到大厅中第( )个游戏室的 [ ]”积木。

Copy[{"clients":1,"createdAt":"2023-04-05T15:22:48.302Z","maxClients":null,"metadata":{"locked":false,"gid":"63e066d11ba8060c1769b63f-serverId"},"name":"match","processId":"0b3kFmIru","roomId":"jFU7S--W0"},...]

icon

没有解散房间积木。只有在所有玩家断开连接后房间才会删除。

image

连接到游戏大厅里的游戏室。

额外数据 代表玩家的额外数据

房间号 代表区分不同房间用的房间 ID

服务器 ID 代表房间使用的服务器 ID

icon

和“加入房间”积木不同,可以在没断开与游戏大厅的连接的时候直接使用此积木,与游戏大厅的连接会自动断开,并且加入游戏室 不过要注意,如果房间号错误,不仅不能加入新的房间,原有的游戏大厅的连接也会自动断开,需要重新连接。 房间号区分大小写,原版的 Scratch 比较时是不区分大小写的。不过由于房间号随机生成,出现一个除了大小写不同之外其他都相同的房间号的可能性非常低。

异常处理

image

在与房间的连接异常断开时触发。

异常断开只包括网络故障(断网,网络波动,或者服务器异常断开),如果是因为服务器 ID房间 ID 错误,或者受到“一号多登”限制掉线,或者作品主动断开了连接(包括点击停止/重新启动按钮),不会触发。

任何时候都可以使用“我的 [ 房间类型 ]”积木的返回值判断连接状态。

其它积木

image

获取当前与 Simple MMO 服务器的连接的延迟,大概每 5 秒更新一次。

image

这个积木用来设置游戏室的属性,所有玩家都能设置,默认是允许。

设置为禁止后,同一个账号将不能同时(打开多个窗口)加入这个游戏室,加入这个游戏室将会导致这个游戏室内共创世界 ID 相同的其他玩家断开连接(异常断开积木不会因此触发)

image

image

image

这类积木和之前提到的积木很像,但是它们不会等待房间连接成功或者失败后再运行后面的积木,而是直接运行后面的积木。

image

在自己进入任意房间后,这个积木会被触发。一般和上面的三个积木配合使用。

无论使用什么积木加入房间,只要加入房间成功,这个积木都会被触发。目前没有加入房间失败触发的积木。

image

获取房间里的玩家列表。

格式

 

描述

 

默认

 

使用逗号和分号分隔的玩家列表。 会话 ID共创世界 ID,玩家名字;

 

JSON

 

使用 JSON 格式的玩家列表,将所有玩家的所有 JSON 数据连成一个数组 玩家所有 JSON 数据格式,请参考快速入门一节

 

默认格式示例

CopyvEOvyf0zm,123456789,John;0CjdCxTmT,234567890,Bob;xuvhtkhP8,123345678901456789,Robert;

JSON 格式示例

Copy

image

image

image

获取相关玩家的属性。属性见上方“第( )个玩家的 [ ]”积木。

(其中离开的玩家的属性只能通过这里的第二个积木获得,因为他已经离开房间了)

目前改变状态的积木存在问题,无法正常触发,正在修复中……

image

判断运行此积木的角色是否是克隆体,包括原版“克隆”积木还是 Simple MMO 的“数据源克隆”积木产生的克隆体。

image

查看克隆体是否是根据自己的会话 ID 克隆的。

较难使用的积木

image

在房间的状态变化(玩家加入或者退出,或者房间额外数据变化)时,触发此积木。

状态变化时该积木可能会多次触发(2 到 4 次)

选项

 

描述

 

任何

 

玩家加入或者退出或者房间额外数据变化都触发

 

在线人数

 

玩家加入或者退出时触发

 

额外数据

 

房间额外数据变化时触发

 

sessionId 当前会话 ID

roomState 当前房间状态,JSON 格式。

属性

 

描述

 

onlineCount

 

在线人数

 

extra

 

额外数据。未设置前无此属性

 

players

 

“当前在线的玩家列表”,JSON 格式

 

Copy

changes 描述房间状态的修改。这是一个 Colyseus 内部数据,不建议直接使用。

image

在玩家的状态改变的时候触发此积木。

目前这个积木存在一些问题,无法正常触发,正在修复。

sessionId 当前会话 ID

name 玩家设定的名字

data 玩家的额外数据

其它问题

如何区分服务器ID房间 ID会话 ID共创世界 ID玩家的额外数据房间的额外数据?(点击左侧三角展开)

如何处理 JSON 格式的文字?“属性”是什么?

房间的创建时间为什么会有几个小时的“偏差”?如何处理时区问题?

所有玩家离开房间后,房间里的数据还能保存/恢复吗?

为什么原版 Scratch 社区会对云变量做出限制?

共创世界以前支持几乎无限制的原版云变量,甚至支持云列表。为什么现在共创世界会对原版云变量做出限制?

参考文章

如何选择合适的云变量替代方案?

云变量,KV 数据库 和 MMO 该怎么选用

Scratch 大多数游戏都是单机的,Scratch 官方提供了 云变量 功能作为联机作品的解决方案。共创世界 CCW 甚至在这个基础上开发了 云列表 功能。 但最近肝酱们发现 CCW 中调低了云变量和云列表的同步时间,导致了这两个功能无法使用。 最新的 Gandi IDE 也计划下线云变量和云列表功能。 就像 Scratch 官方的云变量需要先申请白名单,批准后才能使用, 这是因为云变量的底层设计逻辑是有错的。

getgandi.com

云变量,KV 数据库 和 MMO 该怎么选用

用 Simple MMO 制作联机游戏的教程!

从头开始,制作在线联机游戏 - 匹配系统篇

这个系列教程一共有 3 个章节,这是其中的第一篇,推荐完整阅读全部三篇的内容哦。

getgandi.com

从头开始,制作在线联机游戏 - 匹配系统篇

Simple MMO 英文版教程

Simple MMO, Designed for multiplayer games in Scratch - Gandi IDE

Multiplayer mode makes a game more replayable, but it was hard for creators to design an MMO system. You must build a matchmaking system, a broadcast system, a sync system, and so on. To simplify the development of MMO games, Gandi IDE built this extension.

getgandi.com

Simple MMO, Designed for multiplayer games in Scratch - Gandi IDE

更多细节

Simple MMO 使用开源联机游戏库 Colyseus 实现方便的联机匹配、玩家状态管理以及数据同步等功能。(Colyseus 基于 MIT 协议

Simple MMO 的所有修改都是实时向在同一房间内的其他玩家同步的,而读取不消耗网络流量,和云变量的机制很像。因此你可以任意地读取其他角色的状态而不用担心增大服务器负担,同时,不要随意传输大量数据。

点击停止/重新启动按钮会自动断开连接。

所有玩家退出房间后,房间将会自动删除。

自己的坐标数据以及玩家数据更新是有限速的,每 0.05 秒最多更新 1 次。如果你想让其他玩家的位置更加流畅,需要自己设计让角色平滑移动的积木。

虽然Simple MMO的内部广播名只是以 syscmd: 开头,但是你发出的广播名里的任何位置都不能包含 syscmd: ,无论是 中间有syscmd:而已 还是 末尾有syscmd: 都不行。

兼容性引导

不同社区的云变量机制区别很大,如果你想要使用 Simple MMO 扩展,将你的作品适配到其他社区将会是一件很困难的事。

如果你有将作品发到其他社区的打算,应该在创作作品的时候就做好准备。

一个比较简单的适应不同社区的方法是把所有需要社区专有扩展的积木放在同一个角色中,然后通过列表和广播传送参数来调用。这样,在需要适配其他社区的时候,可以在不影响其他角色的情况下删除这个角色,实现作品多社区适配。

例如一个平面射击游戏,有单人游玩和联机游玩功能。那么可以在不同的社区制作不同的“联机”角色并且导出,在上传到不同社区时,使用删除角色和导入角色的方法适配。如果遇到不支持联机的社区,还可以去掉该角色,禁用联机功能。

备注

此插件由以下肝酱贡献:

icon

Shawn XIAO

西瓜创客共创世界创始人。在 Gandi IDE 团队参与到产品设计、渲染器管线开发及拓展开发。

✉️ shawn@ccw.site | 飞书链接

icon

Cappu

共创世界 开发团队后端负责人。在 Gandi IDE 团队参与到后端服务、Gandi Engine相关架构与研发。

✉️ dudongcheng@ccw.site | 飞书链接

文档由 -6 校对

icon