How can I offset spawns so that they are not directly behind the previous ones?

So, I,ve been struggling to make this work for a while now and need help. What I have right now is a bunch of spawns from a spawnInt counter, and they seem to place spawns directly behind others - which is something I don’t want for this game. 

Is there a way I can make the spawns check and see if they are behind another, and if it is, move slightly to the left or right side?

Here is how I am spawning enemies:

local function spawnEnemy() local xPos = math.random(20,300) -- Value 160 for testing local r = math.floor(math.random() \* 3) -- Random colors are assigned to \>\> 0 = Blue(Rectangle) | 1 = Red(Cross) | 2 = Green(Circle) if(r == 0) then blue = display.newSprite(imageSheetRectangle, sequenceDataRectangle ) blue.name = "Blue" physics.addBody(blue, { isSensor = true }) blue.x = xPos blue:setSequence("blueRect") blue:setFrame(3) --blue:play() blue.y = display.contentHeight - 550 blue:toFront() print(xPos) enemyGroup:insert(blue) elseif(r == 1) then red = display.newSprite(imageSheetCross, sequenceDataCross) red.name = "Red" physics.addBody(red, {isSensor = true }) red.x = xPos red:setSequence("blueCross") red:setFrame(1) red:toFront() --red:play() print(xPos) red.y = display.contentHeight - 550 enemyGroup:insert(red) elseif(r == 2) then green = display.newSprite(imageSheetCircle, sequenceDataCircle ) green.name = "Green" physics.addBody(green, { isSensor = true }) green.x = xPos green:setSequence("blueCirc") green:setFrame(2) green:toFront() print(xPos) --green:play() green.y = display.contentHeight - 550 -- 550 default print(xPos) enemyGroup:insert(green) end if spawned == spawnAmount then --start a new wave in 3 seconds. spawnInt = 100 --stops the gameloop. wave = wave + 1 --Increase the wave we are on spawned = 0 --Reset so that the next wave starts from 0. spawnAmount = perWave + (wave \* perWaveIncrease) local timer = timer.performWithDelay(6000, function() spawnInt = 0; waveText.text = "Wave: "..wave; waveText.x = \_W\*0.1; end, 1) else spawnInt = 0 end end

Something like this should do the trick. I haven’t tested it but you should get the idea. Also streamlined it a bit, makes it really easy to add new enemy types.

You were creating your blue/red/green objects as global variables which could cause complications later.

[lua]

local enemies = {}    – setup a table to hold the enemies – PUT AT TOP OF LUA FILE

local function spawnEnemy()

  local spawnData = {

  {name = “Blue”, seq = “blueRect”, frame = 3, imgSheet = imageSheetRectangle, seqData = sequenceDataRectangle},

  {name = “Red”, seq = “blueCross”, frame = 1, imgSheet = imageSheetCross, seqData = sequenceDataCross},

  {name = “Green”, seq = “blueCirc”, frame = 2, imgSheet = imageSheetCircle, seqData = sequenceDataCircle}

  }     – SETUP TABLE TO STORE DATA FOR EACH ENEMY TYPE

  local xPos = math.random(20,300) – Value 160 for testing

  local r = math.random(1, #spawnData)  – choose one of the enemies to spawn

  local sd = spawnData[r] – get the spawn data for this enemy

  local s = display.newSprite(sd.imgSheet, sd.seqData)

  s.name = sd.name

  physics.addBody(s, { isSensor = true })

  s:setSequence(sd.seq)

  s:setFrame(sd.frame)

  s:toFront()

  s.y = display.contentHeight - 550   – create the enemy sprite using spawnData

  local ok = false    – setup a flag to say whether we found a valid X position

  local gap = 2     – set the minimum gap in pixels between enemies

  local err = 0       – set an error flag to avoid the game hanging if too many enemies

  while ok == false do    – loop until a valid X position is found

    xPos = math.random(20,300)    – choose a new X position

    ok = true   – until we’ve checked against other enemies, assume will be ok

    for i = 1, #enemies, 1 do   – loop through the previously spawned enemies

      local e = enemies[i]    – link to this enemy

      local dist = math.abs(e.x - xPos)   – work out the distance between this enemy and the random position

      if dist < (e.width + gap) then ok = false end   – if the enemies would be too close, try again

    end

    err = err + 1   – increase the count of attempts to find a valid position

    if err > 50 then ok = true end    – after 50 tries assume no empty space and spawn wherever

  end

  s.x = xPos    – set the spawned enemy position

  enemies[#enemies+1] = s   – add the enemy to the enemiers table

  enemyGroup:insert(s)

  print (xPos)

  if spawned == spawnAmount then --start a new wave in 3 seconds.

   spawnInt = 100 --stops the gameloop.

   wave = wave + 1 --Increase the wave we are on

   spawned = 0 --Reset so that the next wave starts from 0.

   spawnAmount = perWave + (wave * perWaveIncrease)

   local timer = timer.performWithDelay(6000, function() spawnInt = 0; waveText.text = "Wave: "…wave; waveText.x = _W*0.1; end, 1)

  else

  spawnInt = 0

  end

end

[/lua]

One way it to do an overlap test.

Do the following to see what I mean:

  1. Download my answer packs:

 http://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2015%20Answer%20Packs.zip

  1. Run the July Answer Pack and Click to page 3.  Then run answer #23 (How to know whether …"

  2. Look at the code for that answer: ‘2015 Answer Packs\7_July\answers\overlapTest’

(Note: Video is of April Answer Pack)

https://www.youtube.com/watch?v=bpctrL7gWn0

@nick_sherman: Hey that looks like its done the job. However, there is a few other issues though: with my game, the enemies get destroyed if they hit the object (castle thingy) at the bottom of the screen, and with this setup its trying to call an arithimitic on a spawns that are getting nil’ out. Im sorry I forgot to mention that part in the OP. This is the section thats calling the error: 

for i = 1, #enemies, 1 do -- Loop through the previously spawned enemies local e = enemies[i] -- Link to this enemy local dist = mAbs(e.x - xPos) -- Work out the distance between this enemy and the random position chosen if dist \< (e.width + gap) then ok = false end -- If the enemies are too close, try agian end 

in the collision I have it so that after a function is called, event.object (the enemies) are removed by 

display.remove(event.object2) event.object2 = nil; 

hmm they still spawn behind each other. Is there a way to check that they are not directly behind each other? I understand the code is looking for the space between the spawns x position and that is working fine, but is there a way to check that against the Y position as well? To help illustrate this issue I have provided a screenshot of the problem. 

naR4VrF.jpg

Hey guys Im still having issues with this, and really can’t get the spawns to spread apart properly as can be seen from the image.

To solve the ‘nil’ problem, you need to check that ‘e’ is not nil before trying to check its position.

You should be able to work out how to check the Y position, just follow the same format as the code which checks the X position.

Ah its working now. Just added a check to see if the enemies table was nil (basically at the start of the game when there isn’t any spawns to put inside). Thanks for the help!

Something like this should do the trick. I haven’t tested it but you should get the idea. Also streamlined it a bit, makes it really easy to add new enemy types.

You were creating your blue/red/green objects as global variables which could cause complications later.

[lua]

local enemies = {}    – setup a table to hold the enemies – PUT AT TOP OF LUA FILE

local function spawnEnemy()

  local spawnData = {

  {name = “Blue”, seq = “blueRect”, frame = 3, imgSheet = imageSheetRectangle, seqData = sequenceDataRectangle},

  {name = “Red”, seq = “blueCross”, frame = 1, imgSheet = imageSheetCross, seqData = sequenceDataCross},

  {name = “Green”, seq = “blueCirc”, frame = 2, imgSheet = imageSheetCircle, seqData = sequenceDataCircle}

  }     – SETUP TABLE TO STORE DATA FOR EACH ENEMY TYPE

  local xPos = math.random(20,300) – Value 160 for testing

  local r = math.random(1, #spawnData)  – choose one of the enemies to spawn

  local sd = spawnData[r] – get the spawn data for this enemy

  local s = display.newSprite(sd.imgSheet, sd.seqData)

  s.name = sd.name

  physics.addBody(s, { isSensor = true })

  s:setSequence(sd.seq)

  s:setFrame(sd.frame)

  s:toFront()

  s.y = display.contentHeight - 550   – create the enemy sprite using spawnData

  local ok = false    – setup a flag to say whether we found a valid X position

  local gap = 2     – set the minimum gap in pixels between enemies

  local err = 0       – set an error flag to avoid the game hanging if too many enemies

  while ok == false do    – loop until a valid X position is found

    xPos = math.random(20,300)    – choose a new X position

    ok = true   – until we’ve checked against other enemies, assume will be ok

    for i = 1, #enemies, 1 do   – loop through the previously spawned enemies

      local e = enemies[i]    – link to this enemy

      local dist = math.abs(e.x - xPos)   – work out the distance between this enemy and the random position

      if dist < (e.width + gap) then ok = false end   – if the enemies would be too close, try again

    end

    err = err + 1   – increase the count of attempts to find a valid position

    if err > 50 then ok = true end    – after 50 tries assume no empty space and spawn wherever

  end

  s.x = xPos    – set the spawned enemy position

  enemies[#enemies+1] = s   – add the enemy to the enemiers table

  enemyGroup:insert(s)

  print (xPos)

  if spawned == spawnAmount then --start a new wave in 3 seconds.

   spawnInt = 100 --stops the gameloop.

   wave = wave + 1 --Increase the wave we are on

   spawned = 0 --Reset so that the next wave starts from 0.

   spawnAmount = perWave + (wave * perWaveIncrease)

   local timer = timer.performWithDelay(6000, function() spawnInt = 0; waveText.text = "Wave: "…wave; waveText.x = _W*0.1; end, 1)

  else

  spawnInt = 0

  end

end

[/lua]

One way it to do an overlap test.

Do the following to see what I mean:

  1. Download my answer packs:

 http://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2015%20Answer%20Packs.zip

  1. Run the July Answer Pack and Click to page 3.  Then run answer #23 (How to know whether …"

  2. Look at the code for that answer: ‘2015 Answer Packs\7_July\answers\overlapTest’

(Note: Video is of April Answer Pack)

https://www.youtube.com/watch?v=bpctrL7gWn0

@nick_sherman: Hey that looks like its done the job. However, there is a few other issues though: with my game, the enemies get destroyed if they hit the object (castle thingy) at the bottom of the screen, and with this setup its trying to call an arithimitic on a spawns that are getting nil’ out. Im sorry I forgot to mention that part in the OP. This is the section thats calling the error: 

for i = 1, #enemies, 1 do -- Loop through the previously spawned enemies local e = enemies[i] -- Link to this enemy local dist = mAbs(e.x - xPos) -- Work out the distance between this enemy and the random position chosen if dist \< (e.width + gap) then ok = false end -- If the enemies are too close, try agian end 

in the collision I have it so that after a function is called, event.object (the enemies) are removed by 

display.remove(event.object2) event.object2 = nil; 

hmm they still spawn behind each other. Is there a way to check that they are not directly behind each other? I understand the code is looking for the space between the spawns x position and that is working fine, but is there a way to check that against the Y position as well? To help illustrate this issue I have provided a screenshot of the problem. 

naR4VrF.jpg

Hey guys Im still having issues with this, and really can’t get the spawns to spread apart properly as can be seen from the image.

To solve the ‘nil’ problem, you need to check that ‘e’ is not nil before trying to check its position.

You should be able to work out how to check the Y position, just follow the same format as the code which checks the X position.

Ah its working now. Just added a check to see if the enemies table was nil (basically at the start of the game when there isn’t any spawns to put inside). Thanks for the help!