Circular Dependencies with RxJS. Modeling spores

2019-08-22 04:50发布

问题:

I try to model some game with RxJS. but I found some troubles with circular dependencies. So, I simplified my game to a simple simulation(I left only 'move' action). You can find code below(I omitted some parts, you can find the repo here)

const rx = require('rx')
const math = require('mathjs')
const _ = require('underscore')

const FIELD_SIZE = 10

const ctx = require('axel');

let getInitialState = () => {
  return {
    size: FIELD_SIZE,
    people: [
      { x: 0, y: 0 },
      { x: 9, y: 9 },
      { x: 5, y: 5 }
    ]
  }
}

var drawWorld = ({size, people}) => {
  // draw world logic
}


let getMove = (index)=> {
  let [xOffset, yOffset] = [[0,1], [1,0]][math.pickRandom([0, 1])]
  let direction = math.pickRandom([-1, 1])
  return (state) => {
    let {people} = state
    let p = people[index]
    people[index] = {
      x: math.max(
          0,
          math.min(p.x + xOffset * direction, FIELD_SIZE-1)),
      y: math.max(
          0,
          math.min(p.y + yOffset * direction, FIELD_SIZE-1))
    }
    return _.extend({}, state, {people})
  }
}

var POOL = []

var produceActions = (state) => {
  _.range(state.people.length).forEach((i) => {
    POOL.push(getMove(i))
  })
}

var stateObservable = rx.Observable
  .interval(100)
  .timeInterval()
  .map(()=> {
    var x = POOL.slice()
    POOL.splice(0, POOL.length)
    return x
  })
  .scan(
      (state, ops) => ops.reduce(
        (st, o) => o(st),
        state
      ),
      getInitialState()
  )

stateObservable.subscribe(drawWorld)
stateObservable.tap(produceActions).subscribe()

Is there a way to rewrite this ugly `produceActions' part which relies on global variable? Like proposed in the @user3743222 answer

回答1:

Following your updated question and request for an example, this is untested but here is my try, based on the provided link on the subject technique. Check if it works, I'll post some explanations after that if it does. Also, please check the syntax, I don't know so much about ES6.

Using a proxy subject for POOL:

var POOL_proxyS = new Rx.BehaviorSubject([]); // instead of var POOL = []

var stateObservable = rx.Observable
  .interval(100)
  .timeInterval()
  .combineLatest(POOL_proxyS, (_, POOL) => POOL.slice() )
  .scan(
      (state, ops) => ops.reduce(
        (st, o) => o(st),
        state
      ),
      getInitialState()
  );

var produceActions = (state) => _.range(state.people.length).map((i) => getMove(i));

stateObservable
  .do(drawWorld)
  .map(produceActions)
  .subscribe(POOL_proxyS);


回答2:

This is a question which has been dealt with a number of times here.

Three ways I can mention (might be others) :

  • use a subject
    • How to handle circularly dependent observables in RxJS?
  • use expand
    • Feedback loop without Subject in RX
    • RxJs: How to loop based on state of the observable?
    • RxJS, how to poll an API to continuously check for updated records using a dynamic timestamp
  • use cycle.js (if you are familiar with RxJS, the learning curve is small, and if you do a lot of games, it might be worth the effort)