前言
贴主是做前端开发的,学习cocos之后发现开发方式比较传统,特别是绘制UI的时候。想把数据渲染成对应的界面,需要繁琐的代码去实现。然而我就在想:为啥不能像前端开发一样,用响应式的方式去实现UI呢?
于是呼,我想到了前端开发中用到的全局store。起初想到了 Redux 、 Mobx 等状态管理库。然而,state是硬编码在脚本里的。对于游戏来说,一个关卡可能涉及很多state。且这些state的默认值可能会被频繁修改调试。这样一来,用传统状态管理库就不是很顺手了。我们通常可能是用excel来管理这些初始值,但这样又失去了响应式的特性。
那么,业内有没有既能满足响应式,又能满足方便管理的解决方案呢?答案是肯定的。于是乎,我找到了 Tinybase 这款数据库。下面我将简单介绍一下如何使用。
Tinybase应用
首先需要安装依赖,Tinybase 采用TS语言编写,所以在 creator 中使用非常友好。
安装
关于如何在 creator 中引用外部依赖库,想必大家都知道,这里就不赘述了。使用一下命令安装即可。
npm install tinybase
使用KV
import { createMergeableStore } from 'tinybase/mergeable-store';
@ccclass('TestStore')
export class TestStore extends Component {
async start() {
// 创建数据库实例
const store = createMergeableStore();
// 向数据库中添加KV
store.setValue('playerName', '小明');
store.setValue('playerLevel', 1);
store.setValue('playerHP', 100);
// 为KV添加监听器,当 playerName 值改变时,会自动运行回调函数
const listenerId = store.addValueListener('playerName', (store, valueId, newValue, oldValue) => {
console.log('playerName 改变:', [oldValue, newValue]);
find('Canvas/UILayer/NameLabel').getComponent(Label).string = newValue as string;
})
// 手动执行一次监听器,为UI初始化数据
store.callListener(listenerId);
// ...
}
}
这样以来,如果玩家修改了 playerName ,我们只需要一行代码即可实现。
// ...
// 通过 setValue 改变值,会自动调用对应的监听器,进而重绘UI
store.setValue('playerName', '小李');
store.setValue('playerLevel', 2);
store.setValue('playerHP', 90);
// ...
上面同时修改三个值的写法很不优雅,所以也可以改一下。
// ...
store.setValues({ playerName: '小李', playerLevel: 2, playerHP: 90});
// ...
假如我们需要增加一个 playerIsLive 的KV来标识玩家是否存活。我们可以这样写。
// ...
import { createMergeableStore } from 'tinybase/mergeable-store';
@ccclass('TestStore')
export class TestStore extends Component {
async start() {
//...
// 初始状态下,playerHP是100,所以playerIsLive我们设为true
store.setValue('playerIsLive', true);
// 在playerHP监听器中去修改playerIsLive的值
const listenerId = store.addValueListener('playerHP', (store, valueId, newValue, oldValue) => {
console.log('playerHP 改变:', [oldValue, newValue]);
if (newValue <= 0) {
store.setValue('playerIsLive', false);
} else {
store.setValue('playerIsLive', true);
}
find('Canvas/UILayer/HPLabel').getComponent(Label).string = newValue as string;
})
}
}
// ...
虽然我们简单实现了想要的效果,但是实现起来貌似也不简单。下面就带大家一起来简化。
使用表
既然是数据库,当然离不开表啦
。
import { createMergeableStore } from 'tinybase/mergeable-store';
@ccclass('TestStore')
export class TestStore extends Component {
async start() {
// 创建数据库实例
const store = createMergeableStore();
// 向数据库中添加 player 表, player01对应行id,name、level等对应列id
store.setTable('player', {
player01: {name: '小明', level: 1, HP: 100, isLive: true}
});
// 为player表的player01行添加监听器,当 player01 行的值改变时,会自动运行回调函数
const listenerId = store.addRowListener(
'player',
'player01',
(store, tableId, rowId, getCellChange) => {
console.log('player01改变');
if (getCellChange(tableId, rowId, 'name')[0]) {
find('Canvas/UILayer/NameLabel').getComponent(Label).string = getCellChange(tableId, rowId, 'name')[2] as string;
}
const hpChange = getCellChange(tableId, rowId, 'playerHP');
if (hpChange[0] && hpChange[2] <= 0) {
store.setCell(tableId, rowId, 'isLive', false);
} else if ((hpChange[0] && hpChange[2] > 0) {
store.setCell(tableId, rowId, 'isLive', true);
}
},
true
);
// 手动执行一次监听器,为UI初始化数据
store.callListener(listenerId);
// ...
}
}
使用插件
插件地址:Cocos Store
虽然使用 Tinybase 可以解决响应式问题,但初始数据全部都写在代码里,管理起来还是不方便。
为此,我写了一个插件,来辅助开发者管理数据。
插件提供了用户界面,可方便的增/删/改/查数据。且修改结果会立即生效,无需反复修改脚本代码重新编译。
演示效果
结语
谢谢大家能够看到这里,希望贴主的分享能够给大家带来一些启发。关于 Tinybase 更多高级用法,可以自行阅读官方文档,文档写得十分详细,API设计得也很明了。






