Wednesday, August 27, 2014

Personal Update

Hello Again,

I have been receiving some messages asking why this blog has stopped. The reason is I got a new full time job. I am getting there early, coding the best I can, and studying related things at home. It is a great opportunity for me and they did bet on me by hiring an inexperienced programmer. I am trying to make that bet pay off, for it would be great for me as well as for them. I have almost no free time now, and that is making it harder to fit time to continue my blogging.

I have not abandoned, nor plan on doing so; I am just overwhelmed by the amount of work I have in my hands and my inexperience is holding me back. Still, I can say I am learning a lot on each day, and I guess this kind of learning only comes with real work experience. Of course, I could say "treat your hobby as work" but I cannot name a single person who can actually be 8h a day programming something without being paid for it.

What I recommend today is: treat it as a job, but don't make it a full time effort. Instead of 8h a day, make it 1.5h. But take it seriously. If you couldn't make up for your hour today, make them two tomorrow. Keep track of the time you missed so you can compensate later.

In addition, create some sort of control to measure how much you progressed. One good way is to create a BitBucket account and store your code there as a Git or Hg repository, commit every time you finish a class or something (it is your repo after all). At work I have been using a website called symphonical, but I'm still getting used to it; as of now I lose more time than I gain by using it as I am working almost by myself in a project. I can certainly see value in it when a small team is working on the same thing though, so I am really using it in order to learn, even if I'm on my own by now.

So, that's it! I've a new job and I'm really happy with it! Hope I can get up to speed soon. If my learning curve stay like this, I'll be much better two weeks from now. If you want to try and get this effect on you, don't treat your GameDev as a hobby, but as a 1.5h/day job, and take it seriously.

I hope to create a useful post soon enough.
Over and Out.

Monday, July 14, 2014

Animated Sprites in Löve2D

Hello Again,

Today, we are going to talk about how to render animated sprites with the Löve2D framework (v0.9.1). The principle is simple, you store the image and shared animation data and keep the animation control data somewhere. Whenever you need to render the sprite, you get the control data, apply the "fix" animation data and in the end render the correct portion of the image with the correct rotation, flips and scale.

The difficulty on this guide here is much higher than that of the introduction. I suggest you master that previous one before venturing any further.

This is what we want to achieve today (source code download at the bottom):



The first thing that comes to mind is that we have data that is shared between all our animated sprites, as the spritesheet, while there is data that is unique for every single animated sprite, as the elapsed time. So, let's determine the necessary data and differentiate what we can share from what we need to duplicate on each instance.

Shared or Instance Data

Straight out, the first things that come to mind are the spritesheet and a name. Every animation needs the access to an image/texture (or maybe the steps to render it with primitives) and a name to identify it, uniquely would be preferable. This data wouldn't change if you had, say, two separate goblins being animated. The sprite's size and speed are also fixed, the number of animations, total frames for each of them and these frames' offset in the texture are also static information, that would not change between the goblins walking side by side.

So, for the fix data, we have:
  1. Sprite Name (or an ID);
  2. Image (may be shared between sprites);
  3. Frame Duration;
  4. Animations (list); and for each animation
    • Size;
    • Frames Offsets (a list or a way to calculate these);
    • Frames Sizes.
Basically, this is all we need to have an animated sprite. If you want, you can store the size and frame duration for each animation or even for every single frame, if you want to; this would add some more flexibility. But here I won't do that since it detracts the code's readability.

Ok, with all these in place, we can calculate what frame to draw at a given time, using an elapsed time and the specific sprite and animation we want to render. So, that is the basics of what we'll need, a variable to control the animation time and another to store what animation is being rendered. We also need to know where on the window we'll render the sprite, and also give it some scale and rotation.

And this is what we need for every copy of the animation being rendered:
  1. Sprite Name (or ID, so we know what static data to use);
  2. Current Animation;
  3. Current Frame;
  4. Elapsed Time (from the last frame change);
  5. Position (x, y);
  6. Scale (in size and speed);
  7. Rotation (in degrees);
  8. Flip (horizontal, vertical, both or none);
  9. Alpha.

Alternatively, you could store just the elapsed time and refrain from keeping the current frame. This would save you a variable, but you'd need to use the % operator for every sprite on every frame. I prefer to use the extra variable and keep the math a little lighter.

That is basically what we'll need. The first list is what we need to have a single copy in memory despite of how many animations are running simultaneously. The second list's variables is what we need to keep a copy for every single instance. Sometimes, it doesn't make sense having rotation, scale, alpha or even flip when your game wouldn't use it.

Sometimes, some of these items can be left off the sprite instance, such as its position, scale, alpha, rotation, flip, as these would actually be stored in your game's entities, not in the rendering information.

As for the data, that is all. Let's move on to the serialization of the static part.


Simple Sprite Serialization with Lua

Lua makes our lives really easier when it comes to data serialization. Lots of content creation tools such as the Tiled map editor export directly to Lua, and all we need to do is call a single function and it is all in memory, ready to use.

SerializedData

return { -- Do you remember? These {} make a table.
    --Insert all the stuff here!
    first_name = "Not",
    last_name = "Sure"
}

GetTheData

myData = dofile("SerializedData.lua")

print(myData["first_name"], myData["last_name"])


Our first script with more than one file. The first file -GetTheData- gets the serialized data from the second one -SerializedData- by executing it with Lua's dofile() function. It then prints the contents of the received table.

The dofile() function simply calls the script file as if it was a single function and returns to us whatever the script file returns. Did you notice the file has a return statement outside of any functions? In Lua, you can treat a file as a single function and add return in its main body, outside of any blocks. That is really useful for serialized data, and we'll do that with almost every serialized table we have.

There's also some other similar functions to load external files: require(...), loadfile(...), load(...), and some others. You can find them here, under the sections 5.1 through 5.3.

This is how we are going to serialize the static data for all our sprites. We start the data file with return { and add all the data we need before closing with the respective }. The importance of the serialization can't be overstated.

Löve2D Note:
In L2D, you can zip your entire game in a single .zip file and rename it to .love. This way people can run it directly with less hassle (info here). The problem is that these loadfile() and dofile() functions do not work, you need to use the love.filesystem counterparts.

Real Example


To create our serialization fie, we'll need a sprite to serve as example. For that, I'll be using some graphics available here. I will need to make some changes so it fits our use better.

Lava Man from Capcom's Breath of Fire 3



Our first step will be serializing the information we need in order to animate the Lava Man. We will do that by creating a new Lua file named LavamanSprite.lua. This file will contain all the fix information for the sprite as described earlier. This is how we are starting our file (warning - this file will be relatively big).

LavamanSprite.lua:


local image_w = 739 --This info can be accessed with a Löve2D call
local image_h = 438 --      after the image has been loaded.
                    --I'm creating these for readability.

return {
    serialization_version = 1.0 -- The version of this serialization process

    sprite_sheet = "images/Sheet.png", -- The path to the spritesheet
    sprite_name = "lavaman", -- The name of the sprite

    frame_duration = 0.10, --How much time on each frame (default)


As you can see, we are using the return {}, that means return the following table; and I start listing miscellaneous information on the sprite. The image_w and image_h variables hold the dimensions of the entire spritesheet. We could get these by loading the image and checking its size or adding these directly as I did here.

The serialization_version variable is very important, as usually the serialization process may change while your game is in active development. Having this version control will allow you to create backward compatibility if no breaking changes are made. So, even if you think you won't use it, I advise you to use this control (or a better one for that matter).

The sprite_sheet and sprite_name won't be used on every frame, but are really important nevertheless. The name is mostly for internal data control, something mnemonic you can use as reference. The sheet should be the path to the image (absolute or relative) so we can get it ready.

The frame_duration here is basically the time our sprite will spend on each frame (in seconds). In here, we'll be using this value for the entire sprite, but you can use one for each animation or even one for each frame.

    --This will work as an array.
    --So, these names can be accessed with numeric indexes starting at 1.
    --If you use < #sprite.animations_names > it will return the total number
    --      of animations in in here.
    animations_names = {
        "idle",
        "attack",
        "get_hit",
        "idle_back",
        "attack_back",
        "get_hit_back"
    },

So, here we list all the animations our sprite will have. If you have seen the video on the top, you have seen that it has 3 different animations in two points of view (6 animations). So, I list their names here so we can access them by index: mySprite.animations_names[1] would return the string 'idle'.

    --The list with all the frames mapped to their respective animations
    --  each one can be accessed like this:
    --  mySprite.animations["idle"][1], or even

    --   ### NOT FINAL CODE ###

    animations = { 
        idle = {},  --These animation entries (arrays) are empty here,
                    --  but we'll fill them with the frames soon.
        idle_back = {},
        attack = {},
        attack_back = {},
        get_hit = {},
        get_hit_back = {}
        
    }
Note: the code above is not in its final form. It is here just to make it clearer for now.
This animations map holds arrays with the frames' offsets and respective sizes. Every animation has one of these arrays.

Now, how can we add the rectangles (as in offset+size) we need in order to render the desired portion of the image? One approach would be to simply add the coordinates to that array, as in here:

        -- ### NOT FINAL CODE ###
        idle = {
            --{X offset, Y offset, Width, Height}
            {100, 100, 50, 30},
            {200, 300, 50, 30}
        },

What is the problem with this approach? None really. It is just not taking advantage of one of Lua's strongest points, the fact that it isn't necessary to parse data serialized with it. If we add the numbers, we will still need to convert these into a Löve2D structure called a quad (docs here).

Creating this quad structure takes some runtime effort, so it is not good to create a quad for every time we'd render a sprite. We could get these numbers and parse them into quads when we first load the sprite, that is much better. But Lua gives us a third option - serializing the quads commands directly. So, we'll need to use the love.graphics.newQuad( x, y, width, height, source_w, source_h ) function (docs here).

So, this is the resulting idle table:
        idle = {
        --  love.graphics.newQuad( X, Y, Width, Height, Image_W, Image_H)
            love.graphics.newQuad( 1, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 83, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 165, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 247, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 329, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 411, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 493, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 575, 1, 81, 64, image_w, image_h )
        },

The first two numbers are the offset for the frame. The origin of the rectangle we want to draw from the image. The 3rd and 4th represent the size of that rectangle. The last two parameters are actually the size of the spritesheet. I have no idea why Löve2D needs this information, but it does. You can do as I did and insert this information manually or you can do it as follows:

local SourceImage = love.graphics.newImage("images/Sheet.png")

local image_w = SourceImage:getWidth() --or SourceImage.getWidth(SourceImage)
local image_h = SourceImage:getHeight()

So, this is how we would list the animations map of arrays in its final form (with one entry per frame on the arrays):

    animations = {
        idle = {
        --  love.graphics.newQuad( X, Y, Width, Height, Image_W, Image_H)
            love.graphics.newQuad( 1, 1, 81, 64, image_w, image_h ),
            --...
        },
        
        idle_back = {
            love.graphics.newQuad( 1, 73, 83, 58, image_w, image_h ),
            --...
        },
        
        attack = {
            love.graphics.newQuad( 1, 137, 122, 71, image_w, image_h ),
            --...
        },
        
        attack_back = {
            love.graphics.newQuad( 1, 217, 103, 83, image_w, image_h ),
            --...
        },
        
        get_hit = {
            love.graphics.newQuad( 1, 309, 69, 58, image_w, image_h ),
            --...
        },
        
        get_hit_back = {
            love.graphics.newQuad( 1, 373, 64, 64, image_w, image_h ),
            --...
        }
        
    } --animations

} --return (end of file)

What takes us to our final version of the LavamanSprite.lua:

LavamanSprite.lua:
--[[
    LavamanSprite.lua - 2014
    
    Copyright Dejaime Antônio de Oliveira Neto, 2014
    
    Released under the MIT license.
    Visit for more information:
    http://opensource.org/licenses/MIT
]]
print("LavamanSprite.lua loaded")

require "love.graphics"


local image_w = 739 --This info can be accessed with a Löve2D call
local image_h = 438 --      after the image has been loaded. I'm creating these for readability.


return {
    serialization_version = 1.0, -- The version of this serialization process

    sprite_sheet = "images/Sheet.png", -- The path to the spritesheet
    sprite_name = "lavaman", -- The name of the sprite

    frame_duration = 0.10,
    
    
    --This will work as an array.
    --So, these names can be accessed with numeric indexes starting at 1.
    --If you use < #sprite.animations_names > it will return the total number
    --      of animations in in here.
    animations_names = {
        "idle",
        "attack",
        "get_hit",
        "idle_back",
        "attack_back",
        "get_hit_back"
    },

    --The list with all the frames mapped to their respective animations
    --  each one can be accessed like this:
    --  mySprite.animations["idle"][1], or even
    animations = {
        idle = {
        --  love.graphics.newQuad( X, Y, Width, Height, Image_W, Image_H)
            love.graphics.newQuad( 1, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 83, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 165, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 247, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 329, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 411, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 493, 1, 81, 64, image_w, image_h ),
            love.graphics.newQuad( 575, 1, 81, 64, image_w, image_h )
        },
        
        idle_back = {
            love.graphics.newQuad( 1, 73, 83, 58, image_w, image_h ),
            love.graphics.newQuad( 85, 73, 83, 58, image_w, image_h ),
            love.graphics.newQuad( 169, 73, 83, 58, image_w, image_h ),
            love.graphics.newQuad( 253, 73, 83, 58, image_w, image_h ),
            love.graphics.newQuad( 337, 73, 83, 58, image_w, image_h ),
            love.graphics.newQuad( 421, 73, 83, 58, image_w, image_h ),
            love.graphics.newQuad( 505, 73, 83, 58, image_w, image_h ),
            love.graphics.newQuad( 589, 73, 83, 58, image_w, image_h )
        },
        
        attack = {
            love.graphics.newQuad( 1, 137, 122, 71, image_w, image_h ),
            love.graphics.newQuad( 124, 137, 122, 71, image_w, image_h ),
            love.graphics.newQuad( 247, 137, 122, 71, image_w, image_h ),
            love.graphics.newQuad( 370, 137, 122, 71, image_w, image_h ),
            love.graphics.newQuad( 493, 137, 122, 71, image_w, image_h ),
            love.graphics.newQuad( 616, 137, 122, 71, image_w, image_h )
        },
        
        attack_back = {
            love.graphics.newQuad( 1, 217, 103, 83, image_w, image_h ),
            love.graphics.newQuad( 105, 217, 103, 83, image_w, image_h ),
            love.graphics.newQuad( 209, 217, 103, 83, image_w, image_h ),
            love.graphics.newQuad( 313, 217, 103, 83, image_w, image_h ),
            love.graphics.newQuad( 417, 217, 103, 83, image_w, image_h ),
            love.graphics.newQuad( 521, 217, 103, 83, image_w, image_h )
        },
        
        get_hit = {
            love.graphics.newQuad( 1, 309, 69, 58, image_w, image_h ),
            love.graphics.newQuad( 71, 309, 69, 58, image_w, image_h ),
            love.graphics.newQuad( 141, 309, 69, 58, image_w, image_h ),
            love.graphics.newQuad( 211, 309, 69, 58, image_w, image_h ),
            love.graphics.newQuad( 281, 309, 69, 58, image_w, image_h ),
            love.graphics.newQuad( 351, 309, 69, 58, image_w, image_h ),
            love.graphics.newQuad( 421, 309, 69, 58, image_w, image_h ),
            love.graphics.newQuad( 491, 309, 69, 58, image_w, image_h ),
            love.graphics.newQuad( 561, 309, 69, 58, image_w, image_h ),
            love.graphics.newQuad( 631, 309, 69, 58, image_w, image_h )
        },
        
        get_hit_back = {
            love.graphics.newQuad( 1, 373, 64, 64, image_w, image_h ),
            love.graphics.newQuad( 66, 373, 64, 64, image_w, image_h ),
            love.graphics.newQuad( 131, 373, 64, 64, image_w, image_h ),
            love.graphics.newQuad( 196, 373, 64, 64, image_w, image_h ),
            love.graphics.newQuad( 261, 373, 64, 64, image_w, image_h ),
            love.graphics.newQuad( 326, 373, 64, 64, image_w, image_h ),
            love.graphics.newQuad( 391, 373, 64, 64, image_w, image_h ),
            love.graphics.newQuad( 456, 373, 64, 64, image_w, image_h ),
            love.graphics.newQuad( 521, 373, 64, 64, image_w, image_h ),
            love.graphics.newQuad( 586, 373, 64, 64, image_w, image_h )
        }
        
    } --animations

} --return (end of file)

Phew! Now our serialization file is ready to be used. And all this hard work paid off, to get all the information serialized in this file in a table is now as simple as this:
lavamanSprite = dofile("LavamanSprite.lua")
And the lavamanSprite variable now holds all the static information on the sprite.

If you think about it, this code is very long, but relatively simple. It is simple enough to be automatically generated with a simple content creation tool. But we'll be doing these manually for now.

Sprites Manager

Now that we have our sprite properly serialized in its own file, we need to manage it somehow. In order to be able to use all that static data, we need to use some dynamic data for every sprite, as well as we need to load the spritesheet into memory. It would also be wise to do some error checking and automate the instantiation of the dynamic data. So, how do we start?

AnimatedSprite.lua

local ManagerVersion = 1.0

sprite_bank = {} --Map with all the sprite definitions
image_bank = {} --Contains all images that were already loaded

So, first thing first, we define the manager version. As of now, it is the same version as our LavamanSprite.lua serialization, but it will come in handy if we need to make changes on either file and need to create some backwards compatibility.

The tables sprite_bank and image_bank both store data mapped to unique indentifier keys, the sprites' being their definition file path and the images' being their own path. (note: if you load them with relative and absolute paths, it'll store one duplicate)

With these tables in hand, we can check whether a given sprite or image has already been loaded and, if not, load it properly. To be able to draw our sprites, we'll need the following functions:
function LoadSprite (sprite_def)
function GetInstance (sprite_def)
function UpdateInstance (sprite_instance, dt)
function DrawInstance (sprite_instance, x, y)

LoadSprite - Loads a sprite from a definition file (such as LavamanSprite.lua)
GetInstance - Gets an instance (dynamic data) for a given sprite
UpdateInstance - Updates an instance with the elapsed time from the last call
DrawInstance - Draws the instance using its own dynamic data at [x, y]

If you call GetInstance in a sprite that is not on memory, it will be loaded automatically. We will always try to pre-load them though, so we avoid loading anything at runtime. LoadSprite and GetInstance can return nil if there is an error (file doesn't exist or something like that).


function LoadSprite (sprite_def)

    if sprite_def == nil then return nil end

    --Load the sprite definition file to ensure it exists
    local definition_file = loadfile(sprite_def)

    --If the file doesn't exist or has syntax errors, it'll be nil.
    if definition_file == nil then
        --Spit out a warning and return nil.
        print("Attempt to load an invalid file (inexistent or syntax errors?): "
                ..sprite_def)
        return nil
    end

We start by checking if the user has passed us a nil value, and aborting if that's the case. Following, we use loadfile(file) and then check whether the file was successfully loaded or not, as it returns nil on error (file not found, bad syntax, etcetera). If we get a nil value, then there is an error. We report the problem by printing to the console and return nil (so the game can continue even on failure).

If the loadfile function return non-nil, then we are good to go as the file exists and is valid. So we need to run the file and check its version.

    local old_sprite = sprite_bank [sprite_def]
    sprite_bank [sprite_def] = definition_file()
    
    --Check the version to verify if it is compatible with this one.
    if sprite_bank[sprite_def].serialization_version ~= ManagerVersion then
        print("Attempt to load file with incompatible versions: "..sprite_def)
        print("Expected version "..ManagerVersion..", got version "
                ..sprite_bank[sprite_def].serialization_version.." .")
        sprite_bank[sprite_def] = old_sprite -- Undo the changes due to error
        -- Return old value (nil if not previously loaded)
        return sprite_bank[sprite_def]
    end

We start by saving the previous content of the sprite. If it wasn't loaded previously, that will be nil. This is done in case the loading fails and we need to revert it at a future point.

Then the second line runs the previously loaded file by calling definition_file as if it was a function. It stores the returned value in our sprite_bank map under an index that is the file path passed to this function. If there was another sprite here, it is overwritten, this allows for hot-reloads while a game is still running, really handy. Just remember to reset the instance if you hot-reload something (or you may crash your game). We could also use dofile() as we did before, but dofile loads the file before running it and this file was already loaded when we checked its validity.

We then compare the versions of both (Manager and Sprite) and report any problems in the console, aborting if they are incompatible. If it is invalid, we need to undo the changes and report it in the console.

The last resource we need to make sure exists and is valid is the image.
    --Storing the path to the image in a variable (to add readability)
    local sprite_sheet = sprite_bank[sprite_def].sprite_sheet

    --Load the image.
    local old_image = image_bank [sprite_sheet]
    image_bank [sprite_sheet] = love.graphics.newImage(sprite_sheet)
        
    --Check if the loaded image is valid.
    if image_bank[sprite_sheet] == nil then
        -- Invalid image, reverting all changes
        image_bank [sprite_sheet] = old_image   -- Revert image
        sprite_bank[sprite_def] = old_sprite    -- Revert sprite
        
        print("Failed loading sprite "..sprite_def..", invalid image path ( "
                ..sprite_sheet.." ).")
    end
    
    return sprite_bank [sprite_def]
end --LoadSprite

As the last step we need to load the image. We first cache the sprite_sheet path (so I don't need to write sprite_bank[sprite_def].sprite_sheet every time) and backup the old image, there's no problem if it is nil. Now we try to load the image by using the Löve2D newImage function (docs here).

Once the image is loaded, we need to check if Löve2D returned nil. If it did, our image couldn't be loaded properly. So, we need to undo what changes have been made by reverting both the image and the sprite to their previous state. We then return the new value (or the old in case of error) to the user.

And that is it for the LoadSprite function. As you can see, you can call it several times for the same sprite and it will only load it once, overwriting if duplicate. That's what we wanted, as this is the static data, fix data, that we will share between all the instances of a given sprite. Being able to hot-reload is a plus.


Sprite Instances


Now I ask you, what does the LoadSprite function do? It grabs all of a sprite's static data and prepares the necessary resources (the image). We can finally start creating a lot of different instances, each with their own set of dynamic values.

But how do we prepare a new instance? If you remember from the beginning of this guide there is some data that we need for every instance. Creating a new instance is as easy as preparing a table with that data inside in their default values. We also want to make sure a sprite is loaded before creating the instance. This what the GetInstance function looks like:
function GetInstance (sprite_def)

    if sprite_def == nil then return nil end -- invalid use
    
    if sprite_bank[sprite_def] == nil then
        --Sprite not loaded attempting to load; abort on failure.
        if LoadSprite (sprite_def) == nil then return nil end
    end
    
    --All set, return the default table.
    return {
        sprite = sprite_bank[sprite_def], --Sprite reference
        --Sets the animation as the first one in the list.
        curr_anim = sprite_bank[sprite_def].animations_names[1],
        curr_frame = 1,
        elapsed_time = 0,
        size_scale = 1,
        time_scale = 1,
        rotation = 0,
        flip_h = 1,
        flip_v = 1
    }
end

Much simpler than the last one isn't it? First we do some simple error checking to check for misuse and if the sprite is ready and, if it is not, try and load it. Abort if necessary, returning nil.


Updating an Sprite Instance

Of course, we need our sprite instances to change their current frame. And this is what the UpdateInstance is here for. We simply call it, pass along the elapsed time from the last update and it will get everything updated.

function UpdateInstance(spr, dt)

    --Increment the internal counter.
    spr.elapsed_time = spr.elapsed_time + dt
    
    --We check we need to change the current frame.
    if spr.elapsed_time > spr.sprite.frame_duration * spr.time_scale then
    
        --Check if we are at the last frame.
        --  # returns the total entries of an array.
        if spr.curr_frame < #spr.sprite.animations[spr.curr_anim] then
            -- Not on last frame, increment.
            spr.curr_frame = spr.curr_frame + 1
        else
            -- Last frame, loop back to 1.
            spr.curr_frame = 1
        end
        
        -- Reset internal counter on frame change.
        spr.elapsed_time = 0
    end

end

The first thing we do when updating an instance is incrementing its internal counter. When this counter exceeds the frame duration (adjusted with the instance's time_scale) we skip to the next frame. If this frame is the last one, we revert back to frame one. Whenever we change the frame, we reset the counter to 0. Note that the time_scale multiplies the frame_duration. So, if it is higher than 1 it will slow the animation down, while values smaller than 1 will make it faster. Any negative values or 0 sets the speed to the maximum (1:1 with the game's fps).

That's it, update counter and check time then change frame and reset counter if needed.

Drawing an Instance


Ok, now we have all our static data in place, we have our image loaded, a central map where we can get our static data reference from, the same with our image. We can also get instances of our unique sprites easily as well as update them with a single function call. There's just on thing missing: drawing. We still can't draw our sprite!

With all that data so neatly organized, drawing the correct rectangle is really straight forward:
function DrawInstance (spr, x, y)

    love.graphics.draw (
        image_bank[spr.sprite.sprite_sheet], --The image
        --Current frame of the current animation
        spr.sprite.animations[spr.curr_anim][spr.curr_frame],
        x,
        y,
        spr.rotation,
        spr.size_scale,
        spr.size_scale,
        flip_h,
        flip_v
    )

end

A single call to Löve2D draw function and we are done! I mean it! That is all it takes! The walls of code on the previous sections have reduced the last two ones into really small chunks with no loops at all. That's good, since this is the part that'll be called repeatedly at runtime.

This is our final AnimatedSprite.lua file:

AnimatedSprite.lua:
--[[
    AnimatedSprite.lua - 2014
    
    Copyright Dejaime Antônio de Oliveira Neto, 2014
    
    Released under the MIT license.
    Visit for more information:
    http://opensource.org/licenses/MIT
]]
print("AnimatedSprite.lua loaded")

local ManagerVersion = 1.0

sprite_bank = {} --Map with all the sprite definitions
image_bank = {} --Contains all images that were already loaded

function LoadSprite (sprite_def)

    if sprite_def == nil then return nil end
    

    --Load the sprite definition file to ensure it exists
    local definition_file = loadfile(sprite_def)

    --If the file doesn't exist or has syntax errors, it'll be nil.
    if definition_file == nil then
        --Spit out a warning and return nil.
        print("Attempt to load an invalid file (inexistent or syntax errors?): "
                ..sprite_def)
        return nil
    end

    --[[Loading the sprite definition as an entry in our table.

        We can execute the file by calling it as a function
            with these () as we loaded with loadfile previously.
        If we used dofile with an invalid file path our program
            would crash. 
        At this point, executing the file will load all the necessary
            information in a single call. There's no need to parse
            this of serialization.
    ]]
    local old_sprite = sprite_bank [sprite_def]
    sprite_bank [sprite_def] = definition_file()
    
    --Check the version to verify if it is compatible with this one.
    if sprite_bank[sprite_def].serialization_version ~= ManagerVersion then
        print("Attempt to load file with incompatible versions: "..sprite_def)
        print("Expected version "..ManagerVersion..", got version "
                ..sprite_bank[sprite_def].serialization_version.." .")
        sprite_bank[sprite_def] = old_sprite -- Undo the changes due to error
        -- Return old value (nil if not previously loaded)
        return sprite_bank[sprite_def]
    end
    
    
    --All we need to do now is check if the image exist
    --  and load it.
    
    --Storing the path to the image in a variable (to add readability)
    local sprite_sheet = sprite_bank[sprite_def].sprite_sheet

    --Load the image.
    local old_image = image_bank [sprite_sheet]
    image_bank [sprite_sheet] = love.graphics.newImage(sprite_sheet)
        
    --Check if the loaded image is valid.
    if image_bank[sprite_sheet] == nil then
        -- Invalid image, reverting all changes
        image_bank [sprite_sheet] = old_image   -- Revert image
        sprite_bank[sprite_def] = old_sprite    -- Revert sprite
        
        print("Failed loading sprite "..sprite_def..", invalid image path ( "
                ..sprite_sheet.." ).")
    end
    
    return sprite_bank [sprite_def]
end

function GetInstance (sprite_def)

    if sprite_def == nil then return nil end -- invalid use
    
    if sprite_bank[sprite_def] == nil then
        --Sprite not loaded attempting to load; abort on failure.
        if LoadSprite (sprite_def) == nil then return nil end
    end
    
    --All set, return the default table.
    return {
        sprite = sprite_bank[sprite_def], --Sprite reference
        --Sets the animation as the first one in the list.
        curr_anim = sprite_bank[sprite_def].animations_names[1],
        curr_frame = 1,
        elapsed_time = 0,
        size_scale = 1,
        time_scale = 1,
        rotation = 0,
        flip_h = 1,
        flip_v = 1
    }
end

function UpdateInstance(spr, dt)
    --Increment the internal counter.
    spr.elapsed_time = spr.elapsed_time + dt

    --We check we need to change the current frame.
    if spr.elapsed_time > spr.sprite.frame_duration * spr.time_scale then
        --Check if we are at the last frame.
        --  # returns the total entries of an array.
        if spr.curr_frame < #spr.sprite.animations[spr.curr_anim] then
            -- Not on last frame, increment.
            spr.curr_frame = spr.curr_frame + 1
        else
            -- Last frame, loop back to 1.
            spr.curr_frame = 1
        end
        -- Reset internal counter on frame change.
        spr.elapsed_time = 0
    end
end

function DrawInstance (spr, x, y)

    love.graphics.draw (
        image_bank[spr.sprite.sprite_sheet], --The image
        --Current frame of the current animation
        spr.sprite.animations[spr.curr_anim][spr.curr_frame],
        x,
        y,
        spr.rotation,
        spr.size_scale,
        spr.size_scale,
        flip_h,
        flip_v
    )

end

Using the Sprite


Now that we have our sprite information serialized as well as means of loading all its resources, checking for errors and instantiating new independent sprites, all we need to do is use it!

The simplest way to do it would be creating it and updating/drawing it on every frame:
--main.lua
require "AnimatedSprite" --Including the file

function love.load()
    lavaman = GetInstance ("LavamanSprite.lua")
end

function love.update(dt)
    UpdateInstance(lavaman, dt)
end

function love.draw()
    DrawInstance (lavaman, 50, 50)
end

With only this handful of lines you get a sprite on its default animation (idle), scale and speed rendering at [50, 50] in the screen. This is what it looks like:


Nice huh? But this is what we can do with just one line of code on each Löve2D callbacks, as simple as it can be. Our simple sprite system can do more than that, can scale, flip, accelerate, rotate and all that stuff. It can also render more than one of the same sprite at a given time without them having to be synchronized. This is the main.lua I used to make the video at the top of the post:

example sprite usage:
--[[
    main.lua - 2014
    
    Copyright Dejaime Antônio de Oliveira Neto, 2014
    
    Released under the MIT license.
    Visit for more information:
    http://opensource.org/licenses/MIT
]]

local draw_x = 0
local draw_y = 0
local next_animation = 2
local enter = 'return' -- Löve2D calls the enter key return.

function love.load()
    
    require "AnimatedSprite"

    LoadSprite ("Smile.lua") --Will print an error.

    Lavaman = GetInstance ("LavamanSprite.lua")

end

function love.update (dt)
    UpdateInstance(Lavaman, dt)
end

function love.draw ()
    DrawInstance (Lavaman, draw_x, draw_y)
    
    love.graphics.print("Frame Rate: "..love.timer.getFPS(), 500, 450)
    love.graphics.print("PgUp & PgDown to change size: "..Lavaman.size_scale, 500, 470)
    love.graphics.print("Home & End to change speed: "..string.format("%.7f",Lavaman.time_scale), 500, 490)
    love.graphics.print("Insert & Delete to Rotate: "..string.format("%.3f",Lavaman.rotation), 500, 510)
    love.graphics.print("Enter to change animation: "..Lavaman.curr_anim, 500, 530)
    love.graphics.print("Backspace to reset the sprite", 500, 550)
    love.graphics.print("Esc to quit", 600, 570)
end

function love.mousepressed (x, y, button)
    draw_x = x
    draw_y = y
end

function love.keypressed (k)

    if k == 'pageup' then
        Lavaman.size_scale = Lavaman.size_scale * 1.25
    elseif k == 'pagedown' then
        Lavaman.size_scale = Lavaman.size_scale * 0.8

    elseif k == 'end' then
        Lavaman.time_scale = Lavaman.time_scale * 1.25
    elseif k == 'home' then
        Lavaman.time_scale = Lavaman.time_scale * 0.8

    elseif k == 'insert' then
        Lavaman.rotation = Lavaman.rotation + math.rad(15)
    elseif k == 'delete' then
        Lavaman.rotation = Lavaman.rotation - math.rad(15)

    elseif k == enter then
        Lavaman.curr_anim = Lavaman.sprite.animations_names[next_animation]
        Lavaman.curr_frame = 1
        next_animation = next_animation + 1
        if next_animation > #Lavaman.sprite.animations_names then
            next_animation = 1
        end


    elseif k == 'backspace' then
        Lavaman = GetInstance ("LavamanSprite.lua")

    elseif k == 'escape' then
        love.event.quit()
    end
end

function love.keyreleased (k)
end


You can download the entire source code (with the image) through this link here.
This is it!
Our next steps are to change this sprite system to use what's called a SpriteBatch, return an error sprite when something goes wrong (something like a red square) instead of returning nil. And then creating a tiled map rendering system.

Looking forward for more!
Over and Out.

Wednesday, June 18, 2014

Introduction to Löve2D

Hello Again,

Today I'm going to talk about Löve2D, an easy to use Lua game development framework. It is licensed under the zlib license, so there is nothing to worry about its license.

The biggest advantage of Löve2D is that you can get things going really fast with it, it is easy to learn and Lua is extremely flexible. This environment requires less planning than my usual C++/SDL, but you still need to plan a bit ahead. The counterpart is that we have breaking changes in every major version, but no backwards compatibility. So, a game made with Löve2D 0.8 probably won't work in 0.9.1, what I consider a major issue. You should always distribute your game with the binary part included to avoid this problem (more info on this here).

Keep in mind that the usual structure of a Löve2D game leaves your source code accessible, so it is not ideal for commercial projects where you wouldn't want to publish your source code. Still, this makes games made with it very extensible, especially if you release the source code as a .love file. Modding the game is as easy as adding some extra Lua files and editing a line or two in the existing ones.


Hello World!

All the code in this post is released under the MIT License.

The Löve2D framework is partially based on callbacks. These are the main ones:

  • love.load() - A function called in the initialization, runs only once
  • love.update(dt) - Called every frame, dt is the elapsed time since last call
  • love.draw() - Called on the draw step, this is where your render logic comes
  • love.mousepressed(x, y, button) - called when a mouse button is pressed
  • love.mousereleased(x, y, button) - as above, when released
  • love.keypressed(key) - Called when a key is pressed on the keyboard
  • love.keyreleased(key) - as above, when released
  • love.focus(f) - Called when the game loses/gains focus

Löve2D Hello World:

function love.draw ()
    love.graphics.print ("Hello World!")
end

Löve2D requires that you have a file named main.lua. It is the core file for your game, and will always be executed by Löve2D. You should include in this file all the callbacks you want from the list above (they are optional though). In this example main.lua file I only overwrite the love.draw function and all this does is print "Hello World!" at the window origin (top-left corner) in white text and black background. This function is called every frame, so remember it is actually being written over and over again.

The rendering usually clocks at 60Hz and you have an easy way to access it. The love.timer.getFPS() function returns the FPS, straight and easy.

x = love.window.getWidth()/2
y = love.window.getHeight()/2

function love.draw ()
    love.graphics.print ("FPS: "..love.timer.getFPS(), x, y)
end

This would print the FPS continuously with the text originating at the middle of the window.

Let's step up a little now and use some more callbacks.

Löve2D Draw On Click:

function love.load()
    Lena = love.graphics.newImage("Lena-small.png")
    
    runningTime = 0

end

function love.update (dt)
    runningTime = runningTime + dt
end

function love.draw ()
    love.graphics.draw(Lena, drawX, drawY)
    love.graphics.print("Total Running Time: "..runningTime.."s", 10, 10)
    love.graphics.print("FPS: "..love.timer.getFPS(), 10, 40)
    
end

function love.mousepressed (x, y, button)
    drawX = x
    drawY = y
end

This main.lua file loads an image called Lena-small.png and draws it wherever we click with the mouse. It also keeps track of the running time (with update() to exemplify). You can use whatever image you want in here, this is just an example.

In here, we are using four different callbacks.
The load() function is called first thing when our game is loaded by Löve2D, and never touched again. The update(dt) function is where we should update our simulation using the elapsed time -dt-. Similarly, mousepressed() is called whenever a button of the mouse is pressed, any button, or the scroll changes its position.

As you can see, the module love.graphics is responsible for anything related to rendering. So, if you are going to load, render, manipulate or do anything with graphics, you'll touch the love.graphics module.

Did you notice that I called love.draw while drawX and drawY were still nil? These variables are only created on the first call to the love.mousepressed callback. Löve2D usually has default values, (0,0) in this case.

Quick Questions:
  1. What happens if we add the keyword local before drawY = y in the love.mousepressed function?
  2. As above, but with drawX? (You can find the love.graphics.draw docs here)
Answers:
  1. The image would move only horizontally, as the drawY is a local variable inside the mouse callback inaccessible globally. This would make drawY nil, so the drawing function receive only drawX love.graphics.draw(Lena, drawX).
  2. The script would crash on the first click as drawY ceases to be nil and turns into a number. The draw function would assume a form that expects an image, a quad (more on that soon) and the x and y coordinates as the first 4 parameters but, as drawX was nil, the quad is invalid and the script crashes. The drawY parameter would actually be interpreted as the x coordinate, in this case.

Wrapping Up

What have we learnt here?
  1. Löve2D has multiple callback functions that you can overload to create your game.
  2. The most important callbacks are love.update, love.draw and the input ones. These are probably the bare minimum for a game.
  3. Most functions have default values for when you don't want to pass on something (works pretty much the same way as in C++).
  4. Variable scopes can get messy, plan ahead. This will become especially visible when we start to move up into more complex stuff.

What's Next?

In the next post, we'll see how to draw an animated sprite with Löve2D, how to serialize it, initialize it and render the correct rectangles from the spritesheet. To do so we should go through some basics of data serialization, one of the strongest points of Lua.

Before going any further, make sure you run this code in your own machine and mess around a bit with it. Try making the image move around with the arrow keys (the key codes are simply 'up,' 'left,' 'right' and 'down'), not only with the mouse. A possible implementation can be found below.

Over and Out.

Moving Image:
--[[
    main.lua - 2014
    
    Copyright Dejaime Antônio de Oliveira Neto, 2014
    
    Released under the MIT license.
    Visit for more information:
    http://opensource.org/licenses/MIT
]]

function love.load()
    Lena = love.graphics.newImage("Lena-small.png")
    
    runningTime = 0
    moveX = 0
    moveY = 0
    drawX = 0
    drawY = 0
end

function love.update (dt)
    runningTime = runningTime + dt
    drawX = drawX + moveX
    drawY = drawY + moveY
end

function love.draw ()
    love.graphics.draw(Lena, drawX, drawY)
    --love.graphics.print(drawX.."X and "..drawY.."Y")
    love.graphics.print("Total Running Time: "..runningTime.."s", 10, 10)
    love.graphics.print("FPS: "..love.timer.getFPS(), 10, 40)
    
end

function love.mousepressed (x, y, button)
    drawX = x
    drawY = y
end


local speed = 5

function love.keypressed (k)
    if k == 'up' then
        moveY = moveY - speed
    elseif k == 'down' then
        moveY = moveY + speed
    elseif k == 'right' then
        moveX = moveX + speed
    elseif k == 'left' then
        moveX = moveX - speed
    elseif k == 'escape' then
        love.event.quit()
    end
end

function love.keyreleased (k)
    if k == 'up' then
        moveY = moveY + speed
    elseif k == 'down' then
        moveY = moveY - speed
    elseif k == 'right' then
        moveX = moveX - speed
    elseif k == 'left' then
        moveX = moveX + speed
    end
end


Tuesday, June 10, 2014

Getting Started with Lua

Hello again,

Warning: there is an update of the blog itself on the next paragraphs. You may skip to the Lua section below if you're not interested.

This blog has been inactive for quite some time as I took some time away from my game development hobby. Good news is: something happened that made me regret this immensely, so I am back. Bad news is: I must focus on my main personal project amap, period in which I'll leave our C++ game unattended given the giant amount of work it would be (as I chose to code the engine from SDL up).

I don't plan on abandoning this though, as I owe this same thing that brought me back to game development so quickly to this very blog. Creating a quality C++ framework that is flexible yet easy to use is something that is sure to take months of work. But I can't spare all this time right now nor want to take the easy route and create lower quality stuff as I wouldn't learn anything from it, in contrast to creating a quality one with which I, myself, would learn a lot by doing. So I'll change this blog's direction for the time being and retake its old course some months from now.

For now, I'll change my focus into getting results faster. I'll be using Löve2D for at least the next weeks, trying to get a hold of how it all works. It is a Lua framework, but my choice has nothing to do with the language, but with the framework itself. It has one of the easiest interfaces I've seen and, if necessary, I can always step into C and do what's necessary, although I don't believe it would really be a necessity.

Lua


It is a scripting language very popular on the game development world, possibly the most popular. The language itself is too big to fit in a blog post, and I am not proficient enough to give a good overview, but I'll still give you guys some short snippets to show the basics of the language.

HelloWorld.lua

print("Hello World!") --Self Explanatory
This print command would output the line to the console, as simple as that. To run this lua script, just type issue the console command [ lua path/HelloWorld.lua ] and it should output the message.

Lua doesn't support the most common syntaxes for most things, what can be a pain sometimes, but you'll soon get the hang of how it works. In the next example, I'll create a Lua array (table with numeric indexes) with two strings in it, and then print them.

Hell-o World.lua

myTable = { --Creates a Table
    "Hell",     --  First Entry
    "o World!"  --  Second Entry
}

print(myTable[1]..myTable[2])-- these .. concatenates strings
myTable here is an 'array' with two entries, that we can access by their numeric values (since it is not a map). The .. operator concatenates strings, really useful.

Did you notice that Lua's array indexes actually start with 1? Some people consider this the best thing about Lua, personally I don't like it as I already had to write workarounds for this when interfacing with C. Doesn't make much difference in the end though.

Metamorphosis.lua

print (myVar)   --myVar doesn't exist yet
                --Output:   nil

myVar = 1       --Creates myVar of number type (double)
print (myVar)   --Output:   1

myVar = {1,3,5} --Changes myVar into an array
print (myVar)   --Output:   table:0x1ed8de0

myVar = myVar[1]..myVar[2]..myVar[3] --Treats numbers as strings
print (myVar)   --Output 135
The output of this script is as follows:
[dejaime@manjaro BuildAndGun]$ lua Metamorphosis.lua
nil
1
table: 0x1e65d90
135

If you try to use a variable that doesn't exist, it will be treated as nil. nil in Lua is a special type of data that means "no data". It is treated as false when used in conditionals.

Lua uses double for all its numeric operations. If you want to change this behavior, you can do so by editing the Lua configuration file. More details on this can be found here.

Notice how a table is printed. First, Lua warns you that it is printing a table, and then it spits its memory address. This happens because it doesn't know exactly how to print a table, but, in case you want Lua to print your table in a given manner, you can use what's called a metatable. You can think of this as overloading some behaviors, but it is not exactly what is happening. If you wish to know more, take a look here.

MetaTable.lua

myTable = {
    "This ",
    "Is ",
    "Awesome!"
}
print(myTable) -- Outputs as usual: "table: 0x1f34abc"

myMetaTable = {
    __tostring = function (table)
        local resultingString = "" -- an empty string
        for i = 1, #table do -- # means total entries
            resultingString = resultingString..(tostring(table[i]))
        end
        return resultingString
    end
}

setmetatable(myTable, myMetaTable) -- sets the behavior for myTable

print(myTable) --Output: "This Is Awesome!"
As you can see, metatables must be explicitly set. In here, we used __tostring, but one can also overwrite several behaviors such as addition (__add), equality (__eq), userdata garbage collection (__gc, not for tables).

That # operator (#table) returns the total number of entries in a given array. 

Other notable thing about this snippet is the word local, right before resultingString. In Lua, everything is defaulted as global, but sometimes you'll want to set variables as local as they can be >3x faster to operate with (as in my benchmarks).

GlobalLocal.lua

GlobalPI = 3.14159

local FileLocalPI = 3.14159

function benchmark ()
    local FunctionLocalPI = 3.14159

    local total_operations = 10^8
    local trashVar

    local initial_time = os.time()

    for i=1,total_operations do
        GlobalPI = i*GlobalPI
        GlobalPI = i^GlobalPI
        GlobalPI = i/GlobalPI
        GlobalPI = i+GlobalPI
        GlobalPI = i-GlobalPI
    end
    
    print("Global: "..os.time() - initial_time.."s")
    initial_time = os.time()

    for i=1,total_operations do
        FileLocalPI = i*FileLocalPI
        FileLocalPI = i^FileLocalPI
        FileLocalPI = i/FileLocalPI
        FileLocalPI = i+FileLocalPI
        FileLocalPI = i-FileLocalPI
    end

    print("File level local: "..os.time() - initial_time.."s")
    initial_time = os.time()

    for i=1,total_operations do
        FunctionLocalPI = i*FunctionLocalPI
        FunctionLocalPI = i^FunctionLocalPI
        FunctionLocalPI = i/FunctionLocalPI
        FunctionLocalPI = i+FunctionLocalPI
        FunctionLocalPI = i-FunctionLocalPI
    end


    print("Function level local: "..os.time() - initial_time.."s")
end

benchmark()
The output I had for this script on my personal machine was the following:
[dejaime@manjaro BuildAndGun]$ lua GlobalLocal.lua
Global: 27s
File level local: 15s
Function level local: 6s
[dejaime@manjaro BuildAndGun]$ lua GlobalLocal.lua
Global: 29s
File level local: 15s
Function level local: 7s
As you can see, global variables are heavier than file-level local variables which are, in turn, heavier than function-level local variables. This is extremely situational though, but sometimes it is worth it copying a global into a local variable if you're going to perform too many operations with it. At least make the test wherever you think globals are holding you back and see if there is any performance gain. I guess this is enough to illustrate the importance of the local keyword in Lua. Keep this in mind when designing performance sensitive code.

Functions.lua

function GlobalPrintAll (tbl)
    for key,value in pairs(tbl) do
        print(key, value)
    end
end


myTable = {
    marco = "polo",
    humpty = "dumpty",
    hot = "dog",
    PrintAll = GlobalPrintAll
}

print("\nExplicit Argument:")
myTable.PrintAll(myTable)

print("\nImplicit Argument:")
myTable:PrintAll()
Output:
[dejaime@manjaro BuildAndGun]$ lua Functions.lua

Explicit Argument:
PrintAll    function: 0x746b20
hot dog
marco   polo
humpty  dumpty

Implicit Argument:
PrintAll    function: 0x746b20
hot dog
marco   polo
humpty  dumpty



Here, you can see that Lua can hold functions as variables. That is why so many Lua frameworks rely on callbacks, it is simply convenient to use callbacks in Lua.

Other important thing, especially when you guys go around hunting for examples in Lua, is the difference between . and : .
When using period, the function is called as one would expect. But the colon is actually OO eye candy, it passes the table itself to the function when calling it. In other words, table:function(p1, p2) is the same as table.function(table, p1, p2) .

ColonPeriod.lua

function GlobalFunction(printme, metoo)
    print(printme, metoo)
end

myTable = {}
myTable["l"] = "Yolo"
myTable["d"] = "Yodo"
myTable["Function"] = GlobalFunction

GlobalFunction(myTable["l"], myTable["d"])
myTable.Function(myTable["l"], myTable["d"])
myTable:Function(myTable["l"], myTable["d"])

Output:
[dejaime@manjaro Desktop]$ lua HelloWorld.lua
Yolo    Yodo
Yolo    Yodo
table: 0x20d9220    Yolo
Did you notice that the last call had its parameters displaced due to the myTable variable being passed along in the first slot? You need to be careful with this kind of thing, since in Lua there is no type checking, finding a problem where you used a colon instead of a period can be quite frustrating. The key is to be consistent and try to use one of the two whenever possible. It is up to personal preference though.


Lua is a really flexible language, and I certainly didn't do it justice with these few snippets. But I hope this was a good introduction nonetheless, and maybe sparked some interest. I'll be using it in the next posts while using Löve2D.

That'll be it for today,
Over and Out.

Tuesday, April 8, 2014

SDL_Vault complete!!-ish

Hello Again,

After a lot of time finding excuses not to work on my main projects, this one got to a point where I can no longer waste time on it. I can't think of anything I'd want to add to it as of now, and now I have no other option but to go back to the code SpaceRock Miners... That probably means it is complete! Well, kind of amateurish, but that's exactly what I am, don't judge me. I am simply adding the files to my projects and they are compiled alongside with my code. It is still missing a decent build mechanism, but this has been postponed indefinitely.

And linux heard: you shall not link this dynamically!

Download

Clone it from GitHub.

Download the PDF Documentation  (two classes, small stuff).


What about it?

For a comprehensive list of goodies:

  • MIT License (Yes!).
  • Allows you to load SDL_Texture, Mix_Music and Mix_Chunk from files
  • Users can push manually hand-loaded assets into the vault and let it care about freeing it later.
  • Configurable expiration time, so it will wait until an asset is unused for too long before freeing it; this is to prevent unnecessary freeing and reloading of assets.
  • Automatic asset loading. If you call a Get function, it automatically load it if it is not already cached.
  • Manual or Timed Automatic freeing of unused assets. You call FreeUnused() function manually or set up a periodic, automatic, cleanup.
  • Strong and Weak references. You can ask for a shared_ptr or for a classic pointer, just make sure you know what you're doing when using plain pointers.
  • No Singletons.
  • Doxygen!
  • Depends on C++11, remember to set the standard for it (-std=c++11) .
Its problems would be:
  • No SDL initialization. You should to initialize SDL yourself; at least Video, Audio and Timer.
  • std::shared_ptr based. It kind of forces you to use shared_ptr (you can still code a wrapper though).
 So, hope you guys like this, and use it, as I certainly will.
Over and Out.

Monday, March 24, 2014

SDL_vault - A helper library

Hello Again,

Lately, I've been working a lot on the blog's pet project, the SpaceRock Miners game. I have already completed a memory pool, and a basic actor-component system. But something really bugged me. I was using a map to store every sound and texture I loaded, and I searched it whenever I needed to check if something was already cached. So I decided to take this a tad further and separate this part of the game engine into a minimalistic helper library, that I'm calling SDL_vault. Named it SDL_vault because I always call this concept asset vault. But why asset vault? Probably because I'm nuts!

This mini library will be based on SDL2, SDL_image and SDL_mixer. It only has two classes, AudioVault and TextureVault; they have their own asset list and take care them. The TextureVault, given the necessity of a renderer to create a texture, has a SDL_Renderer that you need to set up before using (constructor, preferably), otherwise it will fail.

The only thing you need to do is call the FreeUnused function and it will then free any unused assets. If you set an expiration time, it will only free unused assets if their expiration time has passed. The expiration time only starts to count after a FreeUnused call while the asset is not being used anymore (last user dumped it). Sometimes, setting an expiration period of some seconds can be a good idea; something between 5s and a minute is just fine, depending on the case, even more. There's definitely no need to call FreeUnused on every frame.

It was supposed to be finished by now, but when I migrated to SDL 2.0.3 my build system broke somehow. And now I feel like a newbie with linkage problems again... Anyway, guess I'll just clean my SDL installation and rebuild it and... we'll see.
Update: It is finished, tested and ready to use. No warranties though, check the license.

GitHub

Of course, its license will be X11 (MIT). I'll be using a GIT repository:
https://github.com/dejaime/SDL_vault

You can instance multiple audio/texture vaults, with different or the same renderer for textures. Each instance will be independent from each other, so it is possible to have multiple copies of the same texture/audio if you load the same file on different instances. But that also means you can destroy any of them without having to worry about breaking any other instances.

Every asset has an ID, and they are unique even between vault instances. Since this is halting my project on the main engine, I'll finish this asap. Maybe with some basic doxy documentation.

Just to clarify, this is separated from the main engine simply because it will probably be useful outside of it as well.

Hope you guys use it, even if just as an study material on what not-to-do!

Over and Out.