Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon


26. October 2014

 

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:

IMG 0280

 

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.

IMG 0283

 

This will now bring you to the main code editing page.  It will start with the following simple “Hello World” code:

 

-- SpritesheetDemo

 

-- Use this function to perform your initial setup

function setup()

   print("Hello World!")

end

 

-- This function gets called once every frame

function draw()

   -- This sets a dark background color 

   background(40, 40, 50)

 

   -- This sets the line thickness

   strokeWidth(5)

 

   -- Do your drawing here

 

end

 

 

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:

IMG 0279

 

Now select Create New Class

IMG 0278

 

In the text field, enter Spritesheet as the name:

IMG 0281

 

It will start with an empty class definition, we will replace it will this code:

Spritesheet = class()

 

function Spritesheet:init(img,x,y,rows,cols,frameWidth,frameHeight,frame,totalFrames)

   self.x = x

   self.y = y

   self.frame = frame

   self.totalFrames=totalFrames

 

   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.mesh:addRect(0,0,self.frameWidth,self.frameHeight)

end

 

function Spritesheet:draw()

 

   self.counter = self.counter + DeltaTime

   if self.counter > 1/30 then

 

   self.frame = self.frame + 1 

   if self.frame > self.totalFrames then

       self.frame=0

   end

 

   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)

 

   self.mesh:setRectTex(1,

   col * colUnit,

  ( 1-rowUnit) - row * rowUnit,

   colUnit,

rowUnit)

 

       self.mesh:setRect(1,self.x,self.y,self.frameWidth,self.frameHeight)

 

    self.counter = 0

    end

 

   self.mesh:draw()

end

 

function Spritesheet:touched(touch)

 

end

 

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:

 

 

function setup()

   local img = readImage("Documents:birds")

   bird = Spritesheet(img,WIDTH,HEIGHT/2,5,5,240,314,0,21)

end

 

function draw() 

   background(40, 40, 50)

   bird:draw()

end

 

function touched(touch)

   bird:touched(touch)

end

 

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! ):

IMG 0275

 

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:

IMG 0282

 

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:

AnimatedBird

 

So that’s a simple project in Codea.  Next up I’m going to start looking at Vector graphics instead of sprites.

Programming , ,

blog comments powered by Disqus

Month List

Popular Comments