不好问题:
有没有办法在Elm中定义一对相互依赖的信号?
前言:
我正在尝试编写一个小型的Cookie-clicker风格的浏览器游戏,其中玩家正在收集资源,然后花费他们购买自动资源收集结构,这些结构在购买时会变得更加昂贵.这意味着三个相关的信号:( gathered玩家收集spent了多少资源),(玩家已经花费了多少资源)和cost(升级费用多少).
这是一个实现:
module Test where
import Mouse
import Time
port gather : Signal Bool
port build : Signal String
costIncrement = constant 50
cost = foldp (+) 0 <| keepWhen canAfford 0 <| sampleOn build costIncrement
nextCost = lift2 (+) cost costIncrement
spent = foldp (+) 0 <| merges [ sampleOn build cost ]
gathered = foldp (+) 0 <| merges [ sampleOn gather <| constant 1, sampleOn tick tickIncrement ]
balance = lift round <| lift2 (-) gathered spent
canAfford = lift2 (>) balance <| lift round nextCost
tickIncrement = foldp (+) 0 <| sampleOn cost <| constant 0.01
tick = sampleOn (every Time.millisecond) <| constant True
main = lift (flow down) <| combine [ lift asText balance, lift asText canAfford, lift asText spent, lift asText gathered, lift asText nextCost ]
Run Code Online (Sandbox Code Playgroud)
这编译得很好,但是当我将它嵌入一个HTML文件中时,连接了相应的按钮以将消息发送到上面的相应端口,我得到了错误
s2 is undefined
Open the developer console for more details.
Run Code Online (Sandbox Code Playgroud)
问题似乎是书面的,cost取决于canAfford,取决于balance,取决于spent,这取决于cost再次.
如果我修改成本线那么
...
cost = foldp (+) 0 <| sampleOn build costIncrement
...
Run Code Online (Sandbox Code Playgroud)
它开始按预期工作(除了玩家被允许花费在负资源上,这是我想要避免的).
有任何想法吗?
Apa*_*hka 20
不,在Elm中没有通用的方法来定义相互递归的信号.
问题在于Signal榆树中的a 必须始终具有值的约束.如果定义cost要求canAfford,但canAfford在以下方面被定义cost,问题是从哪里开始解决信号的初始值.当您考虑相互递归信号时,这是一个难以解决的问题.
相互递归信号与过去的信号值有关.该foldp构造允许您指定相对于一个点的相互递归信号的等价物.初始值问题的解决方案通过使用显式参数来解决,foldp即初始值.但约束是foldp只采用纯函数.
这个问题很难以不需要任何先验知识的方式清楚地解释.所以这是另一种解释,基于我对你的代码做的图表.

花点时间找到代码和图表之间的联系(注意我省略了main简化图表).A foldp是一个带回路的节点,sampleOn有一个闪电等等(我重写sampleOn了一个恒定的信号always).问题部分是红线上升,canAfford在定义中使用cost.
如您所见,basic foldp具有一个带有基值的简单循环.实现这个比像你这样的任意循环更容易.
我希望你现在明白这个问题.限制在榆树,这不是你的错.
我正在解决Elm中的这个限制,尽管这需要一些时间.
虽然命名信号并与之合作可能会很好,但在Elm中实现游戏时,通常有助于使用不同的编程风格.链接文章中的想法归结为将代码拆分为:
Mouse,Time和端口在你的情况.cost,balance,canAfford,spent,gathered等.通过使用类似的东西将它们组合在一起main = view <~ foldp update modelStartValues inputs.
特别是,我会写它像:
import Mouse
import Time
-- Constants
costInc = 50
tickIncStep = 0.01
gatherAmount = 1
-- Inputs
port gather : Signal Bool
port build : Signal String
tick = (always True) <~ (every Time.millisecond)
data Input = Build String | Gather Bool | Tick Bool
inputs = merges [ Build <~ build
, Gather <~ gather
, Tick <~ tick
]
-- Model
type GameState = { cost : Float
, spent : Float
, gathered : Float
, tickIncrement : Float
}
gameState = GameState 0 0 0 0
-- Update
balance {gathered, spent} = round (gathered - spent)
nextCost {cost} = cost + costInc
canAfford gameSt = balance gameSt > round (nextCost gameSt)
newCost input gameSt =
case input of
Build _ ->
if canAfford gameSt
then gameSt.cost + costInc
else gameSt.cost
_ -> gameSt.cost
newSpent input {spent, cost} =
case input of
Build _ -> spent + cost
_ -> spent
newGathered input {gathered, tickIncrement} =
case input of
Gather _ -> gathered + gatherAmount
Tick _ -> gathered + tickIncrement
_ -> gathered
newTickIncrement input {tickIncrement} =
case input of
Tick _ -> tickIncrement + tickIncStep
_ -> tickIncrement
update input gameSt = GameState (newCost input gameSt)
(newSpent input gameSt)
(newGathered input gameSt)
(newTickIncrement input gameSt)
-- View
view gameSt =
flow down <|
map ((|>) gameSt)
[ asText . balance
, asText . canAfford
, asText . .spent
, asText . .gathered
, asText . nextCost ]
-- Main
main = view <~ foldp update gameState inputs
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
677 次 |
| 最近记录: |