My spawn object with tables doesnt work!

Hey there,

I have created a function which starts with a table that should spawn an object with a random image.
Then after that it gets on the players screen and moves towards the middle of the screen.

That worked out but now i want to use tables in order to be able to spawn multiple objects and to be able to delete all these objects when leaving the scene. But my function doesnt work and i have been tinkering with it for a while.

I have been using another thread as the example but somehow applying it doesnt work out for me… please help me out!

Example: http://forums.coronalabs.com/topic/50141-group-removal-multiple-twin-objects/

My code:
 

function spawnEnemy() enemytable = {} local enemypics = {"images/beetleship.png","images/octopus.png", "images/rocketship.png"} enemypic = enemypics[math.random(#enemypics)] local enemies = display.newGroup() for i=1,5 do enemytable[i] = display.newImage(enemypic) enemies:insert(enemytable[i]) enemytable[i]:addEventListener ( "tap", shipSmash ) end if math.random(2) == 1 then enemytable[i].x = math.random ( -100, -10 ) else enemytable[i].x = math.random ( display.contentWidth + 10, display.contentWidth + 100 ) enemytable[i].xScale = -1 end enemytable[i].y = math.random (display.contentHeight) enemytable[i].trans = transition.to ( enemytable[i], { x=centerX, y=centerY, time=math.random(2500-speedBump, 4500-speedBump), onComplete=hitPlanet } ) speedBump = speedBump + 50 end

The error i am receiving is attempting to index field “?” ( a nil value)

Please help me out!

Regards,

It looks to me like you would want all the code you have in the bottom half of the function inside the

 – for i = 1, 5 do  loop.

You are getting nil, because all the references to ‘i’,  below the ‘for-loop’ see ‘i’ as nil.

Get everything in the loop, except maybe the last line … speedBump = speedBump + 50.  As I am not sure when or how often you want that speedBump incremented. If you put that line inside that ‘for-loop’ then it will increment 5 times each call to spawnEnemy. 

It looks like every time you call the spawnEnemy, you are going to run that loop and create 5 enemy images. Is that your intention? Or were you going to just want to create one enemy image per spawn?  

Hope this helps

Thank you for your reply as i can get it to run now, but my problem is i am spawning too many enemies with this function!
Not even 5 but more like 50!! What do i need to change to make it to spawn only 1? And how do i manually add one later when a certain score has been reached?

Danny,

I kept your code as it was (for the most part).  So, using your code layout I made a few changes. Generally, I would not do it exactly as you have it, but as you have it is okay. There are many ways to do the same thing. So we will stick with how you have it laid out, and just a few changes need to be made.

  1. Took the for loop out of the ‘spawnEnemy’ function

  2. Got rid of the ‘enemies display group’. It is not being used in your code. It is local to the spawnEnemy function and I do not see anywhere that is is actually needed or used… other then inserting the enemy objects into it… it did not seem to me it is needed there.

  3. I moved the enemy table and enemy pics out of the spawnEnemy function. You should put that code at the top of the file.

  4. in the spawnEnemy function, took out the ‘i’ and used enemyCnt, and just increment that at the end of the function each time an enemy is spawned.

  5. Created an ‘enterFrame’ event called gameLoop to check every frame to see if score has reached whatever amount, and spawn and enemy.

good luck.  

  local enemytable = {} local enemypics = {"images/beetleship.png","images/octopus.png", "images/rocketship.png"} local enemyCnt = 1   function spawnEnemy() enemypic = enemypics[math.random(#enemypics)] enemytable[enemyCnt] = display.newImage(enemypic) enemytable[enemyCnt]:addEventListener ( "tap", shipSmash ) if math.random(2) == 1 then enemytable[enemyCnt].x = math.random ( -100, -10 ) else enemytable[enemyCnt].x = math.random ( display.contentWidth + 10, display.contentWidth + 100 ) enemytable[enemyCnt].xScale = -1 end enemytable[enemyCnt].y = math.random (display.contentHeight) enemytable[enemyCnt].trans = transition.to ( enemytable[enemyCnt], { x=centerX, y=centerY, time=math.random(2500-speedBump, 4500-speedBump), onComplete=hitPlanet } ) speedBump = speedBump + 50 enemyCnt = enemyCnt + 1 end   for i = 1, 5 do spawnEnemy() end     local function gameLoop(e) if score == 5000 then spawnEnemy() end end   Runtime:addEventListener("enterFrame", gameLoop)      

Again thank you for answering me so fast.

I have tried to insert your code and i ran into two problems. The first is that if i start the game i get an error which means i can not start the game. If i remove enemytable[enemyCnt]:addEventListener ( “tap”, shipSmash ) then i can start the game but not destroy the ships.
The second is when i start the game it spawns 5 enemies at start without me calling the function yet.
 

EDIT: I removed the for loop and i only get 1 when i press start now! Only need to figure out why the tap shipsmash addeventlistener doesnt work anymore!

Danny,

  1. It spawns 5 enemies without you calling the function, because in the sample I sent, I call the spawning 5 times, with the for loop that I placed just below the function spawnEnemy.  Take that loop and put it in your button event, or whatever mechanism you are using when you want to start spawning.

  2. As to the error that goes away when you remove the line :

enemytable[enemyCnt]:addEventListener ( “tap”, shipSmash )

I am not sure. That was as you had it coded originally, it just ‘indexes’ using enemyCnt instead of i, and as I set the code, that should not have caused an issue. I am not sure if you may have change something else in your code, or accidentally changed something.

Did you delete ‘shipSmash’ function?  Is that function above the ‘spawnEnemy’ function in your code?

You may have to post all (or at least more of your code) for me to see what might be wrong.

You were right! I had to put it above my spawn code now it works halfish. The runtime event of checking for the score(i set it to 5) spawns endlessly instead of 1 time. How do i change this?

There are a lot of ways to do this, depending on what you want to achieve; but here is the quick and simple solution.

This assumes you want to spawn a new enemy every time score increases by 5, see first example below.

If you only want to do it one time, when score is 5, and never again see the second example below.

At the top of your file add something like this:

local scoreStep = 5

local function gameLoop(e) if score == scoreStep then spawnEnemy() scoreStep = scoreStep + 5 end end

second example:

local function gameLoop(e) if score == scoreStep then spawnEnemy() scoreStep = 0 end end

Thank you!! I only have one thing to fix and then its done! I will surely mention you in my credits when my app is done! When i exit the scene to menu the objects do not dissappear and if i re enter the play scene they will crash into my planet and give an error code. My guess is i need to remove all of the enemies before going out of the scene so i tried this.

display.remove(planet) display.remove(enemytable[enemyCnt]) display.remove(scoreTxt)

This is what i have in scene:hide but it doesnt remove the objects from enemies. Do you need more code to know how to do this?

If you are using composer, I am guessing, the code that I sent you and most of your code is in the ‘create’ or ‘show’ functions of the scene. I have not used composer a lot, but each of those functions has a scene display group in them.

local sceneGroup = self.view

in the spawnEnemy function, after you have created an enemy object, make sure to insert it into the sceneGroup.

Right after this line :

enemytable[enemyCnt] = display.newImage(enemypic)

do this:

sceneGroup:insert(enemytable[enemyCnt])

The key is any visual object you want to be hidden when the scene hides itself, you need to make sure to insert into the sceneGroup.

(I am not that experienced with Composer), but when composer hides the scene it will/should hide all those enemies.

I won’t be back on the forum till sometime tomorrow, so if you post a response, I will try to get back to it tomorrow.

Good luck.

by the way, if you do want to manually ‘remove’ the enemies. you would need to do a loop, in reverse to remove the display objects from the table

like:

for i = enemyCnt, 1, -1 do

 display.remove(enemyTable[i])

enemyTable[i] = nil

end

enemyTable = nil

I have been able to remove them from display by putting them into scene.view with this line:

enemytable[enemyCnt] = display.newImage(scene.view, enemypic)

Now they do not show up anymore but they DO keep moving and hitting the planet and respawning. I am trying to stop their transition to the middle with this code when i leave the scene but it doesnt work:

if enemytable.trans then transition.cancel(enemytable.trans) enemytable.trans = nil end

This is put into scene.hide.

If i use this instead

if enemytable[enemyCnt].trans then transition.cancel(enemytable[enemyCnt]..trans) enemytable[enemyCnt].trans = nil end

Then it gives me this error

Make sure you do that step before you remove all the elements from enemytable.

Also using enemyCnt will only cancel the final transition, you need to loop through enemytable and cancel each one.

Im sorry which step do you mean? The one with the reverse loop? I dont have the initial loop because that would spawn 5 enemies at the start so that one didnt work out.

Im now using this one to spawn one additional enemy at the score 5, 10, 15 etc.

local function gameLoop(e) if score == scoreStep then spawnEnemy() scoreStep = scoreStep + 5 end end Runtime:addEventListener("enterFrame", gameLoop)

I mean the step that cyberparkstudios posted to remove the elements from the table. If you aren’t using this it doesn’t matter.

The point remains that you need to loop through the table and cancel the transitions in the ‘will’ phase on scene:hide() because:

[lua]

enemytable[enemyCnt].trans

[/lua]

Will only cancel the transition for the last enemy spawned.

Please bear with me for a little while longer. I have not tried to put the transition cancel in a loop that goes like this:

for i = enemyCnt, 1, -1 do if enemytable[enemyCnt].trans then transition.cancel(enemytable[enemyCnt].trans) enemytable[enemyCnt].trans = nil end end

This is giving me a nil value while i have stated the enemyCnt inside the brackets. Do you know why this is happening?

[lua]

for i = enemyCnt, 1 , - 1 do

      if enemytable[i] .t rans then

      transition.cancel(enemytable[i] .t rans)

      enemytable[i] .t rans = nil

      end

end

[/lua]

As for them continuing to respawn, have you removed the enterFrame listener upon leaving the scene?

Danny,

In regards to the error in scene.hide, the code you show has one typo error.  you  have  2 dots ‘…’  instead of 1 dot.

And don’t use ‘enemyCnt’ there.  There are many factors to consider on what is best way to cancel those transitions, but here is just a quick simple way do it.(see sample code below)

However, If you are only trying to end transition of the ‘last enemy unit’, the loop is not needed; and you can actually, do it the way you have it now, and still use enemyCnt, BUT, you need to make 2 other small changes to make it work.

  1. at the top of your code, initiate enemyCnt = 0, instead of 1

  2. move the line  ‘enemyCnt = enemyCnt + 1’ to be the first line inside of your ‘spawnEnemy’ function, instead of being the last line in that function.

(this is assuming you have not cleared any enemy units from the enemy table) 

  for i = 1, #enemytable do if enemytable[i].trans then transition.cancel(enemytable[i]..trans) enemytable[i].trans = nil end end      

Danny, 

I think the ‘nil’ value you are getting is because of the enemyCnt is 1 more then the actual number of enemies.

The quick fix, is as I mention in the last post,  change the initial value of enemyCnt at the top of your code to 0, not 1.  Then in the ‘spawnEnemy’ function, move the last line in the function  'enemyCnt = enemyCnt + 1 to be the first line in the function.

Then that reverse loop, will start with a valid index.

I am now removing the runtime loop with this piece of code in scenehide:

Runtime:removeEventListener( "enterFrame", gameLoop )

Im not sure this has any effect though.

Also I have set enemyCnt initiatal to 0 and the line enemyCnt = enemyCnt + 1 to the top of the function.
Now i can remove 1 enemy but not more.