Mathis Delaunay
Mathis Delaunay

Reputation: 191

Spawn Balls random position out of the screen

I would like to bring up enemy (var enemis) from outside the screen whether the top, bottom, left and right of the screen. And these enemy have a random direction in tranversant the screen. For the moment, my code do spawning enemy out of the screen top, bottom, left and right but with one direction only and I want make a random direction

func CreationEnnemis(){

        let Enemis = SKSpriteNode(imageNamed: "Meteroites.png")
        let choixDeCote = arc4random() % 4 + 1

        switch choixDeCote {

            case 1 : //Haut
                let MinValue = self.size.width / 8
                let MaxValue = self.size.width - 200
                SpawnX = UInt32(MaxValue - MinValue)
                SpawnX = arc4random_uniform(SpawnX)
                SpawnY = UInt32(self.size.height)
             break

            case 2 ://Bas
                let MinValue = self.size.width / 8
                let MaxValue = self.size.width - 200
                SpawnX = UInt32(MaxValue - MinValue)
                SpawnX = arc4random_uniform(SpawnX)
                SpawnY = UInt32(self.size.height) - UInt32(self.size.height)
             break

            case 3 : //Gauche
                let MinValue = self.size.height / 8
                let MaxValue = self.size.height - 200
                SpawnX = 0
                SpawnY = UInt32(MaxValue - MinValue)
                SpawnY = arc4random_uniform(SpawnY)
             break

            case 4 ://Droite
                let MinValue = self.size.height / 8
                let MaxValue = self.size.height - 200
                SpawnX = UInt32(self.size.width)
                SpawnY = UInt32(MaxValue - MinValue)
                SpawnY = arc4random_uniform(SpawnY)
             break

            default :
             break
        }

        Enemis.position = CGPoint(x: CGFloat(SpawnX), y: CGFloat(SpawnY))
        Enemis.setScale(4)
        Enemis.physicsBody = SKPhysicsBody(rectangleOfSize: Enemis.size)
        Enemis.physicsBody?.affectedByGravity = false
        Enemis.physicsBody?.dynamic = true

        let action = SKAction.moveTo(CGPoint(x: -50,y: -10),duration: 2.5)
        let actionFini = SKAction.removeFromParent()
        Enemis.runAction(SKAction.sequence([action, actionFini]))
        Enemis.runAction(SKAction.repeatActionForever(action))

        self.addChild(Enemis)
    }

Upvotes: 4

Views: 2522

Answers (3)

Marauder2k9
Marauder2k9

Reputation: 21

For anyone that is interested to do this in objective C inside GameScene:

-(void) randomSpawnPosition{
    NSUInteger randPos = arc4random_uniform(4);
    CGPoint spawnPosition;
    CGFloat randFloatX = arc4random_uniform(self.frame.size.width + 10);
    CGFloat randFloatY = arc4random_uniform(self.frame.size.height + 10);

    switch (randPos) {
            //top
        case 1:
            spawnPosition = CGPointMake(randFloatX, self.frame.size.height+10);
            break;
            //bottom
        case 2:
            spawnPosition = CGPointMake(randFloatX, 0-10);
            break;
            //left
        case 3:
            spawnPosition = CGPointMake(0 - 10, randFloatY);
            break;
            //right
        case 4:
            spawnPosition = CGPointMake(self.frame.size.width + 10, randFloatY);
            break;
    }
    [self addEnemy:spawnPosition];
}

Upvotes: 1

Mathis Delaunay
Mathis Delaunay

Reputation: 191

Thanks a lot ! I make a different version of your code because i found solution before your answer

func CreationMeteorites(){

        let Meteorites = SKSpriteNode(imageNamed: "Meteroites.png")
        let choixDeCote = arc4random() % 4 + 1

        switch choixDeCote {

        case 1 : //Haut
            let MinValue = self.size.width / 8
            let MaxValue = self.size.width - 200
            SpawnX = UInt32(MaxValue - MinValue)
            SpawnX = arc4random_uniform(SpawnX)
            SpawnY = UInt32(self.size.height)
            directionX = Int(arc4random()) % Int(self.frame.size.width)
            directionY = 0
            action = SKAction.moveTo(CGPoint(x: CGFloat(directionX),y: CGFloat(directionY)),duration: 4)
            break

        case 2 ://Bas
            let MinValue = self.size.width / 8
            let MaxValue = self.size.width - 200
            SpawnX = UInt32(MaxValue - MinValue)
            SpawnX = arc4random_uniform(SpawnX)
            SpawnY = 0
            directionX = Int(arc4random()) % Int(self.frame.size.width)
            directionY = Int(self.frame.size.height)
            action = SKAction.moveTo(CGPoint(x: CGFloat(directionX),y: CGFloat(directionY)),duration: 4)
            break

        case 3 : //Gauche
            let MinValue = self.size.height / 8
            let MaxValue = self.size.height - 200
            SpawnX = 0
            SpawnY = UInt32(MaxValue - MinValue)
            SpawnY = arc4random_uniform(SpawnY)
            directionY = Int(arc4random()) % Int(self.frame.size.height)
            directionX = Int(self.frame.size.width)
            action = SKAction.moveTo(CGPoint(x: CGFloat(directionX),y: CGFloat(directionY)),duration: 3)
            break

        case 4 ://Droite
            let MinValue = self.size.height / 8
            let MaxValue = self.size.height - 200
            SpawnX = UInt32(self.size.width)
            SpawnY = UInt32(MaxValue - MinValue)
            SpawnY = arc4random_uniform(SpawnY)
            directionY = Int(arc4random()) % Int(self.frame.size.height)
            directionX = 0
            action = SKAction.moveTo(CGPoint(x: CGFloat(directionX),y: CGFloat(directionY)),duration: 3)
            break

        default :
            break
        }

        //Positioner les météorites

        Meteorites.position = CGPoint(x: CGFloat(SpawnX), y: CGFloat(SpawnY))
        Meteorites.setScale(4)
        Meteorites.physicsBody = SKPhysicsBody(circleOfRadius: 30)
        Meteorites.physicsBody?.affectedByGravity = false
        Meteorites.physicsBody?.dynamic = true
        Meteorites.physicsBody?.categoryBitMask = PhysicsCategories.Meteorites
        Meteorites.physicsBody?.contactTestBitMask = PhysicsCategories.Meteorites

        let actionFini = SKAction.removeFromParent()

        Meteorites.runAction(SKAction.sequence([action, actionFini]))
        Meteorites.runAction(SKAction.repeatActionForever(action))

        self.addChild(Meteorites)
    }

And about the collisions do you know a tutorial with a good explain because i don't understand how make collisions.

Upvotes: 0

Whirlwind
Whirlwind

Reputation: 13665

This is just an example to give you an idea how you can spawn enemies at random positions and move them in random directions. I don't use Swift extensively, and this is more like just to show you at which direction you can go, and how to solve the problem. I left to you to care about Swift 2 syntax :D Also, I am currently on outdated version of Swift, so not sure what works for me, will work for you, but the logic is the same.

Here you will see how you can:

  • spawn a node and move it to the opposite side of a screen
  • move a node to the random point of the opposite side of a screen
  • randomize duration of spawning
  • create a random point along the one of the screen's borders
  • create a random number between two numbers
  • using SKAction to do all this

One thing which is important here is how to use strong reference to self inside closure. Because of my Swift version, as I said, what works for me, probably will not work for you, but the logic is the same. Read more here about strong reference cycles if interested :

Here is an code example:

import SpriteKit


class GameScene:SKScene, SKPhysicsContactDelegate{

    override func didMoveToView(view: SKView) {


        self.physicsWorld.contactDelegate = self


        createEnemies()

    }

    deinit{
       print("deinit called")
    }



    func randomBetweenNumbers(firstNum: CGFloat, secondNum: CGFloat) -> CGFloat{
        return CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(firstNum - secondNum) + min(firstNum, secondNum)
    }
     //Helper method for spawning a point along the screen borders. This will not work for diagonal lines.
    func randomPointBetween(start:CGPoint, end:CGPoint)->CGPoint{

        return CGPoint(x: randomBetweenNumbers(start.x, secondNum: end.x), y: randomBetweenNumbers(start.y, secondNum: end.y))

    }


    func createEnemies(){

        //Randomize spawning time.
        //This will create a node every 0.5 +/- 0.1 seconds, means between 0.4 and 0.6 sec
        let wait = SKAction .waitForDuration(0.5, withRange: 0.2)


        weak var  weakSelf = self //Use weakSelf to break a possible strong reference cycle


        let spawn = SKAction.runBlock({

            var random = arc4random() % 4 +  1
            var position = CGPoint()
            var moveTo = CGPoint()
            var offset:CGFloat = 40

            println(random)

            switch random {

                //Top
            case 1:
                position = weakSelf!.randomPointBetween(CGPoint(x: 0, y: weakSelf!.frame.height), end: CGPoint(x: weakSelf!.frame.width, y: weakSelf!.frame.height))

                //Move to opposite side
                moveTo = weakSelf!.randomPointBetween(CGPoint(x: 0, y: 0), end: CGPoint(x:weakSelf!.frame.width, y:0))

                break

                //Bottom
            case 2:
                position = weakSelf!.randomPointBetween(CGPoint(x: 0, y: 0), end: CGPoint(x: weakSelf!.frame.width, y: 0))

                //Move to opposite side
                  moveTo = weakSelf!.randomPointBetween(CGPoint(x: 0, y: weakSelf!.frame.height), end: CGPoint(x: weakSelf!.frame.width, y: weakSelf!.frame.height))

                break

                //Left
            case 3:
                position = weakSelf!.randomPointBetween(CGPoint(x: 0, y: 0), end: CGPoint(x: 0, y: weakSelf!.frame.height))

                //Move to opposite side
                moveTo = weakSelf!.randomPointBetween(CGPoint(x: weakSelf!.frame.width, y: 0), end: CGPoint(x: weakSelf!.frame.width, y: weakSelf!.frame.height))

                break

                //Right
            case 4:
                position = weakSelf!.randomPointBetween(CGPoint(x: weakSelf!.frame.width, y: 0), end: CGPoint(x: weakSelf!.frame.width, y: weakSelf!.frame.height))

                //Move to opposite side
                 moveTo = weakSelf!.randomPointBetween(CGPoint(x: 0, y: 0), end: CGPoint(x: 0, y: weakSelf!.frame.height))
                break

            default:
                break

            }

            weakSelf!.spawnEnemyAtPosition(position, moveTo: moveTo)

        })

        let spawning = SKAction.sequence([wait,spawn])

        self.runAction(SKAction.repeatActionForever(spawning), withKey:"spawning")


    }


    func spawnEnemyAtPosition(position:CGPoint, moveTo:CGPoint){

        let enemy = SKSpriteNode(color: SKColor.brownColor(), size: CGSize(width: 40, height: 40))


        enemy.position = position
        enemy.physicsBody = SKPhysicsBody(rectangleOfSize: enemy.size)
        enemy.physicsBody?.affectedByGravity = false
        enemy.physicsBody?.dynamic = true
        enemy.physicsBody?.collisionBitMask = 0 // no collisions
        //Here you can randomize the value of duration parameter to change the speed of a node
        let move = SKAction.moveTo(moveTo,duration: 2.5)
        let remove = SKAction.removeFromParent()
        enemy.runAction(SKAction.sequence([move, remove]))


        self.addChild(enemy)

    }


    func didBeginContact(contact: SKPhysicsContact) {


    }


    /*

    Added for debugging purposes

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent?) {

        //Just make a transition to the other scene, in order to check if deinit is called
        //You have to make a new scene ... I named it WelcomeScene
        var scene:WelcomeScene = WelcomeScene(fileNamed: "WelcomeScene.sks")

        scene.scaleMode = .AspectFill

        self.view?.presentScene(scene )


    }
    */

}

And here is the result:

spawning nodes

The important part is located in createEnemies() method:

//Top
case 1:
    position = weakSelf!.randomPointBetween(CGPoint(x: 0, y: weakSelf!.frame.height), end: CGPoint(x: weakSelf!.frame.width, y: weakSelf!.frame.height))

    //Move to opposite side
    moveTo = weakSelf!.randomPointBetween(CGPoint(x: 0, y: 0), end: CGPoint(x:weakSelf!.frame.width, y:0))

    break

Here you define spawning location, which can be any point along the top border. Or more precisely a little bit above top border. Nodes are spawned offscreen. And next, you create (randomize) a point where you would like to move a node, and that is an opposite side in compare to spawn location. So, that can be any random point along bottom border.

If you want to stop spawning, you will do this:

if(self.actionForKey("spawning") != nil){
    self.removeActionForKey("spawning")
}

About your physics bodies setup... Note that I've set collisionBitMask of nodes to 0.

enemy.physicsBody?.collisionBitMask = 0 // no collisions

When moving nodes by actions in SpriteKit you are pulling them out of physics simulation and you can get unexpected behaviours if you are expecting to see realistic physics simulation. So, use actions only if you are not interested in collisions (or other sort of physics simulation), but rather just in contact detection. If you need collisions as well, use physics engine and move nodes by applying impulses or forces.

Hope this helps!

Upvotes: 4

Related Questions