Luiz Luiz - 6 months ago 26
iOS Question

Spawn objects at random durations without overlaying

Explanation



I'm building a game where I'm facing some problems. I have two objects (A and B) that run by the same rule: from up to bottom, they spawn, move heading down and get removed when reach the edge. Each one of them has its particular duration before spawning, this way, sometimes, they get overlayed (as you can see below).

enter image description here




Question



The question is quite simple: is there any way to make A and B spawn by different durations, but still random ones?




Code



If you want, you can download this project here.

import SpriteKit

class GameScene: SKScene {

//Declarations
var A = SKSpriteNode()
var B = SKSpriteNode()
var durationA = CGFloat()
var durationB = CGFloat()

//Setup
func setupA(){
A = SKSpriteNode(imageNamed: "A")
A.position = CGPoint(x: self.frame.width / 2, y: self.frame.height)
}
func setupB(){
B = SKSpriteNode(imageNamed: "B")
B.position = CGPoint(x: self.frame.width / 2, y: self.frame.height)
}

//Actions
func actionsA(){

//Start spawning, moving and removing
let spawnA = SKAction.runBlock({
() in

//Spawn A
self.setupA()
self.addChild(self.A)

//Move left and remove when go off screenk
let frameHeight = CGFloat(self.frame.height)
let moveA = SKAction.moveByX(0, y: -frameHeight, duration: NSTimeInterval(0.0028 * frameHeight)) //duration: faster or slower
let removeA = SKAction.removeFromParent()

let moveAndRemoveA = SKAction.sequence([moveA, removeA])

self.A.runAction(moveAndRemoveA)
})

//Spawn A each 1.75~2.25 seconds
durationA = (CGFloat(arc4random_uniform(51)) + 175.0) / 100.0
let spawnAfterDurationA = SKAction.repeatActionForever(SKAction.sequence([SKAction.waitForDuration(Double(durationA)), spawnA]))
runAction(spawnAfterDurationA)
}
func actionsB(){

//Start spawning, moving and removing
let spawnB = SKAction.runBlock({
() in

//Spawn B
self.setupB()
self.addChild(self.B)

//Move left and remove when go off screen
let frameHeight = CGFloat(self.frame.height)
let moveB = SKAction.moveByX(0, y: -frameHeight, duration: NSTimeInterval(0.0028 * frameHeight)) //duration: faster or slower
let removeB = SKAction.removeFromParent()

let moveAndRemoveB = SKAction.sequence([moveB, removeB])

self.B.runAction(moveAndRemoveB)
})

//Spawn B each 0.5~1.0 seconds
durationB = (CGFloat(arc4random_uniform(51)) + 50.0) / 100.0
let spawnAfterDurationB = SKAction.repeatActionForever(SKAction.sequence([SKAction.waitForDuration(Double(durationB)), spawnB]))
runAction(spawnAfterDurationB)
}

override func didMoveToView(view: SKView) {
/* Setup your scene here */
actionsA()
actionsB()
}
}





Timothy Smith's attempt



If you want, you can download it here.

import SpriteKit

class GameScene: SKScene {

//Declarations
var A = SKSpriteNode()
var B = SKSpriteNode()

//Setup
func setupA(){
A = SKSpriteNode(imageNamed: "A")
A.position = CGPoint(x: self.frame.width / 2, y: self.frame.height)
}
func setupB(){
B = SKSpriteNode(imageNamed: "B")
B.position = CGPoint(x: self.frame.width / 2, y: self.frame.height)
}

//Actions
func actionsA(){

//Spawn, move and remove
let spawnA = SKAction.runBlock({
() in

//Spawn A
self.setupA()
self.addChild(self.A)

//Move left and remove when go off screenk
let frameHeight = CGFloat(self.frame.height)
let moveA = SKAction.moveByX(0, y: -frameHeight, duration: NSTimeInterval(0.0028 * frameHeight)) //duration: faster or slower
let removeA = SKAction.removeFromParent()

let moveAndRemoveA = SKAction.sequence([moveA, removeA])

self.A.runAction(moveAndRemoveA)
})

runAction(spawnA)
}
func actionsB(){

//Spawn, move and remove
let spawnB = SKAction.runBlock({
() in

//Spawn B
self.setupB()
self.addChild(self.B)

//Move left and remove when go off screen
let frameHeight = CGFloat(self.frame.height)
let moveB = SKAction.moveByX(0, y: -frameHeight, duration: NSTimeInterval(0.0028 * frameHeight)) //duration: faster or slower
let removeB = SKAction.removeFromParent()

let moveAndRemoveB = SKAction.sequence([moveB, removeB])

self.B.runAction(moveAndRemoveB)
})

runAction(spawnB)
}

//Spawn
func spawn(){

let random = Int(arc4random_uniform(10)+1) //pick a number from 1 to 10

if random <= 8{
actionsB()
}
else{
actionsA()
}
}

override func didMoveToView(view: SKView) {
/* Setup your scene here */
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.waitForDuration(0.5, withRange: 0.25), SKAction.runBlock(self.spawn)])))
}
}




Thanks in advance,

Luiz.

Answer

One possible solution would be to use a single function spawn, called randomly every 0.25 to 1.00 seconds. Inside the function, randomly pick either A or B, and spawn it. It appears that you want B to appear more frequently than A, so you could use a weighted randomness (pick a number from 0.45 to 1 and round to the nearest integer).

Whether this works depends on whether it is acceptable to have a B-separation of more than 1 second.