数据管理

介绍

在 react/vue 中,我们经常使用 redux/vuex 来进行数据管理,维护一套统一的数据,在 Eva.js 体系下,我们设计了一套数据管理系统,叫做 EVAX,通过 EVAX 我们可以维护一份共有数据,多个不同的对象上的组件可能会使用同一份数据进行逻辑运行。

比如游戏中的生命值,不仅在人的头顶上会显示,游戏中的人物也会根据生命值的多少来展示不同的形态,我们将 EVAX 组件绑定到游戏对象上,可对某个数据进行监听,如果数据发生变化,可以操作当前游戏对象上其他组件上的数据或者事件。

Eva.js 开发游戏不是强依赖 EVAX 的,可以按照需要使用。

安装

使用 NPM

  1. npm i @eva/plugin-evax

在浏览器中

  1. <script src="https://unpkg.com/@eva/plugin-evax@1.0.x/dist/EVA.plugin.EVAX.min.js"></script>

创建 store

所有数据需要事先定义好,如果没有定义好,将不会被监听

  1. // 创建 Store
  2. const store = {
  3. user: {
  4. name: 'Mingfei',
  5. age: 18
  6. },
  7. items: [
  8. {
  9. id: 1,
  10. name: '20191111'
  11. }
  12. ]
  13. }

初始化 EVAX

  1. // 引入 evax 插件
  2. import { EvaXSystem, EvaX } from '@eva/plugin-evax'
  3. // 创建游戏,传入store
  4. const game = new Game({
  5. autoStart: true, // 可选
  6. frameRate: 60
  7. })
  8. const store = { a: 1 }
  9. const evaxSystem = new EvaXSystem({
  10. store // 这里将定义的 store 传入
  11. })
  12. game.addSystem(evaxSystem)
  13. const evaxManager = new GameObject('evaxManager')
  14. evaxManager.addComponent(
  15. new EvaX({
  16. events: {
  17. 'store.a': () => {}
  18. }
  19. })
  20. )
  21. game.scene.addChild(evaxMangager)

监听数据变化

  1. // 添加evax组件
  2. go.addComponent(
  3. new EvaX({
  4. events: {
  5. 'store.user': {
  6. deep: true, // store.user 下的属性变化也会触发
  7. handler(store, oldStore) {
  8. console.log(this) // 当前组件
  9. }
  10. },
  11. 'store.user.age'(store, oldStore) {},
  12. 'store.items.0'(store, oldStore) {
  13. // 0 下的属性变化不会触发,如需监听需设置deep
  14. },
  15. 'store.items.0.name'(store, oldStore) {
  16. // name 变化会触发
  17. },
  18. popUp(arg1, arg2) {
  19. // 这是一个事件,参考后面事件触发
  20. }
  21. }
  22. })
  23. )

更新数据

更新单个值

直接修改对象上面的值,如果值与之前相同也会触发监听此值改变的事件

  1. store.user.name = 'Cailun'
  2. // 或者
  3. evaxSystem.store.user.name = 'Cailun'

evax.updateStore 更新所有值

Tip: updateStore 和 forceUpdateStore 只会触发最后一层属性的变化。

全覆盖模式更新,对比内容变化,变化的内容才会触发更新,

  1. const newStore = {
  2. user: {
  3. name: 'Cailun',
  4. age: 18
  5. }
  6. }
  7. evaxSystem.emit('evax.updateStore', newStore)

以上操作会触发 store.user.name 更新,因为 age 没有变化

evax.forceUpdateStore 强制更新所有值

全覆盖模式更新,所有值都会被更新一次,触发所有属性的监听事件

  1. const newStore = {
  2. user: {
  3. name: 'Cailun',
  4. age: 18
  5. }
  6. }
  7. evaxSystem.emit('evax.forceUpdateStore', newStore)

以上操作会触发 store.user.namestore.user.age 数据变化事件,即便没有变化

事件触发

使用 emit 方法触发约定事件,事件 不要 使用 evax. 开头

  1. evaxSystem.emit('popUp', arg1, arg2, //...) // 事件约定,事件**不要**使用 evax. 开头

监听方法:

  1. // 添加evax组件
  2. go.addComponent(
  3. new EvaX({
  4. events: {
  5. popUp(arg1, arg2) {}
  6. }
  7. })
  8. )

使用案例

创建一个文字

这个案例介绍,如果 Store 上面的文字发生变化,我们修改对应组件上面文字的内容

  1. // 创建对象,文字组件使用store里面的名字
  2. const go = new GameObject('go')
  3. const txt = go.addComponent(new Text({ text: '' }))

1、一般来讲,我们的业务逻辑写在脚本组件中,在 EVAX 组件接收到时间或者数据更改的时候,调用脚本组件上面的方法。 2、将一些需要修改的组件,比如 Text 挂在到脚本组件的属性上,以便后续操作。

  1. // 创建一个自定义组件,将方法放到自定义组件中
  2. class AScriptComponent extends Component{
  3. static componentName: 'AScriptComponent',
  4. start() {
  5. this.txt = this.gameObject.getComponent('Text')
  6. // 在组件中使用evax可先绑定evax组件,在evax组件上拿到evax对象,进行事件触发和修改
  7. const evax = this.gameObject.getComponent('EvaX')
  8. this.evax = evax.evax
  9. },
  10. setName(store, oldStore) {
  11. txt.text = store.user.name // 设置新的text内容
  12. setTimeout(()=>{
  13. this.evax.emit('animationDown') // 通知修改完毕,由其他的组件来承接变化,不在此案例中
  14. this.evax.store.age += 1 // 由其他的组件来承接变化,不在此案例中
  15. }, 1000)
  16. },
  17. popUp(store) {
  18. // 做事件对应操作
  19. }
  20. })
  21. // 添加自定义组件
  22. const aScript = go.addComponent(new AScriptComponent)

创建 evax 组件,将需要绑定的事件写入,并且调用自定义组件上的事件

  1. // 添加evax组件
  2. go.addComponent(
  3. new EvaX({
  4. events: {
  5. 'store.user.name'(store, oldStore) {
  6. // 建议把触发的方法放在其他自定义组件内部,以便与后续场景编辑器使用
  7. go.getComponent(AScriptComponent).setName(store, oldStore)
  8. },
  9. 'store.user.age'(store, oldStore) {
  10. // ...
  11. },
  12. popUp(...args) {
  13. go.getComponent(AScriptComponent).popUp(...args)
  14. }
  15. }
  16. })
  17. )