2013-12-19 4 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 

Как сделать скачок игрока (возможно, установив его у скорости), когда приходит событие прыжок?

ответ

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 

По какой-то причине высота прыжка всегда изменяется совсем немного, но это, вероятно, не связанная с проблемой делать с моим временем вещей.

Я не стану отмечать этот вопрос так же, как и на тот момент, если кто-то хочет поделиться лучшим.

Смежные вопросы