我在一个简单的射击游戏中使用redux
作为状态容器。状态是完全确定的,系统接收的唯一输入是用户输入(例如,武器被解雇等)。
我的问题是我必须跟踪(和处理)在游戏过程中发生的某些事件(例如某些东西被破坏等),而我不太清楚如何做到这一点。
我目前的解决方案是reducer在当前状态下维护一个events
数组,每个reducer只是向它添加事件。
FIRE_WEAPON+-+ FIRE_WEAPON+-+
| |
| |
+-v--------+--------------v------------->
|
|
+->PLAYER_DESTROYED
此处,reduce会收到两个FIRE_WEAPON
操作,并且“发出”一个PLAYER_DESTROYED
事件(现在,它用于在那里渲染爆炸)。
project是开源的,reducer看起来像这样(它只是伪代码,但这里是relevant game logic):
// combine is just (f, g) => ((s, a) => g(f(s, a), a))
const reducer = combine(
// simulate world
(state, action) => {
while (state.time < action.time) {
state = evolve(state, delta); // evolve appends the happened in-game events to the state
}
return state;
},
// handle actual user input
(state, action) => {
return handleUserInput(state, action);
}
);
const evolve = (state, delta) => {
const events = [];
// some game logic that does some `events.push(...)`
return {
...state,
time: state.time + delta,
events: state.events.concat(events),
};
}
我们可以假设,handleUserInput
是一个简单的x => x
标识函数(它不会触及events
数组)。
在evolve
期间,我想“发出”事件,但由于这会使evolve
不纯,我不能这样做。
正如我所说,现在我通过将发生的事件存储在州中来做到这一点,但可能有更好的方法。有什么建议吗?
这些events
在渲染过程中使用,看起来像这样:
let sprites = [];
// `subscribe`d to store
function onStateChange() {
// `obsolete` removes old sprites that should not be displayed anymore
// `toSprite` converts events to sprites, you can assume that they are just simple objects
sprites = sprites.filter(obsolete).concat(store.getState().events.map(toSprite));
}
function render(state) {
renderState(state);
renderSprites(sprites);
}
但后来我想在服务器上使用events
(上面描述的reducer也在服务器上运行),用于计算各种统计数据(例如,被摧毁的敌人等)。
Ps:这些“发出的”事件对国家没有影响(它们完全无关),所以我确信它们不应该是行动(因为它们会离开国家)不变)。在reducer完成后处理它们,之后它们可以被删除(reducer总是接收一个空的events
数组)。
答案 0 :(得分:0)
我很确定您可以将其分为三个部分:
-操作:
const fireWeapon = ()=>({
type: FIRE_WEAPON
})
您可以启动fireWeapon okey之类的操作,正如您所说的reducers是纯函数,因此您可以在状态中存储已启动该操作的次数。
-减速器起火
initialState: { fireWeapon: 0, fireShotgun:0}
CASE FIRE_WEAPON:
return {...state, fireWeapon: state.fireWeapon+1}
最后,关键部分是一个名为redux-observable的库,它基于rxjs和反应式编程。您可以订阅一系列操作并发出新的操作。
一个非常简单的例子是:
export const clearDeletedSiteEpic = (action$,state$) =>
action$.pipe(
ofType(FIRE_WEAPON),
map(() => {
if (state$.value.fires.fireWeapon % 2 === 0){
playerDestroyed() // action is launched
}
}
);