The movie analogy
Here in Stockholm it's been unusually hot and dry for this season of the year and I'm quite convinced that the pharmacies have broken a new record in anti-histamine sales. Last night we were finally blessed with thunder and rain and today the air is cool and nice and the pollen gone.
I've sneezed quite a lot the last couple of weeks but I've also done some coding. My primary focus has been building an animation framework for use in intro, cutscenes and background movements and coding an editor for animating sprites. Ester (Eraserhead animation editor) will be the subject of an upcoming dev log and this dev log will be about the animation framework.
This is an animation demo and not part of the game
Animation framework
The purpose of the animation framework is to ease setting up and running sequences of multiple animations. The need for this arose with my desire to create an animated intro with objects moving in different patterns. But I will also use this framework for pre- and post-fight-animations as well as background animations.
When finished the animation framework will contain:
Support for spritesheet-based animations
Builders for setting up animations by code
Simple script-language for setting up scenes
Loader and parser for script-files
In addition to this, I will probably build an editor to use with the script-language for trying out and previewing animations.
The movie analogy
When designing and naming the building blocks of the framework I've taken a "movie scene"-approach and used a nomenclature found in movie scripts. That gave me following main classes:
Scene
Actor
Action
Animation
"Animation" might not be a name known from movie scripts, but I kept the name to encourage its use outside of the "animated scene" context. As long as you keep track of calling the update- and draw-methods both actors and animations can be used without a scene.
This is a simplified diagram describing the relationships between the classes:
Scene
Think of a scene just the like a scene in a movie or a theater. It's a "room" where something takes place. A scene can have a name, background image and any number of actors. You draw it on the screen by calling its Draw-method.
Background for our demo
Actor
Unlike in a movie or theater, an actor is not only characters but all things living or dead that has it's own image and is separate from the background e.g. character, bullets flying, rising sun.
An actor has a location, it can be visible or hidden, and has a collection of actions to perform that can be looped when done. An actor also has an animation as it's current "gesture".
Action Just like in the movies, an action is something an actor does, i.e. an actor will act according to its actions.
Some of the available actions are:
Show - draw animation
Hide - don't draw animation
SetPosition - set position of actor
BasicMove - move actor to destination with given velocity and acceleration
ChangeGesture - change animation
Animation
An animation is based on a spritesheet, start index in the sheet and a frame count. This determines how the actor will appear on the screen.
A note on naming. The property for the animation is named Gesture in the Actor-class, that is a choice I made to keep the movie analogy consistent. I've named the class Animation to encourage use of it outside of the "animated scene"-context.
Our famous actor doing one of it's gestures
How to
To create the scene in the demo above following steps have to be made:
Load content
Create an animation sheet configuration
Create an animation factory
Create an actor
Create the scene
Start the scene
Draw scene
Step 1 - 5 can all be done in the Initialize-method of the Game-class.
Step 1 - Load content
As a first step we load background- and spritesheet-images as textures.
var background = Content.Load<Texture2D>("Animation_demo_background"); var texture = Content.Load<Texture2D>("Animation_demo_spritesheet");
Step 2 - Create animation sheet configuration
Then we create a configuration describing animations found in the spritesheet. This object will later be used as argument to our animation factory.
var sheetConf = AnimSheetConfigBuilder .Begin() .Name("Samurai gestures") .GridSize(new Point(13, 4)) .SpriteSize(new Point(160, 160)) .DefaultFrameDuration(150) .AddAnimation("Idle", new Point(0, 0), 6) .AddAnimation("Bow", new Point(0, 3), 11) .AddAnimation("Draw", new Point(0, 2), 13) .AddAnimation("Walk wo sword", new Point(0, 1), 8) .AddAnimation("Walk w sword", new Point(0, 4), 8) .Build();
We create a configuration describing a spritesheet with a size of 13 columns and 4 rows where each sprite has a size of 160 x 160 pixels. The spritesheet is called "Samurai gestures" and default frame duration for all animations in this sheet is 150 milliseconds. It contains four different animations. Note that all names must be unique.
Step 3 - Create animation factory
When the sheet config is ready this step is easy. Call the AnimationFactory-constructor passing in the spritesheet texture and the sheet configuration. Our factory is ready.
var animFactory = new AnimationFactory(texture, sheetConf);
Step 4 - Create actor
Just as it takes some time for an actor to prepare for a big movie role, it takes some coding for us to set up the actor for our scene.
var actor = ActorBuilder .Begin(animFactory) .Actions( actionBuilder => { return actionBuilder .Hide() .SetPosition(new Point(-120, -4)) .ChangeAnimation("Walk wo sword") .LoopAnimation() .Show() .Move(new Point(-60, -4), 0.1f, 0.0f) .ChangeAnimation("Bow") .WaitForAnimation() .ChangeAnimation("Walk wo sword") .LoopAnimation() .Move(new Point(110, -4), 0.1f, 0.0f) .ChangeAnimation("Draw") .WaitForAnimation() .ChangeAnimation("Idle") .WaitForAnimation() .ChangeAnimation("Walk w sword") .LoopAnimation() .Move(new Point(312, -4), 0.1f, 0.0f) .Build(); }) .Build(); actor.Loop = true;
Here we use the ActorBuilder in combination with the ActionBuilder to create the actor and the collection of actions to perform. All these actions will be performed in sequence and when done the actions will, thanks to the "actor.Loop = true;" statement, be restarted.
Step 5 - Create scene
As a last building step we tie everything together by creating our scene, and for this, we also have a dedicated builder.
_scene = SceneBuilder .CreateScene(animFactory) .Name("Demo") .Background(background) .AddActor(actor) .Build();
Our scene is now ready.
Step 6 - Start scene
If you run the project you'll find that nothing happens. That's because we haven't included the scene in the game loop yet.
Add following lines to the Update-method:
if (_scene.State == State.NotStarted) _scene.Start();
_scene.Update(gameTime);
Step 6 - Start scene
Still, nothing happens. It's because we're still not drawing the scene. And following line to the Draw-method:
_scene.Draw(_spriteBatch, Vector2.Zero); Run the project and enjoy!
The future
You're as always more than welcome to download the code and use it in any way you like, but since it's still early days, please regard it more as inspiration than a working framework. I'm sure there are lots of bugs. And changes will come.
If not discouraged, visit my BitBucket-account and get going, or wait for an announcement of a more stable version.
Please visit Eraserhead Studio for more.
Happy coding! /jan.
NOTE. As always, everything I publish here or on any other site is work in progress and subject to change.