鉴于以下内容:
integralB :: Num a => Behavior t a -> Behavior t a -- definite integral of a behaviour
eJump :: Event t a -- tells the player to jump
bYAccel = pure 4000 -- y acceleration
bYVel = integralB bYAccel -- y velocity
bY = integralB bYVel -- y position
当跳跃事件到来时,如何让玩家跳跃(可能通过设置y速度)?
答案 0 :(得分:2)
你需要能够对跳跃的Y速度施加冲动。从你自己的答案中,你已经想出了一种方法,通过总结跳跃中的所有脉冲并将它们加到加速度的积分中。
你的加速度也是不变的。如果你不希望玩家不断下降,你需要这样的东西:
bYAccel = (ifB airborne) 4000 0
airborne = fmap (>0) bY
ifB :: Behavior t Bool -> a -> a -> Behavior t a
ifB boolBehavior yes no = fmap (\bool -> if bool then yes else no) boolBehavior
跳跃高度变化的一个可能原因是当玩家降落时你没有重置速度。如果你有规则让玩家高于某个位置(如地板),并且当玩家击中地板时以某种方式停止加速,那么如果它在地板方向上,你还需要将速度设置为0。 (如果你不在地板方向时将它设置为0,则玩家永远无法让速度离开地面。)
这会导致跳跃高度不稳定的原因是,当玩家着陆时的最终速度将接近你申请他们起飞的冲动。使用你的数字,如果以-5000的速度开始跳跃,并以4800的速度结束,下一次跳跃将增加-5000的脉冲,使跳跃的起始速度仅为-200。那可能有300的结束速度,所以下一次跳跃将是一个几乎完全-4700跳跃。
这是一个完整的工作示例。它使用光泽库进行输入和显示。 gameDefinition
对应于您问题中引入的组件。 integrateDeltas
等同于你的integralB
,但会产生冲动的事件,这些事件很容易在像光泽这样的时钟框架中生成,并且易于与其他导致冲动的事件混合使用,例如跳跃。 / p>
{-# LANGUAGE RankNTypes #-}
module Main where
import Reactive.Banana
import Reactive.Banana.Frameworks.AddHandler
import Reactive.Banana.Frameworks
import Data.IORef
import qualified Graphics.Gloss.Interface.IO.Game as Gloss
gameDefinition :: GlossGameEvents t -> Behavior t Gloss.Picture
gameDefinition events = renderBehavior
where
bY = accumB 0 (fmap sumIfPositive yShifts)
yShifts = integrateDeltas bYVel
bYVel = accumB 0 yVelChanges
yVelChanges = apply ((ifB airborne) (+) sumIfPositive) yVelShifts
yVelShifts = union (integrateDeltas bYAccel) (fmap (const 3) eJump)
bYAccel = (ifB airborne) (-10) 0
airborne = fmap (>0) bY
eJump = filterE isKeyEvent (event events)
integrateDeltas = integrateDeltaByTimeStep (timeStep events)
renderBehavior = (liftA3 render) bY bYVel bYAccel
render y yVel yAccel =
Gloss.Pictures [
Gloss.Translate 0 (20+y*100) (Gloss.Circle 20),
Gloss.Translate (-50) (-20) (readableText (show y)),
Gloss.Translate (-50) (-40) (readableText (show yVel)),
Gloss.Translate (-50) (-60) (readableText (show yAccel))
]
readableText = (Gloss.Scale 0.1 0.1) . Gloss.Text
-- Utilities
sumIfPositive :: (Ord n, Num n) => n -> n -> n
sumIfPositive x y = max 0 (x + y)
ifB :: Behavior t Bool -> a -> a -> Behavior t a
ifB boolBehavior yes no = fmap (\bool -> if bool then yes else no) boolBehavior
integrateDeltaByTimeStep :: (Num n) => Event t n -> Behavior t n -> Event t n
integrateDeltaByTimeStep timeStep derivative = apply (fmap (*) derivative) timeStep
isKeyEvent :: Gloss.Event -> Bool
isKeyEvent (Gloss.EventKey _ _ _ _) = True
isKeyEvent _ = False
-- Main loop to run it
main :: IO ()
main = do
reactiveGame (Gloss.InWindow "Reactive Game Example" (400, 400) (10, 10))
Gloss.white
100
gameDefinition
-- Reactive gloss game
data GlossGameEvents t = GlossGameEvents {
event :: Event t Gloss.Event,
timeStep :: Event t Float
}
makeReactiveGameNetwork :: Frameworks t
=> IORef Gloss.Picture
-> AddHandler Gloss.Event
-> AddHandler Float
-> (forall t. GlossGameEvents t -> Behavior t Gloss.Picture)
-> Moment t ()
makeReactiveGameNetwork latestFrame glossEvent glossTime game = do
eventEvent <- fromAddHandler glossEvent
timeStepEvent <- fromAddHandler glossTime
let
events = GlossGameEvents { event = eventEvent, timeStep = timeStepEvent }
pictureBehavior = game events
pictureChanges <- changes pictureBehavior
reactimate (fmap (writeIORef latestFrame) pictureChanges)
reactiveGame :: Gloss.Display
-> Gloss.Color
-> Int
-> (forall t. GlossGameEvents t -> Behavior t Gloss.Picture)
-> IO ()
reactiveGame display color steps game = do
latestFrame <- newIORef Gloss.Blank
(glossEvent, fireGlossEvent) <- newAddHandler
(glossTime, addGlossTime) <- newAddHandler
network <- compile (makeReactiveGameNetwork latestFrame glossEvent glossTime game)
actuate network
Gloss.playIO
display
color
steps
()
(\world -> readIORef latestFrame)
(\event world -> fireGlossEvent event)
(\time world -> addGlossTime time)
在此示例中,bY
通过累积脉冲来检查与0的楼层的冲突,但是将累计值约束为0以上。
速度bYVel
在空中传播时累积所有脉冲,但只有那些在没有空降时被引导离开地面的冲动。如果你改变了
yVelChanges = apply ((ifB airborne) (+) sumIfPositive) yVelShifts
到
yVelChanges = fmap (+) yVelShifts
它重现了不稳定的跳跃错误。
加速度bYAccel
仅在空降时出现。
我在向上方向(与加速度相反)使用了一个+ Y轴的坐标系。
最后的代码是一个小型框架,用于将反应性香蕉挂到光泽上。
答案 1 :(得分:0)
解决了!我之前没有想到这一点感觉有点傻,但我只是在每个eJump上增加一个计数器并将该计数器添加到bYVel。
bJumpVel = sumB $ (-5000) <$ eJump
bYVel = (+) <$> bJumpVel <*> integralB bYAccel
-- gives the sum of the events
sumB :: Num a => Event t a -> Behavior t a
sumB e = accumB 0 $ (+) <$> e
由于某些原因,跳跃的高度总是变化很大,但这可能与我的时间安排无关。
如果有人想要分享更好的问题,我不会将此问题标记为已回答。