WRONG Prototype

Weaponified Reverse pONG - Marmalade Quick demo game - prototype version

View project on GitHub

Hey there! This repository contains the source for a game called "Wrong". Well, actually, in the spirit of Wrong, it's not actually the source to the game at all... yet! Currently this is the source for a prototype version, and the blog below is a getting started guide and walkthrough for building it. Stay tuned for an update where I'll be pushing the full game project and linking from here.

Meanwhile, let's take a look at the prototype and tools it was built with...

The Wrong prototype was built as part of a blog series on getting started with Marmalade Quick, by Marmalade dev advocate, and owner of this GitHub account, Nick Smith (that's me).

Marmalade Quick is 2D Lua based engine that ships with the Marmalade SDK. It allows you to code 2D games quickly using Lua and, combined with the regular Marmalade tools, lets you easily publish to iOS, Android, Windows 8/10 Phone and Store, other PC stores and Mac. Marmalade has some cool features, like the ability to make local iOS builds on a PC, the ability to extend the engine (the C++, Objective-C, C#, Java and Lua parts) and it's free to use.

Check out the original Marmalade blog for more like this: http://www.madewithmarmalade.com/blog/


Building a Marmalade Quick Game with Nick Smith, part 1: Getting Started

Now that Marmalade Quick is available (download it right here), I'll be building an app or two with Marmalade Quick and doing regular blog posts detailing the highs and lows encountered on the way. I'll be highlighting features, walking through how to use both tools and APIs, and giving tips and hints for getting the most out of Marmalade Quick. Since we've just gone into the first full release following a public beta version, I'll also be highlighting changes from the beta version, giving updates on new features we've added and previewing what's on the way in the next release.I started using Marmalade Quick in earnest over Christmas, with little-to-no Lua experience and having never worked on any of its internals or design. These blog posts will take us from "What is Marmalade Quick?! What is Lua?!" right through to uploading to the various stores and dropping the source onto GitHub.

So, what actually is Quick?

Marmalade Quick aims to be the fastest, most flexible, most open way to create cross-platform 2D games and apps for mobile devices.

Marmalade itself is a C++/native SDK for building high-performance apps with a single open-standards API set, and deploying them effortlessly to a wide range of devices - iOS, Android, BlackBerry 10, Windows, Mac... - without needing to use any OS-specific code or tools.

Marmalade Quick is essentially middleware that sits on top of Marmalade and provides a high level, easy-to-use interface via the Lua scripting language. Quick allows you to write interactive apps using 2D graphics, physics and touch interactivity in as little as 15 lines of code. Internally it uses best-in-class C++ modules like Cocos2D and Box2D to do the hard work for you.

Marmalade Quick comes with pre-built C++ apps and open source Lua scripts that combine to load and run your Lua/Quick code. You don't need to touch or understand the C++ parts, but both those and the Lua internals are provided as source, which you can modify and extend if you want. Quick comes with Lua wrappers for the core Marmalade C APIs plus its own 2D graphics and physics APIs. You can essentially extend Marmalade Quick to provide Lua interfaces to any Marmalade C APIs (we'll cover that in a future post!).

Marmalade Quick also has a new Launchpad tool that lets you manage, run and deploy projects from a simple interface without touching an IDE or Marmalade's standard deploy tool. So what does that mean in practice? Basically, you open the Launchpad, click "New Project", type some simple Lua code in a text editor, and get a 2D game up and running with no C++ or graphics programming knowledge. You can hit "Launch" to test on desktop, "Deploy" to run on devices and "Publish" to spit out a build ready for an app store.

What you need to use Marmalade Quick

Download, license and install the latest Marmalade SDK build from here: https://www.madewithmarmalade.com/downloads

New users can sign up for free and use an eval license (eval licenses mean deployed apps will be watermarked and not valid for publishing). You can use Mac or PC for both Marmalade SDK and Quick and you can deploy to all standard platforms with either.

Download Marmalade Quick for Windows or Mac from here: http://www.madewithmarmalade.com/quick Its a zip file, just extract it into the root folder of your 6.2 SDK. It's safe and only adds files (no overwriting). On Mac, make sure you merge folders instead of replacing them! (use terminal with "rsync -arvu " if needed) (Quick is now bundled inside the regular Marmalade SDK and requires no additional setup steps)

Documentation for Marmalade Quick is online here: http://quick-docs.madewithmarmalade.com (now at http://docs.madewithmarmalade.com/display/MD/Marmalade+Quick)

We don't currently offer offline documentation, but feel free to save them from the browser. Search will work without an Internet connection.

Marmalade Quick doesn't need any additional SDKs or even an IDE. You can edit game code in a text editor, the app binary is pre-built for you and your code is compiled automatically at runtime.

As with the regular Marmalade SDK, in order to deploy to devices, you will need:

  • iOS and BlackBerry signing assets and accounts (certificates, profiles etc - we'll cover these in another post!)
  • Java runtime for Android and BlackBerry signing
  • On Mac you need Python 2.6 or newer (python ships built-in with Marmalade for Windows)

First steps

Once Marmalade is installed (and licensed - follow the registration prompt at the end of installation), just go to /quick/tools and run the Quick Launchpad exe/app file from there.

Follow the steps in A Quick App in 5 Minutes in the docs: http://quick-docs.madewithmarmalade.com/AQuickAppIn5Minutes.html (http://docs.madewithmarmalade.com/display/MD/A+Quick+App+in+5+Minutes)

I'll be walking through some real world code in the next few posts. For now, I'd encourage you to dive in and see what you can build! A good starting point is the examples in quick/data/examples (there's a lot more there than the ones listed by default in the Launchpad).

Quick tip: You don't need to close and re-launch the Simulator when you edit code. Just save your Lua file(s) and hit Ctrl-R in the Simulator to reload the Lua code. Ctrl-T will restart the whole app (useful if you manage to really confuse the Simulator).

Where is Support for Platform X?

Quick is completely platform-independent: There is, for example, no iOS-specific code anywhere in it. Marmalade itself takes care of platform abstraction and so Quick will technically run on all Marmalade-supported devices. However, for Quick we decided to implement a new project management and deployment GUI, the "Quick LaunchPad", which provides a new front end to Marmalade's existing deployment tools. In the initial release, only iOS, Android, BlackBerry PlayBook and BlackBerry 10 are provided in the Launchpad, and not all of the usual Marmalade deployment options (for things like specific icons and permissions) are supported. However, you can still run Marmalade Quick with all existing Marmalade deploy options on all supported platforms... You'll need to use the standard Deploy Tool in order to access additional platforms and options. You can launch this directly from within the new Launchpad: In the launchpad, use the Advanced > Launch Deploy Tool option Under "Stage: Select Build" choose ARM GCC Release for devices or x86 Release for desktop Under Platform Selection choose the OS/platforms you want to deploy to Under Configuration, select the options you need (see below) Under Deploying, do "package" or "package and install" to put on device. Standard Deployment Tool settings are all documented here Note that Quick has not been well tested against the Marmalade 6.2 beta SDK for Windows Phone 8 and we are not officially supporting Quick on WP8 yet.

(NB: From Marmalade 7.0, Quick uses the new Marmalade Hub GUI and supports most platforms out of the box. See http://docs.madewithmarmalade.com/display/MD/What+do+we+support)


Building a Marmalade Quick Game with Nick Smith, part 2: Getting to Grips with Lua

I'll preface this post by saying that I'm mostly a C++ and Python developer these days. I first used Quick when the alpha release came out and only seriously dove into it over Christmas. Before Quick, I had only ever used Lua to set a few values in scripts for games and had basically no concept of how it actually works; most "issues" I encountered getting to grips with Quick, were actually just getting to grips with Lua!The way I talk about Lua objects and behaviour here is also going to be coloured heavily by my use of C++ and object oriented (OO) programming, so I will definitely be misusing technical terms as much as I possible can. As a first step for users who are new to Lua, I very much recommend that you understand how it handles objects/values, specifically the way that everything is a reference and everything is global by default. With that in mind, for this post I'm going to skip Quick almost entirely and kick off with an idiots guide to Lua.

Whitespace and block endings

  • Lua uses whitespace only to separate API calls. Unlike Python for example, indentation is irrelevant and only used to make code readable.
  • Lua does not have any end-of-line characters (like semicolons in C/Java)
  • Code blocks such as if statements, functions and for loops use the "end" keyword to enclose their content

Example:

function MyFunction(value)
    value = value * 5
    if value > 20 then
        print("value > 20")
    elseif value > 10
        print("value > 10")
    end
    if value then print("value is " .. value) end
end

Tip: Concatenate strings and numbers using '..' as above.

Comments

  • Comments are any text following two hyphens (--) in a line.
  • Multi-line comments start with --[[ and end with ]]--

Lua is small!

  • There are no classes, private/public, queues or other fancy data types, etc.
  • Implement them yourself! Or better, find a module from the web.
  • Lua has no +=, ++, etc. operators or any shorthand notation. Ctrl-C/V is your friend!

Value types

Lua only provides the following types:

  • floating point numbers - there is no integer type and no concept of short vs long, float vs double, etc. Just write the number to declare it
  • strings - just declare using value="my string here"
  • tables (access a group of value by keys)
  • functions
  • true & false
  • nil - no object, like null or nil in other languages
  • Tables
  • Tables store values (numbers/strings/functions/tables) which are indexed by keys. A key can be any Lua type (string/number/function/table).

Example:

mytable = {} --empty table
mytable = {"hello", "value two", 7, 5, "another value"}--table initialised with values - no specified keys means keys are auto-assigned numbers starting at 1 (1,2,3,4, etc)
mytable2 = {name = "bob", age=20, colour="orange"} --special case: you can initialise values with string keys like this.

You can access table values as follows:

print(mytable[1]) --prints hello
print(mytable["age"]) --prints 20
print(mytable.age) --prints 20
print(mytable.1) --error! can't use numbers with the dot operator

Dynamic assignment:

  • Variable types are dynamic, so you can do things like:
a=1 then a="string" then a = {} then a = myFunction()...end

nil

  • You can include an undeclared variable in your code and instead of causing an error/exception, it will just evaluate to nil.
  • In conditionals (if, while, etc), nil evaluates to false.
  • Unlike in C, zero is not equivalent to false, so "if 0" evaluates to true.

Functions

  • Functions objects are declared with the "function" keyword and a parameter list in parentheses "()".
  • A function with parentheses in your code calls the function.
  • Specifying it without parentheses treats it as a value that is passed by reference like other objects (see further down).
  • Parameters are local variables pointing to the object passed to the function (ditto).

Example:

function myFunction(someValue)
    print(someValue)
end

myVar = myFunction
    myVar("hello") --prints "hello"
    anotherVar = myVar
    anotherVar("goodbye")

myTable = {}
myTable.memberFunction = function()
    --do something here
end

References and values

  • Everything in Lua is an object
  • Variables are all references that point at these objects, so all variable assignments are by reference!
  • Strings and numbers are essentially immutable objects, so when you do myVariable="mystring" you are essentially saying "create an object, 'mystring'" and "myVariable is a pointer to that object".
  • If you have two variables referring to the same string and set the first variable to another string, it wont change the second variable; they are now pointing to different strings.
  • Think of every unique number or string as a new object defined somewhere inside Lua.
  • This is why you can use a string as a key in a table and not worry about the key itself changing.

Example:

a=5
b=a
c=7
  -> a=5, b=5, c=7 (a and b are references to same number!)

a=2
  -> a=2, b=5, c=7 (we're not changing the value of a number when we change a! b still points to the object "5")

a="a string"
b=a
  -> a="a string", b="a string" (both variables are references to same string object)

a="another string"
  -> a="another string", b="a string"

a = { key1=2, key2=4, key3="hello" }
b = a
  -> b & a refer to same object

a.key1 = "new value"
  -> b.key1 = "new value"

When using things like function declarations and for loops, bear in mind that you are assigning by reference still and the parameters in those are all new local variables pointing to some object. So "v" in "for k,v in pairs(myTable)" (see for loops below) is just a new reference to the object myTable[k] points to. Doing v=newValue wont change the object in the table (myTable[k] will still return the old value), but both v & myTable[k] will return a pointer to the same object.

However, for tables, you can change the values inside a table since they are also references:

a = {one=1, two=2, three=3}
b = a
b.one=7
  -> a.one = 7 (because we have accessed the "one" variable and made it point to a new object)

Default parameters for functions

You can pass fewer parameters to a function than it expects, in which case the un-set ones ones will just be nil. You can use this to implement default parameters, but only for values at the end of the list.

Example:

myFunction = function(v1,v2,v3,v4,v5)
    v4 = v4 or 100 --default to 100 if nil
    v5 = v5 or "something"
    ...
end

Variable Scope (local vs global)

  • By default, all variables are global! This is unusual if you're coming from C, Python, etc.
  • If you have two (global) variables of the same name in different functions, they will point to the same object.
  • You have to explicitly use the "local" keyword to declare a variable local.

This is a typical place to create buggy code! Bear local vs global and code order in mind if you get errors like "value is nil" or you expect an event function to be called and nothing happens.

A common error I've seen is having a local-declared Quick scene object and having a global function for that object, but the function is declared first so Lua thinks the local object is different to the global similarly-named one...

Example:

function myScene:setUp(event)
    ...
end

local myScene = director:createScene() -- myScene here is NOT the same "myScene" as the one above! This is a new local object!

Destruction and garbage collection

  • Lua has no explicit destructor for objects.
  • The "nil" operator is similar to null in other languages.
  • Lua garbage-collects objects when there are no longer any active references to them.
  • To make sure there are no references to an object, set all variables that pointed to the object to be nil.
  • Lua uses incremental mark and sweep garbage collection.
  • By default, garbage collection runs at some interval which is not opaque to the user.

Background reading:

For Loops

Lua provides 3 useful types of for loop:

  • numeric - like a C for loop
  • pairs - for looping through an arbitrary table
  • ipairs - for looping through an "array" style table (see further down)

Numeric:

for var=exp1, exp2, exp3
    do something
end
  • var is a local variable that only exists in the loop.
  • var starts at exp1, increments by exp3, loop ends when var > exp2
  • exp1/2/3 must all be integers.
  • exp 3 is optional; defaults to 1.
  • Don't change var during the loop! Behaviour is unpredictable.
  • All three expressions are evaluated once, before the loop starts.

Example:

for n=5, math.min(somevalue, 0), -1 do
    print(n) --loop will run 5 times, math.min is called once
end

pairs (generic for loop):

for key,value in pairs(myTable)
    do something
end
  • As above, variables are local to the loop and pairs is only run once
  • "value" is optional (use "for key in pairs(..." if you only care about the key)
  • The loop may go through keys in any arbitrary order. If your keys are 1,2,3,4, etc. they may not be accessed in that order!
  • Use myTable.key= to change the table, not value= (see below)

ipairs:

for key,value in ipairs(myArray)
    do something
end
  • As above but designed for tables that are "arrays" (keys are ordered integers starting at 1 - see below).
  • The loop goes from key=1, in order 2,3,4,etc till the last key that is not nil.

You can break out of a for loop with the "break" keyword. Sadly there is no "continue" keyword in Lua.

Tables as "arrays"

Lua tables can be treated as arrays if the keys are all contiguous integers, starting from 1.

  • Lua arrays start at 1 not zero!
  • You can initialise an array easily by not specifying keys, as in our earlier example:
mytable = {"hello", "value two", 7, 5, "another value"}
  • Get the length of an array using table.getn(myTable). NB: "table" is a global object with various helper functions for other tables
  • The length of an array is defined as the last integer index, starting at 1, which does not have a value of nil.
  • Therefore, if you set a value in the middle of the array to nil then the rest of the array is then ignored in subsequent for ipairs() loops and table.getn() becomes unpredictable!

Changing arrays:

  • Arrays are dynamic (as they are tables) so you can add and remove values.
  • Add to an array using table.insert(myTable, value, )
  • Remove from an array using table.remove(myTable, )
  • Using .remove makes the table automatically fill the hole by moving other values down one index. Using myTable[index] = nil will NOT make the table re-size (actually it may depending on Lua implementation but it's not guaranteed so don't do it!).
  • Warning! The value returned by table.getn() is updated when you do inserts, but not removes!
  • If you set the LAST element to nil, then the array WILL "shrink" inside for ipairs() loops, but the behaviour of table.getn() isn't guaranteed.

Sounds bug-friendly, right? It's probably safer to track dynamic array sizes yourself with a custom variable than to use table.getn().

If you are adding and removing a lot of values in loops and don't care about the index value being a list of integers, I'd recommend just using a table with unique IDs as keys. That means you can add and remove from all over your code without fear of bugs.

Tables as "classes" and the hidden "self" parameter

Lua functions can be passed an implicit "self" reference by declaring functions using the ":" operator as opposed to "."

Example:

Simple function:
myObject = {}
myObject.name = "bob"
myObject.myFunction = function(value)
    print("value")
end

myObject.myFunction("hello")
":" version:
myObject = {}
myObject.name = "bob"
myObject:myFunction = function(value)
    print("value, my name is " .. self.name) -- we can access the table that owns this function using "self"
end

myObject:myFunction("hello") -- now prints "hello, my name is bob"

This is used a lot in Marmalade Quick, e.g. the event functions for scenes.

We can take advantage of this to create "classes" and do proper Object-Oriented (OO) coding. This is pretty fundamental if you want to have dynamically-created objects and to get access to objects from events. Note that there is no actual Lua concept of a "class", we are inventing it here!

Example:

MyClass = {} --declare a global table that will "create" objects of this "class" for us
MyClass.__index = MyClass -- .__index uses Lua's "metatable" feature to allow one table to use functions and values from another
-- a global function to create objects (tables) that all have the values and classes of MyClass
function MyClass.Create(someInitialiserValue, anotherInitialiser)
    local myObject = {} -- create object
    setmetatable(myObject,MyClass) -- give access to MyClass members
    myObject.val1 = someInitialiserValue
    myObject.val2 = anotherInitialiser
end

function MyClass:PrintSomeValueAndVal1(someValue)
    print(someValue)
    self:PrintVal1()
end

function MyClass:PrintVal1()
    print(self.val1)
end

someObject = MyClass.Create(4,8)
someObject:PrintSomeValueAndVal1(27)

secondObject = MyClass.Create(5,1)
print(secondObject.val2) -- can access members still like any table

NB: you can still call functions without the ":", in which case they expect self to be passed! This can lead to bugs, e.g. someObject.PrintSomeValueAndVal1(27) would result in self=27 and someValue=nil! Be aware of this generally when using . vs : with Quick's API. Getting an error with param = nil when you know you have passed the values expected is a typical indication of using . instead of :

Including code from other files

Use "doFile" to load and execute another file.

doFile("path/to/otherfile.lua")

This is the same as just substituting the doFile statement with the contents of the other file. So, if otherfile has a "return" statement at the end, you can do:

myValue = doFile("path/to/otherfile.lua")

You can also use "require", which does the same thing but searches a list of paths and won't re-load code that has already been included with another require call. That makes it more like normal "library loading" and generally a better idea for large projects. See http://www.lua.org/pil/8.1.html

Some Thoughts on Lua as a Noob

As a big fan of Python (for non-gaming), I find Lua a little frustrating in that the syntax is so small. No +=, no built-in classes, no default parameters... Lua is not meant for throwing together a complex bit of hard-core data processing in a few lines! As a C person, I also wish I could control garbage collection better. However the "every variable is a reference; everything object is a string/number/function/table" simplicity of Lua is a breath of fresh air in terms of knowing exactly what's going on without having to understand any Lua internals at all. Once you get that and how to do pseudo-classes, you pretty much know everything about Lua! It's very easy in something like Python to get lost building a pass-by-value monster chunk of data that kills all your RAM. Lua objects are super-easy to visualise and it's pretty easy to produce fast, efficient code.

Next time we'll be putting all that into practice building a real game. We'll see how Quick uses lua "classes" to implement it's "objects", "libraries" and "events" and how to best take advantage of Quick's node-based scene management.

Building a Marmalade Quick Game pt 3: Building the "Wrong" Prototype

Having played around briefly with the Quick examples, I decided to go straight for building a game and to figure out the rest on the way. I wanted something with some simple controls and screens that I could build on and polish later. I recently unearthed a very old nameless pong clone that I'd built in an old visual game maker app a very long time ago. It's basically pong with weapons, but backwards as you have to avoid the balls. I've decided to resurrect and improve my little reverse-pong game, now official know as "Wrong" (Weaponified Reverse pONG!) I'll be re-building Wrong from scratch and adding new features as I go. These blog posts will include code snippets that you can steal and learn from. At the end I'll upload to iOS, Android, BlackBerry and Windows Phone 8 stores, plus put the full source code up on GitHub.

The screen below gives you and idea of the original looked: original game screenshot

Be warned that the final version is not gonna look quite like that!

To implement Wrong, we basically need:

  • a background image
  • objects for the player "sleds"
  • ball objects
  • counters for health
  • counters, icons and objects for all the weapons
  • controls to move the players
  • logic for ball and weapon movement, bouncing and collision detection for hitting players
  • health, weapon firing and power-up logic
  • lots of simple animations for impacts etc.
  • menus and battle setup logic

Importantly, we also need to put some thought into adapting the game for modern devices:

  • The original version was fixed at 640x480 fullscreen for PC; the new one needs to run on phones and tablets in all sorts of resolutions and aspect ratios (just take a look at http://en.wikipedia.org/wiki/Comparison_of_Android_devices !!).
  • The original was 2 player only, on a single device; the new one probably ought to have some one player and networked play options (two players on one phone? Not likely!).
  • The original was controlled by each player having keyboard keys for up, down, fire and a separate key to select each weapon; the new one will need to support touch-only interfaces.
  • The original graphics are nice in an amateur pixel art kind of way; I want something a little more polished and memorable.

Initially, I just needed a prototype and to keep it simple, so I decided to skip weapons and controls for player 2, plus stick with single-touch controls and keep the menu super-minimal. This will be a learning experience; we want to get something off the ground, see what Quick is capable of and then make some design decisions based on that before doing any serious coding. I ripped the graphics files out of the original to get some art to work with, though at the prototype stage I'll be using some vectors to avoid wasting time polishing bitmaps that will probably not make it into the final build anyway. I'm also starting with little in the way of animation, but will build on that later.

Creating scenes and a background

I'm including all the source and steps for the prototype build. Feel free to follow along.

Starting a new project: 1. Open /quick/tool/quickLaunchPad.exe/app 1. Create a New Project 1. Click "Open Folder" to get to the project source 1. All assets including the Lua source live in "resources" by default, so open that folder.

NB: by default quick will package all files in that folder into your app when you deploy to device. Exisiting Marmalade users will notice that it's included in the project's "assets" block. If you're new to Marmalade we'll cover that stuff at a later date.

I'm going to use backdrop.png (ripped from my old game) so I've put that in resources/textures. To get started, edit main.lua (I recommend Notepad++ or the ZeroBrane Lua editor). This is the default main code file for Marmalade Quick.

-- app globals
appWidth = director.displayWidth
appHeight = director.displayHeight
battleCount = 0

-- Main menu --------------------------------------------------------------------

sceneMainMenu = director:createScene() --this becomes the current scene automatically

function sceneMainMenu:setUp(event) -- declare a member functions of the scene object
    dbg.print("sceneMainMenu:setUp")

    -- adding a label as member of the scene itself allows us to manage it easily
    self.label = director:createLabel({x=appWidth/2, y=appHeight/2, text="Main Menu"})
    self.label.x = self.label.x - self.label.xText/2
end
function sceneMainMenu:tearDown(event)
    dbg.print("sceneMainMenu:tearDown")
    self.label = nil -- good practice to nil unused objects
end

sceneMainMenu:addEventListener({"setUp", "tearDown"}, sceneMainMenu)

-- battle screen -------------------------------------------------------

sceneBattle = director:createScene()
function sceneBattle:setUp(event)
    dbg.print("sceneBattle:setUp")

    -- director:create makes the current scene the parent node. The scene keeps a reference to its children.
    -- director's coordinates (x=,y=) are relative to the parent, i.e. the scene in this case which always
    -- has (0,0) at the bottom left of the screen
    background = director:createSprite(0, 0, "textures/backdrop.png") --file paths are relative to the root of the "resources" folder

    -- debug label to check we're in right scene!
    self.label = director:createLabel({x=appWidth/2, y=30, text="Battle " .. battleCount})
    self.label.x = self.label.x - self.label.xText/2
end

function sceneBattle:tearDown(event)
    dbg.print("sceneBattle:tearDown")
    self.label = nil

    background:removeFromParent()
    background = nil

    dbg.print("sceneBattle:tearDown done")
end
sceneBattle:addEventListener({"setUp", "tearDown"}, sceneBattle)

------------------------------------------------------------------------------------

director:moveToScene(sceneMainMenu) -- start game with instantaneous change to main menu (last scene created is current)

-- simple global event to change current scene on touch
local touch = function(event)
    if event.phase == "began" then
        if director:getCurrentScene() == sceneMainMenu then
            battleCount = battleCount+1
            director:moveToScene(sceneBattle, {transitionType="slideInL", transitionTime=0.5})
        else
            director:moveToScene(sceneMainMenu, {transitionType="slideInL", transitionTime=0.5})
        end
    end
end
system:addEventListener("touch", touch)

A couple of notes:

  • I'm using global variables for the scene to keep it simple
  • setUp and tearDown are predefined valid event names for scenes
  • By declaring them as member variables using the ":" operator, we get an automatic "self" reference to the scene itself from within the function.
  • addEventListener registers a list of members (i.e. functions by name) for the given table (i.e. the scene)
  • Let's run the code. On the Test tab in the launchpad, make sure Debug mode is selected and hit the Launch button. You should get an assert message box pop up when clickting to change the scene. It's fairly obviously pointing to a missing texture file. If you hit continue or ignore, the app will eventually display an error message and quit. This is because the lack of texture is an unrecoverable error. Quick looks for assets in the "resources" folder, so save the image below to resources/textures/backdrop.png.

stars backdrop image

Debug vs Release

Try running again with Build Mode set to Release in the Launchpad. Note that in release mode you only get the error message and no asserts. But you still get a stack trace (in orange) in the console view.

Everything should work with the texture added, but the screen size probably doesn't match the background size (depending on if you've been fiddling with the Simulator previously...) Go to Simulator > Configuration > Surface and set the width and height to 640x480 to match our background. Restart the app with Ctrl-T. Ctr-T is needed to restart the whole app - rather than Ctr-R, which just reloads the lua code - since Quick has internally cached the screen size on startup.

We've got a huge image as our background and it's mostly black - a bit of waste of resources. Why not have a randomly generated star-field instead! In setUp, replace the background creation with:

    background = director:createRectangle({
        x=0, y=0,
        w=appWidth, h=appHeight,
        strokeWidth=0,
        color=color.black, alpha=1.0})

    math.randomseed(os.time())

    for n=0, 100, 1 do
        -- star is local to a single loop call, but on each call a new vector object is created
        -- Without "local", this would still work, but we'd have to do star=nil at the end or
        -- the final object would still be referenced at the end
        local star = director:createLines({x=math.random(0, appWidth), y=math.random(0, appHeight), coords={0,0, 1,1}, strokeWidth = 1})

        -- set start colour: get a random white value then allow some variance in each channel for off-white result
        local brightness = math.random(20, 127)
        star.strokeColor = {math.random(brightness-20, brightness), math.random(brightness-5, brightness), math.random(brightness-20, brightness)}

        background:addChild(star)

        -- NB: background.children is now a table with references to the star objects.
        -- star coords are now relative to background. We can now easily move them all by moving the background
    end

And in tearDown, before background:removeFromParent, add:

    for k,v in pairs(background.children) do
        v:removeFromParent() -- take object out of the scene, no references left so lua will garbage collect stars
    end

Quick Tip

When a Lua error occurs, the app will output info to the console but will usually continue running unless a serious error is encountered in the internal C++ code (usually due to your bad Lua code :p). This can make finding the error in the console awkward as trace will keep running way past the error itself.

A good solution is to add the following to your resources/app.icf file:

[s3e] WindowSuspendOnFocusLoss=1

For those new to Marmalade, app.icf is a standard Marmalade config file for setting runtime options. This option will suspend the app on Desktop when the window is out of focus (device builds are automatically paused on focus loss). With this set, you can just click on the trace console window to make the app window, and therefore all tracing, pause.

It's always worth scrolling back through the trace if it doesn't appear to tell you a lot. A Lua error may have occured long before you notice any side-effects on screen.

Notes on updates from Beta version to v1.0 release

  • myNode:destroy() was listed in the beta docs... this is an error as it doesn't actually exist! We "destroy" a Quick node by doing myNode:removeFromParent() then myNode = nil (see the background object in the code).
  • myNode.children is a table with references to each object added with myNode:addChild() - this is really useful but was not documented in the beta.
  • Rectangles and circles now have default anchor points of (0,0) like any other node. In the beta, they had undocumented (0.5,0.5) anchors so they were drawn centred on the x & y position but this was considered confusing: inconsistent with other shapes; children positions were non-intuitive as they ignore the anchor offset.

Adding balls and timers

First, we'll define some useful globals for managing the scene:

minX = -appWidth/2
maxX = appWidth/2
minY = -appHeight/2
maxY = appHeight/2
ballRadius = 8

In SceneBattle:SetUp(), we set up timers to add more balls. We also create an "origin" node object at screen center so that other objects can be positioned relative to this point by making them its children.

    ballSpeed = 1
    ballCreateFlag = 10 -- queues up balls to add at any time
    ballTimer = system:addTimer(addNewBall, 10, 0)
    ballReplaceTimer = system:addTimer(replenishBalls, 0.2, 0)
    origin = director:createNode({x=appWidth/2, y=appHeight/2})

Lets create a ball object (using the "class" method with metatable lookup). We'll manage ball movement ourselves as its so simple but can "upgrade" to Quick's built in physics (box2d) if needed.

Collidable = {}
Collidable.__index = Collidable

collidables = {}

function Collidable.Create(objType, xPos, yPos, startVector)
    local collidable = {}                -- the new object
    setmetatable(collidable,Collidable)  -- make Player handle lookup

    if objType == "expander" then --extend to ues for weapons
        -- do 
    elseif objType == "heatseeker" then
        -- do
        -- etc
    else
        collidable = director:createSprite({
            x=xPos,
            y=yPos,
            xAnchor=0.5,
            yAnchor=0.5,
            source="textures/wrongball.png"})

        tween:from(collidable, {xScale=0, yScale=0, time=0.5}) --simple fade in anim
        end
    collidable.vec = startVector
    collidable.objType = objType

    origin:addChild(collidable)
    collidables[collidable.name] = collidable --node.name is a unique ID so we can track balls in the scene easily
    return collidable
end

And finally implement the timer functions for adding new balls:

function VectorFromAngle(angle, size)
    return {x = (math.sin(angle) * size), y = (math.cos(angle) * size)}
end

local addBall = function()
    -- we push the ball straight to a global table in create so no need to get a ref here
    Collidable.Create("ball", 0, 0, VectorFromAngle(math.rad(math.random(0,359)), math.random(ballSpeed,ballSpeed+3)))
end

local addNewBall = function(event)
    ballSpeed = ballSpeed + 1
    addBall()
end

local replenishBalls = function(event)
    if ballCreateFlag > 0 then
        ballSpeed = ballSpeed + 0.3
        ballCreateFlag = ballCreateFlag -1
        addBall()
    end
end

Adding players, controls and health

We add a player class like the collidables. Note that we're doing cheap collision detecting so have hard coded some size values. There's also a simple text label for health and we jump straight back to the main menu on health hitting zero.

Player = {}
Player.__index = Player -- meta table to implement a "class" in lua

function Player.Create(id, health)
    local player = {}
    setmetatable(player,Player)

    -- initialize the object
    player.id = id -- player is just a table and we're assigning key-value pairs to it
    player.touch = {}
    player.touch.x = nil
    player.touch.y = nil
    player.velocity = 0
    player.moveWithFinger = false
    player.health = health

    player.touchPosDiff = nil
    player.halfHeight = 21 --for detecting collisions
    player.sledColour = nil

    -- Visual stuff
    -- Sleds are 8x42 with anchor at "front"
    -- y pos of sled used for movement
    local mirrorX = nil
    local xPos = nil

    if id == 1 then
        mirrorX = 1
        player.sledColour = color.fuchsia
        xPos = -appWidth/2 + 20
    else
        mirrorX = -1
        player.sledColour = color.yellow
        xPos = appWidth/2 - 20
    end

    -- for now, use cheap labels for health
    player.label = director:createLabel({x=20, y=appHeight-40, text=health})
    player.label.color = player.sledColour
    if id == 2 then player.label.x=appWidth-40 end
    player.sled = director:createSprite({x=xPos, y=0, xAnchor=0.5, yAnchor=0.5, source="textures/sledp" .. id .. ".png"})

    -- pre-calculate collision pos for balls to do super-cheap collision detection
    player.collideX = player.sled.x + mirrorX*ballRadius
    player.collideY = player.halfHeight + ballRadius --relative to sled

    origin:addChild(player.sled)
    return player
end

function Player:Destroy()
    self.sled:removeFromParent()
    self.label:removeFromParent()
end

-- some functions we'll add later
function Player:AddAmmo(amount)
end

function Player:AddHealth(amount)
end

function Player:Fire(weapon)
end

function Player:TakeHit() -- simple "flash" anim
    tween:to(self.sled, {alpha=0.2, time=0.1})
    tween:to(self.sled, {alpha=1.0, time=0.1, delay=0.1})
    self.health = self.health -1
    if self.health == 0 then
        director:moveToScene(sceneMainMenu, {transitionType="slideInR", transitionTime=0.5})
    end

    self.label.text = self.health
end

Back in our SceneBattler:SetUp event, we set up the players:

    player1 = Player.Create(1, 5)
    player2 = Player.Create(2, 5)

    players = {}
    table.insert(players, player1)
    table.insert(players, player2)

Collisions and Touch Control

Finally, we add some touch controls for player one and simple collision testing. The touch events move the sled exactly with the finger, and then apply some decelerated velocity on release.

function sceneBattle:touch(event)
    event.y = event.y - appHeight/2 --touch 0,0 always bottom left; align with our origin

    if event.phase == "began" then
        player1.touch.x = event.x
        player1.touch.y = event.y
        player1.velocity = 0
        player1.touchPosDiff = event.y - player1.sled.y
    end

    if event.phase == "ended" then
        player1.moveWithFinger = false
    end

    if event.phase == "moved" then
        xDiff = event.x - player1.touch.x
        yDiff = event.y - player1.touch.y
        player1.touch.x = event.x
        player1.touch.y = event.y

        player1.moveWithFinger = true
        player1.velocity = yDiff --on finger-off, continue moving (will decelerate)
    end
end

function sceneBattle:update(event)
    -- Sled Movement:

    -- move exactly with finger while finger is down
    if player1.moveWithFinger == true then
        player1.sled.y = player1.touch.y - player1.touchPosDiff
    else
        -- if finger is up, keep moving but decelerate
        player1.sled.y = player1.sled.y + player1.velocity

        if player1.velocity > 0 then
            player1.velocity = player1.velocity - 1
        elseif player1.velocity < 0 then
            player1.velocity = player1.velocity + 1
        end
    end

    -- keep within screen bounds
    if player1.sled.y > maxY - player1.halfHeight then
        player1.sled.y = maxY - player1.halfHeight
        player1.velocity = 0
    elseif player1.sled.y < minY + player1.halfHeight then
        player1.sled.y = minY + player1.halfHeight
        player1.velocity = 0
    end

    -- Balls:
    for k,obj in pairs(collidables) do
        -- movement:
        obj.y = obj.y + obj.vec.y
        obj.x = obj.x + obj.vec.x

        -- super simplistic bounce function. We put the ball on the screen edge rather than moving exactly
        if obj.x > maxX then
            obj.x = maxX
            obj.vec.x = -obj.vec.x
            -- bullets bounce once
            if obj.objType == "bullet" then obj.bulletFlag = 1 end
        end
        if obj.x < minX then
            obj.x = minX
            obj.vec.x = -obj.vec.x
            if obj.objType == "bullet" then obj.bulletFlag = 1 end
        end
        if obj.y > maxY then
            obj.y = maxY
            obj.vec.y = -obj.vec.y
        end
        if obj.y < minY then
            obj.y = minY
            obj.vec.y = -obj.vec.y
        end

        -- Collisions (cheap collisions, ignoring fact ball is rounded!)
        for pK,player in pairs(players) do
            playerCollideYTop = player.sled.y + player.collideY
            playerCollideYBot = player.sled.y - player.collideY

            if ((player.collideX < 0 and obj.x < player.collideX) or (player.collideX > 0 and obj.x > player.collideX))
                and obj.y < playerCollideYTop and obj.y > playerCollideYBot then

                player:TakeHit()
                local fx = director:createCircle({
                    x=obj.x,y=obj.y,
                    xAnchor=0.5,yAnchor=0.5,
                    radius=ballRadius,
                    color=color.lightBlue, strokeWidth=0})
                origin:addChild(fx)
                tween:to(fx, {radius=ballRadius*3, alpha=0, time=0.3, onComplete=NodeDestroy})

                print("destroy obj " .. obj.name)
                obj:removeFromParent()
                collidables[obj.name] = nil

                --replace destroyed ball
                ballCreateFlag = ballCreateFlag + 1
                break
            end
        end
    end
end

function NodeDestroy(target)
    target:removeFromParent()
end

In the battle setUp and tearDown events we register and cancel the event handlers:

    system:addEventListener({"touch", "update"}, sceneBattle)
    ...
    system:removeEventListener({"touch", "update"}, sceneBattle)

And that's it for our basic game logic.

new game screenshot

Testing on device

I'm not gonna cover setting up for device testing in this post (check the basics here: http://quick-docs.madewithmarmalade.com/usingthelaunchpad.html) but we have an issue that our game is fixed to 640x480 resolution.

Initially we'll take the easiest, but not the most elegant, way to deal with this and just fix to landscape and stretch our app to the screen. Open resources/app.icf and add the following:

[S3E]
DispFixRot=Landscape

[GL]
VirtualWidth=640
VirtualHeight=480
VirtualLetterbox=1

The source for the Wrong prototype v1 is up on Github here: github.com/nickmarmalade/wrong-prototype

Building a Marmalade Quick Game part 4: API Best Practice

This post goes into detail about all the important concepts and best-practice tips learnt in my initial few weeks working with Marmalade Quick. It takes a lot of concepts from my previous post on Lua and puts them into context with Marmalade Quick.

To find out more about Marmalade Quick, you can head over to this page where you'll get the latest on the new version, as well as links to download so you can start coding straight away. If you have any burning questions that aren't covered in this series, you can head over to our developer network, Marmalade Answers, where you'll find helpful tips from our community and can even post your own requests for help.

Arrays vs Tables

Variable length Lua arrays can be confusing in Lua! (see 'Tables as "Arrays"' in the Lua post)

If you don't need to use contiguous integer values to access items in a list (e.g. you don't need them in a given order when looping though) then you should probably just stick to plain tables. You can loop through items in a table with a for loop. If you need a unique value to use for the key to an item and your item is (or contains as a member) a Quick "node" object, then you can just use myNode.name to get a guaranteed unique ID to use as the key. Unlike an array, you can also safely nil a table value in a pairs() loop which makes destroying game objects easy.

Example:

myNodes = {}
node1 = director:createNode(...)
node2 = director:createNode(...)
myNodes[node1.name] = node1
myNodes[node2.name] = node2
for key,value in pairs(myNodes) do
    --value is the node
    --key is node.name

    value = nil
    -- This will only set the local reference "value", not affect the table!
    -- myNodes[key] will still return the node now, not nil.

    myNodes[key] = nil
    -- If this index referred to a table (it does since "nodes" are tables), the object is no longer referenced by mNodes and will be garbage collected when appropriate. Note that we still have a reference to it, e.g. "node1", so that wont happen till that's also nilled.
end

Using Object Oriented Design

Out of the box, Lua basically only supports values (numbers and strings) and key-indexed tables. But you can use tables to implement a wide range of other data types, including class-like objects. You'll almost definitely want to implement classes of some sort for a game. Quick's own object types like the node also provide their own object oriented functionality.

Creating Classes

Read 'Tables as "classes" and the hidden "self" reference' in the introduction to Lua blog post above for info on creating basic classes in Lua. That method uses metatables to create classes. Quick provides some helpers to make this easier, namely the baseClass global and the inheritsFrom() function. These implement the metatables trickery for you so you dont have to worry about how to do that. You can create new classes by just calling myClass = inheritsFrom(baseClass) and then implementing new() and init() functions for your class. You can then also do single inheritance using myOtherNewClass = inheritsFrom(myClass). See Lua and object orientation for a guide to doing this.

These methods works well for classes you create yourself, but you cannot use them to inherit from Quick's node objects...

Classes and Quick objects vs. Lua tables

NB: The "objects" and "libraries" provided with Quick look like lua tables. However, it is important to know that Quick objects are not actually tables! They are userdata types.

The userdata type allows arbitrary C data to be stored as a Lua variable. Userdata types are used to represent new types created by an application or library written in C. Internally, Quick uses Lua's C binding functionality to expose C objects (from the fast C++ Quick engine) to the Lua API, and it creates metadata types to do this. You don't need to understand how this works at all, but it means that while Quick's built in objects like nodes and sprites look like Lua tables, you cannot always use them in the same way as tables.

The notable constraints for Quick's metadata objects are:

  • Nodes are always created by the director, using director:createXXX() functions. This means that you cant just have an "empty" object that you can tweak values for before creating and pushing into the scene. They exist the moment the director creates them. You can however hide a visual node using myNode.isVisible=false or move it out of the current scene using myNode:removeFromParent() while keeping a handle to it until it is needed later.
  • You cannot use setmetatable() with metadata.

The two constraints above collectively mean that you cannot use Quick's node objects with inheritsFrom() or easily create a class that inherits a Quick object. That means that, for example, if you want to have a class that encapsulates all the data for your player in your game, you cannot have a Player class that inherits from a Quick sprite. You would create a Player class, and then have to have the sprite as a member of the Player.

You can directly access member values and functions of Quick's objects just as with tables. That includes non-documented internals - remember that Quick is open source so you can view and edit the Lua internals. Lua internal code all lives in the "quicklua" folder of your app. You can also add values, so for example with our Player example above you could create a sprite and then add a .player value to the sprite which points to the Player the sprite belongs to.

Quick's functions use either the "." operator or the ":" operator. Functions declared with ":" versions will be automatically passed a self parameter (a reference to the object/table the function is a member of) when they are called. Quick's "node" object and its descendants like "sprite" and "circle" have functions for which the self parameter will give you access to the unique node involved, which is useful in callback style situations.

Using Marmalade Quick's Node Object

If you need to create a class for some object in your app, remember that you cannot re-use/inherit a Marmalade Quick node for the reasons described above. You need to create a custom class and then add any nodes needed as members. For example:

myShip = {}
myShip.health = 10
myShip.img = director:createSprint(...)

There are many functions that trigger events - for example, node:addTimer() or tween:to(). These functions will have one parameter that is the function to call when they complete (a "callback"), for example the function when the timer expires (funcortable) or when the tween is over (onComplete). They also either take a reference to the node involved or implicitly know the node as they are member functions of it (e.g. myNode:addTimer). You will usually want a handle back to the object that the event is related to when the event occurs, for example if you set a timer after which you want an object to "explode". Quick handles this by passing an table called event to the callback function, where event.target is the object. Often you want a handle to your own game class, i.e. myShip above, and not the Quick node so you will have to create a pointer from the node back to your object.

myShip.img.ship = myShip --pointer from Quick object back to custom one
myShip.img:addTimer(Explode, 20, 1) --start a timer that will internally track the img node that called it
function Explode(event) --when timer ends, event.target is the img node that called it
    event.target.ship:MakeExplode() --we can get the ship back from the img via our .ship reference
end

Quick timer functions can either be called from the global system object or from a node (or descendant of node like vector, sprite, circle, etc.) If you use the node version (e.g. myNode:addTimer(myFunction,...) then the callback function (e.g. myFunction here) gets a handle back to the node itself via event.target as shown above. If the node gets destroyed then the timer will also automatically cancel itself (the timer gets nil-ed with the node). If you use system:addTimer(funcortable,...), the only way to track an object is by passing the object as the funcortable parameter (see Functions with "funcortable" parameters below). This is less flexible since the object needs to have a function matching the event name, so it is recommended to use node:addTimer, etc. if dealing with nodes.

Here's an example of a class using timers, a node and the inheritsFrom

function:
cSpaceShip = inheritsFrom(baseClass)

function cSpaceShip:new(health, ammo, startX, startY)
    local o = cSpaceShip:create()

    o.visualShip = director:createNode({x=startX, y=startY, ...})
    o.visualShip.ship = o
    o.health = health
    o.ammo = ammo
    ...
    add other visual stuff as children
    set other internal values
    etc
    ...
    return o
end

function RestoreHealth(event)
    event.target.ship.health = event.target.ship.health + 1
end

myShip = cSpaceShip:new(10,100, 50,50)
myShip:addTimer(RestoreHealth, 20, 0)

Marmalade Quick Parent and Child Nodes and Using Local Coordinate Spaces

Marmalade Quick uses descendants of nodes - circles, vectors, sprites, etc - to display and position visual objects. Each object of these types has a visual element, a position, various attributes, and can be made a child of other nodes. Children are positioned relative to their parent's x & y origin values (i.e. the values given as the parent's x= and y= constructor values). Nodes that aren't explicitly made a child of another node are children of the scene. The scene itself is a type of node and has its x & y origin at the bottom left of the screen.

All nodes are drawn with their bottom left corner at their x & y origin. One type that is slightly different is vectors/lines which has it's origin at it's specified x & y position, with its various points/lines relative to that position (since you can have negative points in a line).

Marmalade Quick has a concept of "anchors" which can be set for any object. Anchors move the object visually from it's x & y position, by a proportion of the size of the node itself. You specify x & y anchors as a values from 0 to 1, not in pixels. xAnchor=0 means position in default place. xAnchor=1 means move the "anchor" to the right by the width of the node, which effectively means that the image is drawn to the left of the default position, so that its right edge is at the nodes x,y position. Children of a node are positioned relative to the node's original x,y position and not its anchor offset. The point of anchors is basically to allow you to position sprites in a more intuitive way. For example a "wheel" sprite might want the anchor at (0.5,0.5) so it is centred at the node's x,y position.

NB: Anchors are not designed for creating some sort of local coordinate system for an object. Do not try to use them to position children relative to the parent!

For example, if you want to position a parent at (100,50) and a child at 10,10 from a specific point on the parent, you can't easily use the anchor to do that since children do not inherit the anchor offset. Also, the anchor values are fractions of the parent size, which is very unintuitive for positioning in pixels. You could use the anchors in this way, but then you also will likely end up setting the parent's x & y position to something unintuitive and are limited to 0-1*width or height to offset by.

What if you want to create a local coordinate space to position a group of objects relative to? The simplest example is if you want to have the centre of the screen as you scene origin. To achieve this, just use a non-visual "node" object (not a sprite, circle, etc) as the parent, by calling director:createNode(...). You can then treat this as a local origin by adding other nodes as children of it. Similarly, you might want a local origin so objects can rotate around some given point, and again a "dummy node" is the solution.

Example - A ship object, positioned by it's centre point and relative to the screen centre, with 2 gun objects that rotate around a point on the front of the ship: worldOrigin = director:createNode(appwidth/2,appheight/2)

myShip = director:createSprite({"myship.png", x=100, y=100, xAnchor=0.5, yAnchor=0.5})
worldOrigin:addChild(myShip)
-- ship png is facing upwards by default, using anchor to have the displayed sprite centred on its position.
-- children are still positioned relative to x & y (i.e. middle of the diplsyed ship) as they dont inherit anchors.

shipGuns = director:createNode(x=0,y=30)
myShip:addChild(shipGuns) --guns node now sits towards front of ship (30 pixel offset)

gunLeft = createSprite({"gun.png", x=-10, y=0, xAnchor=0.5, yAnchor=0})
shipGuns:addChild(gunLeft)
gunRight = createSprite({"gun.png", x=10, y=0, xAnchor=0.5, yAnchor=0})
shipGuns:addChild(gunRight) -- guns are positioned to sides of non-visual pivot point

Destroying Objects and Removing Them From the Scene

The correct way to "destroy" an object is to call myNode:removeFromParent(), or myParent:removeChild(myNode) and then make sure all references to myNode in your code are set to nil. At any point, an active node will have a parent node. This might be another node - e.g. one sprite is the child of another via node:addChild() - or it may be the current scene. director:createNode/Sprite/etc. makes the new node a child of the current scene (the scene itself is a type of node). Nodes only ever have one parent; if a node already has a parent, calling myNewParent:addChild(myNode) will automatically remove it from it's existing parent. Often the only variable pointing to a node will be another node's .children table so you won't have to explicitly nil anything after calling removeFromParent(). See Cleaning up scene texture resources - sprites, atlases, animations and fonts in the Quick docs for info on cleaning up any resources like textures once you have finished with them.

Events

In a nutshell, events work as follows:

  • The event itself is a table that is returned to a handler function in some specific situation.
  • You declare functions to handle events. These functions take a single parameter which the event object (table) will be passed to. Call the param what you want, but it's good practice to use "event"! e.g. myHandlerFunction = function(event).
  • You register for events by calling handler registration functions of relevant objects.
  • The objects in Marmalade Quick that handle events are:
  • The global "system" object - add handlers with system:addEventListener listOfEventNames, funcortable).
  • "nodes" - add handlers with myNode:addEventListener(listOfEventNames, funcortable).
  • There are also events specifically for "scenes" (a special type of node) which occur when director:moveToScene() is called.
  • For timer events, event.timer is a handle to the timer itself.
  • For timer or tween events that were called for a node, event.target is a handle to the node.

Functions with "funcortable" Parameters

Functions documented as having a "funcortable" parameter can be passed either:

  • a reference to a single function of any name
  • a reference to a table, which must contain a member function (or functions) named the same as the event(s) being registered

Typically, funcortables are useful when working in an object-oriented fashion. You can pass a class-style object as the funcortable value and give that object functions that match the expected names. Note that the event names are passed as a list (table) so you can register lots of events at once. Simple example.

sceneMainMenu = director:createScene()
sceneMainMenu:addEventListener({"setUp", "exitPostTransition"}, sceneMainMenu)

function sceneMainMenu:touch(event)
    if event.phase == "began" then
        director:moveToScene(nextScene, {transitionType="slideInR", transitionTime=1.0})
        system:removeEventListener("touch", sceneMainMenu)
    end
end

function sceneMainMenu:setUp(event)
    -- setup scene objects here
    system:addEventListener("touch", sceneMainMenu)
end

function sceneMainMenu:exitPostTransition(event)
    -- destroy scene objects here
end

Note that because the functions above are declared with ":" syntax, they can also access the sceneMainMenu object itself using "self". This is the main upside of using a table. A simple function is useful if you want to register lots of functions of the same type of event. For example if you want to add a timer to a node, passing the node as the funcortable value means you can only register a single function, myNode:timer, whereas you likely want to have multiple timer events on one object. In this case you need to use a plain function and store your own pointer from the timer to the node if needed.

Debugging and Testing

When a Lua error occurs, the app will output info to the console but will usually continue running unless an assert or serious error is encountered in the internal C++ code. This can make finding the error in the console awkward as trace will keep running.

A good solution is to add the following to your app.icf:

[s3e] WindowSuspendOnFocusLoss=1

This will suspend the app when the window is out of focus, so you can just click on the trace console to pause everything.

Note that you're app won't automatically suspend timers, animations, etc on suspend events. Timer callback events will queue internally and then fire in rapid succession when the app resumes! But you can pause all timers and tweens manually yourself using node:pauseTimers() etc.

Note: The info below is for Marmalade 6. With Marmalade 7.0 and newer, Quick has been integrated into the new shared "hub" GUI and can be used with most platforms out of the box. See Using the Hub in the official docs

The launchpad lets you pick "Debug" or "Release" mode with a radio button at the top for both Simulator and devices. Debug uses versions of the C++ Marmalade Quick pre-built binary and Marmalade loader (platform abstraction layer) that include tracing and asserts but will run slower; Release has no C++/native debug info or events. Note that option does not affect the Lua code at all! By efault dbg.print() will continue to output to console even in release mode.

To control lua debugging, you need to use config.lua (see http://quick-docs.madewithmarmalade.com/ConfiguringYourApp.html#config-lua-file) Essentially, Lua debugging features are mostly turned on by default and will slow your app down a lot, so for the store and when testing performance, you should do the following:

  • Add a config.lua file next to your main.lua
  • Set the following in config.lua:
config =
{
    debug =
    {
        general = false,
        assertDialogs = false,
        typeChecking = false,
        traceGC = false,
    }
}

In Marmalade Quick 1.0 when launching the Simulator from the Launchpad, even if you pick Release mode it will still use the debug loader which means there will be a performance hit - see below for a workaround. This does not affect device builds.

If you want someone to test your build without a Marmalade SDK or importing your Quick project, you can use the standard Deploy Tool (Advanced > Launch Deploy Tool) to do Windows/Mac x86 deployments if you have a Plus license or higher. The Deploy Tool is also useful as you can separately select between debug and release versions of the Marmalade loader (abstraction layer) and Quick app (C++ implementation of Quick). Release builds will run at full speed while debug ones will output internal debug info to trace but run slower. If you use the Deploy Tool release builds will use the release loader by default. If you have an Indie or community license, you can still pick Windows/Mac Simulator to test on desktop at full speed. The difference between Simulator and full deployment is that Simulator gives you settings menus and requires the SDK to run.

You probably want to test deployed builds at specific resolutions. Desktop deployed builds run windowed at 800x600 by default. Use [s3e] WinHeight= WinWidth= WinResizable= ICF options in app.icf to specify window size and behaviour when you don't have the Simulator menus.

Upgrading Projects From Older Quick Versions

We changed some APIs and broke back-compatibility between the beta and 1.0 Quick release version! If upgrading a project, you must replace the "quicklua" folder in the old project with a copy of the new one found in /quick/data/quicklua Some minor breaking changes were also added in Quick 1.1. See the change log in the Quick docs for more info.

What Happened to "Wrong"?

My little Weaponified Reverse pONG game is moving along slowly. As a proof of concept, I've uploaded a version (that's slightly more advanced than the one in part 3...) to the BlackBerry World store for BlackBerry 10. Treat that as a preview of the weapons, effects and retro styling I'll be covering in the next post: View and download Wrong for BlackBerry 10

Authors and Contributors

The prototype and blog are both written by Nick Smith @nickmarmalade.