Detecting range in a tower defense game

I am having trouble detecting when an enemy goes through a towers range in a basic tower defense game. We tried using physics but when you give an object a body everything just bounces off it. We are somewhat new to lua, so we don’t know if we are just missing something simple but we could use some help. 

Two tips:

  1. If you want to use physics to detect objects passing through another object, make the ‘pass through’ object (i.e. the detector) a sensor.

  2. Using physics sensors may be OK, but you’d be better served doing 2D math.

Ex: To calculate the distance between object A and B do this:

local objA = display.newCircle( 100, 100, 10) local objB = display.newCircle( 150, 100, 10) -- Calculate vector from A to B local vx,vy = objB.x - objA.x, objB.y - objA.y -- Calculate length of that vector local len = math.sqrt( vx \* vx + vy \* vy ) print( len ) -- Should print 50 (based on positions above)

Ok, we are using the code you provided (the 2D collision detection) to detect if the enemy is in range of the tower. When the enemy’s hp reaches 0 we remove the enemy, but when the enemy health reaches 0 the enemy removes itself and we get an error because we can no longer detect the enemy because it no longer exists.

Another problem we are having is that when we spawn enemies we would like to give them each a different name and then check if they are in range. We were going to put them in a group but we assumed that if we used the group when 1 enemy gets in range of the turret they will all lose health because we are checking for the enemy group rather than a certain enemy. Is there any way we can detect them as a group, and still only affect 1 enemy at a time?

Hi @tylerjlockhart98,

Are you using physics (Corona’s physics engine) or not using it? I give this advice often:

  1. If you  are  using physics, then use physics for all that it’s worth! The physics engine is extremely powerful and can be used to overcome countless challenges.

  2. If you’re  not  using physics except for one small aspect, and that aspect can be accomplished through another method, then don’t use physics.

In other words, if you’re using physics in your game already, then I suggest that you use circular sensors to detect range, and scrap all of the 2D vector math entirely. Of course it’s up to you… for a tower defense game, you don’t necessarily “need” physics (because it’s just top-down, no gravity involved, etc.), but the ability to use sensors alone is quite beneficial.

Brent

Instead of checking the group, use a loop to iterate through the group and check each object in it. This just shows that you can remove individual group objects based on a health parameter by looping through the group.

local enemyGroup = display.newGroup() local enemy001 = display.newRect(enemyGroup,100,100,100,100) enemy001:setFillColor(1,0,0) enemy001.health = 100 enemy001.name = "red" local enemy002 = display.newRect(enemyGroup,100,200,100,100) enemy002:setFillColor(0,1,0) enemy002.health = 100 enemy002.name = "green" local enemy003 = display.newRect(enemyGroup,100,300,100,100) enemy003:setFillColor(0,0,1) enemy003.health = 100 enemy003.name = "blue" local function reduceHealth() for i = 1, enemyGroup.numChildren do if enemyGroup[i].name == "red" then print(enemyGroup[i].health) enemyGroup[i].health = enemyGroup[i].health - 5 elseif enemyGroup[i].name == "green" then enemyGroup[i].health = enemyGroup[i].health - 10 elseif enemyGroup[i].name == "blue" then enemyGroup[i].health = enemyGroup[i].health - 20 end end end local function checkHealth() for i = 1, enemyGroup.numChildren do if (enemyGroup[i] ~= nil) then if enemyGroup[i].health == 0 then enemyGroup[i]:removeSelf() enemyGroup[i] = nil end end end end timer.performWithDelay(100,reduceHealth,20) Runtime:addEventListener("enterFrame",checkHealth)

Two tips:

  1. If you want to use physics to detect objects passing through another object, make the ‘pass through’ object (i.e. the detector) a sensor.

  2. Using physics sensors may be OK, but you’d be better served doing 2D math.

Ex: To calculate the distance between object A and B do this:

local objA = display.newCircle( 100, 100, 10) local objB = display.newCircle( 150, 100, 10) -- Calculate vector from A to B local vx,vy = objB.x - objA.x, objB.y - objA.y -- Calculate length of that vector local len = math.sqrt( vx \* vx + vy \* vy ) print( len ) -- Should print 50 (based on positions above)

Ok, we are using the code you provided (the 2D collision detection) to detect if the enemy is in range of the tower. When the enemy’s hp reaches 0 we remove the enemy, but when the enemy health reaches 0 the enemy removes itself and we get an error because we can no longer detect the enemy because it no longer exists.

Another problem we are having is that when we spawn enemies we would like to give them each a different name and then check if they are in range. We were going to put them in a group but we assumed that if we used the group when 1 enemy gets in range of the turret they will all lose health because we are checking for the enemy group rather than a certain enemy. Is there any way we can detect them as a group, and still only affect 1 enemy at a time?

Hi @tylerjlockhart98,

Are you using physics (Corona’s physics engine) or not using it? I give this advice often:

  1. If you  are  using physics, then use physics for all that it’s worth! The physics engine is extremely powerful and can be used to overcome countless challenges.

  2. If you’re  not  using physics except for one small aspect, and that aspect can be accomplished through another method, then don’t use physics.

In other words, if you’re using physics in your game already, then I suggest that you use circular sensors to detect range, and scrap all of the 2D vector math entirely. Of course it’s up to you… for a tower defense game, you don’t necessarily “need” physics (because it’s just top-down, no gravity involved, etc.), but the ability to use sensors alone is quite beneficial.

Brent

Instead of checking the group, use a loop to iterate through the group and check each object in it. This just shows that you can remove individual group objects based on a health parameter by looping through the group.

local enemyGroup = display.newGroup() local enemy001 = display.newRect(enemyGroup,100,100,100,100) enemy001:setFillColor(1,0,0) enemy001.health = 100 enemy001.name = "red" local enemy002 = display.newRect(enemyGroup,100,200,100,100) enemy002:setFillColor(0,1,0) enemy002.health = 100 enemy002.name = "green" local enemy003 = display.newRect(enemyGroup,100,300,100,100) enemy003:setFillColor(0,0,1) enemy003.health = 100 enemy003.name = "blue" local function reduceHealth() for i = 1, enemyGroup.numChildren do if enemyGroup[i].name == "red" then print(enemyGroup[i].health) enemyGroup[i].health = enemyGroup[i].health - 5 elseif enemyGroup[i].name == "green" then enemyGroup[i].health = enemyGroup[i].health - 10 elseif enemyGroup[i].name == "blue" then enemyGroup[i].health = enemyGroup[i].health - 20 end end end local function checkHealth() for i = 1, enemyGroup.numChildren do if (enemyGroup[i] ~= nil) then if enemyGroup[i].health == 0 then enemyGroup[i]:removeSelf() enemyGroup[i] = nil end end end end timer.performWithDelay(100,reduceHealth,20) Runtime:addEventListener("enterFrame",checkHealth)