Spawn help

Hello!

     I am making a shooting game that, like most, requires enemies to spawn.  What I want is for the enemy to walk to the middle of the screen, stop, and start attacking.  I have code that creates an image each time the function is called, but I have no way of differentiating between the different instances.  Here is what happens:  the level starts…a timer calls a spawn function…the spawn function creates an image and starts its transition…the spawn function calls an attack function which changes the animation and gives me an error.  Here is my code:

– Up here, I have code to load audio and sprite data and images.

lvl1 = { enemyIterations = 5 }

enemy = {}

function attack()

enemy[i]:setSequence(“enemyShoot”)

enemy[i]:play()

audio:play(sndEnemy1)

timer.performWithDelay( 1000, idle)

end

function idle()

enemy[i]:setSequence(“enemyIdle”)

enemy[i]:play()

timer.performWithDelay( math.random(700, 1300), attack)

end

function spawnEnemy()

i = i + 1

local enemy[i] = display.newSprite( enemySheet, sequenceDataEnemy )

enemy[i].xScale = 0.7

enemy[i].yScale = 0.7

local enemyX = math.random(1,2)

if(enemyX == 1) then

enemy[i].x = display.contentWidth + 50

enemy[i]:setSequence(“enemyWalkingRight”)

enemy[i].targetX = enemy[i].x - math.random(100, 400)

enemy[i].frame = “walkingRight”

transitionTime = (enemy[i].x-100)/enemy[i].targetX*1000

elseif(enemyX == 2) then

enemy[i].x = -50

enemy[i]:setSequence(“enemyWalkingLeft”)

enemy[i].frame = “walkingLeft”

enemy[i].targetX = enemy[i].x + math.random(100, 400)

transitionTime = enemy[i].targetX/(enemy[i].x+100)*1000

end

enemy[i].y = math.random(200, 300)

enemy[i]:play()

transition.to( enemy, {x = enemy[i].targetX, time = transitionTime, onComplete = idle})

print(“spawn enemy”…i)

end

function startLevel()

timer.performWithDelay( math.random(1000, 2000), spawnEnemy, t.enemyIterations)

end

startLevel(lvl1)

If anyone has help of any kind, it would be much appreciated.  

Firstly can I just request that when you post code on the forum, that you use the < > button and post the code in the box that appears. It makes it much easier to read.

Secondly, if you are getting an error message it is helpful if you post that in too.

I think your problem is caused by your variable “i”. It seems to be a global variable, that you increment whenever you spawn a new enemy. For a start, I wouldn’t recommend calling a global variable “i”, it’s commonly used in for loops etc, and will likely cause you some problems further down the line. I think the main problem though, is that “i” is causing you to potentially trying to reference objects that don’t exist yet.

Your spawn function starts by incrementing “i” by one. I’m presuming “i” is initially set to 0, you haven’t included that in your post. So it gets incremented to 1, and so you create enemy[1]. This shouldn’t have “local” in front of it btw, I’m surprised that hasn’t given you errors. If you want to make the enemies local, then add local before the enemy = {} line at the top of your code.

So you now have enemy[1], and that’s fine. You call a transition, which triggers another when it completes. Her’s where your “i” variable causes a problem. Your spawn function may trigger before the transitions have completed, which will increment “i” to 2. The idle and attack functions use “i” to work out which enemy to move, so potentially they are trying to reference a table entry which doesn’t exist yet. In addition to that, because they are using a global variable, you will only be able to perform an action on the last created enemy. If an enemy is created before the previous enemy has moved, idled and attacked, the idle and attack calls will be applied to the new enemy, because “i” now refers to that enemy.

A better bet would be to use lua closures:

lvl1 = { enemyIterations = 5 } --changed "i" to "enemyCount" which will make it easier to distinguish from "i" in a for loop local enemyCount = 1 local enemy = {} function attack(enemyObj) enemyObj:setSequence("enemyShoot") enemyObj:play() audio:play(sndEnemy1) local function callIdle() idle(enemyObj) end timer.performWithDelay( 1000, callIdle) end function idle(enemyObj) enemyObj:setSequence("enemyIdle") enemyObj:play() local function callAttack() attack(enemyObj) end timer.performWithDelay( math.random(700, 1300), callAttack) end function spawnEnemy() --I've made an enemy which is local to the function, and then added that to the --enemy table, this will make it easier to reference in the function calls below local newEnemy = display.newSprite( enemySheet, sequenceDataEnemy ) newEnemy.xScale = 0.7 newEnemy.yScale = 0.7 local enemyX = math.random(1,2) if(enemyX == 1) then newEnemy.x = display.contentWidth + 50 newEnemy:setSequence("enemyWalkingRight") newEnemy.targetX = newEnemy.x - math.random(100, 400) newEnemy.frame = "walkingRight" transitionTime = (newEnemy.x-100)/newEnemy.targetX\*1000 elseif(enemyX == 2) then newEnemy.x = -50 newEnemy:setSequence("enemyWalkingLeft") newEnemy.frame = "walkingLeft" newEnemy.targetX = newEnemy.x + math.random(100, 400) transitionTime = newEnemy.targetX/(newEnemy.x+100)\*1000 end newEnemy.y = math.random(200, 300) newEnemy:play() enemy[enemyCount] = newEnemy --I've created an argumentless function which will call your idle function --which now has an argument (the enemy to idle) local function callIdle() idle(newEnemy) end transition.to( newEnemy, {x = newEnemy.targetX, time = transitionTime, onComplete = callIdle}) print("spawn enemy"..i) --move this to the end so that it cannot cause objects to be reference before they are created --it has initially been set to 1 instead of 0, so will be correct on first spawn. enemyCount = enemyCount + 1 end function startLevel() timer.performWithDelay( math.random(1000, 2000), spawnEnemy, t.enemyIterations) end startLevel(lvl1)

Hopefully, this fixes your problem. If not, then let us know what the errors are.

Thanks so much!  I just have a few bugs with timing and audio to work out, but the bulk of my issues are solved!  Thanks again!

Firstly can I just request that when you post code on the forum, that you use the < > button and post the code in the box that appears. It makes it much easier to read.

Secondly, if you are getting an error message it is helpful if you post that in too.

I think your problem is caused by your variable “i”. It seems to be a global variable, that you increment whenever you spawn a new enemy. For a start, I wouldn’t recommend calling a global variable “i”, it’s commonly used in for loops etc, and will likely cause you some problems further down the line. I think the main problem though, is that “i” is causing you to potentially trying to reference objects that don’t exist yet.

Your spawn function starts by incrementing “i” by one. I’m presuming “i” is initially set to 0, you haven’t included that in your post. So it gets incremented to 1, and so you create enemy[1]. This shouldn’t have “local” in front of it btw, I’m surprised that hasn’t given you errors. If you want to make the enemies local, then add local before the enemy = {} line at the top of your code.

So you now have enemy[1], and that’s fine. You call a transition, which triggers another when it completes. Her’s where your “i” variable causes a problem. Your spawn function may trigger before the transitions have completed, which will increment “i” to 2. The idle and attack functions use “i” to work out which enemy to move, so potentially they are trying to reference a table entry which doesn’t exist yet. In addition to that, because they are using a global variable, you will only be able to perform an action on the last created enemy. If an enemy is created before the previous enemy has moved, idled and attacked, the idle and attack calls will be applied to the new enemy, because “i” now refers to that enemy.

A better bet would be to use lua closures:

lvl1 = { enemyIterations = 5 } --changed "i" to "enemyCount" which will make it easier to distinguish from "i" in a for loop local enemyCount = 1 local enemy = {} function attack(enemyObj) enemyObj:setSequence("enemyShoot") enemyObj:play() audio:play(sndEnemy1) local function callIdle() idle(enemyObj) end timer.performWithDelay( 1000, callIdle) end function idle(enemyObj) enemyObj:setSequence("enemyIdle") enemyObj:play() local function callAttack() attack(enemyObj) end timer.performWithDelay( math.random(700, 1300), callAttack) end function spawnEnemy() --I've made an enemy which is local to the function, and then added that to the --enemy table, this will make it easier to reference in the function calls below local newEnemy = display.newSprite( enemySheet, sequenceDataEnemy ) newEnemy.xScale = 0.7 newEnemy.yScale = 0.7 local enemyX = math.random(1,2) if(enemyX == 1) then newEnemy.x = display.contentWidth + 50 newEnemy:setSequence("enemyWalkingRight") newEnemy.targetX = newEnemy.x - math.random(100, 400) newEnemy.frame = "walkingRight" transitionTime = (newEnemy.x-100)/newEnemy.targetX\*1000 elseif(enemyX == 2) then newEnemy.x = -50 newEnemy:setSequence("enemyWalkingLeft") newEnemy.frame = "walkingLeft" newEnemy.targetX = newEnemy.x + math.random(100, 400) transitionTime = newEnemy.targetX/(newEnemy.x+100)\*1000 end newEnemy.y = math.random(200, 300) newEnemy:play() enemy[enemyCount] = newEnemy --I've created an argumentless function which will call your idle function --which now has an argument (the enemy to idle) local function callIdle() idle(newEnemy) end transition.to( newEnemy, {x = newEnemy.targetX, time = transitionTime, onComplete = callIdle}) print("spawn enemy"..i) --move this to the end so that it cannot cause objects to be reference before they are created --it has initially been set to 1 instead of 0, so will be correct on first spawn. enemyCount = enemyCount + 1 end function startLevel() timer.performWithDelay( math.random(1000, 2000), spawnEnemy, t.enemyIterations) end startLevel(lvl1)

Hopefully, this fixes your problem. If not, then let us know what the errors are.

Thanks so much!  I just have a few bugs with timing and audio to work out, but the bulk of my issues are solved!  Thanks again!