Luiz
Luiz

Reputation: 1315

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.

Upvotes: 2

Views: 67

Answers (2)

Kashif
Kashif

Reputation: 4632

Everytime you run (arc4random_uniform(50)+51)/100 it will give you a random duration between 0.5 and 1.0 seconds. you can use below code to generate random durantions for A & B

durationA = (arc4random_uniform(50)+51)/100 durationB = (arc4random_uniform(50)+51)/100

Additionally, you can run a for loop to compare durationB with durationA and only accept durationB if it is not the same as durationA.

Upvotes: 0

Timothy Smith
Timothy Smith

Reputation: 848

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.

Upvotes: 1

Related Questions