2013-12-19 57 views
1

考慮以下幾點:如何讓玩家跳躍(設定速度)?

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速度)時,跳事件到達?

回答

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,但產生衝動的事件,這些衝動很容易在像光澤一樣的時鐘框架中生成,並且易於與引起衝動的其他事件混合使用,如跳躍。

{-# 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軸的座標系,向上方向(與加速度相反)。

最後的代碼是一個小型的框架,將反應香蕉掛到光澤。

+0

您的診斷是正確的,您甚至可以在沒有看到它的情況下猜測我的代碼出了什麼問題。難以置信的答案! – mcjohnalds45

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 

由於某些原因,跳躍的高度總是變化很大,但這可能是與我的時間安排無關的問題。

我不會將此問題標記爲已回答,以防有人想分享更好的問題。