我读过FRP,非常兴奋。 它看起来很棒,所以你可以编写更多高级代码,而且所有内容都更具组合性等等。
然后我试图用普通的js到Bacon重写我自己的小游戏。
我发现不是编写高级逻辑代码,而是使用Bacon.js并遵守原则。
我遇到了一些令人头痛的问题,主要是干扰代码干净
.take(1)
而不是获得价值,我应该创造丑陋的结构。
有时它们应该是逻辑的。但在FRP中实施它是可怕的
即使是bacon.js的创建者也有troubles。
这里的例子是代码的和平来证明问题:
任务是不允许两名球员留在同一个地方
用bacon.js实现
http://jsbin.com/zopiyarugu/2/edit?js,console
function add(a) {return function(b){return a + b}}
function nEq(a) {return function(b){return a !== b}}
function eq(a) {return function(b){return a === b}}
function always(val) {return function(){return val}}
function id(a){return a}
var Player = function(players, movement, initPos){
var me = {};
me.position = movement
.flatMap(function(val){
return me.position
.take(1)
.map(add(val))
})
.flatMap(function(posFuture){
var otherPlayerPositions = players
.filter(nEq(me))
.map(function(player){return player.position.take(1)})
return Bacon
.combineAsArray(otherPlayerPositions)
.map(function(positions){
return !positions.some(eq(posFuture));
})
.filter(id)
.map(always(posFuture))
})
.log('player:' + initPos)
.toProperty(initPos);
return me;
}
var moveA = new Bacon.Bus();
var moveB = new Bacon.Bus();
var players = [];
players.push(new Player(players, moveA, 0));
players.push(new Player(players, moveB, 10));
moveA.push(4);
moveB.push(-4);
moveA.push(1);
moveB.push(-1);
moveB.push(-1);
moveB.push(-1);
moveA.push(1);
moveA.push(-1);
moveB.push(-1);
我想证明的是:
me.positions
依赖自己的可能我会错过一些基本的东西。也许我的实施在FRP风格中并非如此?
也许这段代码看起来不错,它只是不熟悉新的编码风格?
或者这个众所周知的问题,我应该选择最好的邪恶?如此描述的FRP的麻烦,或OOP的麻烦。答案 0 :(得分:4)
我尝试用Bacon和RxJs编写游戏时遇到了类似的经历。具有自我依赖性的东西(如玩家的位置)很难以“纯粹的FRP”方式处理。
例如,在我早期的Worzone游戏中,我包含了一个可变的targets对象,可以查询玩家和怪物的位置。
另一种方法是像Elm那样做:将完整的游戏状态建模为单个属性(或在Elm中调用的信号),并根据完整状态计算下一个状态。
到目前为止,我的结论是FRP不太适合游戏编程,至少是以“纯粹”的方式。毕竟,可变状态对于某些事情可能是更可组合的方法。在一些游戏项目中,比如Hello World Open赛车,我使用了可变状态,比如用于存储状态的DOM和用于传递事件的EventStreams。
所以,Bacon.js不是银弹。我建议你找出自己,在哪里申请FRP,哪里不用!
答案 1 :(得分:1)
我有时会有类似的填充。对我来说,用FRP编程的经验主要是解决谜题。其中一些很容易,一些不容易。那些我觉得容易的人可能会比同事更难,反之亦然。关于FRP,我不喜欢这样。
不要误会我的意思,我喜欢解决难题,这很有趣!但我认为付费工作的编程应该更加......无聊。更可预测。代码应该尽可能简单,甚至是原始代码。
但当然全球可变状态也不是我们应该采取的方式。我认为我们应该找到一种让FRP更无聊的方法:)
同样是对您的代码的评论,我认为这将是更多的FRP' ish(未经测试的草案):
var otherPlayerPositions = players
.filter(nEq(me))
.map(function(player){return player.position});
otherPlayerPositions = Bacon.combineAsArray(otherPlayerPositions);
me.position = otherPlayerPositions
.sampledBy(movement, function(otherPositions, move) {
return {otherPositions: otherPositions, move: move};
})
.scan(initPos, function(myCurPosition, restArgs) {
var myNextPosition = myCurPosition + restArgs.move;
if (!restArgs.otherPositions.some(eq(myNextPosition))) {
return myNextPosition;
} else {
return myCurPosition;
}
});