Writing a game with F# and RxNA

Intro
I wanted to test my hand on writing a short F# program and using functional
reactive programming in it. Because lot of this would be new to me, I
deliberately chose to write a simple shooter game.

F# is functional programming language on .Net platform. It can integrate with
existing .Net code easily and has decent tooling with Visual Studio. Some of
the features I really like are immutability and type inference. Immutability
means that I don’t have to worry that much about global state of the system
while working with some detail. Type inference means that I don’t have to
specify types all the time, but the compiler can figure them out from the
context (usually, sometimes it does need a little bit help).

Rx (or Reactive Extensions) is quite neat library for composing programs using
Observables, queries and schedulers. I found that by using Rx, my program
naturally fell into small pieces or streams that communicated with each other.
When ever something changed, all the dependent things reacted too.

MonoGame is an open source library for cross-platform game development. The
API is really close to now defunct XNA and there are plenty of resources out
there, so finding help or information usually isn’t particularly difficult.

RxNA is an open source library that adds a set of Observables that are suited
in interacting with MonoGame.

Overview
Since I’m using MonoGame, I need to have a class that inherits
Microsoft.Xna.Framework.Game. This is the main hub for the program and the
location where our code and MonoGame framework mostly interacts. Four main
methods are Initialize, LoadContent, Update and Draw.

In initialize, the program first sets up the graphics setting and then
constructs various streams and subscribes listeners to them. This
creates a (mostly) directed graph of how data will flow through the
program and what functions are responsible to reacting them. We will go over
the details later in the post.

LoadContent simply just loads graphics and sound resources from disk and
creates a RenderResources record to hold them. This is one of the few
mutable data structures in the system.

MonoGame will call Update and Draw methods when ever it has decided that the
system should update its internal state or render something on the screen.
Depending on the settings, the frequency and order of how this is done varies.
For our point of view, these are the locations where new data is fed into the
streams. New data causes the system to update its state or render the state on
screen.

Dependency injection
A word about dependency injection: it’s perfectly viable to do dependency
injection with functions alone. F# supports partial application, so I can
call a function with fewer parameters than it requires. The return value is a
new function that accepts parameters that weren’t supplied. For this reason,
I usually have resources and other dependencies first in parameter list.

Common example shown in code is how update functions are registered in streams:

let powerUpStream = gameRunningTimeStream
|> Observable.zip deadEnemies
|> Observable.scanInit (initialPowerUps renderResources)
                       (powerUpUpdater renderResources rng playerPowerUpCollisions)
|> Observable.publish

powerUpUpdater function has need for renderResources, random number generator
and info about player-powerup – collisions. Observable.scanInit however,
requires a function that takes previous state, input and produces a new state.
By calling powerUpUpdater with only resources it needs, we create a new
function that satisfies signature requirement of scanInit. When scanInit calls
this new function, all those resources we passed in earlier are at our
disposal.

I could have had random number generator and other things declared global and
directly accessed them from the functions. I chose against this, since I
wanted to understand where these things are used and keep some control on
where they could be used.

note:
What’s with all those |> and <| operators? They are used to pass result of one
side to other side. In case of powerUpStream, it could have been written as:

let powerUpStream = Observable.publish
(Observable.scanInit (initialPowerUps renderResources)
 (powerUpUpdater renderResources rng playerPowerUpCollisions)
 (Observable.zip deadEnemies gameRunningTimeStream))

That’s a mouthful and would require one to start reading from the last line
and then proceed towards the beginning to get the whole story.
So a |> f is same as f a.

Types
Most of the types are specified in one location, while functions that are used
to manipulate them are divided according to their usage. F# requires things to
be defined before they can be used. This is true both inside a file, but also
in the project and is the reason why order of files is important. While
sometimes annoying, I found in the end that it didn’t slow down development.
Resulting cleaner dependency graph probably makes difference in a large
project, but in such a small system as the game is, it doesn’t make much
difference.

Types used in game are fairly conservatives. Mostly records and discriminated
unions. Discriminated unions are like enum types on steroids. For example:

type NormalPlayerInfo = { location: Location
                          speed: Speed
                          texture: Texture
                          lives: int }
                          member this.Location = this.location
                          member this.Texture = this.texture

type ExplodingPlayerInfo = { location: Location
                             speed: Speed
                             explosionTime: float
                             lives: int }

type Player =
   | NormalPlayer of NormalPlayerInfo
   | ExplodingPlayer of ExplodingPlayerInfo

Player type is discriminated union that can have values
NormalPlayer of NormalPlayerInfo and ExplodingPlayer of ExplodingPlayerInfo.
NormalPlayerInfo and ExplodingPlayerInfo in turn are records that have the
data and properties that are important for that given situation. In code I
can pass around Player and pattern match it:

match state with
    | NormalPlayer info -> ... pile of code used to manipulate normal player ...
    | ExplodingPlayer _ -> state

so if state happens to be NormalPlayer, we pull out the assiciated data into
info value and start manipulating it. However, if the player is exploding, we
aren’t interested the data (hence the underscore) and just return the state
unchanged.

Main Rx features used
Game doesn’t use any fancy tools provided by Rx, mainly because it’s really
simple. It’s mostly just mapping (transforming a type of stream to another
type of stream), merging (combining two streams of same type), zipping
(combining streams of different kind), scanning (storing local state in a safe
place) and subscribing call back functions.

Mutating the immutable
So, if the data is (mostly) immutable, how in earth I’m going to have anything
change in the game? The trick is to keep changes local to single place and
everywhere else deal with immutable data. Instead of mutating state of the
start field here and there, there is a single function starUpdater with two
parameters (current state and GameTime) and which returns new state. Every time
when time passes (MonoGame calls Update method, which in turn enters a new
value into gameTimeStream) this function will eventually be called and new
state stored.

We have following snippet of code:

let starStream = menuTimeStream
|> Observable.merge gameRunningTimeStream
|> Observable.scanInit (initialStarField renderResources rng time) starsUpdater
|> Observable.publish

Since stars are supposed to be running both in the main menu and during the
gameplay, we start by merging menuTimeStream and gameRunningTimeStream. The
result is a time stream that receives new values while either menu is being
displayed or the actual gameplay is running. We then register our starsUpdater
function into it with scanInit and also supply the initial state of the star
field. Last step is to publish the stream as IObservable, so that possible
multiple listeners won’t cause us problems.

scanInit is where the beef is. It will hold the initial state for us until
stream receives an update. Then it will call starsUpdater with the state and
GameTime object. The resulting new state will be stored for the future use and
also passed forward in the stream (starsStream in this case). When the next
update comes, starsUpdater is called again with the current state, which gets
replaced by the new state and passed on. And this keeps repeating until stream
ends.

This is the basic building block I built the system on. For every set of
similar entities (stars, player, bullets, enemies, power ups), there is a
stream that holds their state over time and a function that can take that
state, some other inputs and create a new state. Most of these streams are
dependent on other streams and figuring out the order they should be connected
was one of the challenges.

a word about time:
There is no guarantee that MonoGame will call Update at fixed interval.
However, MonoGame is kindly enough to include GameTime object with each of the
calls that tells us exactly how long the game has been running and how long
has passed since the last update. When we know this, it is easy enough to
calculate how much something should move in this frame. Since the calculation
is common and repeated all over the place, I created a helper function
timeCoeff that does exactly this calculation.

Enemies, lots of them (like 2 different kinds)
No shooter is complete without enemies to shoot at. Data type for enemies is
pretty simple (pay attention to Location and Texture properties, we are going
to use those in the next step too):

type Enemy = { location: Location
               speed: Speed
               texture: Texture
               hp: int
               score: int }
             member this.Location = this.location
             member this.Texture = this.texture

Enemies in the game are pretty simple. They start from one edge of the screen
and move at constant speed across. If they collide with player, their ship is
destroyed and it’s game over. If a bullet collide with them, hp of enemy is
reduced and eventually enemy is destroyed. If they make it across the screen,
they just spawn somewhere else.

But in this simplicity there was pretty complicated problem (well, initially
at least): player needs enemy information to calculate possible collision,
bullets need player information to know where to spawn and enemies need to
know bullet information to calculate collisions and reduce health. So it’s a
circle. If I just wired the streams together like that, I would get an
infinite loop (not to mention that it would be a bit tricky with how F#
requires things to be declared before they can be referenced).

The solution was to declare a new stream and use that:

let enemyBulletCollisions = new BehaviorSubject<BulletCollisionInfo list>([])

BehaviorSubject is a special kind of subject that remembers its last state
and that can be queried with Value property. This way bulletsUpdater can feed
new values into the stream and enemiesUpdater can check it with Value
property. No need to merge or zip streams and inifinite loop has been avoided.
It probably is a good idea to avoid using BehaviorSubjects, since they can
be easily abused as global variables that can be changed from anywhere. The
game has total four of them (not counting the ones used for keeping track of
time, rendering or controls): two for collisions, one for dead enemies and one
for triggering sounds. Collisions are for avoiding circular streams, while
others are for branching things (I wonder if there are easier way doing that).

Collisions
Collisions is one part of the system that I didn’t finish anywhere close to
what I wanted it to be. There was supposed to be bounding spheres around each
objects and when they collided, the game would start checking with more
precision if the objects actually collided. Currently there’s only bounding
spheres, but they seems to work okayish most of the time.

Actual collision function is pretty short (this is the version using
that was supposed to use pixelPerfectCollision, the actual code just return!s
result of boundingSphereCollision:

let inline collision gameTime mob1 mob2 =
    maybe {
            let! coll1 = boundingSphereCollision gameTime mob1 mob2
            return! pixelPerfectCollision mob1 mob2
    }

We need gameTime, since state of animation depends on time and animation can
have effect on the size and shape of the texture. I’m using maybe builder here
to chain together two operations. First it checks if bounding spheres of two
objects collide. If the result is Some, it then checks for the pixel perfect
collision and returns result of that. However, if result of bounding sphere
collision is None, maybe builder automatically shortcuts and returns None.
It’s a very simple use case, but in more complex cases where multiple
operations are chained together, maybe builder can be pretty handy.

But why is the function marked as inline? And what types are we passing in?
The answer is found in boundingSphereCollision:

let inline boundingSphereCollision gameTime (mob1:^a) (mob2:^b) =
    let location1 = (^a : (member Location : Location) mob1)
    let location2 = (^b : (member Location : Location) mob2)
    let texture1 = currentFrame gameTime (^a : (member Texture : Texture) mob1)
    let texture2 = currentFrame gameTime (^b : (member Texture : Texture) mob2)
    let dx = location1.x - location2.x 
    let dy = location1.y - location2.y
    let width = (float32)(texture1.Width + texture2.Width) / 2.0f
    let distance = width * width
    if dx*dx + dy*dy <= (float32)distance
        then Some ((location1 + location2) / 2.0f);
        else None

mob1 has type of ^a and mob2 has type of ^b. These are something called type
constraints. Since I’m not using interfaces here (I could, that would work
just as well actually), I’m just saying that ^a and ^b has to be something
that has Location and Texture properties, with correct signature (returning
Location and Texture respectively). Also they don’t have to be of same type.

The compiler will check that types passed in as parameters are satisfying
these constraints and will raise an error if this is not the case. Inline
keyword is needed so the compiler can generate multiple versions of the
function (possibly with different signatures) and inline them where they are
used.

So the end result is a system that works with any types that satisfy certain
constraints, supports multiple collisions checks with differing precisions
(although in game I don’t have pixel perfect collisions) and only performs
minimum amount of checks to detect that objects aren’t colliding. Return type
is Location option that indicates the spot where the collision happened
(handy for placing explosions and other effects there).

Handling multiple inputs
I have an old XBox-controller I wanted to use with the game. At the same time,
I had to support keyboard for those people who didn’t want to use controller.
The easieast way to achieve this was to read state of controller and keyboard
at the beginning of update, map the state to something more reasonable and
merge resulting streams. This way rest of the program didn’t have to care if
it was being controlled by keyboard or controller.

Rendering, how does that work?
All that fancy stuff is no use, if we can’t display it to the player. This is
done every time that MonoGame calls Draw method, which in turn inserts a new
value into renderStream. Since all the streams are kept strictly in sync, I
could have zipped them together and updated the screen every time each and
every stream has produced a value. Since I was unsure that I could actually
manage to do that, I opted merging the streams, keeping the latests state of
each stream and updating the screen only when Update was actually called.

This is done by mapping each and every stream into Frame record, that has
option type for each stream. Every type of stream has to have its own mapping
function that creates a Frame record. There is also a custom operator ++ that
is used in gameRunningRenderer to add new frame to old frame, selecting Some
values for each and every field (remember, gameRunningRenderer is called
every time any of the streams produce a new Frame object that mostly has
None fields and only one or two Some fields).

gameRunningRenderStream
|> Observable.map mapRenderStreamToFrame
|> Observable.merge <| Observable.map mapPlayerToFrame playerStream
|> Observable.merge <| Observable.map mapEnemiesToFrame enemiesStream
|> Observable.merge <| Observable.map mapStarsToFrame starStream
|> Observable.merge <| Observable.map mapBulletsToFrame bulletStream
|> Observable.merge <| Observable.map mapExplosionsToFrame explosionStream
|> Observable.merge <| Observable.map mapPowerUpsToFrame powerUpStream
|> Observable.merge <| Observable.map mapScoreToFrame scoreStream
|> Observable.scanInit initialFrame gameRunningRenderer
|> Observable.subscribe (fun x -> ())
|> ignore

Notice the two last lines. Observable.subscribe (fun x -> ()) is there to
subscribe something into stream, so data starts flowing through it. There
might be a better way doing this, but I didn’t find it. ignore is used to
throw away final IObservable that could be used to dispose this stream.

Rendering is then dispatched to individual render functions, who know how to
render given set of entities. For example in case of stars:

starsRenderer newFrame.starField res time

As you notice, I decided not to pull out value of starField from the option
field, but pass that as is. It makes the main rendering function cleaner. This
on the other hand means, that I need to deal with option at the starsRenderer:

let starsRenderer stars res time =
    Option.iter
    <| List.iter (renderStar res time)
    <| stars

In case stars is Some, Option.iter will call the supplied function with Value
of it (ie. Mob list, not Mob list option), otherwise it simply returns unit
(similar to void in C#). Before I learned about this function, I had to
check for Some in stars and only then render. Not a big difference, but cuts
down typing a bit and looks cleaner.

Originally I had subscribed each and every individual render function to
renderStream, but I wasn’t happy with the solution as I didn’t know how to
reliably enforce correct drawing order.

Animations, with immutable data?
Initially I thought that this would be a tricky thing to do, but like always,
it turned out a lot easier than I feared. Animation doesn’t really need to
mutate in order to change frames. It is enough to know when the animation
started, frames and how long each of them is shown and if the animation should
loop or not. Based on that information and current time the rendering function
can calculate which frame to display. Types for animations are following:

type TextureSpecification =
    | Infinite of texture: Texture2D
    | Timed of texture: Texture2D * duration: float
    | Animation of frames: TextureSpecification list
    | LoopingAnimation of frames: TextureSpecification list

type Texture =
     | SingleFrame of texture: Texture2D * duration: float option
     | MultipleFrames of frames: Texture list * start: float * loop: bool 
     member this.Duration =
        match this with
            | SingleFrame (t, (Some d)) -> d
            | SingleFrame (t, None) -> 0.0
            | MultipleFrames (t, _, _) -> List.map (fun (frame:Texture) -> frame.Duration) t
                                          |> List.max

TextureSpecification is used to store specifications for various animations:
single frame, single shot animation and looping animation. Texture is
something attached to visible entity, it can be either single or multiple
frames and in case of multiple frames also has starting time. It comes with
a handy property to calculate total duration of the animation.

In addition there are convert (create Texture from TextureSpecification and
current time), isFinished (to check if an animation is finished) and
currentFrame (calculating which frame to display). Better name for convert
would probably be createForSpecification or something similar.

In closing
Biggest challenges were in keeping various streams in sync with each other.
It wasn’t once or twice when I had a situation where one of the streams was
producing values at different speed from others, resulting to nasty bugs in
system. This was so tricky with the approach that I had chosen that I
eventually game up the idea of having multiple lives and possibility to
return to main menu. As the game stands now, you have one ship and after it’s
destroyed the game ends and exits (less than ideal solution, I admit).

One of the ideas I was toying with, but didn’t implement was to have all the
streams running all the time. This would ensure they stay in sync. This would
require each stream to check current mode of the game and react accordingly.
This wouldn’t only look ugly, but would require a quite a bit more typing and
boring boiler plate code.

I didn’t plan the program much, simply just started with a star field and
started adding things on top of that. Type inference was quite nice, because
when I changed some type into more specific, I often didn’t have to update
type signatures through the whole call chain, only in places where I was
actually performing some operations with the data.

Code for the game is available at GitHub and prebuilt binaries for Windows at BinTray.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s