Corona – Game Loops part 3

Following up on my previous two posts, this discussion will continue with the game loop topic. Here I’ll create a couple game objects, or actor objects, that will work with the game loop.

As previously discussed, actors work need to interface with the game loop by implementing the update(deltaTime) method. This interface is just a function that each actor object must posses. How the actors implement this function is up to the actor.

In this example I’ll create two types of objects. One object will start at the top of the screen and move down the screen. I’ll call these objects rocks.

The second object will move to the location of a touch on the screen. I’ll call this a ship.

Object when created will need to add themselves to the game_loop. To make this easy we’ll use the singleton nature of Lua modules to help. Every file that uses require(“game_loop”) will be getting a reference to the same programming object. Any module that require game loop will be talking to the same game loop.

The rock objects will remove themselves from the display when they reach the bottom of the screen. This means, rocks will also have to remove themselves from the game loop at the time they remove themselves from the display. To make this easy we’ll make use of the singleton nature of the game_loop module again.

rocks.lua –
I made a file named rocks.lua. This file contains the code that will produce “rock” objects. The rocks will fall down the and remove themselves when they get past the bottom edge of the screen.

The rocks module have the following methods.

make() rock – This function returns a rock.

This module requires game_loop.

The rocks module acts as factory that produces rock objects. Each rock is is a Corona shape object, a rectangle. Corona display objects are essentially tables. Each rock has two methods.

rock:remove() – removes the rock from game_loop, and then from display.
rock:update( deltaTime ) – updates the position of the rock.


--------------------------------------------------------------------
--
-- rocks.lua 
-- 
--------------------------------------------------------------------
local M = {}
--------------------------------------------------------------------

local game_loop = require("game_loop")

local xMax = display.contentWidth
local xMin = 0
local yMax = display.contentHeight + 40
local yMin = 0

local function make()
	local rock = display.newRect( 0, 0, 40, 40 )
	rock:setFillColor( 0, 1, 0 )
	rock.x = math.random( xMin, xMax )
	rock.y = yMin - 40
	rock.speed = math.random() * 3 
	
	function rock:remove()
		game_loop.remove( self )
		display.remove( self ) 
	end
	
	function rock:update( dt ) 				-- receive dt here as a parameter. 
		self.y = self.y + self.speed * dt 	-- Use dt, multiply by pixels per frame (speed)
		if self.y > yMax then 
			
			self:remove()
		end 
		return self
	end
	
	game_loop.add( rock )
	 
	return rock
end 
M.make = make

--------------------------------------------------------------------
return M

ship.lua –
The ship.lua module is very similar to the rocks module. This module acts as factory that produces ship objects. The ship module has a single method.

make() ship – The make method returns a a ship object.

The ship module requires the game_loop module. Each ship created is added to game_loop.

Ship objects have three methods.

ship:move_to( x, y ) – starts the ship moving towards the x, and y.
ship:remove() – Removes the ship from game_loop and from the display.
ship:update( deltaTime ) – Updates the position of the ship.


-----------------------------------------------
--
-- ship.lua
-- 
-----------------------------------------------
local M = {}
-----------------------------------------------
local game_loop = require( "game_loop" )
-----------------------------------------------

local function make()
	local ship = display.newRect( display.contentCenterX, display.contentHeight - 100, 20, 40 )
	
	ship.target_x = ship.x
	ship.target_y = ship.y
	
	function ship:move_to( x, y )
		self.target_x = x
		self.target_y = y
	end 
	
	function ship:remove()
		game_loop.remove( self )
		display.remove( self )
	end 
	
	function ship:update( dt )
		ship.x = ship.x - (ship.x - ship.target_x) * 0.1
		ship.y = ship.y - (ship.y - ship.target_y) * 0.1
	end 
	
	game_loop.add( ship )
	
	return ship
end 
M.make = make

-----------------------------------------------
return M 

Here’s a simple program to test out the modules defined above.


-----------------------------------------------------
-- Import the game loop module 
local game_loop = require("game_loop")
game_loop:run()
-----------------------------------------------------

-- Make a player
local ship = require( "ship" ) 
local player = ship.make()

local function move_ship( x, y )
	player:move_to( x, y )
end 

local function on_touch( event )
	if event.phase == "began" then 
		move_ship( event.x, event.y )
	elseif event.phase == "moved" then 
		move_ship( event.x, event.y )
	end
end 
Runtime:addEventListener( "touch", on_touch )
-----------------------------------------------------

-- A function to make rocks
local rocks = require( "rocks" )

local function make_rock()
	local rock = rocks.make()
end 

-- Use a timer to make rocks periodically
local rock_timer = timer.performWithDelay( 900, make_rock, 0 )
-----------------------------------------------------

Let’s take a look at each part. The first two lines load up the game_loop module, and start it running.


local game_loop = require("game_loop")
game_loop:run()

Next we make a player object. The player object will be made from a ship object. To move the ship I’m calling on the ship objects move_to( x, y ) method. Here I made a touch event that calls on move_to() with the event.x and event.y. Which tells the player/ship to move to the position of the touch event.

The event calls move_ship() during both the began and moved phases. The effect is that the ship will start moving to the position of the point your finger makes contact with the screen, and updates position as you drag your finger across the screen.


local ship = require( "ship" ) 
local player = ship.make()

local function move_ship( x, y )
	player:move_to( x, y )
end 

local function on_touch( event )
	if event.phase == "began" then 
		move_ship( event.x, event.y )
	elseif event.phase == "moved" then 
		move_ship( event.x, event.y )
	end
end 
Runtime:addEventListener( "touch", on_touch )

Lest create a timer to create a new rock every 900 milliseconds.


local rocks = require( "rocks" )

local function make_rock()
	local rock = rocks.make()
end 

-- Use a timer to make rocks periodically
local rock_timer = timer.performWithDelay( 900, make_rock, 0 )

Here’s a few ideas to try on your own.

Make a new object type that moves differently from the two examples here.
Add some new features to the one or both of the objects here. For example make the rocks rotate as they move down the screen.
Use images, or better yet sprites, in place of the rectangles I used.

Corona – Game Loop part 2

Here’s a follow up to the previous post on game loops. The game loop works with actor objects. Actors are stored in an array. The game loop sends it’s actors an update message each frame, and passes the delta time.

Here I have created a Lua module to handle the game and appropriately named it: game_loop. Using a module gives us three advantages.

First, the code is easily portable. You can copy this Lua file/Module into another project and use the game there.

Second, the code is isolated. This makes it easier to edit the game loop if necessary.

Last, Lua modules are only loaded once. If we load this file with require() in file A, and then load it again with require() in file B, file B sees the same game_loop with the same variable values. It doesn’t load a new unique instance. In effect Lua modules act like singleton objects. For the game loop this is perfect. We will only need one game loop, and many other modules will want to access it.

Here’s the game_loop module. Move all of the code here into a file named game_loop.lua.


---------------------------------------------------
--
-- game_loop.lua
--
---------------------------------------------------
local M = {}
---------------------------------------------------

local MSPF = 1000 / display.fps
local actors_array = {}
local last_ms = 0
local isRunning = false


function add( obj ) 
	if table.indexOf( actors_array, obj ) == nil then 
		actors_array[#actors_array+1] = obj
	end 
end 
M.add = add
	
function remove( obj )
	local index = table.indexOf( actors_array, obj )
	if index ~= nil then 
		table.remove( actors_array, index )
	end  
end 
M.remove = remove
	
function enterFrame( event )
	local ms = event.time
	local dt = MSPF / ( ms - last_ms )
	last_ms = ms
	
	for i = #actors_array, 1, -1 do 
		local actor = actors_array[i]
		actor:update( dt )
	end 
end 

	
function run()
	if isRunning == false then 
		Runtime:addEventListener( "enterFrame", enterFrame )
		isRunning = true
	end 
end 
M.run = run	


function pause()
	if isRunning then 
		Runtime:removeEventListener( "enterFrame", enterFrame )
		isRunning = false
	end 
end 
M.pause = pause
---------------------------------------------------
return M

Load game_loop.lua with require(“game_loop”). When using require() do not include the .lua file extension.

The game_loop object/table has four methods.

game_loop.add( obj ) — Adds obj to the game loop, obj must implement
the update(dt) method.

game_loop.remove( obj ) — Removes obj from the game loop.

game_loop.run() — Starts the game loop by adding an enterFrame listener.

game_loop.pause() — pauses the game loop by removing the enterFrame listener.

A game_loop keeps track of objects, we’ll refer to these as actors, in an array/table. Each frame game_loop sends each of the actors an update() message and passes the delta time (dt).

To use this in a game all actor objects must implement the update() method.
Actors are added to the game_loop using game_loop.add( actor ). When an actor
is to be removed from the display it should also be removed from the game_loop using

game_loop.remove( actor )

The game_loop can be stopped and started with:

game_loop.run() 
game_loop:pause()

To test this you can add the following code to main.lua in a new project. The code below first creates a new game loop. Touching the screen creates a new box. Boxes are added to the game loop that sends them update messages each frame. Touching a box removes it from the game loop, and then from the screen.

At the bottom I created two buttons to to run and pause the game loop.

-- Import the game loop module 
local game_loop = require("game_loop")
-- Start the game loop 
game_loop.run()

-- make green boxes
local function make_green_box()
	local box = display.newRect( 0, 0, 40, 40 )
	box:setFillColor( 0, 1, 0 )
	box.x = 0
	box.y = math.random( 0, 480)
	box.speed = math.random() * 3 
	
	function box:update( dt ) -- receive dt here as a parameter. 
		self.x = self.x + self.speed * dt -- Use dt, multiply by pixels per frame (speed)
		if self.x > 320 then 
			self.x = 0
		end 
	end 
	return box
end 




-- Tap a box to remove a box
local function remove_box( event )
	if event.phase == "began" then 
		-- Remove the box from the loop
		local box = event.target
		game_loop.remove( box )		-- Remove box from loop
		display.remove( box )	-- Remove box from display
	end 
	return true
end 


-- Tap the screen to add a box
local function add_box( event )
	if event.phase == "began" then
		local box = make_green_box() -- Make a new box
	
		box.x = event.x	-- Position box
		box.y = event.y
	
		box:addEventListener( "touch", remove_box ) -- Add a touch event 
	
		game_loop.add( box )	-- Add box to the game loop 
	end 
	return true
end 

Runtime:addEventListener( "touch", add_box )





local run_button = display.newRect( 31, 21, 60, 40 )
run_button:setFillColor( 0, 200/255, 0 )

local pause_button = display.newRect( 92, 21, 60, 40 )
pause_button:setFillColor( 200/255, 0, 0 )

local function touch_run( event )
	if event.phase == "began" then 
		game_loop.run()
	end 
	return true 
end 

local function touch_pause( event )
	if event.phase == "began" then 
		game_loop.pause()
	end 
	return true
end 

run_button:addEventListener( "touch", touch_run )
pause_button:addEventListener( "touch", touch_pause )

To expand on what’s here, you could imagine every actor in the actor_array as a table. These
actor tables could contain properties describing how the game_loop should treat each
actor. A simple idea might be to give each a type, and active property. As is the game
loop can be paused and run. Using this idea you could pause or run actors in groups.

Corona – Display objects properties practice

Here’s a few ideas if you want to practice your programming skills.

Let’s talk about display objects. All of the display objects act the same way and support the same properties. This includes, images, shapes, like the rectangle and circle, text objects. These properties also apply to the group. Think of a group as an empty display container that you can add other objects. It’s like a layer in Illustrator.

Display objects have the properties that determine how they are displayed. Properties are always accessed with the dot (.), and you set their value with the equal sign. For example:

obj.property = value

Here’s a list of properties.

x – horizontal position

y – Vertical position

xScale – horizontal scale

yScale – vertical scale

anchorX – horizontal position of the object’s anchor point

anchorY – vertical position of the object’s anchor point

Here’s a few things to try with this. Create a new project with the default settings. In main.lua add the following at the top.

local box = display.newRect( 0, 0, 40, 40 )

This should create a rectangle in the upper left corner. The first two parameters (0,0) set the x and y initially. The center of the object is located at the upper left corner.

Set the position of the box to the center of the screen. Since the default size is 320 by 480 the center is 160, 240.
 box.x = 160
 box.y = 240

Set some other properties.

box.rotation = 33 -- this degrees
 box.xScale = 0.5 -- should be 50% scale on the horizontal
 box.yScale = 2 -- should be 200% on the vertical
 box.alpha = 0.5 -- Should be 50% transparent

Try changing these values to see what happens. You could have done all of this with any other type of display object like text, or images.

Generate a random number with math.random(). This can be used in two ways. Either to generate a random number from 0 to 1, for example 0.826727384. Or generate an integer value between any two numbers. For example 1 to 6. Here’s what this would look like in code form:

math.random() -- something like 0.826727384
math.random( 1, 6 ) -- a random from 1 to 6.

You can test these in the terminal with

print( math.random() )
print( math.random(1,6) )

Apply this to the box:

box.x = math.random( 40, 280 ) -- Moves the anywhere from x 40 to x 280.

You can make this interactive with:

local function move_box( event )
box.x = math.random(40, 280)
end
box:addEventListener( "tap", move_box )

Tap the box and it moves to a random position. Randomize the y property of box on your own.

This would be more interesting with some motion. Modify the function above to look like this:

local function move_box( event )
transition.to( box, {x=math.random(40, 240), time=1000} )
end
box:addEventListener( "tap", move_box )

Animate the y on your own.

Let’s make this more challenging. Make 10 boxes. Use a loop. The basic loop syntax looks like this:

for i = 1, 10 do
-- repeat this code 10 times
end

so to make 10 boxes. Use the following.

for i = 1, 10 do
local box = display.newRect( 0, 0, 30, 30 )
box.x = math.random( 40, 280 )
box.y = math.random( 40, 440 )
end

You should have 10 boxes all over the screen. Here’s where things get interesting. Since you have declared box as local within the loop this variable is local to that code block it isn’t accessible outside the loop.

Try this, remove all of the other code except for the code below.

for i = 1, 10 do
local box = display.newRect( 0, 0, 30, 30 )
box.x = math.random( 40, 280 )
box.y = math.random( 40, 440 )
end
box.x = 100 -- error on this line!

You should get an error message pointing to the last line. Read the error message closely, it should include the line number for the last line: box.x = 100. We can’t access box, since it’s local to the loop!

Beside this which box would be accessing? We’d like to get to tap on any of the boxes and have them move and spin to a new location.

Add this function above the loop.

local function move_box( event )
 local new_x = math.random( 40, 280 )
 local new_y = math.random( 40, 440 )
 local new_rotation = math.random( 0, 360 )
 transition.to( box, {x=new_x, y=new_y, rotation=new_rotation, time=1000} )
 end
for i = 1, 10 do
local box = display.newRect( 0, 0, 30, 30 )
box.x = math.random( 40, 280 )
box.y = math.random( 40, 440 )
box:addEventListener( "tap", move_box )
end

Add the text in red to the loop. This still won’t work. Box is not accessible remember? We’re talking about this line in move box:

transition.to( box, {x=new_x, y=new_y, rotation=new_rotation, time=1000} )

The event object has a reference to the box that generates the event. Each box has been assigned an event listener. When tapping a box, that box will generate the event. In this way move_box will know which box to move. Edit the line above:

transition.to( event.target, {x=new_x, y=new_y, rotation=new_rotation, time=1000} )

The event variable is a table containing properties describing the event that just occurred. One of those properties, target, contains a reference to the object that generated the event. Try it now.

Corona – config.lua and making universal apps

Here’s a link to a couple articles on the Corona web site about making apps to fit a wide range of screen sizes. Be sure to comments there’s a lot of good questions and answers there. The second articles takes the whole concept to a very elegant solution.

  1. http://www.coronalabs.com/blog/2012/12/04/the-ultimate-config-lua-file/
  2. http://coronalabs.com/blog/2013/09/10/modernizing-the-config-lua/

Corona – More Easing types

More easing types

The easing types provided out of the box with Corona are limited. Some of the classic easing types that people have long considered standard, like elastic, just aren’t there.

Fear not, these can be added through an external library. I found some code on the Corona site here: https://developer.anscamobile.com/code/more-easing.

Note: The discussion talks about changes and modifications to the first code block presented. The code I’m using here comes from further down the page at post #7.

This library expands the easing types to include the following new types:

  • easeIn
  • easOut
  • easInOut
  • easeOutIn
  • easeInBack
  • easeOutBack
  • easeInOutBack
  • easeOutInBack
  • easeInBounce
  • easeOutBounce
  • easeInOutBounce
  • easeOutInBounce
  • easeInElastic
  • easeOutElastic
  • easeInOutElastic
  • easeOutInElastic

These add a lot more possibilities to motion created with transition.to()

Here’s some sample code. The example below imports the new easing library and then applies an elastic ease to an object.

To make this example work you’ll need to get the code from the link above and save into a file named easingX.lua. This new file will need to be in the same folder as main.lua.

-- The easing methods included here expand on the default easing
-- included with Corona. The extra library of easing formulas is
-- imported through the easing.lua. The code for this was obtained
-- at: https://developer.anscamobile.com/code/more-easing
-- Note: The discussion talks about changes and modifications to
-- the first code block presented. The code I'm using here comes
-- from further down the page at post #7.
-- require the easingX library
easingX = require "easingX"

local box = display.newRect( 0, 0, 50, 50 )

box.x = display.contentCenterX

-- Note the use transition in the parameters.
-- get the newer easing ypes from easingX which was required above.
transition.to( box, {time=3000, y=display.contentCenterY, transition=easingX.easeOutElastic} )

Here you can see the box moves with an elastic quality swinging back and forth past the  target value before settling into stop.

Corona – for loop

The for loop

Often when writing a program you will want to repeat the same process. Luckily there is a construct designed especially for this.

Meet the for loop

The for loop is a construct that repeats a block of code a set number of times. Here’s an example that counts from 1 to 10:

for count = 1, 10, 1 do
    print( “count:”, count )
end

Here count is a variable assigned a starting value of 1. 10 is defined as the ending value for count. And, 1 is the value that will increment count with each repeat. That is 1 will be added to count with each repeat.

The code between do and end is repeated with each count.

The variable used to keep track of the loop can use any name you like. For example you’ll often see i used here.

for i = 1, 12, 1 do
    print( “Count:”, i )
end

Essentially the loop counts from the first number to the second number, adding or stepping with the third number.

Count backwards

For loops can count forward or backward. Try this example:

for count = 10, 1, -1 do
    print( “Count:”, count )
end

This time the loop starts at 10 and counts by subtracting 1 each time until count reaches a value of 1.

Stepping by values other than 1

The step can also be any value you like. For example:

for count = 2, 10, 2 do
    print( “Count:”, count )
end

This time the loop begins the count on 2, and steps 2 with each repeat, counting the event numbers from 2 to 10.

A few notes on loops

When a loop is run the computer does not up the screen. Therefore you can not use a loop to animate objects! All other processes are suspended until the loop completes.

That said you can use a for loop inside of an enterFrame handler to animate a list of objects.

Why use a loop?

Many times you will have lists of things to work with. Imagine a game like Tetris. A for loop makes a good choice for updating all of the tiles on the board when the game updates.

Space Invaders type games might use a for loop to loop through all of the invaders to move and check for collisions.

Any program might us a for loop to loop through a list of display objects to position and initialize them when the program begins.

Corona – Arrays

The Array

Let’s talk Arrays. In any language an Array is variable that holds a list of values. Array sounds so technical. Just call it a list, everyone understands a list. Here’s an example:

{ "Apples", "Bananas", "Cherries" }

Note the syntax. The whole thing is wrapped in the { (left curly brace) and the } (right curly brace). These define the extent of the list. Each list item is separated by a comma (,).

Make an array

Try it yourself. Make a new Corona project and type the following into main.lua. Below I have defined a local variable and set it equal to an array.

local fruit_array = { "Apples", "Bananas", "Cherries" }

Continue reading Corona – Arrays