0

I'm trying to incorporate an animation of 5 frames into my existing spawn function for a spritenode. Currently a crow moves across the screen from right to left, I would like to animate this however no matter what I try I keep generating Thread 1 errors.

By commenting out certain bits of code I can either animate the bird in a static position on screen or move the bird from right to left but not animated (comment out spawn func).

I know the below code wont work in it's current form but it's everything I'm working with, hopefully someone can help me.

Below is all my code I'm trying to slot together...

Thank you,

//did move to view

var crowTexture1 = SKTexture(imageNamed: "crow1")
crowTexture1.filteringMode = SKTextureFilteringMode.Nearest
var crowTexture2 = SKTexture(imageNamed: "crow2")
crowTexture2.filteringMode = SKTextureFilteringMode.Nearest
var crowTexture3 = SKTexture(imageNamed: "crow3")
crowTexture3.filteringMode = SKTextureFilteringMode.Nearest
var crowTexture4 = SKTexture(imageNamed: "crow4")
crowTexture4.filteringMode = SKTextureFilteringMode.Nearest
var crowTexture5 = SKTexture(imageNamed: "crow5")
crowTexture5.filteringMode = SKTextureFilteringMode.Nearest


    var animFly = SKAction.animateWithTextures([crowTexture1, crowTexture2, crowTexture3, crowTexture4, crowTexture5], timePerFrame: 0.1)
    var fly = SKAction.repeatActionForever(animFly)

    var distanceToMoveBird = CGFloat(self.frame.size.width + 2 * crowTexture1.size().width);
    var moveBirds = SKAction.moveByX(-distanceToMoveBird, y:0, duration:NSTimeInterval(0.0040 * distanceToMoveBird));
    var removeBirds = SKAction.removeFromParent();
    moveAndRemoveBirds = SKAction.sequence([moveBirds, removeBirds,]);

    var spawnBirds = SKAction.runBlock({() in self.spawnBird()})
    var delayBirds = SKAction.waitForDuration(NSTimeInterval(4.0))
    var spawnThenDelayBirds = SKAction.sequence([spawnBirds, delayBirds])
    var spawnThenDelayForeverBirds = SKAction.repeatActionForever(spawnThenDelayBirds)
    self.runAction(spawnThenDelayForeverBirds)

//spawning function

func spawnBird() {


    var bird = SKSpriteNode()
    bird.position = CGPointMake( self.frame.size.width + crowTexture1.size().width * 2, 0 );
    var height = UInt32( self.frame.size.height / 1 )
    var height_max = UInt32( 500 )
    var height_min = UInt32( 500 ) //300
    var y = arc4random_uniform(height_max - height_min + 1) + height_min;
    var bird1 = SKSpriteNode(texture: crowTexture1)



    bird1.position = CGPointMake(0.0, CGFloat(y))
    bird1.physicsBody = SKPhysicsBody(rectangleOfSize: bird1.size)
    bird1.physicsBody?.dynamic = false
    bird1.physicsBody?.categoryBitMask = crowCategory
    bird1.physicsBody?.collisionBitMask = catCategory | scoreCategory
    bird1.physicsBody?.contactTestBitMask = 0
    bird.addChild(bird1)

    bird.runAction(moveAndRemoveBirds)

    birds.addChild(bird)

}
Rich Townsend
  • 571
  • 1
  • 5
  • 21

1 Answers1

0

To setup my example in order to work (if you just copy & paste code), you need an atlas named crow.atlas with textures named crowAnimation1@2x.png , crowAnimation2@2x.png etc. If you don't want to use an atlas, you can initialize textures manually, like you are already doing(just put your code inside initializeCrowAnimation method).

I wrote all needed comments inside the code:

import SpriteKit

class GameScene: SKScene {

    let CrowCategory   : UInt32 = 0x1 << 1
    let CatCategory    : UInt32 = 0x1 << 2
    let ScoreCategory  : UInt32 = 0x1 << 3

    var animationFrames : [SKTexture]!

    override func didMoveToView(view: SKView) {

        //1 - Get textures

        initializeCrowAnimation()

        // 2 - Generate birds
        generateBirds()

    }


    //Initialize crow animation - first way

    func initializeCrowAnimation(numOfFrames:Int){

        //You can use numOfFrames parameter if you want to get only certain  number of textures from atlas

        animationFrames =  [SKTexture]()

        let atlas = SKTextureAtlas(named: "crow")


        for var i = 1; i <= atlas.textureNames.count; i++ {

            let textureName = "crowAnimation\(i)"


            animationFrames.append(atlas.textureNamed(textureName))
        }

    }

    //Initialize crow animation - second way 

    func initializeCrowAnimation(){


        let atlas = SKTextureAtlas(named: "crow")

        animationFrames = [
           atlas.textureNamed("crowAnimation1"),
           atlas.textureNamed("crowAnimation2"),
           atlas.textureNamed("crowAnimation3"),
           atlas.textureNamed("crowAnimation4"),
           atlas.textureNamed("crowAnimation5"),
        ]


    }

    //Helper methods

    func randomBetweenNumbers(firstNum: CGFloat, secondNum: CGFloat) -> CGFloat{
        return CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(firstNum - secondNum) + min(firstNum, secondNum)
    }

    func getRandomPoint()->CGPoint{

        var xRange:UInt32 = UInt32(frame.size.width)  // you can change the range here if you like
        var yRange:UInt32 = UInt32(frame.size.height) // you can change the range here if you like

        return CGPoint(x:Int(arc4random()%xRange),y:Int(arc4random()%yRange))
    }


    //Method for creating bird node

    func getBird()->SKSpriteNode{

        let bird = SKSpriteNode(texture: animationFrames.first)
        bird.physicsBody = SKPhysicsBody(rectangleOfSize: bird.size)
        bird.physicsBody?.dynamic = false
        bird.physicsBody?.categoryBitMask = CrowCategory
        bird.physicsBody?.collisionBitMask = CatCategory | ScoreCategory
        bird.physicsBody?.contactTestBitMask = 0
        bird.name = "crow" //will help you to enumerate all crows if needed

        //Flying animation
        let flyAnimation = SKAction.repeatActionForever( SKAction.animateWithTextures(animationFrames, timePerFrame: 0.1))

        bird.runAction(flyAnimation, withKey: "flying") // you can stop flying animation at any time by removing this key

        //Moving animation

        let move = SKAction.moveTo(getRandomPoint(), duration: NSTimeInterval(randomBetweenNumbers(4, secondNum:6)))

        let remove = SKAction.removeFromParent()

        let moveAndRemove = SKAction.sequence([move,remove])

        bird.runAction(moveAndRemove, withKey: "moving")


        return bird

    }

    //If needed you can use something like this to stop generating birds, or any other action associated by certain key

    func stopGeneratingBirds(){

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

            self.removeActionForKey("spawning")
        }

    }

    func generateBirds(){

        //spawn a bird with delay between 2 + (+1 / -1) means, between 1 and 3 seconds
        let delay = SKAction.waitForDuration(2, withRange: 1)


        var block = SKAction.runBlock({

            //Note that here if you don't use something like [unowned self] or [weak self] you will create a retain cycle

            //Because I am using outdated Swift version (and retain cycle is a whole new topic), I will skip it

            var bird = self.getBird()

            bird.position = self.getRandomPoint()

            println(bird.position)

            self.addChild(bird)

        })



        let spawnSequence = SKAction.sequence([delay, block])


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

    deinit{
        println("Deinited")
    }

    override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
    }

}

Note that this is just an example which can give you a basic idea in which direction you can go, and to compare your code with this to see why your code isn't working.

This is tested, and its working but be aware that maybe I've missed something.

About retain cycles, and how to avoid them you can read more here (and update your code accordingly). I've skipped to add that part in my answer because my Swift version is outdated and probably what works for me, will not work for you, but the key is to use [unowned self] or [weak self]. Same topic can be found in docs here.

Community
  • 1
  • 1
Whirlwind
  • 14,286
  • 11
  • 68
  • 157
  • thank you for your comment, I've been playing around with your code and I like the concept, I can't seem to integrate it with my code where a single bird enters the screen from right to left within certain height limits. The bird needs to cross the entire screen as this is the trigger the increase the score once the bird hits a contact on the left side of the screen... If you or anyone else would know how to achieve this that is what I'm looking for and I would be really grateful. – Rich Townsend Sep 14 '15 at 21:19