Archive

Posts Tagged ‘Corona SDK’

Collision detection without physics.

December 14th, 2011 4 comments

A frequent question that comes up in the Corona SDK forums is how to detect when two images on a screen hit each other. This is known as a “Collision”. The ability to determine when two things have bumped into each other is called “Collision Detection”.

Within Corona SDK, being that its an event driven system, we like the idea that we can have the system tell us when things hit each other and we just have to write the code to handle the interaction of the two. This is a coo concept and it works, but . . .

It requires using the Physics engine to do so.

Corona SDK uses a wonderful 2D Box model for physics and that model supports this event driven model and it works well. But if you’re game does not need physics and many apps don’t, like a card game where you move a card to a stack, its kind of silly and wasteful to include the overhead of the physics engine. You don’t need all of those angular momentum floating point mathematics going on for your game.

So how do I detect collisions? I don’t see any API calls to do that?

We really can’t use an event driven model like the physics engine, but there is a very simple method (well there are two) that use other features of Corona SDK, in particular using an “enterFrame” event on the Runtime listener.

Basically we have to write the code that the physics engine is doing for us and it’s not all that complex.

In my personal model, I typically have a table/array of all of my on screen objects and then I have my player’s avatar object.

1
2
3
4
5
6
local objects = {}
for i=1, 20, do
objects[i] = spawnEnemy() -- spawnEnemy creates on display objects and returns it.
end
local player = display.newImageRect("avatar.png", 64, 64) -- create me.

At this point we have a table of enemies in the “objects” table and my player object.

For the Runtime enterFrame event, you need a function to process the collision detection.

1
2
3
4
5
6
7
local function testCollisions
for i=1, #objects do
if hasCollided(objects[i], player) then
-- do what you need to do if they hit each other
end
end
end

So we simply iterate over the list of objects and see if any have hit the player object using a function called hasCollided.

This is a function that you also have to write.

There are two main methods for doing this, one is based on rectangles overlapping each other. The other involves circle testing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
-- rectangle based
local function hasCollided(obj1, obj2)
if obj1 == nil then
return false
end
if obj2 == nil then
return false
end
local left = obj1.contentBounds.xMin <= obj2.contentBounds.xMin and obj1.contentBounds.xMax >= obj2.contentBounds.xMin
local right = obj1.contentBounds.xMin >= obj2.contentBounds.xMin and obj1.contentBounds.xMin <= obj2.contentBounds.xMax
local up = obj1.contentBounds.yMin <= obj2.contentBounds.yMin and obj1.contentBounds.yMax >= obj2.contentBounds.yMin
local down = obj1.contentBounds.yMin >= obj2.contentBounds.yMin and obj1.contentBounds.yMin <= obj2.contentBounds.yMax
return (left or right) and (up or down)
end
-- circle based
local function hasCollidedCircle(obj1, obj2)
if obj1 == nil then
return false
end
if obj2 == nil then
return false
end
local sqrt = math.sqrt
local dx = obj1.x - obj2.x;
local dy = obj1.y - obj2.y;
local distance = sqrt(dx*dx + dy*dy);
local objectSize = (obj2.contentWidth/2) + (obj1.contentWidth/2)
if distance < objectSize then
return true
end
return false
end

Depending on the shape of your player and objects, you may find that circles work better or rectangles work better. In my game Omniblaster I used both methods depending on the objects. My space ship has rounded shields and my rocks were basically round, and my enemies were basically round, so using the hasCollidedCircle made more sense to use.

When I say rounded, the images have transparency that when using rectangles would cause the objects to hit before they looked like it.

Yet, when I fired my phasers or torpedoes, those graphics fit nicely into rectangles, so using the rectangle hasCollided method was a better choice.

One gotcha that you need to be aware of is that if your processing takes too long, (longer than 1/30th of a second or 1/60th depending on frame rate) then your collision detection will fire again before you finish, so I usually change my collision handler to something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
local isCheckingCollisions = false
local function testCollisions
    if isCheckingCollisions then
   return true
    end
    isCheckingCollisions = true
for i=1, #objects do
if hasCollided(objects[i], player) then
-- do what you need to do if they hit each other
end
end
isCheckingCollisions = false
return true
end

This should be a lighter weight collision detection system than the overhead of the physics engine. Though you don’t get filters, being able to define polygon shapes unless you roll you’re own!

Happy coding!

Implementing a Game Center button in your Corona SDK Game.

June 13th, 2011 6 comments

I’ve been trying to figure out how to do a Game Center button for my game and be able to link to the Game Center leaderboards and achievements directly from my Corona SDK based game.

I had not found much information on the Internet on how to do this. Most examples were involving Objective C using Xcode and adding the Game Center framework to the game, then calling relevant methods from the framework.

I basically gave up on the idea about connecting directly to the leaderboards and decided to just try and launch the Game Center app from my game.

Here is the code I used (mostly lifted from Jayant C Varma’s “rating.lua” file found on the Corona SDK site):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
local function testNetworkConnection()
local netConn = require('socket').connect('www.apple.com', 80)
if netConn == nil then
return false
end
netConn:close()
return true
end
    
local function gotoGameCenter()
local device = system.getInfo ( "environment" )
local appURL = "gamecenter:"
if device == "simulator" then
print ( "Cannot spawn this URL in the simulator" )
else
if testNetworkConnection() then
system.openURL ( appURL )
end
end
end
local gameCenterButton = ui.newButton{defaultSrc="gamecenterbtn.png", defaultX = 164, defaultY = 32, onRelease=gotoGameCenter}
gameCenterButton.x = display.contentWidth / 2
gameCenterButton.y = 110
highScores:insert(gameCenterButton)

And much to my surprise, it works like a charm. Now to track down those pesky specific URL’s…. I should note, this is a new addition to my game and it has not been submitted to Apple yet. No warranties expressed or implied.

Implementing a Health Status Bar in Corona SDK

May 30th, 2011 No comments

Implementing a Health Status Bar in Corona SDK

Many games need a way to show the players how well they are doing. It could be a simple counter to show the number of lives left or a bar that decreases and increases as a players energy goes up and down.

There are as many different ways to do this as there are games but lets look at a couple of different ways.

The simplest may be simply using text to display the health status:

1
2
3
4
5
local lives = 5
local livesLabel = display.newText("Lives: ", 10, 30, native.systemFont, 16)
local livesValue = display.newText(string.format("%d", lives), livesLabel.contentWidth+20,30,native.systemFontBold, 16)
livesLabel:setTextColor(225,225,225)
livesValue:setTextColor(255,255,255)

Then later in your code when a player looses a life (or gains one back) simply change the text value of “livesValue” and Corona will magically update the value for you:

1
2
3
4
if lives &gt; 0 then
lives = lives - 1
end
livesValue.text = string.format("%d", lives)

Of course you might want to drop a graphic behind the text, set the colors accordingly, and possibly drop a duplicate text string underneath offset by a pixel to give a little stroke/shadow to the text.

But our players expect more from games and a simple text display won’t be accepted as high quality. Traditional arcade games have shown your lives as small versions of your player’s graphic. If your game involves a ship, perhaps you see a display of 5 little ships to show your life count.

With a display of say 5 small rockets, you will need one image and you will replicate it several times.

1
2
3
4
5
6
7
8
9
local lifeIcons = {}
local lives = 5
local maxLives = 5
local i
for i = 1, maxLives do
lifeIcons[i] = display.newImage("lifeicon.png")
lifeIcons[i].x = 10 + (lifeIcons[i].contentWidth * (i - 1))
lifeIcons[i].y = 30 -- start at 10,10
end

This will create a display of 5 little icons that shows your full life status. Later in your code when you need to change the display:

1
2
3
4
if lives &gt; 0 then
lifeIcons[lives].isVisible = false
lives = lives - 1
end

Later when you need to give a life back:

1
2
3
4
5
lives = lives + 1
if lives &gt; maxLives then
lives = maxLives
end
lifeIcons[lives].isVisible = true

But what if you want more of a bar-graph display. As long as you’re using discrete values, you can continue to use a similar method using different graphics for each value, in this example, there are 5 lives, so you can create 5 different graphics for each of the 5 life states.


Instead of using a loop to load in the same graphic, you would need to load in each of the separate five graphics in and position them all at the same location:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
local lifeIcons = {}
local lives = 5
local maxLives = 5
lifeBar[0] = display.newImage("lifeicon0.png")
lifeBar[1] = display.newImage("lifeicon1.png")
lifeBar[2] = display.newImage("lifeicon2.png")
lifeBar[3] = display.newImage("lifeicon3.png")
lifeBar[4] = display.newImage("lifeicon4.png")
lifeBar[5] = display.newImage("lifeicon5.png")
local i
for i = 1, maxLives do
lifeBar[i].x = 10
lifeBar[i].y = 30
lifeBar[i].isVisible = false
end
lifeBar[lives].isVisible = true

Then to change the graphic:

1
2
3
4
5
if lives &gt; 0 then
lifeBar[lives].isVisible = false
lives = lives - 1
lifeBar[lives].isVisible = true
end

This same concept can be done with MovieClips.

1
2
3
4
healthSprites = movieclip.newAnim{"lifeicon1.png", "lifeicon2.png", "lifeicon3.png", "lifeicon4.png", "lifeicon5.png", "lifeicon0.png"}
healthSprites.x = 50
healthSprites.y = 10
healthSprites:play{startFrame=1, endFrame=lives, loop=1, remove=false}

Then to show the current health level:
[/crayon]

lives = lives – 1
if lives < 0 then
lives = 0
end
if lives == 0 then
healthSprites:play{startGrame=6, endFrame=6, loop=1, remove=false}
else
healthSprites:play{startFrame=1, endFrame=lives, loop=1, remove=false}
end
[/crayon]

This gives you the benefit of a little animation as well. Unlike the example above, we load in the “No life left” graphic in frame 6, because movieclip starts at frame 1. We could have put frame 0 first, and just done a “lives + 1″ to get the right offset.

Hope this is helpful!