Skip to content

Iteration 11

Sami Hangaslammi edited this page Apr 5, 2011 · 5 revisions

Iteration 11: Pew Pew

Previous: Iteration 10: Collisions

The source code for this iteration can be found here.

All the changes from the previous iteration can be viewed in diff format here.

Bullets

Since the game is about destroying asteroids, it's about time we start implementing the shooting mechanics. We'll begin with a new data type for the bullets.

Haskeroids/Bullet.hs

bulletSpeed = 10.0
bulletLine = LineSegment ((0,0),(0,bulletSpeed))

data Bullet = Bullet Body

instance LineRenderable Bullet where
    interpolatedLines f (Bullet b) = [transform b' bulletLine]
        where b' = interpolatedBody f b

We leverage the Body functionality again, so the code for bullets has a very similar structure with Player and Asteroid. The visual presentation for a bullet is just a single pointing towards the bullet's movement direction.

The following utility function is used for creating new bullets from the player's ship.

-- | Initialize a new bullet with the given position and direction
initBullet :: Vec2 -> Float -> Bullet
initBullet pos angle = Bullet $ Body (pos' /+/ vel) angle vel 0 pos' angle
    where vel  = polar bulletSpeed angle
          pos' = pos /+/ (polar 12.0 angle)

And finally a simple update function, which just uses the familiar updateBody function, just like all our moving objects.

-- | Update a bullet to a new position
updateBullet :: Bullet -> Bullet
updateBullet (Bullet b) = Bullet $ updateBody b

Firing Logic

We'll use the space bar to shoot.

Haskeroids/Controls.hs

shoot     = Char ' '

This is the first occasion where we want to dynamically create new objects for our state, and for now we'll settle for a very simple solution instead of a more generic one. Since the player can only fire a single bullet during any one game tick, we embed the information directly to player data.

Haskeroids/Player.hs

-- | Data type for tracking current player state
data Player = Player {
    playerBody   :: Body,
    playerAlive  :: Bool,
    playerBullet :: Maybe Bullet
    }

The type is Maybe Bullet, so it'll be Nothing if no bullet was fired and Just Bullet otherwise. This is set in the player's tick function:

instance Tickable Player where
    tick _  p@(Player _ False _) = p
    tick kb p@(Player b _ _) = p {
            playerBody   = updatePlayerBody turn acc b,
            playerBullet = bullet }
        where turn | key turnLeft  = -0.18
                   | key turnRight = 0.18
                   | otherwise     = 0

              acc | key thrust = 0.7
                  | otherwise  = 0

              bullet | key shoot = Just $ initBullet (bodyPos b) (bodyAngle b)
                     | otherwise = Nothing

              key = isKeyDown kb

Tracking Bullets in Game State

Just like with asteroids, we add a list of bullets to the GameState data structure.

Haskeroids/State.hs

-- | Data type for tracking game state
data GameState = GameState {
    statePlayer    :: Player,
    stateAsteroids :: [Asteroid],
    stateBullets   :: [Bullet]
    }

Bullets are rendered with the state just like asteroids.

instance LineRenderable GameState where
    interpolatedLines f (GameState p a b) = plines ++ alines ++ blines
        where plines = interpolatedLines f p
              alines = concatMap (interpolatedLines f) a
              blines = concatMap (interpolatedLines f) b

In the tick function of State, we update each bullet and add a possible new bullet, if one exists.

-- | Tick state into a new game state
tickState :: Keyboard -> GameState -> GameState
tickState kb s@(GameState pl a b) = s {
    statePlayer    = collidePlayer p' a',
    stateAsteroids = a',
    stateBullets   = b'
    }
    where  p' = tick kb pl
           a' = map updateAsteroid a
           b' = map updateBullet $ newBullets
           newBullets = case (playerBullet p') of
                Nothing -> b
                Just x  -> x:b

Now we have the basics down, but of course, there are still several issues that we need to take care of in the few following iterations.

Next: Iteration 12: Bullet Limits