GBC Object Pool New Feature – Passing Parameters

The latest update to GBC Object Pool now supports the passing of parameters to your create and return functions! By passing in a table of key/value pairs, you can reduce the amount of create and return functions needed when creating object pools.

Previously, you had to code a separate create function for every pool you created. Seems like a lot of code for creating similar objects.

Check out this example below:

Old Way

local function CreateImagePool1()
    local obj = display.newImageRect("image.png", 50, 50)
    physics.addObject(obj, "dynamic", {
        friction = myParams.friction,
        bounce = myParams.bounce,
    })

    obj.id = myParams.id
    return obj
end

local function CreateImagePool2()
    local obj = display.newImageRect("image.png", 50, 50)
    physics.addObject(obj, "dynamic", {
        friction = myParams.friction,
        bounce = myParams.bounce,
    })

    obj.id = myParams.id
    return obj
end

imagePool_1 = GBCObjectPool.init(CreateImagePool1)

GBCObjectPool.create(imagePool_1 , 10, 0, false, {
    friction = 0.1,
    bounce = 0.1,
    id = "Image Pool 1"
})

imagePool_2 = GBCObjectPool.init(CreateImagePool2)

GBCObjectPool.create(imagePool_2 , 10, 0, false, {
    friction = 0.25,
    bounce = 0.5,
    id = "Image Pool 2"
})

New Way

local function CommonCreateFunction(myParams)
    local obj = display.newImageRect("image.png", 50, 50)

    if myParams ~= nil then
        physics.addObject(obj, "dynamic", {
            friction = myParams.friction,
            bounce = myParams.bounce,
        })

        obj.id = myParams.id
    end

    return obj
end

imagePool_1 = GBCObjectPool.init(CommonCreateFunction)

GBCObjectPool.create(imagePool_1 , 10, 0, false, {
    friction = 0.1,
    bounce = 0.1,
    id = "Image Pool 1"
})

imagePool_2 = GBCObjectPool.init(CommonCreateFunction)

GBCObjectPool.create(imagePool_2 , 10, 0, false, {
    friction = 0.5,
    bounce = 0.25,
    id = "Image Pool 2"
})

In the example above, using the old method, you had to code a new create function for every pool you wanted to use.  This was a lot of code, especially for pooled objects that had very similar, but different, settings. Notice that friction and bounce settings are slightly different.  Same with your return to pool function. It gets worse as you add more pools.

Using the new method, we use a single create function to create multiple image pools, and pass in parameters that are used to modify physics properties for the pool. We also add an id field to each image to show that you can create your own fields to store data in each object.

Another example demonstrates passing in the name of the actual image into the create function:

local function CommonCreateFunction(myParams)
    if myParams ~= nil then
        local obj = display.newImageRect(myParams.image, 50, 50)

        physics.addObject(obj, "dynamic", {
            friction = myParams.friction,
            bounce = myParams.bounce,
        })

        obj.id = myParams.id
    end

    return obj
end

imagePool_1 = GBCObjectPool.init(CommonCreateFunction)

GBCObjectPool.create(imagePool_1 , 10, 0, false, {
    image = "coolimage.png",
    friction = 0.1,
    bounce = 0.1,
    id = "Image Pool 1"
})

imagePool_2 = GBCObjectPool.init(CommonCreateFunction)

GBCObjectPool.create(imagePool_2 , 10, 0, false, {
    image = "anothercoolimage.png",
    friction = 0.5,
    bounce = 0.25,
    id = "Image Pool 2"
})

In the example above, we use a common create function to create an image with physics properties. Passing in the parameters for the actual image to use allows us to have a single, generic create function instead of creating multiple functions to create your pool.

Examples Available

Check out the GBC Object Pool Git for source code and compiled examples.  If you have any questions, I am available in the Corona Forums.

[divider]

GBC Object Pool – Pooling Complex Physics Objects

GBC Object Pool is a CoronaSDK plugin that simplifies the management of Object Pooling. In this article, I describe the method to pool complex physics objects.

A complex physics object consists of several display objects connected together by a physics joint. I recently added another example to the GBC Object Pool sample app that demonstrates how to pool complex physics objects. This article will go through some of the code in that example.

Using the traditional way of object creation, you would need to create multiple objects and joints in order to display a complex physics object. Depending on the complexity of this object, there may be visible performance issues (screen stutter or delay) when creating these types of objects. Utilizing object pooling will reduce or eliminate this delay.

The general solution is to create as many pools that are needed in order to create the object, and then join them via joints after you extract them from the pool. For example, connecting similar items may only require one pool. In the example, though, we connect three circles to a square, so I created two pools… every time we wish to display this type of complex object (when we tap on the screen), we grab one square from the square pool and three circles from the circle pool, and join them together.

squarePool = GBCObjectPool.init(createMainObject, returnMainObject)
GBCObjectPool.create(squarePool, 10, 0, true) 
        
circlePool = GBCObjectPool.init(createSatellite, returnSatellite)
GBCObjectPool.create(circlePool, 100, 0, true)

-- This creates a complex object using multiple pools when the screen is touched.
-- We grab 1 object from the square pool, and 3 objects from the 
-- circle pool.
-- Notice, we then create the proper joints, and event listeners.
-- We also have to save a reference to the joints and the circles so that
-- they can be returned to the pool later.
function onScreenTap(event)
    local Circles = {}
    local Joints = {}
    
    local square = GBCObjectPool.get(squarePool)
    
    for i = 1, 3 do
        Circles[i] = GBCObjectPool.get(circlePool)
    end
    
    square.x = event.x
    square.y = event.y
    Circles[1].x = square.x - 70
    Circles[1].y = square.y
    Circles[2].x = square.x + 70
    Circles[2].y = square.y
    Circles[3].x = square.x + 5
    Circles[3].y = square.y + 70
    
    Joints[1] = physics.newJoint("rope", square, Circles[1], 0, 0, 0, 0)
    Joints[2] = physics.newJoint("rope", square, Circles[2], 0, 0, 0, 0)
    Joints[3] = physics.newJoint("rope", square, Circles[3], 0, 0, 0, 0)
    
    -- Save a reference to all the joints and circles in the square object
    -- We will need this later when placeing back into the pool
    square.satellites = Circles
    square.joints = Joints    
    
    square:addEventListener("tap", onSquareTap)
    
    return true
end

We need a way to track all the objects used, so we can return them to the pool later. Notice above, we save a reference to the joints and circles in the square object itself. Since the square object will contain a listener that will start removing everything when tapped, it’s a great place to store it.

The code below is the listener that is called when you tap the square. This function will dismantle the complex object and return the parts to their appropriate pool.

-- When you tap on the square object, the entire complex object
-- is returned to the proper pools.
-- Note that since we are not destroying objects (we are pooling them)
-- we have to manually remove and nil all physics joints, since we created them
-- when we created this complex object.
function onSquareTap(event)
    local object = event.target
    
    -- remove the physics joints
    for i = #object.joints, 1, -1 do
        display.remove(object.joints[i])
        object.joints[i] = nil
    end
    
    -- put the circles back into the pool
    for i = #object.satellites, 1, -1 do
        GBCObjectPool.put(circlePool, object.satellites[i])
    end
    
    -- nil out the joint and circle variables
    object.satellites = nil
    object.joints = nil
    
    -- remove listener from square
    object:removeEventListener("tap", onSquareTap)
    
    -- place square into pool
    GBCObjectPool.put(squarePool, object) 
    
    return true
end

That’s really all there is to it. In summary, just remember:

  • When creating a complex object
    • Grab the objects from the pools
    • Create your physics joints
    • Store a reference to all the objects and joints used
  • To remove a complex object
    • Remove the joints using display:remove(joint) or joint:removeSelf()
    • Return all items to the appropriate pools

[divider]

GBC Object Pool Performance

The sample app included in my GBC Object Pool plugin has a performance scene that compares object pooling to traditional object creation. I took that scene, and modified it to execute 100 times automatically. After each run, it dumped the amount of time it took to execute.

Here are the results I experienced using the following settings in the app:

  • Display 100 images, 200 times
  • Use colors, physics, and scaling
  • Display the images using traditional methods, then pooling. Record both times
  • Repeat 100 times

Note that while the app is running, I did not attempt to access the devices, and no other apps were running in the background. Basically, I ran the app, clicked Start, and walked away.

Windows Desktop Performance

For this test, I used my development machine… a 2 year old Dell XPS 8700 with 16GB RAM.

Notice the tightness of the pooling times? GBC Object Pool (and object pooling in general) is pretty consistent, operating on average around 6700 milliseconds to perform the task.

The blue dots represents the traditional method of creating 200 images via display.newImageRect() 100 times, and then removing them. Notice the sporadic times and wide distribution. The dotted line represents a linear average where 50% percent of the dots are above the line and 50% are below the line. Notice that all of the object pooling tests are mapped below this line.

Android Performance

Moving on to smart devices, I used my Android phone… a 2+ year old mid-range Samsung.

Again, notice how pooling provides a more consistent result and maps faster than traditional average times?

What is interesting is the first few items between run 1 and run 10 (and again between run 61 and 75). Check out the data listed here:

Run
Traditional
Pooling
Difference
1 7706 6678 -13.34%
2 7962 6722 -15.57%
3 8844 6671 -24.57%
4 7787 6680 -14.22%
5 8030 6651 -17.17%
6 8243 6739 -18.25%
7 7679 6928 -9.78%
8 7221 6846 -5.19%
9 7278 6988 -3.98%
10 7318 6886 -5.90%

Something definitely was happening on the phone (memory allocation? background OS task?), but you will notice that it wasn’t a noticeable factor when using pooling. Since it is not possible to know when or if a background task will start executing while a user is playing your game, I would expect the game to stutter if you were creating a lot of objects during this time via the traditional method.

Announcing: GBC Object Pool

My latest plugin, GBC Object Pool, is now in the marketplace.

The GBC Object Pool plugin allows the CoronaSDK developer to easily and quickly implement object pooling within your application. Using two lines of code, you can create an object pool. GBC Object Pool optimizes the management of pooling, allowing for object reuse and eliminates garbage collection inherently found with traditional create/destroy functions.

The plugin provides several examples of pooling.  If there are any questions or requests, please let me know. I plan on writing more examples, blogs, and perhaps some videos on the  use of this plugin.

Some links:

GBC Object Pool in the Corona Marketplace

Link to documentation

Link to example code

[divider]

Using GBC Data Cabinet

I think it’s time to give GBC Data Cabinet some love… it’s been in the Corona Marketplace for over a year and it just works!

GBC Data Cabinet is my free Corona SDK plugin that can be used to keep temporary and persistent data for your Corona written apps.  It is very easy to use… simply create a data repository, and start adding and reading data.  That’s two lines of code!

GBC Data Cabinet does not use global variables to store data. Any data type, including tables, can be stored and used between scenes as well.

Do you need to store data between runs of your game? Just call save and pass in the data repository, and values are saved. Use the load command to bring everything back the next time you need it.

[divider]

Bat Cave – Post Mortem

Bat Cave is a simple shooter I created for Ludum Dare 37, and this article attempts to document what went right, what went wrong, and some general thoughts on the entire weekend.

Development

Looking through the list of potential themes Friday evening, I put together some ideas. Bat Cave was hashed out and was my choice for a few themes, including “One Room”, which was ultimately picked.

Once the theme was decided, it was time to get to work. I spent a lot of time the first day working on designing the room and testing collision detection. Not sure why this took so long, but around 3:30am Saturday I had working collisions, and decided that was it for the evening. Saturday morning rolls around, and it’s time to work on some art, and get to coding. Sunday was spent finishing up the game, tweaking options, and adding sound and title screen.

I used Unity for this compo, and the actual coding went rather well. I like to use my simple object pooler script to handle the management of game objects, and I created some controller scripts to handle the audio, game management, and enemy management.

What Went Right

  • I released a playable game!
  • “Keep it simple” is definitely the way to go.  Since you have only 48 hours, don’t bite off more than you can chew… if time permits, you can add some features. For me, I had a couple hours left and added the scrolling background, looping bats, and audio manager. I also wanted to add a “boss” character, but time ran out.
  • I now know the difference between stalactites and stalagmites!

What Could Have Gone Better

  • My art skills are “meh” at best. I did manage to animate a bat though. I wanted to draw and animate a person instead of the gun turret I used, but I was spending too much time on it and it was not anywhere near what I would consider good enough. While I think the art is good for this game (other may disagree!), I need to keep this limitation of my skills in mind for future compos.
  • I did’t think the entire game through, and I realized some issues while developing:
    • I wanted the power-ups to be lowered down a mine shaft and have the player pick it up to use. I don’t think I would of finished that, so they just randomly drop at some interval.  If you look at my time lapse video, you might see a mine shaft in the game for quite a bit of time before I decided to remove it.
    • Once a stalagmite forms, it was impossible to shoot any drips coming down since you had to be under the stalagmite, and that would cause instant death.  I decided to add a bomb to remove stalagmites, and I also allow the player to jump/shoot over smaller ones.

Conclusion

Another successful Ludum Dare for me… I had a great time developing and releasing Bat Cave, and even though there were moments of doubt, I continued on. I hope that this game rates higher than my previous two entries… time will tell.