Skip to content

状态数据同步 » Schema

还没使用 TypeScript?

强烈建议您使用 TypeScript 以便更好地定义 Schema 结构并提高整体开发体验. TypeScript 支持的 "实验性修饰器" 会在本手册内大量使用.

如何定义可同步结构

  • Schema 结构由服务器定义, 用于房间状态服务端客户端数据同步.
  • 只有以 @type() 修饰的字段才会被用于同步.
  • (可同步 Schema 结构仅应被用于需要服务器客户端同步的数据.)

定义 Schema 结构

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

export class MyState extends Schema {
    @type("string") currentTurn: string;
}
// MyState.ts
const schema = require('@colyseus/schema');
const Schema = schema.Schema;

class MyState extends Schema {
}
schema.defineTypes(MyState, {
  currentTurn: "string"
});

"这个 @type() 关键字是什么? 我之前从未见过!"

您看见的在本页大量使用的 @type() 是一个即将推出的 JavaScript 功能, 还没有被 TC39 正式认可. type 其实只是一个从 @colyseus/schema 模块导入的函数. 在属性层级调用带有 @ 前缀的 type, 意味着我们将其作为一个 属性修饰器 进行调用. 在这里查看修饰器方案.

在您的 Room 内使用状态

// MyRoom.ts
import { Room } from "colyseus";
import { MyState } from "./MyState";

export class MyRoom extends Room<MyState> {
    onCreate() {
        this.setState(new MyState());
    }
}

使用 Schema

  • 只有服务器端有权修改 Schema 数据
  • 客户端要包含用 schema-codegen 生成的, 与服务器端相一致的 Schema 定义. (如果使用 JavaScript SDK 则此条为可选项)
  • 为了从服务器获得更新, 需要 在客户端把回调附加在 schema 实例上.
  • 客户端永远不应主动修改 schema - 因为在收到来自服务器的下一帧更新时就会把它覆盖掉.

基本类型

基本类型为数字, 字符串和布尔型.

类型 描述 范围
"string" utf8 字符串 最大 4294967295 字节
"number" 又称为 "正整数". 自动检测数字类型. (编码时可能会多用1个字节) 取值范围 018446744073709551615
"boolean" truefalse 取值为 01

特定数值类型:

类型 描述 范围
"int8" 有符号 8-bit 整数 -128127
"uint8" 无符号 8-bit 整数 0255
"int16" 有符号 16-bit 整数 -3276832767
"uint16" 无符号 16-bit 整数 065535
"int32" 有符号 32-bit 整数 -21474836482147483647
"uint32" 无符号 32-bit 整数 04294967295
"int64" 有符号 64-bit 整数 -92233720368547758089223372036854775807
"uint64" 无符号 64-bit 整数 018446744073709551615
"float32" 单精度浮点数 -3.40282347e+383.40282347e+38
"float64" 双精度浮点数 -1.7976931348623157e+3081.7976931348623157e+308

复杂类型

复杂类型由 Schema 嵌套而成. 它们也可以包含 集合类型 (array, map 等).

import { Schema, type } from "@colyseus/schema";

class World extends Schema {
    @type("number") width: number;
    @type("number") height: number;
    @type("number") items: number = 10;
}

class MyState extends Schema {
    @type(World) world: World = new World();
}
const schema = require('@colyseus/schema');
const Schema = schema.Schema;

class World extends Schema {
}
schema.defineTypes(World, {
  width: "number",
  height: "number",
  items: "number"
});

class MyState extends Schema {
    constructor () {
        super();

        this.world = new World();
    }
}
schema.defineTypes(MyState, {
  world: World
});

集合类型

ArraySchema

ArraySchema 是 JavaScript 内置 Array 类型的一种可同步版本.

示例: 自定义 Schema 类型 数组

import { Schema, ArraySchema, type } from "@colyseus/schema";

class Block extends Schema {
    @type("number") x: number;
    @type("number") y: number;
}

class MyState extends Schema {
    @type([ Block ]) blocks = new ArraySchema<Block>();
}
const schema = require('@colyseus/schema');
const Schema = schema.Schema;
const ArraySchema = schema.ArraySchema;

class Block extends Schema {
}
schema.defineTypes(Block, {
  x: "number",
  y: "number"
});

class MyState extends Schema {
    constructor () {
        super();

        this.blocks = new ArraySchema();
    }
}
schema.defineTypes(MyState, {
  blocks: [ Block ],
});

示例: 基本类型数组

数组元素必须是同一数据类型.

import { Schema, ArraySchema, type } from "@colyseus/schema";

class MyState extends Schema {
    @type([ "string" ]) animals = new ArraySchema<string>();
}
const schema = require('@colyseus/schema');
const Schema = schema.Schema;
const ArraySchema = schema.ArraySchema;

class MyState extends Schema {
    constructor () {
        super();

        this.animals = new ArraySchema();
    }
}
schema.defineTypes(MyState, {
  animals: [ "string" ],
});

array.push()

在一个数组后面添加一个或多个元素, 并返回该数组更新后的长度.

const animals = new ArraySchema<string>();
animals.push("pigs", "goats");
animals.push("sheeps");
animals.push("cows");
// 输出: 4


array.pop()

移除一个数组的最后一个元素并返回该元素. 该方法会改变数组的长度.

animals.pop();
// 输出: "cows"

animals.length
// 输出: 3


array.shift()

移除一个数组的第一个元素并返回该元素. 该方法会改变数组的长度.

animals.shift();
// 输出: "pigs"

animals.length
// 输出: 2


array.unshift()

在一个数组的开头添加一个或多个元素, 并返回该数组更新后的长度.

animals.unshift("pigeon");
// 输出: 3


array.indexOf()

返回数组中找到给定元素的第一个索引, 如果不存在则返回 -1

const itemIndex = animals.indexOf("sheeps");


array.splice()

移除替换现有元素或 在指定位置 添加新元素来更改一个数组的内容.

// 找到需要移除元素的索引
const itemIndex = animals.findIndex((animal) => animal === "sheeps");

// 移除元素!
animals.splice(itemIndex, 1);


array.forEach()

迭代数组的每个元素.

this.state.array1 = new ArraySchema<string>('a', 'b', 'c');

this.state.array1.forEach(element => {
    console.log(element);
});
// 输出: "a"
// 输出: "b"
// 输出: "c"
State.array1.ForEach((value) => {
    Debug.Log(value);
})
state.array1:each(function(value, index)
    print(index, "=>")
    pprint(value)
end)
for (index => value in state.array1) {
    trace(index + " => " + value);
}

Array 还有更多函数可用

详见 MDN 文档.

MapSchema

MapSchema 是基于 JavaScript 内置 Map 的一种可同步版本.

推荐使用 Maps 里的 id 来追踪游戏实体, 比如玩家, 敌人等.

当前仅支持字符串类型的 id

目前, MapSchema 允许您自定义值的类型, 但是键的类型必须为为 string.

import { Schema, MapSchema, type } from "@colyseus/schema";

class Player extends Schema {
    @type("number") x: number;
    @type("number") y: number;
}

class MyState extends Schema {
    @type({ map: Player }) players = new MapSchema<Player>();
}
const schema = require('@colyseus/schema');
const Schema = schema.Schema;
const MapSchema = schema.MapSchema;

class Player extends Schema {
}
schema.defineTypes(Player, {
  x: "number",
  y: "number"
});

class MyState extends Schema {
    constructor () {
        super();

        this.players = new MapSchema();
    }
}
schema.defineTypes(MyState, {
  players: { map: Player }
});

map.get()

通过键得到 map 的值:

const map = new MapSchema<string>();
const item = map.get("key");


map.set()

通过键来设置 map 的值:

const map = new MapSchema<string>();
map.set("key", "value");


map.delete()

通过键移除 map 的值:

map.delete("key");


map.size

返回 MapSchema 对象中元素的数量.

const map = new MapSchema<number>();
map.set("one", 1);
map.set("two", 2);

console.log(map.size);
// 输出: 2


map.forEach()

以元素插入顺序迭代 map 中的键值对.

this.state.players.forEach((value, key) => {
    console.log("key =>", key)
    console.log("value =>", value)
});
State.players.ForEach((key, value) => {
    Debug.Log(key);
    Debug.Log(value);
})
state.players:each(function(value, key)
    print(key, "=>")
    pprint(value)
end)
for (key => value in state.players) {
    trace(index + " => " + value);
}

Map 还有更多函数可用

详见 MDN 文档.

SetSchema

SetSchema 仅支持 JavaScript

目前 SetSchema 只能在 JavaScript 中使用. 尚不支持 Haxe, C#, LUA 和 C++ 客户端.

SetSchema 是一个基于 JavaScript 内置 Set 的可同步版本.

SetSchema 的用法和 [CollectionSchema] 十分类似, 最大区别在于 Set 的值具有唯一性. Set 没有直接获取值的方法. (比如像 collection.at())

import { Schema, SetSchema, type } from "@colyseus/schema";

class Effect extends Schema {
    @type("number") radius: number;
}

class Player extends Schema {
    @type({ set: Effect }) effects = new SetSchema<Effect>();
}
const schema = require('@colyseus/schema');
const Schema = schema.Schema;
const SetSchema = schema.SetSchema;

class Effect extends Schema {
}
schema.defineTypes(Effect, {
  radius: "number",
});

class Player extends Schema {
    constructor () {
        super();

        this.effects = new SetSchema();
    }
}
schema.defineTypes(Player, {
  effects: { set: Effect }
});

set.add()

SetSchema 添加元素.

const set = new SetSchema<number>();
set.add(1);
set.add(2);
set.add(3);


set.delete()

按值删除元素.

set.delete("three");


set.has()

检查集合中是否有该值.

if (set.has("two")) {
    console.log("Exists!");
} else {
    console.log("Does not exist!");
}


set.size

返回 SetSchema 里元素的长度.

const set = new SetSchema<number>();
set.add(10);
set.add(20);
set.add(30);

console.log(set.size);
// 输出: 3

Set 还有更多函数可用

详见 MDN 文档.

CollectionSchema

CollectionSchema 仅支持 JavaScript

目前 CollectionSchema 只能在 JavaScript 中使用. 尚不支持 Haxe, C#, LUA 和 C++ 客户端.

CollectionSchema 的用法与 ArraySchema 类似, 需要注意的是, 它不具备某些数组可用的函数.

import { Schema, CollectionSchema, type } from "@colyseus/schema";

class Item extends Schema {
    @type("number") damage: number;
}

class Player extends Schema {
    @type({ collection: Item }) items = new CollectionSchema<Item>();
}
const schema = require('@colyseus/schema');
const Schema = schema.Schema;
const CollectionSchema = schema.CollectionSchema;

class Item extends Schema {
}
schema.defineTypes(Item, {
  damage: "number",
});

class Player extends Schema {
    constructor () {
        super();

        this.items = new CollectionSchema();
    }
}
schema.defineTypes(Player, {
  items: { collection: Item }
});

collection.add()

CollectionSchema 添加元素.

const collection = new CollectionSchema<number>();
collection.add(1);
collection.add(2);
collection.add(3);


collection.at()

获取 index 处的值.

const collection = new CollectionSchema<string>();
collection.add("one");
collection.add("two");
collection.add("three");

collection.at(1);
// 输出: "two"


collection.delete()

按值删除元素.

collection.delete("three");


collection.has()

检查集合中是否有该值.

if (collection.has("two")) {
    console.log("Exists!");
} else {
    console.log("Does not exist!");
}


collection.size

返回 CollectionSchema 里元素的长度.

const collection = new CollectionSchema<number>();
collection.add(10);
collection.add(20);
collection.add(30);

console.log(collection.size);
// 输出: 3


collection.forEach()

迭代 CollectionSchema 中的键值对, 以元素插入顺序.

collection.forEach((value, at) => {
    console.log("at =>", at)
    console.log("value =>", value)
});

按客户端过滤数据

此功能为实验性质

@filter() / @filterChildren() 为实验性质, 可能不适合快节奏游戏.

过滤用来为指定客户端隐藏部分状态数据, 防止作弊, 防止玩家获取全部数据.

数据过滤器回调, 可以针对 每个客户端每个字段 进行触发 (如果使用了 @filterChildren, 还可针对每个子结构触发). 如果过滤器回调返回 true, 则该字段数据将会发送给那个指定的客户端, 否则不发送.

请注意, 只有被过滤字段 (或其子字段) 数据更新时, 过滤器回调才能被触发. 要想手动触发请参考 此问题 里描述的方法.

@filter() 属性修饰器

@filter() 属性修饰器可作用于整个 Schema 字段.

下面展示了 @filter() 的函数签名:

class State extends Schema {
    @filter(function(client, value, root) {
        // client 参数是:
        //
        // 当前将要接受数据的客户端. 可以通过其
        // client.sessionId, 及其他信息判定是否
        // 要把数据同步给这个客户端.

        // value 参数是:
        // 被 @filter() 标记过滤的字段值

        // root 参数是:
        // 房间 Schema 实例引用. 方便在是否过滤的
        // 决策过程中
        // 访问房间状态.
    })
    @type("string") field: string;
}
const schema = require('@colyseus/schema');
class State extends schema.Schema {}

schema.defineTypes(State, {
    field: "string"
});

schema.filter(function(client, value, root) {
    // client 参数是:
    //
    // 当前将要接受数据的客户端. 可以通过其
    // client.sessionId, 及其他信息判定是否
    // 要把数据同步给这个客户端.

    // value 参数是:
    // 被 @filter() 标记过滤的字段值

    // root 参数是:
    // 房间 Schema 实例引用. 方便在是否过滤的
    // 决策过程中
    // 访问房间状态.
    return true;
})(State.prototype, "field");

@filterChildren() 属性修饰器

@filterChildren() 属性修饰器可用于过滤掉 array, map, set等集合类型的内容. 它的签名与 @filter() 基本相同, 但是在 value 之前添加了 key 参数 - 表示 ArraySchema, MapSchema, CollectionSchema 等内容的索引.

class State extends Schema {
    @filterChildren(function(client, key, value, root) {
        // client 参数是:
        //
        // 当前将要接受数据的客户端. 可以通过其
        // client.sessionId, 及其他信息判定是否
        // 要把数据同步给这个客户端.

        // key 参数是:
        // 集合内容的当前索引

        // value 参数是:
        // 集合内容当前索引处的值

        // root 参数是:
        // 房间 Schema 实例引用. 方便在是否过滤的
        // 决策过程中
        // 访问房间状态.
    })
    @type([Cards]) cards = new ArraySchema<Card>();
}
const schema = require('@colyseus/schema');
class State extends schema.Schema {}

schema.defineTypes(State, {
    cards: [Card]
});

schema.filterChildren(function(client, key, value, root) {
    // client 参数是:
    //
    // 当前将要接受数据的客户端. 可以通过其
    // client.sessionId, 及其他信息判定是否
    // 要把数据同步给这个客户端.

    // key 参数是:
    // 集合内容的当前索引

    // value 参数是:
    // 集合内容当前索引处的值

    // root 参数是:
    // 房间 Schema 实例引用. 方便在是否过滤的
    // 决策过程中
    // 访问房间状态.
    return true;
})(State.prototype, "cards");

示例: 在卡牌游戏中, 应该只有卡牌的持有者知道每个卡片的数据, 或者在特定条件下才能知道这些数据 (例如摊牌)

参考 @filter() 回调签名:

import { Client } from "colyseus";

class Card extends Schema {
    @type("string") owner: string; // 用来保存卡牌持有者的 sessionId
    @type("boolean") discarded: boolean = false;

    /**
     * 不要在 `@filter` 函数里使用箭头函数
     * (会造成 `this` 指针丢失)
     */
    @filter(function(
        this: Card, // 定义 `@filter` 的类 (这里 this 就是 `Card` 的实例)
        client: Client, // 要被过滤的客户端 `client` 实例
        value: Card['number'], // 要被过滤的字段值. (这里是 `number` 字段的值)
        root: Schema // 房间状态 Schema 实例
    ) {
        return this.discarded || this.owner === client.sessionId;
    })
    @type("uint8") number: number;
}
const schema = require('@colyseus/schema');

class Card extends schema.Schema {}
schema.defineTypes(Card, {
    owner: "string",
    discarded: "boolean",
    number: "uint8"
});

/**
 * 不要在 `@filter` 函数里使用箭头函数
 * (会造成 `this` 指针丢失)
 */
schema.filter(function(client, value, root) {
    return this.discarded || this.owner === client.sessionId;
})(Card.prototype, "number");

客户端

C#, C++, Haxe

在使用强类型语言时, 需要基于 Typescript schema 定义手动生成客户端 schema 文件. 生成客户端 schema 的方法.

回调

服务器状态数据更新应用到客户端时, 会根据变更的类型自动触发本地实例上的回调.

回调通过房间状态实例触发. 使用前要确保该实例上已实现回调函数.

.listen(prop, callback)

侦听单个属性更新.

参数:

  • property: 想要侦听更新的属性名称.
  • callback: 当 property 更新时触发的回调.
state.listen("currentTurn", (currentValue, previousValue) => {
    console.log(`currentTurn is now ${currentValue}`);
    console.log(`previous value was: ${previousValue}`);
});
state.OnCurrentTurnChange((currentValue, previousValue) => {
    Debug.Log(currentValue);
    Debug.Log(previousValue);
})
state:listen("currentTurn", function (current_value, previous_value)
    pprint(current_value);
    pprint(previous_value);
end)
state.listen("currentTurn", (currentValue, previousValue) => {
    trace(currentValue);
    trace(previousValue);
});

.listen() 返回的句柄可用于移除侦听器

const removeListener = state.listen("currentTurn", (currentValue, previousValue) => {
    // ...
});

// 之后, 如果不需要侦听器了, 可以调用 `removeListener()` 来移除对 `"currentTurn"` 的侦听.
removeListener();

listenonChange 有什么区别?

.listen() 方法是专为监听单个属性的 onChange 的简化版本. 下面是把 .listen() 写成 onChange 的样子:

state.onChange = function(changes) {
    changes.forEach((change) => {
        if (change.field === "currentTurn") {
            console.log(`currentTurn is now ${change.value}`);
            console.log(`previous value was: ${change.previousValue}`);
        }
    })
}


onAdd (instance, key)

只有集合 (MapSchema, ArraySchema 等) 可以使用 onAdd 回调. 集合更新后触发 onAdd 回调, 外加已更新内容的键作为参数.

room.state.players.onAdd((player, key) => {
    console.log(player, "has been added at", key);

    // 在游戏中加入player!

    // 要想跟踪地图上物体的移动, 通常要这么做:
    player.onChange(function(changes) {
        changes.forEach(change => {
            console.log(change.field);
            console.log(change.value);
            console.log(change.previousValue);
        })
    });
});
room.state.players:on_add(function (player, key)
    print("player has been added at", key);

    -- 在游戏中加入player!

    -- 要想跟踪地图上物体的移动, 通常要这么做:
    player:on_change(function(changes)
        for i, change in ipairs(changes) do
            print(change.field)
            print(change.value)
            print(change.previousValue)
        end
    end)
end)
room.State.players.OnAdd((string key, Player player) =>
{
    Debug.Log("player has been added at " + key);

    // 在游戏中加入player!

    // 要想跟踪地图上物体的移动, 通常要这么做:
    player.OnChange += (changes) =>
    {
        changes.ForEach((obj) =>
        {
            Debug.Log(obj.Field);
            Debug.Log(obj.Value);
            Debug.Log(obj.PreviousValue);
        });
    };
});

onRemove (instance, key)

只有映射 (MapSchema) 和数组 (ArraySchema) 可以使用 onRemove 回调. 集合更新后触发 onRemove 回调, 外加已移除内容的键作为参数.

room.state.players.onRemove((player, key) => {
    console.log(player, "has been removed at", key);

    // 从游戏中移除player!
});
room.state.players:on_remove(function (player, key)
    print("player has been removed at " .. key);

    -- 从游戏中移除player!
end)
room.State.players.OnRemove((string key, Player player) =>
{
    Debug.Log("player has been removed at " + key);

    // 从游戏中移除player!
});

onChange (changes:DataChange[])

Schema 上的 onChange 和集合上的不一样. 对于 集合结构(数组, 映射等)的 onChange 请参考这里.

可以注册 onChange 以跟踪 Schema 实例属性的变更. onChange 的参数数组包含已变更的属性以及变更前的值.

room.state.onChange((changes) => {
    changes.forEach(change => {
        console.log(change.field);
        console.log(change.value);
        console.log(change.previousValue);
    });
};
room.state:on_change(function (changes)
    for i, change in ipairs(changes) do
        print(change.field)
        print(change.value)
        print(change.previous_value)
    end
end)
room.State.OnChange((changes) =>
{
    changes.ForEach((obj) =>
    {
        Debug.Log(obj.Field);
        Debug.Log(obj.Value);
        Debug.Log(obj.PreviousValue);
    });
};

没与客户端同步过的状态上不能注册 onChange 回调.


onChange (instance, key)

Schema 上的 onChange 和集合上的不一样. 对于 SchemaonChange 请参考这里.

当集合里的 基本 类型 (string, number, boolean 等) 值更新时, 将触发此回调.

room.state.players.onChange((player, key) => {
    console.log(player, "have changes at", key);
};
room.state.players:on_change(function (player, key)
    print("player have changes at " .. key);
end)
room.State.players.OnChange((string key, Player player) =>
{
    Debug.Log("player have changes at " + key);
});

对于 非基本 类型 (各种 Schema 集合), 请先注册 onAdd 再注册 onChange.

onChange, onAddonRemove互斥的

onChange 回调在 onAddonRemove 期间不会被触发.

如果想要跟踪的更新包括 onAddonRemove, 请注册这两个回调.


生成客户端 schema 的方法

schema-codegen 是一个转译工具, 用于把服务器端的 schema 定义文件转换为客户端可以使用的版本:

要在客户端正确解码 state, 客户端的 schema 定义文件必须与服务器端相兼容.

在使用 JavaScript SDK 时不必使用此工具

只有在客户端使用强类型语言, 如 C#, Haxe 等时, 才必须使用 schema-codegen.

使用方法

要在终端查看使用方法, 先 cd 进入服务器目录, 然后运行以下命令:

npx schema-codegen --help

输出:

schema-codegen [path/to/Schema.ts]

Usage (C#/Unity)
    schema-codegen src/Schema.ts --output client-side/ --csharp --namespace MyGame.Schema

Valid options:
    --output: fhe output directory for generated client-side schema files
    --csharp: generate for C#/Unity
    --cpp: generate for C++
    --haxe: generate for Haxe
    --ts: generate for TypeScript
    --js: generate for JavaScript
    --java: generate for Java

Optional:
    --namespace: generate namespace on output code

举例: Unity / C

下面是用 Unity 演示项目 生成 C# schema 文件的实例.

npx schema-codegen src/rooms/schema/* --csharp --output ../Assets/Scripts/States/"
generated: Player.cs
generated: State.cs

使用 npm 脚本:

方便起见, 推荐您把 schema-codegen 的参数保存在 package.json 文件中的 npm 脚本里:

"scripts": {
    "schema-codegen": "schema-codegen src/rooms/schema/* --csharp --output ../Assets/Scripts/States/"
}

这样, 运行 npm run schema-codegen, 就可以代替完整的命令:

npm run schema-codegen
generated: Player.cs
generated: State.cs

版本及向下/向上兼容

通过在现有结构末尾声明新字段, 可以实现向下/向上兼容, 不应删除先前的声明, 而是应该根据需要将其标记为 @deprecated(). 下面是一个例子.

import { Schema, type, deprecated } from "@colyseus/schema";

class MyState extends Schema {
    @type("string") myField: string;
}
import { Schema, type, deprecated } from "@colyseus/schema";

class MyState extends Schema {
    // 标记此字段作废.
    @deprecated() @type("string") myField: string;

    // 保证不同客户端版本的服务器兼容.
    @type("string") newField: string;
}
import { Schema, type, deprecated } from "@colyseus/schema";

class MyState extends Schema {
    // 标记此字段作废.
    @deprecated() @type("string") myField: string;

    // 再次标记此字段作废.
    @deprecated() @type("string") newField: string;

    // 最新的字段总是保持在最下边
    @type("string") anotherNewField: string;
}

这对于本地编译类语言很有用, 如 C#, C++, Haxe 等 - 即使这些客户端编译时没有最新的 schema 定义.


限制和最佳实践

  • 每个 Schema 结构最多可以保存 64 个字段. 如果需要更多字段, 可以嵌套使用 Schema 结构.
  • NaNnull 数字被编码为 0
  • null 字符串被编码为 ""
  • Infinity 数字被编码为 Number.MAX_SAFE_INTEGER
  • 不支持多维数组. 查看如何将一维数组作为多维数组使用
  • @colyseus/schema 编码顺序按照字段定义顺序.
    • 编码器 (服务器) 和解码器 (客户端) 都必须拥有相同的 schema 定义.
    • 字段的顺序也要相同.

集合

集合类型 (ArraySchema, MapSchema 等) 里的元素类型必须相同, 或者基类相同.

支持以下写法:

class Item extends Schema {/* 基类 Item */}
class Weapon extends Item {/* 武器类 */}
class Shield extends Item {/* 盾牌类 */}

class Inventory extends Schema {
    @type({ map: Item }) items = new MapSchema<Item>();
}

const inventory = new Inventory();
inventory.set("left", new Weapon());
inventory.set("right", new Shield());

Back to top