1
votes

I am trying to create a simple animation (with 3 textures) that should run only once every time the player dies. I have written the code below by looking online for how to change textures and make the program wait between changing the textures (I wanted to do it this way and not with an atlas because it is only 3 textures).

The game works, so that the ball stays at the position where it dies. Then it stays at that position while the print(I waited) prints 3 times. However, in between the waits the texture of my ball does not change. That is the only thing not working in the code below.

How can I make this code work, or is there another simple way of creating a one time animation?

override func update(_ currentTime: TimeInterval) {

    //CHECK IF BALL IS INSIDE THE MAP
    if (map_1_buffer.contains(ball.position)) {
           } else {
        let texture1 = SKTexture(imageNamed: "death_1")
        let texture2 = SKTexture(imageNamed: "death_2")
        let texture3 = SKTexture(imageNamed: "death_3")
        let normal = SKTexture(imageNamed: "Ball1")

        ball.texture = texture1
        sleep(UInt32(2))
        print("i waited 1")
        ball.texture = texture2
        sleep(UInt32(2))
        print("i waited 2")
        ball.texture = texture3
        sleep(UInt32(2))
        print("i waited 3")
        ball.texture = normal


        death += 1
        ball.physicsBody?.velocity.dx = 0
        ball.physicsBody?.velocity.dy = 0
        ball.position = CGPoint(x: -120, y: 40)

    }
}
2
I suspect you may be running into a problem now with the ball being detected as dead multiple times by the map check is in the update loop. Maybe add a flag ballDying that's initially false, set that to true when you are about to run the animation, guard the map1_buffer.contains check with && !ballDying, and have resetBall clear the flag again. - bg2b
@bg2b thank you for taking time helping me! I added the flag, so that the program only runs through the resetBall function once. The last problem I am experiencing is the line changing the texture in the rest function is not executing. I posted an answer elating on the problem a bit more - Thibault-problems
This question was substantially modified into a new question after the answer below was given by bg2b, invalidating their answer. We do not do that here. The reason is that Stack Overflow is a curated set of Q&A, and we hope that future readers will learn from the material; if questions may be edited such that the given answers no longer make sense, we are taking away from readers an opportunity to learn something. - halfer
In general, as a question author, if a solution for you prompts a new question, then consider asking a new question. It is sometimes OK to add an addendum to the existing question (as long as the additional question is strongly related) but in most cases it is just better to ask a new question. It is OK to link from the new question to the old one to give context. It is OK to self-answer as you have done, but not to post questions - the answer space is for answers only. - halfer
@halfer Ok! Thank you for clarifying - Thibault-problems

2 Answers

0
votes

The update function is intended to be called once for every display refresh, so putting things like sleep calls in it is not going to do what you want. I'd suggest reading the overview of the render cycle here: https://developer.apple.com/documentation/spritekit/skscene/responding_to_frame-cycle_events

The render cycle runs continually, and your goal is to put things in update or in physics handling routines like didBegin or whatnot to detect and respond to things happening in the game. The responses include things like adjusting node properties, adding or removing nodes, or adding actions to nodes. In your particular case, it seems like you want to have an animation when a condition is detected. You can do that in update with something like the following. It has the ball run an animation, and when the action completes it will run some additional code to reset the ball.


func checkOutOfBounds() {
  if (!map_1_buffer.contains(ball.position)) {
    let texture1 = SKTexture(imageNamed: "death_1")
    let texture2 = SKTexture(imageNamed: "death_2")
    let texture3 = SKTexture(imageNamed: "death_3")
    let deathAnimation = SKAction.animate(with: [texture1, texture2, texture3], timePerFrame: 2.0)
    ball.run(deathAnimation) {
      self.death += 1
      self.ball.texture = SKTexture(imageNamed: "Ball1")
      self.ball.physicsBody?.velocity.dx = 0
      self.ball.physicsBody?.velocity.dy = 0
      self.ball.position = CGPoint(x: -120, y: 40)
    }
  }
}

override func update(_ currentTime: TimeInterval) {
  checkOutOfBounds()
}

(I don't have the framework to actually test this code so there may be typos, and some of the stuff in the completion action to reset the ball may not have self depending on its scope, but the idea is hopefully clear.)

0
votes

Thank you bg2b for the help; my question is almost resolved. The code I have now waits for the animation to be executed and then goes to the resetBall function only once (thanks to the binary flag you suggested) so the death counter works.

There is still one problem I have been trying to resolve, and I will ask that as a new question.

func resetBall() {
    ballDying = false
    ball.physicsBody?.velocity.dx = 0
    ball.physicsBody?.velocity.dy = 0
    let normalTexture: SKTexture = SKTexture.init(imageNamed: "Ball1")
    self.ball.texture = SKTexture(imageNamed: "Ball1")
    self.ball.position = CGPoint(x: -120, y: 40)
    self.ball.size = CGSize(width: 40, height: 40)
    death += 1
    nbr_touches = 0
}

var ballDying = false

override func update(_ currentTime: TimeInterval) {
    if ((map_1_buffer.contains(ball.position)) == false) && !ballDying {
        ballDying = true
        let texture1 = SKTexture(imageNamed: "death_1")
        let texture2 = SKTexture(imageNamed: "death_2")
        let texture3 = SKTexture(imageNamed: "death_3")
        let deathAnimation = SKAction.animate(with: [texture1, texture2, texture3], timePerFrame: 0.03)
        ball.physicsBody?.velocity.dx = 0
        ball.physicsBody?.velocity.dy = 0
        self.ball.run(deathAnimation, completion: resetBall)
    }
}