您需要能夠對跳躍的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
,但產生衝動的事件,這些衝動很容易在像光澤一樣的時鐘框架中生成,並且易於與引起衝動的其他事件混合使用,如跳躍。
{-# 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軸的座標系,向上方向(與加速度相反)。
最後的代碼是一個小型的框架,將反應香蕉掛到光澤。
您的診斷是正確的,您甚至可以在沒有看到它的情況下猜測我的代碼出了什麼問題。難以置信的答案! – mcjohnalds45