SMS: The Next Mission – September Update

Lots of progress this past month, despite trying to get some coding in due to other obligations. I successfully added an “80s Home Computer” mode to SMS. Below is how it currently looks… pixel graphics, classic fuel bar, and 8-bit text in all its glory.  Click the image to see a larger screen shot.

80s Home Computer
80’s Home Computer Mode

I spent a lot of the first half of August researching how to handle the graphics for this mode.  I though of recreating all the graphics in pixel art, but it seemed like a lot of work to animate the ship to give it a true 8-bit rotation look.  I tried a shader, but it was a bit limited due to the layers I was using in the game. I finally settled on using a render texture, and it works great. I think it captures the look and feel of the 1980s. The score and fuel gauge were then created on a HUD UI layer.

Scope Creep

It’s been about 7 months since I started this project (albeit “off and on”), and I am still adding features. I still want to add an optional “60’s Mainframe Mode”, and I haven’t started on the “Current Year Shmup” mode (although I have some ideas). Once I get these modes where they are playable, I would like to release some video and a playable demo.

This month I plan on tweaking the graphics and code for the 3 modes, defining what I want in the Shmup mode, and putting together a title and options screen.

SMS: The Next Mission – August Update

It’s been a few months since my last status update for SMS: The Next Mission. Over the past two months, I have decided to remove, then re-add, the shmup version of the game.  I am also again on the fence on whether I should include multiplayer support like I had originally planned (and then decided against).

Originally, I wanted to recreate the mobile version of Space Mission: Survival on the PC and Mac.  A desktop version has been running in development for several months now. The PC opens up a lot more options, so I wanted to add a lot of bells and whistles including multiplayer, and expand on the “Classic 90s” retro look.  A bit later, I decided to drop multiplayer and focus on single player.  I added a “Retro 70s” look as well.

A couple of months later I had a playable game that had two styles, Classic 90s and Retro 70s.  Same game, two styles that the player can play from beginning to end. At this point I decided that maybe it would be cooler if the player was some sort of time traveler and can warp to different video game eras. I like it!

So, right now, I have a Retro 70s, Home Computer 80s, and Classic 90s version of the game. The 2000s version will be the shmup level and I will start development on that soon. I am actually thinking of adding a “Text Based 60s” mode to see if I can pull that off.

I am trying to keep each level accurate to the decade. This includes graphics and text styles, sound, and game play.

I am not sure if changing my mind so many times is a good or bad omen, but I believe the current idea of traveling through decades and playing the game with features available at that time will be fun and challenging. The only other decision I have to make (other than a name change, perhaps) is whether I want to add (local) multiplayer support, which would be ideal for consoles.

My Improved Unity Object Pool Class

Nice, little updates to code are… well, nice! I’ve been using a home-grown object pooler in Unity, and you may recall I wrote about that here.  It’s a very simple, yet effective way of creating a pool of objects.

Originally, I used List<GameObject> and looped through the list to find an unused game object. Thinking about it a bit, I realized I could use a queue instead.  Here’s the updated code:

using UnityEngine;
using System.Collections.Generic;

public class Pooler : MonoBehaviour {

    public GameObject pooledObject;     // object to pool, drag into Inspector
    public int pooledAmount;            // number of objects, drag into Inspector
    public string objectName;           // name of object, enter into Inspector

    private Queue<GameObject> pooledObjects;

    /// <summary>
    /// Inits the object pool.  Renames the pool objects to the objectName
    /// </summary>
    void Awake() {
        pooledObjects = new Queue<GameObject>();

        for (int i = 0; i < pooledAmount; i++) {
            GameObject obj = (GameObject) Instantiate(pooledObject, Vector3.zero, Quaternion.identity);
            if (objectName != "") obj.name = objectName;
            obj.SetActive(false);
            obj.transform.SetParent(transform, false);
            pooledObjects.Enqueue(obj);
        }
    }

    /// <summary>
    /// Gets the pooled object.
    /// </summary>
    /// <returns>The pooled object or null if all objects are used.</returns>
    public GameObject GetPooledObject() {
        if (pooledObjects.Count == 0) return null;
        return pooledObjects.Dequeue();
    }

    public void ReturnToPool(GameObject obj) {
        obj.SetActive(false);
        pooledObjects.Enqueue(obj);
    }
}

Using Queue<GameObject>, there is no need to loop through a list.  The Dequeue statement at line 33 will remove the first item from the queue and return it. I added the ReturnToPool method, so I can disable the object and add it back to the queue when I am done with it.

This object pooler is a basic and easy to use. Feel free to use or modify it if it works for you.

To use it, place this script on an empty game object, drag in a prefab, choose how many objects you want to create, and name it something.

Eating Your Own Dog Food

There’s a popular term in business called “Eating your own dog food”. In other words, “use your own products”. I had that opportunity during my recent upgrade to Tappy Holidays. I wanted to add a persistent high score and a game counter for achievements, and what better way to do that than with GBC Data Cabinet.

GBC Data Cabinet is a Corona SDK library I wrote to manage temporary and persistent data within your application. It’s on Corona’s plug-in store, and it’s free! Let me know what you think.

The following is the code I used to check for an existing persistent cabinet file. If it does not exist, then create one, and get it ready for upcoming data during game play. If persistent data does exist, then read the values.

CabExist = GBCDataCab.load("playerdata")

if CabExist == false then
    CabExist = GBCDataCab.createCabinet("playerdata")
end
    
if CabExist then
    highScore = GBCDataCab.get("playerdata", "highscore")
    if highScore == nil then highScore = 0 end
        
    intTotalGamesPlayed = GBCDataCab.get("playerdata", "gamesplayed")
    if intTotalGamesPlayed == nil then intTotalGamesPlayed = 0 end
else
    highScore = 0
    intTotalGamesPlayed = 0
end

 

Announcing GBC Language Cabinet Plugin for CoronaSDK

GBC Language Cabinet is an easy to use way to display text in different languages. Simply specify which languages you are supporting, create or import tables of translated text, and use a basic function call to return the text in the correct language.

Several options exist to make your life even easier:

  • Text can be created with embedded keys. During the getText call, parameters can then be passed via a table. The returning text will be formatted to replace the keys with the parameters you passed. See below for examples.
  • You can import a comma-delimited file or a json file containing your text (including embedded keys!) instead of using multiple addText calls.

GBC Language Cabinet is available on the Corona Plugin Store.

Coding and Testing In-App Purchases in Corona – Part 1

Recently, I spent some time updating one of my apps, and spent a lot of time looking over my in-app purchase (IAP) code. Corona makes this very easy to implement, but there are some minor things to watch out for, especially if you are publishing to multiple stores. This page on the Corona site is a valuable resource, and hopefully this post will give you additional information.

Testing, on the other hand, is not as easy as you would think, since each store has their own way of handing this.

In this two part blog, I will outline my recent experiences in coding and testing IAP in Corona. Part one is all about coding IAP using Corona. We will cover testing in Part 2.

Coding for Various Stores
Three Stores, Three Libraries

Let’s configure the Apple, Google, and Amazon stores. First, we need to determine which library to use.

local store = nil
local platform = system.getInfo("targetAppStore")

if platform == "apple" then
    store = require ("store")
elseif platform == "google" then
    store = require("plugin.google.iap.v3")
elseif platform == "amazon" then
    store = require ("plugin.amazon.iap")
end

Easy enough, find the target app store and load the library. Of course, make sure your build.settings file includes the Google and Amazon IAP plugins.

Next, we need to initialize the store library.  If using Composer, the scene:show or scene:create function is probably a good place for this.

if platform == "amazon" then
    store.init(StoreListener)
else
    store.init(platform, StoreListener)
end

Notice the difference?  The Amazon store does not require the store name.

That’s really all you need for set up. Next, let’s cover making purchases. This is also not too hard to code in Corona, but you need to be aware of what is returned to your listener for each store, since it is different. To get you started, we’ll build a simple store listener below.

When a user attempts to make a purchase, you need to let the store know, and you also need to listen for the store’s response.  The following code does this.

local function StoreListener(event)
    local transaction = event.transaction

    if transaction == "purchased" then
        -- successful purchase. Give user what they paid for

    elseif transaction == "cancelled" then
        -- User decided to cancel purchase

    elseif transaction == "failed" then
        -- D'oh, something went wrong. User could not purchase item.
    end

    store.finishTransaction(transaction)
end

-- Enter your purchase string here
local itemToPurchase = "com.mydomain.item"

-- make a purchase
if platform == "apple" then
    store.purchase({itemToPurchase})
else
    store.purchase(itemToPurchase)
end

Notice that the Apple store expects a table of items… even if there is only one. Amazon and Google do not support purchasing multiple items, so we pass in the actual string of the item we wish to purchase.

In the store listener, we are listening for a successful purchase, a cancel, or a failure. It’s up to you to code what you want to do when these events come in.

Notice that we must call finishTransaction to clean up, otherwise the store will not know that the transaction is complete. This is required for Apple, but no harm calling this for all other stores.

Restoring A Purchase

In the event a user had to reinstall your app, or in the event a person upgrades their phone, it’s a good idea to allow a user to restore their purchase. This is usually done when a user taps on a “Restore Purchase” button, and it’s as simple as calling store.restore() and listening for the “restored” event, with one exception. Google does not have a “restored” event. When restoring on the Google Play store, a “purchased” event is returned. So, how do you determine whether the “purchased” event is from a purchase or a restore? Let’s look at our modified store listener:

local function StoreListener(event)
    local transaction = event.transaction

    if transaction == "purchased" then
        if isGoogleRestore then
            -- Restore purchase code goes here
        else
            -- successful purchase. Give user what they paid for
        end

    elseif transaction = "restored" then
        -- Apple or Amazon restore code goes here

    elseif transaction == "cancelled" then
        -- User decided to cancel purchase

    elseif transaction == "failed" then
        -- D'oh, something went wrong. User could not purchase item.
    end

    store.finishTransaction(transaction)
end

-- define this variable 
local isGoogleRestore

-- place this in your restore button handler
if platform == "google then isGoogleRestore = true else isGoogleRestore = false end
store.restore()

Here it gets a bit tricky. For Apple and Amazon, you handle a restore by listening for a “restored” event. For Google, you can set a boolean value (isGoogleRestore) during your restore purchase code and listen for a “purchased” event. In the purchased event of your store listener, determine whether this is a Google restore or a normal Google/Apple/Amazon purchase by checking isGoogleRestore, and handle appropriately.

A Couple of Other Events

Amazon can send a “revoked” event, and Google can send a “refunded” event in cases where refunds were applied. If you listen for these, it’s again up to you to decide how to handle, but it basically involved removing the features that were previously purchased.

Consumables

Consumables are items that can be purchased over and over again (coins, power-ups, etc). You should not and can not restore consumables via the store, but a user can buy these as many times as they want.

Google treats all items as non-consumable (Apple and Amazon allow you to create these items and identify them as consumable in their dashboards). Since non-consumable items can only be purchased once, how do we allow a consumable item to be purchased again?  Easy, with this piece of code:

if platform == "google" then
    -- call this for every consumable item you have in the store
    store.consumePurchase(myConsumableItem)
end

This call may take some time, so I like to place it in my scene:show event (in Composer). You can listen for a “consumed” event in your store listener if you need to do some post-processing. If the user now decides to purchase a consumable item, Google will allow it.

Conclusion

This is the basis of coding IAP in Corona. I did not cover everything (such as loading products), but this should get you going. In a future blog post, we will discuss how to test IAP on each store.

Easter Eggs

Easter just passed and that got me thinking about Easter Eggs of another type… those in software.

Back in the day, before the Internet ruled supreme, I did some development for the Worldgroup BBS platform. The Worldgroup client consisted of a Windows 3.11 front end that called different modules (email, chat, games), which connected to a server to allow communications with others.

I wrote a few clients and placed some small, and somewhat lame, Easter Eggs in them. To this day, over 20 years later, I believe none of these were found. I never heard anyone discuss them, no one contacted me about them, and the Internet took over and Worldgroup was put out to pasture.

Just prior to working on Worldgroup clients, I released a Windows version of my shareware music collection application “Songanizer”, which also contained a somewhat lame, but undiscovered, Easter Egg.

I like the idea of Easter Eggs and love discovering them, even though I rarely find them on my own. I have all the code for the Worldgroup and Songanizer applications on 3.5″ floppies. If there is any interest, maybe I will post some screen shots with details on how to see these Eggs.

I haven’t placed any Eggs in any of my mobile apps, but I am thinking of adding a few. Any good eggs in your games?

Object Pooling in Unity

In my previous post, I covered object pooling in CoronaSDK. This article will cover object pooling in Unity, using C#. While the concept of Object Pooling is the same no matter what language, there are obvious syntax and set-up differences, and we’ll cover those Unity specific things here.

I am going to demonstrate a very simple object pooling class to get you started. There are more robust solutions on the Internet and in the Unity Asset Store, and each solution is a bit different, but this is what I have been using and it demonstrates the concept as well.

Step 1 – Initial Set Up

To use this solution, you need a couple of things:

  1. A prefab object that you will use as the object to pool. For this example, we’ll just create a cube, and we’ll call it “EnemyPrefab”.
  2. An empty Game Object. For this example, let’s rename it to “EnemyPool”.

Once you have a prefab and an empty game object, create a new C# script called Pooler. Here is the code for this script:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Pooler : MonoBehaviour {
	
	public GameObject pooledObject;		// object to pool, drag into Inspector
	public int pooledAmount;			// number of objects, drag into Inspector
	public string objectName;			// name of object, enter into Inspector
	
	private List<GameObject> pooledObjects;
	
	/// <summary>
	/// Inits the object pool.  Renames the pool objects to the objectName
	/// </summary>
	void Awake () {
		pooledObjects = new List<GameObject>();
		for (int i = 0; i < pooledAmount; i++) {
			GameObject obj = (GameObject)Instantiate(pooledObject, Vector3.zero, Quaternion.identity);
			if (objectName != "") obj.name = objectName;
			obj.SetActive(false);
			pooledObjects.Add(obj);
		}
	}

	/// <summary>
	/// Gets the pooled object.
	/// </summary>
	/// <returns>The pooled object or null if all objects are used.</returns>
	public GameObject GetPooledObject() {
		for (int i = 0; i < pooledObjects.Count; i++) {
			if (!pooledObjects[i].activeInHierarchy) {
				return pooledObjects[i];
			}
		}
		
		return null;
	}
}
 Step 2: Pool your Prefab

Pooler PropertiesAttach the Pooler script to the empty game object, and look at the Inspector. You will notice 3 public properties:

  • Pooled Object – drag your prefab here. This is the object you want to pool.
  • Pooled Amount – enter the number of pooled prefabs you want.
  • Object Name – the name of the object. More on that later

Pooler HierarchyWhen your project is run, you will see a number of pooled objects in your Hierarchy view.

See line 20? I like to rename my objects when they are created, but this is up to you how to rename them, if at all. If you enter text in the Object Name property, the object is renamed.

Step 3: Use Objects in Pool

To get an object in the pool:

GameObject obj = GameObject.Find("EnemyPool").GetComponent<Pooler>().GetPooledObject();

or, to break it down:

GameObject gameObj = GameObject.Find("enemyPool");
Pooler enemyPool = gameObj.GetComponent<Pooler>();
GameObject pooledObject = enemyPool.GetPooledObject();

To return an object to the pool, inactivate it:

gameObj.SetActive(false);
That’s it!

There you have a very simple object pooler class. Of course you can expand on this class to do all sorts of things: update other properties, allow the class to grow if needed, etc.

Object Pooling in CoronaSDK

Creating and removing game objects is a very expensive operation. Couple that with enabling and removing physics properties on these objects during game play, and it is no wonder you will notice your game stuttering and lagging during these operations. As more objects are added to the screen, the situation gets worse.

The concept of Object Pooling is something that every new game developer needs to understand. With Object Pooling, you create a set of game objects and configure them as much as you can before the game starts, usually during scene load. During game play, you use and reuse these previously created objects.

Fortunately for Corona developers, creating an object pool is very easy. In this article, we’ll step through this to create a pool of reusable game objects. As an example, I will use one sprite from my game Space Mission: Survival, and I will show you how I create a pool of enemy ships.

Some things to note… for simplicity, I am going to create a pool of static images. In my games, I create my pools using sprite sheets, so I can animate these game objects, but that is outside the scope of this tutorial.

Step 0… Planning

You should determine how you want to pool your game objects. Personally, I like to pool my objects by type, and not mix them.  For example, Space Mission Survival contains an enemy pool and a bullet pool. By keeping them separate, you can manage them easier.  Further, my game contains a bullet pool for the player and a bullet pool that is shared by all enemies, even though the bullet sprite is identical.

Also note that since you are creating a pool of objects, memory will be allocated so try to determine the amount of objects you really need.  For example, if you know you need 50 enemies, create 50 objects, not one hundred.  The extra ones won’t be used, and they will consume memory. In cases where you are not really sure how many you need, you can create extra objects during times where there is little action going on (level starts, info screens, etc).

Step 1… Creation

So, what’s a pool in Corona?  It’s just a table of sprites. Let’s create a pool of enemies:

local maxEnemies = 50
local Enemies = {}

for i = 1, maxEnemies do
    Enemies[i] = display.newImage("enemy.png", 0, 0)
    Enemies[i].isVisible = false
    Enemies[i].isAlive = false

    -- if you need physics on these game objects
    physics.addBody(Enemies[i], "dynamic", params)
    Enemies[i].isBodyActive = false
end

The for loop creates and adds 50 enemies to a table named Enemies, and sets them invisible. Line 7 is a property I added which will keep track of live enemies (enemies currently on the screen).

If you need physics on these game objects, lines 10 and 11 set that up. Line 11 disables any physics checks on this object since it is not alive.

That’s it! This code takes care of the the processor intensive creation of objects that would otherwise slow your game down during game play.

You now have a table of game objects set up with physics, but they are not in the game.  How do you use them?

Step 2 – Use
-- return an available enemy, or nil if there is no enemies left
local function spawnEnemy()
    for i = 1, #Enemies do
        if not Enemies[i].isAlive then
            return Enemies[i]
        end
    end

    -- if we get here, there are no more available enemies in the pool
    return nil
end

-- upon death of enemy, back to the pool
local function killEnemy(enemy)
   enemy.isBodyActive = false
   enemy.isVisible = false
   enemy.isAlive = false
end

-- get enemy from the pool and activate it
local enemy = spawnEnemy()

if enemy ~= nil then
    enemy.x = 100
    enemy.y = 100
    enemy.isVisible = true
    enemy.isAlive = true
    enemy.isBodyActive = true
end

-- do a bunch of stuff in your game and kill enemy when appropriate
killEnemy(enemy)

In the above code, the spawnEnemy function will return a reference to an available enemy in the enemy pool, or nil if one is not available.  The killEnemy function will return the enemy back to the pool.

Step 3 – Clean Up

When done, don’t forget to clean up! Cycle through the pool to remove all event listeners and physics, and don’t forget to nil out the game objects.

Conclusion

Utilizing object pooling is a great way to keep your game responsive and allows you to mange objects by creating and using only what you need. Object reuse keeps your game from having to allocate and release memory by creating the same objects over and over.

This was a basic tutorial on how to get started with object pooling in CoronaSDK. Play around a bit… use sprite sheets to animate pooled objects, or change the sprite of an object. Create a dynamically growing pool in cases where you need more objects than you created. Or, create a generic pooler library to handle creation, deletion, spawning, and removal of all objects in your game.