Adding animation to the platformer

As it currently stands the platformer we’ve been working on seems pretty bland. When we move around, nothing really happens with our player so let’s add some animation to him. We’ll aim to make it as functional as possible so no side effects.

The first thing we need is a type that reflects all of our associated data for an animation. So I’ll be adding most of this in a new file called PlatformerAnimation.fs which you’ll need to add right at the top of our files list. Then we’ll represent it like follows and add a function to create an animation.

module PlatformerAnimation

open Microsoft.Xna.Framework
open Microsoft.Xna.Framework.Graphics

let FrameWidth = 32
let FrameHeight = 32

type Animation =
 {
 TextureStrip : Texture2D;
 FrameCount : int;
 CurrentFrame : int;
 CurrentTime : int;
 TimePerFrame : int;
 }

let CreateAnimation (texture:Texture2D) frameLength =
 let frameCount = texture.Width / FrameWidth
 { TextureStrip = texture; FrameCount = frameCount; CurrentFrame = 0; CurrentTime = 0; TimePerFrame = frameLength }

Next we need to update it and return an appropriate new state given that we know how long it’s been between updates. All we have to do is check to see how long it’s been since an update and increment the frame counter and then if that’s too high, we set that back to 0.

let UpdateAnimation (gameTime:GameTime) animation =
 let time = animation.CurrentTime + (int gameTime.ElapsedGameTime.TotalMilliseconds)
 let newFrame = if time > animation.TimePerFrame then
 let newFrame' = animation.CurrentFrame + 1
 if newFrame' >= animation.FrameCount then
 0
 else newFrame'
 else
 animation.CurrentFrame
 let counter = if time > animation.TimePerFrame then 0
 else time
 { animation with CurrentFrame = newFrame; CurrentTime = counter; }

We’ll also need a way of drawing the animation so let’s create that. We need a few other details though, specifically the SpriteBatch we’ll be using to draw it and also the position of it.

let DrawAnimation (spriteBatch:SpriteBatch) animation (position:Vector2) =
 let rect = System.Nullable(Rectangle(animation.CurrentFrame * FrameWidth, 0, FrameWidth, FrameHeight))
 spriteBatch.Draw(animation.TextureStrip, position, rect, Color.White)

Let’s change the signature of our WorldActor as well so that everything has an animation rather than a static texture. We can do this because a static texture can be represented by an animation with a single frame. We’ll also need to add a function to update the actor’s animation.

type WorldActor =
 {
 ActorType : ActorType;
 Position : Vector2;
 Size : Vector2;
 Animation : Animation option;
 BodyType : BodyType
 }
let UpdateActorAnimation gameTime (actor:WorldActor) =
 let animation = if actor.Animation.IsSome then
 Some(UpdateAnimation gameTime actor.Animation.Value)
 else None
 { actor with Animation = animation }

Finally we need to update our PlatformerGame file. We need to change the Update function to now handle the new Update function so we’ll chain that onto the end. We also need to change our DrawActor function to now use the animation drawing.

let DrawActor (sb:SpriteBatch) actor =
 if actor.Animation.IsSome then
 do DrawAnimation sb actor.Animation.Value actor.Position
 ()

override x.Update (gameTime) =
 let AddGravity' = AddGravity gameTime
 let HandleInput' = HandleInput (Keyboard.GetState ())
 let UpdateActorAnimation' = UpdateActorAnimation gameTime
 let current = WorldObjects.Value
 do WorldObjects <- lazy (current
 |> List.map HandleInput'
 |> List.map AddGravity'
 |> List.map AddFriction
 |> HandleCollisions
 |> List.map ResolveVelocities
 |> List.map UpdateActorAnimation')
 do WorldObjects.Force () |> ignore
 ()

To test this, I’ve added an animated sprite to the GitHub repository. Also you can get the final version of this code here.

One comment

Leave a comment