Showing posts with label lua. Show all posts
Showing posts with label lua. Show all posts

Friday, July 5, 2013

Tutorial 31 - Codea Collision Detection



31.1 Overview


Collision detection is a common requirement in game or simulator programming. It involves determining whether two or more objects have intersected. We will only be examining this problem in 2D (two dimensions) for this tutorial.

There are two main approaches to detecting collisions, discrete and continuous. In the discrete case, we advance the physical model by a small time step, and then check if any of our objects are intersecting, or are so close to each other that for all intents and purposes we consider them intersecting. For the continuous case, we write a collision detection algorithm which will be able to predict the trajectories of the physical bodies. The instants of collision are calculated, and the physical bodies never actually have to collide for us to detect the collision. 

The continuous approach is more accurate but is also more difficult. So for this tutorial, we will develop a few different discrete detection algorithms. Discrete detection will get the job done in many situations and is well suited to the Codea draw loop architecture. As long as your time steps are small enough, there aren't too many objects and the objects are large enough, you shouldn't miss any collisions.

Note that collision detection can chew up a lot of CPU time. If n is the number of objects on the screen that we need to test, then the:

Number of collision detection tests = (n² - n) / 2

Thus the number of tests increases exponentially with the number of objects being modeled. Consequently, if you have more than a few objects, you will want to look at optimising your tests.

31.2 Bounding Box Detection


If we have two rectangular objects with origins at (x1, y1) and (x2, y2) and dimensions (i.e. width and heights) of (w1, h1) and (w2, h2), then we can use the following function to determine whether the two rectangles are overlapping at a particular point in time.

function CollisionDetected(x1,  y1,  w1,  h1,  x2,  y2,  w2,  h2) 

  Collision = false

 
  if (y2 >= y1 and y1 + h1 >= y2) or (y2 + h2 >= y1 and y1 + h1 >= y2 + h2) 
    or (y1 >= y2 and y2 + h2 >= y1) 
        or (y1 + h1 >= y2 and y2 + h2 >= y1 + h1) then
     if x2 >= x1 and x1 + w1 >= x2 then          -- corner 1
        Collision = true
     end
     if x2 + w2 >= x1 and x1 + w1 >= x2 + w2 then  -- corner 2
        Collision = true 
     end
     if x1 >= x2 and x2 + w2 >= x1 then           -- corner 3
       Collision = true 
     end
     if x1 + w1 >= x2 and x2 + w2 >= x1 + w1 then  -- corner 4
       Collision = true
     end
  end

  return Collision -- return whether or not a collision is detected 


end
 


A faster version (about 10%) of the same function (albeit a bit more difficult to follow) is:

function CollisionDetected(x1,  y1,  w1,  h1,  x2,  y2,  w2,  h2) 
return not ((y1+h1 < y2) or (y1 > y2+h2) or (x1 > x2+w2) or (x1+w1 < x2))
end

And here is another version:

function CollisionDetected(x1,  y1,  w1,  h1,  x2,  y2,  w2,  h2) 
    local ax2, bx2, ay2, by2 = x1 + w1, x2 + w2, y1 + h1, y2 + h2
    return ax2 > x2 and bx2 > x1 and ay2 > y2 and by2 > y2
end

We will leave it as an exercise for the reader to demonstrate that these functions are all logically equivalent.


31.3 Bounding Circle Detection


The simplest method to detect circles colliding is to check whether the circles are overlapping. This can be done by calculating the distance between the centers of the two circles and seeing if it is less than or equal to the sum of the radii of the circles. If it is then a collision has occurred. 

function CollisionDetected(x1, y1, r1, x2, y2, r2)

    local dx = x2 - x1

    local dy = y2 - y1

    return math.sqrt(dx^2 + dy^2) <= r1 + r2


end


The square root in the distance formula is unnecessary because the inequality will still hold if we square both sides. Removing the square root will increase your collision detection speed, especially if you are checking for many collisions, because this is a relatively expensive calculation. 

function CollisionDetected(x1, y1, r1, x2, y2, r2)

    local dx = x2 - x1

    local dy = y2 - y1

    return (dx^2 + dy^2) <= (r1 + r2)^2


end





31.3 Box2D Collision Detection


If you don't want to roll your own collision detection, then you can utilise the built in collision detection functionality of Codea, which is provided courtesy of its Box2D implementation. To use this, the bodies which are colliding need to be created using the physics.body() function.  The downside of using this approach is that physics bodies can only have the following shapes (the property is called shapeType):
  • Circle; 
  • Polygon; 
  • Chain; or 
  • Edge (usually used for the ground or walls).

So for pixel perfect collisions you will need to use another approach if your sprites can't be outlined using one of the shapes above. In many cases however, you can approximate the sprite shape using these.

In addition to the shape type, we need to define the body type for our physics object. There are three options:
  1. Dynamic - these objects move under the influence of collisions, forces, joints and gravity (we will use this type for our asteroids);
  2. Static - these objects are not supposed to move and are unaffected by collisions and forces (e.g. the ground or walls); and 
  3. Kinematic - these objects can move by setting their linear velocity but like static bodies are unaffected by collisions and forces (we will use this type for our ship, if the ship collides with an asteroid we want it to explode not bounce away). 
To demonstrate how easy this is to implement we will create a simple Asteroids game with a ship in the center of the screen and randomly generated asteroids, which will destroy our ship if we detect a collision.

While Box2D will model the movement of our physics bodies for us, it is our responsibility to render (i.e. draw) representations of these bodies on the screen. To do this we will use a simplified version of the PhysicsDebugDraw class (called PhysicsDraw) which is included with the Physics Lab example. A copy of this simplified class is included at the end of the tutorial.

function setup()

    physics.gravity(0.0, 0.0)
    physicsDraw = PhysicsDraw()
    ship = createShip(WIDTH/2, HEIGHT/2)
    physicsDraw:addBody(ship)
    asteroidTimer = 0

end


The physics.gravity function allows us to set the gravity for our game for the x and y axis respectively. The units are pixels per second squared. We have set our program gravity to zero since we are in space. The createShip function will create a new physics body which we add to the table of bodies in PhysicsDraw. Every draw cycle, PhysicsDraw will render all the physics bodies at their current location.

function createShip(x, y, w, h)

   -- (x, y) are the centre co-ordinates of the ship.
   -- (w, h) define the width and height of the box containing the ship,
   -- if not specified they are 50 and 35 respectively.

   local width = w or 50
   local height = h or 35
   local shipBody = physics.body(POLYGON, 
                                  vec2(0, height), vec2(width, height/2), 
                                  vec2(0, 0), vec2(height/2, height/2), 
                                  vec2(0, height))

   shipBody.x = x
   shipBody.y = y
   shipBody.type = KINEMATIC
   shipBody.sleepingAllowed = false
   shipBody.info = "ship"
   shipBody.interpolate = true

   return shipBody


end

Next up we need functions to create our asteroids. We will use the same function (createRandPoly) to generate the debris field for when our ship collides with an asteroid and explodes.

function createRandPoly(x, y, s1, s2)

   -- s1 and s2 define the range of length for the poly sides
   -- count defines the number of sides of the poly

   local minLength = s1 or 1
   local maxLength = s2 or 5
   local count = math.random(5,10)
   local r = math.random(minLength, maxLength)
   local a = 0
   local d = 2 * math.pi / count
   local points = {}

   for i = 1,count do
       local v = vec2(r,0):rotate(a) 
                     + vec2(math.random(-10,10), math.random(-10,10))
       a = a + d
       table.insert(points, v)
   end

   local poly = physics.body(POLYGON, unpack(points))

   poly.x = x
   poly.y = y
   poly.type = DYNAMIC
   poly.sleepingAllowed = false
   poly.restitution = 0.5
   poly.info = "poly"
   physicsDraw:addBody(poly)

   return poly

end

function createAsteroid(x, y)

   return createRandPoly(x, y, 25, 65)


end

The asteroids are placed off the screen in random locations and accelerated towards the screen where eventually one of them will collide with our ship. Inside the draw() function we create a new asteroid every second.

function placeRandomAsteroid()

   -- Generate (x, y) co-ordinates which are
   -- initially off the screen.

   local x = math.random(WIDTH)

   if x < WIDTH/2 then
       x = x - WIDTH
   else
       x = x + WIDTH
   end

   local y = math.random(HEIGHT)

   if y < HEIGHT/2 then
       y = y - HEIGHT
   else
       y = y + HEIGHT
   end

   -- Create an asteroid at the new co-ordinates

   local asteroid = createAsteroid(x, y)

   -- Give the asteroid a linear velocity
   -- towards the screen (and our ship)

   local dX, dY = math.random(50, 100), math.random(50, 100)

   if x > WIDTH then
       dX = -dX
   end

   if y > HEIGHT then
       dY = -dY
   end

   asteroid.linearVelocity = vec2(dX, dY)

end

function createExplosionAt(x, y)

   -- Creates 6 small polygon moving out from (x, y)
   -- to simulate our ship exploding.
   --
   -- the body.info field is set to debris so that we
   -- can render it the same colour as our ship (i.e. to
   -- distinguish them from the asteroid polygons.

   randPoly1 = createRandPoly(x, y)
   randPoly1:applyForce(vec2(50,50))
   randPoly1.info = "debris"
   randPoly2 = createRandPoly(x, y)
   randPoly2:applyForce(vec2(50,50))
   randPoly2.info = "debris"
   randPoly3 = createRandPoly(x, y)
   randPoly3:applyForce(vec2(50,50))
   randPoly3.info = "debris"
   randPoly4 = createRandPoly(x, y)
   randPoly4:applyForce(vec2(-50,50))
   randPoly4.info = "debris"
   randPoly5 = createRandPoly(x, y)
   randPoly5:applyForce(vec2(-50,50))
   randPoly5.info = "debris"
   randPoly6 = createRandPoly(x, y)
   randPoly6:applyForce(vec2(-50,50))     
   randPoly6.info = "debris"   

end

-- This function gets called once every frame

function draw()

   -- This sets a dark background color 

   background(40, 40, 50)

   -- Generate a random asteroid every second

   asteroidTimer = asteroidTimer + DeltaTime

   if asteroidTimer > 1.0 then
       placeRandomAsteroid()
       asteroidTimer = 0
   end

   -- Do your drawing here

   physicsDraw:draw()

end

-- Collision Detection
--
-- This is simply a matter of checking whether one of the two
-- colliding bodies are our ship.

function collide(contact)

   if contact.bodyA == ship or contact.bodyB == ship then
       createExplosionAt(ship.x, ship.y)
       ship.info = "destroyed"
       ship = nil
   end


end

Collision detection is then just a matter of calling the built in collide() function and checking whether one of the two colliding bodies is our ship. Included below is the simplified PhysicsDebugDraw class which is responsible for rendering our physics bodies each frame.

--# PhysicsDraw

PhysicsDraw = class()

-- Simplified Physics Debug Draw class from the Physics Lab
-- example in Codea.

function PhysicsDraw:init()
   self.bodies = {}
   self.contacts = {}
end

function PhysicsDraw:addBody(body)
   table.insert(self.bodies,body)
end

function PhysicsDraw:draw()

   pushStyle()
   smooth()
   noFill()

   for i,body in ipairs(self.bodies) do

       pushMatrix()
       translate(body.x, body.y)
       rotate(body.angle)

       if body.type == STATIC then
           stroke(255,255,255,255)
       elseif body.type == DYNAMIC and body.info ~= "debris" then
           stroke(150,255,150,255)
       else
           stroke(150,150,255,255)
       end

       if body.shapeType == POLYGON and body.info ~= "destroyed" then
           strokeWidth(3.0)
           local points = body.points
           for j = 1,#points do
               a = points[j]
               b = points[(j % #points)+1]
               line(a.x, a.y, b.x, b.y)
           end
       elseif body.shapeType == CHAIN or body.shapeType == EDGE then
           strokeWidth(3.0)
           local points = body.points
           for j = 1,#points-1 do
               a = points[j]
               b = points[j+1]
               line(a.x, a.y, b.x, b.y)
           end      
       elseif body.shapeType == CIRCLE then
           strokeWidth(3.0)
           line(0,0,body.radius-3,0)            
           ellipse(0,0,body.radius*2)
       end

       popMatrix()
   end 

   stroke(255, 0, 0, 255)
   fill(255, 0, 0, 255)

   for k,v in pairs(self.contacts) do
       for m,n in ipairs(v.points) do
           ellipse(n.x, n.y, 10, 10)
       end
   end

   popStyle()
end

function PhysicsDraw:collide(contact)
   if contact.state == BEGAN then
       self.contacts[contact.id] = contact
       sound(SOUND_HIT, 2643)
   elseif contact.state == MOVING then
       self.contacts[contact.id] = contact
   elseif contact.state == ENDED then
       self.contacts[contact.id] = nil
   end

end

A complete download of the code presented in this tutorial is available from our Gist repository.

Thursday, April 25, 2013

Tutorial 30 - Codea v1.5.2: Objective C Add On, Game Center

Figure 0. iOS Dev Center


30.1 Overview


CodeaAddon is an experimental protocol for adding native extensions to exported Codea projects. You must deal with Lua directly to register your functions and globals. This protocol should be considered alpha and is subject to change in future Codea releases.As with the previous tutorial on iAds, we have provided a lot of background on Game Center in these tutorials:
Have a look at the above to refresh your knowledge on what is required from an iTunes Connect perspective as we will only cover that area briefly in this tutorial.


Figure 1. Find out the Bundle Identifier for your App.


30.2 Registering your Application in iTunes Connect


In order to test your app's Game Center functionality you need to register an app ID associated with this app which enables Game Center. The association is done via the bundle identifier of your app (Figure 1). Click on the app title in the top left of the project navigator screen in Xcode to bring up this screen. Make sure that you have selected the "Summary" tab. Write down the bundle identifier shown, we will need this shortly.

Figure 2. Set up a new App ID for your app.

Log into your apple developer account, go to the iOS Dev Center (Figure 0) and select the Certificates, Identifiers & Profiles link on the right hand side of the page. Select a name for your app ID (pick something you can remember e.g. we used AudioDemoAppID) and fill in the App ID Suffix bundle identifier. This is the critical step which will link your app to the configuration in iTunes Connect. Use the bundle identifier that you wrote down earlier. Click "confirm" and then "submit".

Figure 3. Associate your bundle identifier with the App ID.

Head back to the iOS Dev Center (Figure 0) and this time click on iTunes Connect link in the top right of the page. You will be asked to sign in again, do so and then click on the Manage your Apps link (Figure 4).

Figure 4. iTunes Connect

On the Manage your Apps page (Figure 5), click on the "Add New App" button at the top left of the screen. Click on iOS App on the next screen.

Figure 5. Manage your Apps

Fill in the app name, SKU number (this can be any unique identifier, we normally use the date), and then select the App ID that you just created for the bundle identifier (Figure 6). Click "Continue" when you are done.

Figure 6. iTunes Connect App Information.

Fill in all the meta data (have a look at the earlier tutorials if you get stuck) and save the configuration. When you are done (Figure 7), we can enable Game Center for your app. Click on the "Manage Game Center" button on the right hand side of the page (Figure 7).

Figure 7. Metadata addition complete.

Click on the button that says "Enable for Single Game" and then either configure some achievements and leader boards or just click "Done". We can now test our exported app once we have included the Game Center add on.

Figure 8. Adding the GameKit Framework.


30.3 Add the GameKit Framework to Your App


Fire up Xcode and load the exported version of your Codea application (See Tutorial 27). Click on the imported project file at the top of the project navigator then in the Build Phases tab, scroll down to the link binary with libraries area and select the drop down arrow. Click on the "+" button below your existing frameworks to add a new framework. Find GameKit and click on "Add"


30.4 Changes to your Exported AppDelegate Files


All of the source code files are provided at the end of this tutorial. You need to update AppDelegate.h as follows:

//
// AppDelegate.h
// AudioDemo
//
// Used to demonstrate the audio, game center and iAds add on libraries
//
// Created by Reefwing Software on Sunday, 14 April 2013
// Copyright (c) Reefwing Software. All rights reserved.
//

    #import <UIKit/UIKit.h>
    #import "AudioAddOn.h"
    #import "IAdsAddOn.h"
    #import "GameCenterAddOn.h"

    @class CodeaViewController;

    @interface AppDelegate : UIResponder <UIApplicationDelegate>

    @property (strong, nonatomic) IAdsAddOn *iAdsAddOn;
    @property (strong, nonatomic) AudioAddOn *audioAddOn;
    @property (strong, nonatomic) GameCenterAddOn *gameCenterAddOn;
    @property (strong, nonatomic) UIWindow *window;
    @property (strong, nonatomic) CodeaViewController *viewController;

@end


And AppDelegate.mm, should now look like:

//
// AppDelegate.mm
// AudioDemo
//
// Created by Reefwing Software on Sunday, 14 April 2013
// Copyright (c) Reefwing Software. All rights reserved.
//

#import "AppDelegate.h"
#import "CodeaViewController.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.viewController = [[CodeaViewController alloc] init];

    // Create and add our AudioAddOn to Codea

    self.audioAddOn = [[AudioAddOn alloc] init];
    [self.viewController registerAddon: self.audioAddOn];

    // Create and add our iAdsAddOn to Codea

    self.iAdsAddOn = [[IAdsAddOn alloc] init];
    [self.viewController registerAddon: self.iAdsAddOn];

    // Create and add our GameCenterAddOn to Codea

    self.gameCenterAddOn = [[GameCenterAddOn alloc] init];
    [self.viewController registerAddon: self.gameCenterAddOn];

    NSString* projectPath = [[[NSBundle mainBundle] bundlePath]    stringByAppendingPathComponent:@"AudioDemo.codea"];

    [self.viewController loadProjectAtPath:projectPath];

    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];

    return YES;

}

- (void)applicationWillResignActive:(UIApplication *)application
{

}

- (void)applicationDidEnterBackground:(UIApplication *)application
{

}

- (void)applicationWillEnterForeground:(UIApplication *)application
{

}

- (void)applicationDidBecomeActive:(UIApplication *)application

}

- (void)applicationWillTerminate:(UIApplication *)application
{

}

@end


Obviously there is no need to register the iAds and Audio add ons if you are just using Game Center. We have just left these in to demonstrate how you can stack add ons.



Figure 9. If you try to show Achievements or Leaderboards with none set up in iTunes Connect, you will see this screen.

30.5 The Game Center Add On


You need to add the GameCenterAddOn.h and GameCenterAddOn.m files to your project (plus the other iAds and Audio add on classes if you are using them). These are available below.

To do this, right click on the Addons folder in the project navigator and select Add files to "YourProjectName"... Navigate to where ever you saved these files and select them.

This Game Center Add On will make four new functions available in your Lua code:

  • gameCenterStart(); 
  • showLeaderBoardWithIdentifier(int ident); 
  • showAchievementsView(); and
  • playerIsAuthenticated;

You need to call gameCenterStart() first as your game must authenticate a local player before you can use any Game Center classes. 


Figure 10. You may need to sign in the first time you try to authenticate a player.

If you want to add more Game Center functionality the just follow the pattern in the add on. For example, if you want to add a save score function then, in GameCenterAddOn.h add:

static int saveScore(struct lua_State *state);

Then in GameCenterAddOn.m add/modify:

// Add to method

- (void)codea:(CodeaViewController*)controller didCreateLuaState:(struct lua_State*)L
{
    ...
    lua_register(L, "saveScore", saveScore);
    ...
}

// New Objective C method

- (void) saveNewScore: (int) score
{
    // INSERT YOUR LEADERBOARD IDENTIFIER IN THE LINE BELOW
    // Replace "Easy Difficulty" with your identifier from iTunes Connect

    GKScore *scoreReporter = [[GKScore alloc] initWithCategory: @"Easy Difficulty"];

    if (scoreReporter)
    {
        scoreReporter.value = score;

        [scoreReporter reportScoreWithCompletionHandler: ^(NSError *error)
        {
            if (error != nil)
            {
                // handle the reporting error

                NSLog(@"Game Center: Error Saving Score - %@", [error localizedDescription]);
            }
        }];   
    }
}

// New C function

static int saveScore(struct lua_State *state)
{
    [gameCenterAddOnInstance saveNewScore: lua_tonumber(state, 1)];

    return 0;
}

Then in your Lua code you can use saveScore(yourNewScore).

Figure 11. Player Authenticated.


30.6 Download the Code



Sunday, January 27, 2013

Tutorial 25 - A Lua Primer for Codea (Part 1)

25.1 Introduction


Every year around Christmas there is a surge of activity on the Codea Forums and on this tutorial site. A common theme in the comments and emails that we receive, is that this site assumes quite a bit of knowledge and as Codea appears to be attractive to first time programmers there are some gaps which need to be filled.

This tutorial is an attempt to fill in some of those gaps. In particular, we will try and provide a quick primer on the Lua language which Codea uses. To ensure that Codea does not incur the wrath of Apple, there are some features of the Lua language which have been disabled in Codea. We will discuss these briefly but the list is diminishing with every new release of Codea and there isn't anything which will likely cause you any problems.


25.2 Lua


Lua is a light weight scripting language written in C. By light weight we don't mean that it isn't capable of writing complicated code but that the syntax is stream lined and straight forward. This makes it a great first language to learn. 

Lua means "moon" in Portugese, it is the evolution of another language called SOL (Portugese for "sun"). It was created in 1993 by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes, members of the Computer Graphics Technology Group (Tecgraf) at the Pontifical Catholic University of Rio de Janeiro, in Brazil (hence the Portugese naming).


25.3 Variables and Data Types


Variables are memory locations that hold data. There are three kinds of variables in Lua: global variables, local variables, and table fields. All variables are global unless defined otherwise. Defining an identifier as local is done using the local keyword. Local identifiers are not visible outside the block in which they were declared, but are visible inside sub-blocks. This is called lexical scoping. For Codea a tab is considered a chunk,  which translates as follows:
  1. If you define a variable within any of the tabs it will, by default be treated as global.
  2. If you define a local variable within a tab (but outside a function), you will only be able to access it within that tab.
  3. If you define a local variable within a function, you can only access it from within that function. However, if a function is enclosed in another function, then it has access to all the local variables of that function. In these circumstances, the external local variable is called an "upvalue". We cover this in Interlude 7.
Assigning to a variable that has not been declared locally within the current chunk will recursively search for that name in the parent chunk, up to the top-level. If the name is found, the assignment is made to that variable. If the name is not found, the assignment becomes a global (either creating a new variable, or replacing an existing global). The consequence of this behaviour is that using local variables is much faster than globals.

Global identifiers are stored in the implicit global environment table, which can explicitly be accessed through the name _G (see section 25.5).  

Variable names must start with a letter or underscore and can contain letters, digits or underscores. Defined keywords in Lua can not be used as a variable name (e.g. and, break, do, else and end). As a convention, variables in capitals starting with an underscore (e.g. _G or  _VERSION) are reserved for internal Lua global variables.

Lua supports only a small number of data types. The ones you will use the most are boolean (true or false), numbers, strings and tables. 

By default, Lua's number type is represented by double-precision floating-point numbers. However, Codea's Lua interpreter uses another internal representation for numbers: single-precision float. This gives a precision of 6 to 9 significant decimal digits and a range for positive values of between about 1.4e−45 to about 3.4e+38 (reference from Codea Wiki).

Lua strings can hold any 8-bit character, including embedded zeros. Strings can be enclosed in single or double quotes, pick one style and stick with it, we tend to go with double quotes. Strings in Lua are immutable values, and thus you cannot change a character inside a string. You can also delimit literal strings using matching double square brackets. For example:

aLongString = [[
                   line 1
                   line 2
                   line 3
                   line 4
              ]]

Boolean variables are a relatively recent introduction to Lua. The boolean type has two values, true and false. Conditional tests consider false and nil as false and anything else as true (including 0 and "").

Lua is a dynamically typed language, which means that variables do not have a defined type, they get their type based on the data assigned to them. This means that the following is a valid chunk of code in Lua (albeit probably not best practise as it would make your program hard to follow):

x = 1
print("x as an integer: " .. x)
x = 3.141592654
print("x as a float or real number: " .. x)
x = 0xFE
print("x as a hexadecimal converted to decimal: " .. x)
x = "now a string"
print("x as a string: " .. x)
x = true
print(x)
x = {value = 999}
print("x as a table: " .. x.value)
x = function(n) return n*2 end
print("x as a function: " .. x(2))

A few comments about the preceding code and some general observations on variables: 
  • Lua is case sensitive, so x and X are different variables. 
  • In the example above, when we store a hexadecimal value in x it is automatically converted and printed as a decimal.
  • You can concatenate (i.e. join) two strings or a string and a number using the".." operator. Numbers are automatically converted to strings in this situation. You can't concatenate a boolean.
  • This automatic conversion (or to use the technical term - coercion,  works the other way around as well, a string will be treated as a number if used in that context. Any arithmetic operation applied to a string tries to convert this string to a number. Note that comparison operators (== ~= < > <= >=) do not coerce their arguments. Thus a number is not equal to its string representation.
  • Each line of code is terminated by a new line. You can optionally use a semi-colon (as used in C, but it isn't recommended). 
  • You can do multiple assignments on the one line (e.g. x, y = 1, 2), and you can use this to swap two variables (e.g. x, y = y, x).
  • A variable that hasn't been assigned a value will be nil by definition. 
  • Unlike C, the value 0 is not a false test condition in Lua, only nil or false is. You can use the fact that nil equates to false to assign default values to a variable (e.g. x = x or 2 will assign the variable x a value of 2 if it hasn't been previously assigned a value).
  • Note that you can assign a function to a variable. As an example, we make use of this in Codea to pass call back functions to our Button class to indicate what function to call when a button is tapped. The technical term for this feature is first-class functions. As such, they can be created during runtime, stored in variables, and passed to and returned from other functions.
There are two other data types called userdata and thread. Userdata and tables will be covered in subsequent sections. Thread is way outside the scope of this tutorial so we are just going to ignore them.


25.4 Tables


Tables in Lua are amazingly versatile. Which is just as well since they are the only built in composite data type available. The technical term for tables in Lua is a hashed heterogeneous associative array and they are worthy of a separate tutorial, which is exactly what we have done. You can access our tutorials on tables here:
  1.  Understanding Tables;
  2.  Converting a string to table and table to string
  3.  Saving and Loading complicated tables; and
  4.  Classes in Lua and Codea.

25.5 The Global Variable Table _G


This section goes beyond the scope of this tutorial, so feel free to skip ahead. It is included here out of interest. We mentioned above that Lua has a number of internal variables. One of these is _G, a global variable which points to the global environment. It includes all of the global variables and functions and even includes a reference to itself! It is sometimes useful to understand what has been defined globally and you can use the following to display the contents of _G: 

for k, v in pairs(_G) do print(k, v) end


25.6 Userdata


Userdata are variables that encapsulate arbitrary C/C++ data within a Lua interface. Many Lua modules extend the capabilities of Lua by binding external libraries, including the creation of new types as userdata. Userdata variables can only be created using the C API (i.e. in our context this means the Codea runtime), this can't be done in Lua.

Userdata is largely outside the scope of this tutorial but you need to know that Codea extends Lua with 12 user-defined types such as codeaimage, mesh, matrix, vec2, vec3, touch and color. You can read all about these on the Codea User Defined Types page.

Userdata is also useful when we want to expose C/Objective C functions in Lua via the runtime. Have a look at our tutorials on integrating Game Centerbuilding a Universal App or implementing iAds if you are interested.


25.7 Operators


Lua supports the following arithmetic operators:  + (addition), - (subtraction), * (multiplication), / (division), % (modulo),  ^ (exponentiation) and  - (negation).

The relational operators in Lua are: == (equal), ~= (not equal), < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to). These operators will return a boolean (true or false).

The logical operators in Lua are and, or, and not. As for control structures, all logical operators consider both false and nil as false and anything else as true.

As mentioned in section 25.3, the string concatenation operator in Lua is denoted by two dots ('..'). If either operand is a number, then it is converted to a string before joining.

The length operator is denoted by #. The length of a string is its number of bytes (i.e. characters). The length of a table is more complicated. For a table used as a simple array the length operator will work as expected and return the number of elements in the array. For more complicated tables, the returned length can be any of the indices that directly precedes a nil value. You can read more about this in our tutorial on tables.

25.8 Classes


In Interlude 11 we spoke about the use of classes in Codea. Codea comes with a built-in global function called class() that is used to emulate the functionality of a class data structure using a table (and metatable). We use classes extensively in our tutorials but we haven't explained the difference between the "." and ":" operators when applied to a class. As this has been the subject of a number of questions, we will cover the proper usage in this section.

In order to demonstrate, we will first construct a simple ship class. By convention the base class name starts with a capital, while instances of the class have names which start in a lower case letter. 

The Codea class() constructor is a function which, when called, sets up a new table and attaches the class metatable to it. The metatable redirects unrecognized events to the class method table (as well as possibly handling events itself). This is covered in some depth on the Codea wiki.

Ship = class()

function Ship:init( hitPoints )
    self.points = hitPoints or 100
end

function Ship:hit( damage )
    self.points = self.points - damage
    print( self.points )
end

In our Main tab, setup() function we may then instantiate our class as follows:

myShip = Ship()

This will create a new object called myShip of class Ship. Note that we called ship without any parameters. This means that hitPoints will be nil and self.points will be assigned 100 (our default value) since nil is interpreted as false by Lua. 

One of the potentially confusing parts of the class definition is where does "self" come from and why do we use it? The self variable is created automatically by calling class() and provides a reference to the object created when we instantiate a class. This means that when we change the points variable using self.points we only change it for that object, not for every object of class Ship. So in our example above self = myShip. 

Try converting the Ship class so that it uses points instead of self.points. If you do this and have created another ship (e.g. myOtherShip = Ship()), then every time you call myShip:hit(20) this will also reduce the hit points of myOtherShip, which is usually not what you want.

If our ship gets hit then we want to record the damage and print out the current hit points using the hit(damage) function of our class. There are two ways you could do this (the right way and the wrong way!). In the Main tab of your program, you could use:

myShip:hit(20)

or you could try to use:

myShip.hit(20)

Which is correct? To work this out we have to understand the difference between the two statements. Using ":" we are actually passing two parameters to hit(), the damage and a hidden reference to the object being hit (i.e. self). So myShip:hit(20) is equivalent to myShip.hit(self, 20). 

The second option - myShip.hit(20) will throw an error in Codea. If you want to access the class instance variables directly from the Main tab, you can do something like:

myShip.points = myShip.points - 20

This will operate as expected, but only if you have defined it as self.points in your class.


25.9 Other Resources


If what is provided here isn't sufficient then have a look at the official Lua Tutorial site, which has a MUCH more detailed treatment on Lua. The Codea reference documentation is also very useful, as is the active forum and wiki (in particular have a look at the Hints and Tips page which contains references to items not covered elsewhere). 

Codea (v1.4) uses version 5.1 of Lua (you can determine the current version of Lua using the statement print(_VERSION)). The definitive treatment of the Lua language can be found at the official on-line Lua 5.1 Reference Manual