XNA Tutorials, Samples and Thoughts
ToonBoom
Loading ToonBoom animations in XNA
Aug 29th
Last time, I talked about my experience with drawing and animating 2D images with ToonBoom. In the end of that post, I said that one way to load ToonBoom animations in XNA was to export the animation as a sequence of images, and then load these images and animate them in XNA. So today we will look at a quick way to to just that.
The exporting part is easy enough: just user File->Export Movie in ToonBoom, and then select Image Sequence as the Export Format. The result will be a collection of images, depending on the framerate you choose. The result from my cat animation can be seen in this archive.
The next step is assembling all the images in a single image, usually called a sprite-sheet. Of course, you could also simply load all the images as they are in several Texture2D variables, but working with sprite-sheets yields better performance. For the creation of the sprite-sheet, there are several options.
The first one is to open an image processing application, load all small images, and assemble them in a single image manually. You can imagine this is time consuming, and only worthy if you plan on doing this operation for a very small number of sprites.
The second option, which I used for this sample, is to write a small program that takes the images as input, and generates the sprite-sheet as you want it. My version of this was quickly hacked in a few minutes, so you can see from the source code (get it here) that it makes several assumptions:
- all input images are the same size, and are rectangular
- the output sprite will contain all input sprites arranged on a single row
- the input files are all named similarly (cat1.png, cat2.png, …, cat14.png)
While definitely not an elegant solution, it did it’s job, and I was quickly able to obtain the following image (click for larger size):
The third option, which is the recommended one for a larger project, is to use a more complex program for creating sprite-sheets. You can either create one yourself, based on certain file formats you want to use internally, or around certain restrictions you want to impose on your engine; or you can use an existing one, such as Ziggyware’s Sprite Sheet Creator. Note that if you go for this option, you’ll have to follow certain conventions in your runtime code, imposed by your program used for creating the sprite-sheets.
That being said, we now have a sprite sheet with all the frames of the walking cat animation, and are ready to load this in XNA.
After creating a new XNA project, I read through Nick’s article about Sprite Sheet Animations, and created the following class to hold all information needed for animations. For a detailed explanation, go ahead and read Nick’s article.
1: class Animation
2: {
3: Rectangle[] frames;
4: float frameLength = 1f / 5f;
5: float timer = 0f;
6: int currentFrame = 0;
7:
8: /// <summary>
9: /// Gets of sets the FPS of the animation
10: /// </summary>
11: public int FramesPerSecond
12: {
13: get { return (int)(1f / frameLength); }
14: set { frameLength = 1f / (float)value; }
15: }
16:
17: /// <summary>
18: /// Gets the Rectangle containing the current frame of animation
19: /// </summary>
20: public Rectangle CurrentFrame
21: {
22: get { return frames[currentFrame]; }
23: }
24:
25: /// <summary>
26: /// Creates an animation object
27: /// </summary>
28: /// <param name="width"> the total width of the input image</param>
29: /// <param name="height"> the height of the input image</param>
30: /// <param name="numFrames"> the number of frames in the sprite-sheet</param>
31: /// <param name="xOffset"> the X origin of the sprite-sheet</param>
32: /// <param name="yOffset"> the Y origin of the sprite-sheet</param>
33: public Animation(int width, int height, int numFrames, int xOffset, int yOffset)
34: {
35: frames = new Rectangle[numFrames];
36: int frameWidth = width / numFrames;
37: for (int i = 0; i < numFrames; i++)
38: {
39: frames[i] = new Rectangle(xOffset + (frameWidth * i), yOffset,
40: frameWidth, height);
41: }
42: }
43:
44: /// <summary>
45: /// update the animation
46: /// </summary>
47: /// <param name="elapsed"> seconds since the last frame</param>
48: public void Update(float elapsed)
49: {
50: timer += elapsed;
51:
52: if (timer >= frameLength)
53: {
54: timer = 0f;
55: currentFrame = (currentFrame + 1) % frames.Length;
56: }
57: }
58:
59: /// <summary>
60: /// resets the animation
61: /// </summary>
62: public void Reset()
63: {
64: currentFrame = 0;
65: timer = 0f;
66: }
67:
68: }
Next, I created a class for the cat, holding a few members we need for the animation.
1: class Cat
2: {
3: Texture2D texture; //sprite-sheet containing the cat animation
4: Animation walkingAnimation; //animation object used for animating
5: Vector2 velocity; //movement direction
6: float movementSpeed; //movement speed
7: Vector2 origin; //origin of the image
8: bool isMirrored = false; //draw the image mirrored
9:
10: public Vector2 Position { get; set; } //position on the screen
Nothing really special here… Maybe except the isMirrored variable. We use this because our image for the cat shows it facing left. So when we want it to move towards the right, rather than creating a separate sprite with the cat facing right, we mirror the existing sprite. Of course, you can’t always use this trick (or a right-handed character suddenly becomes left-handed), but there are many cases when this trick is enough.
The constructor is straightforward, with some hard-coded values tweaked until I was satisfied with how things looked.
1: public Cat(Texture2D texture, int frameCount, Vector2 origin)
2: {
3: this.texture = texture;
4: //create a new animation object
5: walkingAnimation = new Animation(texture.Width, texture.Height, frameCount, 0, 0);
6:
7: //tweak the FramesPerSecond and movementSpeed values until you're satisfied with how things move
8: walkingAnimation.FramesPerSecond = 14 * 3;
9: movementSpeed = 256;
10:
11: //reset position
12: Position = Vector2.Zero;
13:
14: this.origin = origin;
15: }
For movement, input from both keyboard and gamepad is analyzed, and the velocity’s value is updated.
1: public void HandleInput()
2: {
3: KeyboardState keyState = Keyboard.GetState();
4: GamePadState gamepadState = GamePad.GetState(PlayerIndex.One);
5:
6: velocity = Vector2.Zero;
7:
8: if (gamepadState.IsConnected)
9: {
10: velocity = gamepadState.ThumbSticks.Left;
11: velocity.Y *= -1;
12: }
13:
14: if (keyState.IsKeyDown(Keys.Left))
15: velocity.X = -1.0f;
16: if (keyState.IsKeyDown(Keys.Right))
17: velocity.X = 1.0f;
18: if (keyState.IsKeyDown(Keys.Up))
19: velocity.Y = -1.0f;
20: if (keyState.IsKeyDown(Keys.Down))
21: velocity.Y = 1.0f;
22: }
In the Update function, we first look which way the cat is facing, and update the value of isMirrored. Then, if the cat is moving, we update the animation and position on the screen.
1: public void Update(GameTime gameTime)
2: {
3:
4: //mirror the cat if we are moving towards the right
5: if (velocity.X > 0.1f)
6: isMirrored = true;
7: if (velocity.X < -0.1f)
8: isMirrored = false;
9:
10: //normalize velocity vector
11: if (velocity.Length() > 1.0f)
12: velocity.Normalize();
13:
14: //get elapsed seconds
15: float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
16:
17: //only animate if we are moving
18: float speed = velocity.Length();
19: if (speed > 0.0f)
20: walkingAnimation.Update(elapsed * speed);
21:
22: //update position
23: Position += velocity * elapsed * movementSpeed;
24: }
Finally, the Draw function takes as a parameter the active spriteBatch, and draws the current animation frame using it. Here we can see how to mirror the sprite using SpriteEffects.FlipHorizontally.
1: public void Draw(SpriteBatch spriteBatch)
2: {
3: SpriteEffects spriteEffect = SpriteEffects.None;
4:
5: //mirror the image if necessary
6: if (isMirrored)
7: spriteEffect = SpriteEffects.FlipHorizontally;
8:
9: spriteBatch.Draw(texture, Position, walkingAnimation.CurrentFrame,
10: Color.White, 0.0f, origin, 1.0f, spriteEffect, 1.0f);
11: }
With this class created, using it in our Game class is trivial. Simply load the texture in LoadContent() and create a Cat object, update it’s input and internal state in Update() and draw it in Draw().
1: public class Game1 : Microsoft.Xna.Framework.Game
2: {
3: GraphicsDeviceManager graphics;
4: SpriteBatch spriteBatch;
5: Cat cat;
6:
7: public Game1()
8: {
9: graphics = new GraphicsDeviceManager(this);
10: Content.RootDirectory = "Content";
11: }
12:
13: protected override void LoadContent()
14: {
15: spriteBatch = new SpriteBatch(GraphicsDevice);
16:
17: //load cat texture
18: Texture2D catTexture = Content.Load<Texture2D>("catWalk");
19:
20: //create cat object
21: cat = new Cat(catTexture,14,new Vector2(64,128));
22:
23: //place it in the center of the screen
24: cat.Position = new Vector2(400, 300);
25: }
26:
27: protected override void Update(GameTime gameTime)
28: {
29: // Allows the game to exit
30: if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
31: this.Exit();
32:
33: //update cat's input
34: cat.HandleInput();
35: //update cat's state
36: cat.Update(gameTime);
37:
38: base.Update(gameTime);
39: }
40:
41: protected override void Draw(GameTime gameTime)
42: {
43: GraphicsDevice.Clear(Color.CornflowerBlue);
44:
45: spriteBatch.Begin();
46: //draw cat
47: cat.Draw(spriteBatch);
48: spriteBatch.End();
49:
50: base.Draw(gameTime);
51: }
52: }
If you run the game now, you’ll be able to move the cat around the screen with the keyboard or the mouse. As you can see, getting an animation from ToonBoom into an XNA game is not difficult at all.
I hope you enjoyed this short post, and if you did, you can download the source for this sample here: ToonBoomAnimationSample.zip
My experience with Toon Boom Studio
Jul 30th
Over the last week or so, I had the chance to play around with ToonBoom Studio, which is a software for creating graphics and animations using a large range of techniques. I won’t start to enumerate the whole feature set, which you can see in the video tour, or the detailed features list. However, I will talk about my own experiences with the software, about what I was able to do and of course about integrating it with XNA.
The first thing that needs to be said is the ToonBoom is not just a software for drawing images, but has features designed specifically for creating animations, which lack in other drawing packages such as Photoshop, GIMP, or Paint.Net. So because they serve different purposes, you can’t directly compare Toon Boom with these tools.
The interface is nice and after a few hours of fooling around, and reading some tutorials, you get the hang of it and start being productive. After some experiments that are not really worthy of being shown, my first serious attempt was to try and draw the following image in ToonBoom.
My favorite feature of ToonBoom is the ability to take an image, and pu
t it on the Static Light Table (semi-transparent background) so you can draw over it using the ToonBoom drawing primitives. In less than an hour, I was able to create something similar to the source cat image, but all done with vector graphics.
Now it was time to animate it, and the technique that seemed easiest to me was the cut-out animation technique. In this technique, you can isolate areas of the image, creating body parts, and link them together to create a hierarchy of part, just like you would do a skeleton in a 3D modeling animation. Then, in order to animate, you simply set the keyframes with different position and rotation values for each body part, and ToonBoom will take care of interpolating and animating your scene.
Since this was also a learning exercise, it took several hours, but in the end, I had the following animation:
Considering it’s my first jab at 2D animation, I was very satisfied with the results.
The whole cat drawing was based on someone else’s art as a source image, so I felt like a little cheater
This is why today I found one of my older “concept drawings” (if you can name it so), and decided to try and make the character in ToonBoom.
As you can see, my art skills are not something to be proud of. However, about 2 hours later, the result looked much better.
I’m sure a talented artist would have done this much faster, and with much better results, but from my point of view, it is a great progress. Some things I did to help me out:
- I used my concept art for static light, and drawn over it using the brush tool, or the poly-line tool
- I used random images on the internet to try and understand how to make a sword look sharp
- I used a picture of Mickey Mouse as inspiration for drawing the shape of the boots
- For the clothing details, I took them one at at time, and tried to add some simple details (the belt, the buttons, the shine on the boots) to make them look more interesting.
From now on, if I want to animate this particular fellow, rigging him for the cut-out animation technique should be rather easy.
A point of interest is how to integrate this tool with XNA for creating games. The first thing to know is that ToonBoom doesn’t have any special features to connect it to XNA.
What it can do is to export the animation as a sequence of images (at the framerate you desire, where intermediate frames will be generated automatically by the software), so after that, you still have to take those images, assemble them as sprite sheets, and write code to animate them in your game. You can’t fault ToonBoom Studio for that, because there really isn’t any software out there that does more than this.
But I can still dream. [dream state on] Just like ToonBoom Studio has a plugin to load a ToonBoom animation project in Flash and just use it directly, I think there is potential for something similar to be done for XNA. If the project format was open, I’m sure someone could create the necessary components (Content Importer + Content Processor + Runtime classes) to ensure an easy transition of animations and scenes from ToonBoom to XNA. Not for the vector art (which would need to be converted into sprites), but at least for the animations and scenes planning. [dream state off]
With that said, I think ToonBoom Studio is a wonderful tool for 2D drawing and animation, and I wholeheartedly recommend everyone to go and download the trial, and see for yourselves if it can help your game. I, for one, am going to use it for all my future 2D game graphics needs.
P.S. Creator’s Club Premium Members get a nice discount for Toon Boom Studio. See this for more information.
P.P.S. Some links with Toon Boom tutorials:
- http://www.toonboom.com/products/toonBoomStudio/eLearning/tutorials/
- http://www.connectedconcepts.net/toonboom/, with an index of previous tutorials here.
- http://www.steveryan.net/Tutorials/Toonboom.htm
![istockphoto_3797058-smiling-cat-cartoon[1] istockphoto_3797058-smiling-cat-cartoon[1]](http://www.catalinzima.com/wp-content/uploads/2009/07/istockphoto-3797058smilingcatcartoon1.jpg)
