0

我正在尝试创建一个浏览器游戏。游戏状态存储在具有多个层的对象中。

let State = {
    points: 0,
    health: 50,
    currentLocation: {x: 5, y: 55},
    inventory: { bread: 8, water: 33, money: 20 }
    abilities:
        charisma:5,
        perseverance: 3,
        speed: 8
    }
    buildings {
        bakery: { location: {x: 23, y: 41}, unlocked: 1, visited: 1},
        homestead: { location: {x: 3, y: 59}, unlocked: 0, visited: 0},
        forge: { location: {x: 56, y: 11}, unlocked: 1, visited: 0}
    }
    
}

我希望能够根据 State 的当前值来控制游戏逻辑。

有些情况很简单 if(State.health == 0) { Game.die(); }

大多数情况要复杂得多

if(State.buildings.bakery.unlocked == 1) {

    // calculate player's distance to bakery
    let dX = Math.abs(State.buildings.bakery.location.x - State.currentLocation.x)
    let dY = Math.abs(State.buildings.bakery.location.y - State.currentLocation.y)
    let d = DX + dY;
    
    if(State.inventory.bread == 0 && d < State.inventory.abilities.speed) {
        Game.die();
    }
    
}

实现这样的目标的最佳方法是什么?循环遍历所有条件似乎是对资源的不必要使用。我研究过 getter、setter 和 Proxy,但我真的不知道我在做什么!理想情况下,我只想在状态的相关部分发生变化时检查逻辑。

4

2 回答 2

0

这就是我最终做的......

const state = {

    _data:{}, // object in which the data is actually stored

    get:(path) => { // safely get the value or return undefined 
        return path.split('.').reduce((o,i)=>(o||{})[i], state._data)
    },
    
    set:(fullpath,value,silent=0) => { // safely set the value
        
        function cycle(obj,path,value) {
            let i = path[0]
            
            if(path.length==1) { // if this is the end of the path
                obj[i] = value; // change the value
                if(!silent) { Pubsub.publish('state.'+fullpath, value) ; } // and publish the event
            } else { // if this is not the end of the the path
                if(typeof obj[i] !== 'object') { obj[i] = {} } // create this part of the path if it doesn't exist
                cycle(obj[i],path.slice(1), value) // move on to the next part of the path
            }
        }
        
        cycle(state._data,fullpath.split('.'),value)
            

    }

第 1 步:获取和设置

我创建了两个自定义函数来获取和设置状态。

get() 将点符号路径作为字符串,例如state.get('buildings.bakery.unlocked').

set() 还将点符号路径作为字符串以及值,例如state.set('buildings.bakery.unlocked', 1).

我使用了这个线程的一些代码。使用这些函数意味着可以轻松操作嵌套属性,而不必担心可怕的 TypeError。

第 2 步:发布订阅

点符号路径还提供了发布/订阅模型。我正在使用PubSubJS。调用 set() 还会发布与路径匹配的事件,例如state.set('abilities.strength', 5)也发布'state.abilities.strength'

要监控状态变化,您只需订阅相关事件:

PubSub.subscribe('state.abilities.strength', () => console.log('strength value changed'))

发布的事件也会传回新的值,所以你可以用它做你想做的事:

PubSub.subscribe('state.inventory.bread', (path, value) => console.log('bread value changed, you now have ' + value))

第 3 步:实施以进行比较

PubSub 具有作为基于主题的系统的好处。这意味着您可以订阅非最终节点:

PubSub.subscribe('state.inventory', () => console.log('something in the inventory changed'))
PubSub.subscribe('state.inventory.water', () => console.log('water changed'))

改变inventory.bread会触发第一个,改变inventory.water会触发两者。

这使我可以将游戏逻辑与状态分开。有两种比较值的方法...

    // function to compare two values
    let comparison = function () {

        console.log('running comparison');

        if(state.get('test.valueA') > state.get('test.valueB')) {
            console.log('A is bigger than B');
        }
    }

    // you either subscribe to both the values, and compare whenever either one changes
    PubSub.subscribe('state.test.valueA', comparison)
    PubSub.subscribe('state.test.valueB', comparison)
    
    // or alternatively, if they have a shared topic, then subscribe to that
    PubSub.subscribe('state.test', comparison)
于 2020-07-27T09:06:36.477 回答
0

我只想在状态的相关部分发生变化时检查逻辑。

然后使用一个gettersetter这样的:

let State = {
  buildings: {
    bakery: {
      _unlocked: false,
      get unlocked() {
        return this._unlocked;
      },
      set unlocked(value) {
        this._unlocked = value;
        if (value == true) {
          console.log("Calculating player's distance to bakery ...");
        }
      }
    }
  }
}

State.buildings.bakery.unlocked = true;
// Calculating player's distance to bakery ...

使用该_unlocked变量是因为如果你没有,第一次unlocked访问,它会递归地触发自己的getter,直到你得到一个堆栈溢出错误。

于 2020-07-19T18:30:17.557 回答