As I mentioned earlier in the introduction to Codea, it does not support Spritesheets out of the box. I also mentioned earlier that it was a fairly simple thing to add. In this tutorial I am going to show how I created a class in Codea for enabling sprite sheets, which can also serve as a bit of an introduction to non-trivial Codea development.
A spritesheet is simply a number of sprite graphics that have put together into a single image. This often results in better performance because of more efficient memory usage, as well as quicker load times and easier asset management. There area number of spritesheet creation tools such as TexturePacker which I discussed how to use here and here. I am also using the spritesheet graphic mentioned in this post. It’s important to realize that tools like TexturePacker can create very tightly packed sheets that rotate textures to fit them in best. In this case however, we are expecting our textures to be arranged in a simple grid pattern.
Let’s jump right in to Codea and create a new project. At the home screen simply click the Add New Project button shown below:
In the resulting dialog, name your project, I’m going with SpritesheetDemo. When done, click Create. In my case the create button is slightly bugged, in that case, click slightly to the left of it.
This will now bring you to the main code editing page. It will start with the following simple “Hello World” code:
-- Use this function to perform your initial setup
-- This function gets called once every frame
-- This sets a dark background color
background(40, 40, 50)
-- This sets the line thickness
-- Do your drawing here
What we want to do now is add a new class for our Spritesheet, simply click the + icon in the top right corner of the Codea screen:
Now select Create New Class
In the text field, enter Spritesheet as the name:
It will start with an empty class definition, we will replace it will this code:
Spritesheet = class()
self.x = x
self.y = y
self.frame = frame
self.frameWidth = frameWidth
self.frameHeight = frameHeight
self.frameRows = rows
self.frameCols = cols
self.frameSize = vec2(self.frameWidth,self.frameHeight)
self.counter = 0
self.mesh = mesh()
--self.texture = img
self.mesh.texture = img -- self.texture
self.counter = self.counter + DeltaTime
if self.counter > 1/30 then
self.frame = self.frame + 1
if self.frame > self.totalFrames then
self.x = self.x -1
colUnit = 1/self.frameCols
rowUnit = 1/self.frameRows
row = math.floor(self.frame / self.frameRows)
col = math.floor(self.frame % self.frameCols)
col * colUnit,
( 1-rowUnit) - row * rowUnit,
self.counter = 0
A quick explanation of what the above code does. First off, you create a Spritesheet by passing in the image to use for the sheet, the x and y coordinates to draw the individual sprite at, the number of rows and columns in your sprite grid, the width and height of each row and column, the frame to start on as well as the total number of frames in the sheet. The rest of the init() method is mostly about storing these values. The key call is addRect() which you can think of as creating a viewport into the sprite sheet, so we can view a single sprite. The critical part is we create a mesh. This is a quad that we apply our texture to. In the draw() method you will see why this is important.
The draw() function has a lot of functionality built in that you obviously wouldn’t want in a more generalize Spritesheet class. It does however show you how to display a single frame within the sheet and to iterate through each frame. First we update our internal counter by DeltaTime, which is the amount of time since the last frame was drawn. We are animating at a fixed rate of 30hz (obviously something you would want to change ), and each time 1/30th a second elapses, we move on to the next frame, unless its the last frame of animation, in which case we go back to first frame.
It’s all of the following bit that is the core of how we make the sprite sheet work. We have created a mesh and applied the image texture to it. Obviously though, that texture has a number of sprites on it. What we are doing is finding the coordinates of our current frame of animation within the mesh. The critical part here is mesh does not use pixel coordinates! Instead it uses standard GL device coordinates ( similar to UV coordinates ), that start at 0 and go to 1. The coordinate (0,0) is the bottom left corner of the texture, while (1,1) is the top right. So we have to map our texture coordinates to pixel coordinates within the image. This is also a challenge since our frames of animation start at the top left, not bottom left. This is why we do (1-rowUnit). In simpler English, colUnit and rowUnit convert from pixel sizes to fractions of 1.0. For example, if we have 5 columns, each column is then 0.2 in width. Once we have figured out the location of our individual frame in texture space, we set it using setRectTex(). Finally we set our elapsed time counter back to 0 then draw our updated mesh using mesh:draw().
Now let’s take a look at how we actually use spritesheet in code: Go back to your Main and alter it like so:
local img = readImage("Documents:birds")
bird = Spritesheet(img,WIDTH,HEIGHT/2,5,5,240,314,0,21)
background(40, 40, 50)
Here we simply load our spritesheet from the local pictures ( or use dropbox, wherever you wish ). We then create a Spritesheet passing in the image, as well as the parameters that describe the sheet. Draw and touched aren’t called automatically, so each time we get a draw or touched call in our app, we simply call our sprite’s draw and touched functions.
When you run the code you should see ( assuming you used the same image I did that is! ):
If you are curious how I got an animated gif of a playing application out of Codea and onto this site, there is a neat bit of functionality built into Codea. You can record a video of your game running in the run screen of Codea using this button:
As you may be able to guess, I then used the iPad app PicPlayPost to convert to animated gif. It did a solid job and produced a small file, but it also stripped away all of the colour! So instead here it is exported as MP4 and created with my ole trust Gif Brewery:
So that’s a simple project in Codea. Next up I’m going to start looking at Vector graphics instead of sprites.