Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how come sprites start flashing?

SO I created a game where you tap balls to make them jump up. You get a point for every tap. Why do my sprites start flashing when I reach 10 points. I'm thinking it has something to do with the update(timeInterval) method. It may be a bug or bad coding.

import SpriteKit

class GameScene: SKScene {
let backgroundNode = SKSpriteNode(imageNamed: "redbackground")
override func didMoveToView(view: SKView) {



    backgroundNode.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
    self.addChild(backgroundNode)

 //=======Ball 1=======//
    let ball = Ball()

    ball.position = CGPoint(x:self.frame.midX, y:440)

    addChild(ball)



    ball.physicsBody = SKPhysicsBody(circleOfRadius: 90)
    ball.physicsBody?.dynamic = true
    ball.physicsBody?.allowsRotation = false

    ball.physicsBody?.friction = 0
    ball.physicsBody?.angularDamping = 0
    ball.physicsBody?.linearDamping = 0
    ball.physicsBody?.usesPreciseCollisionDetection = true
    ball.physicsBody!.categoryBitMask = 0
}

class Ball: SKSpriteNode {



    init() {
        let texture = SKTexture(imageNamed: "Ball")
        super.init(texture: texture, color: .clearColor(), size: texture.size())
        userInteractionEnabled = true

    }


    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        let scene = self.scene as! GameScene
        scene.score += 1

        physicsBody?.velocity = CGVectorMake(0, 100)
        physicsBody?.applyImpulse(CGVectorMake(0, 900))



    }

 override func update(currentTime: CFTimeInterval) {


    if (score >= 10){
        self.backgroundNode.texture = SKTexture(imageNamed: "orangebackground")
    }


    if (score >= 20){
        self.backgroundNode.texture = SKTexture(imageNamed: "yellowbackground")
    }


    if (score >= 30) {
        self.backgroundNode.texture = SKTexture(imageNamed: "greenbackground")
    }

}

See this to see what I mean.(Click or tap link)

This shows the sprites flashing at 10 points

thanks in advance, Jordan

like image 629
DrPepperGrad Avatar asked Jan 06 '23 13:01

DrPepperGrad


2 Answers

Instead of changing the background image in update, you can add a property observer to score that changes the background only when the score reaches specific values. For example:

let backgroundNames = ["orangebackground","yellowbackground","greenbackground"]
// The didSet observer is called when the score is updated
var score:Int = 0 {
    didSet {
        // Check if score is divisible by 10
        if score % 10 == 0 {
            // Determine the array index
            let index = score / 10
            // Wrap to avoid index out of range
            let name = backgroundNames[index % backgroundNames.count]
            print (name)
        }
    }
}

That said, the flashing could be caused by the order in which the nodes are rendered. From Apple's documentation,

...with [ignoresSiblingOrder = true], you cannot predict the rendering order for nodes that share the same height. The rendering order may change each time a new frame is rendered.

You can resolve this by setting the zPosition of the background to a negative value, for example

self.backgroundNode.zPosition = -100

so the background is always drawn before the game nodes.

like image 133
Epsilon Avatar answered Jan 14 '23 14:01

Epsilon


It is flashing because of this

override func update(currentTime: CFTimeInterval) {
    if (score >= 10){
        self.backgroundNode.texture = SKTexture(imageNamed: "orangebackground")
    }


    if (score >= 20){
        self.backgroundNode.texture = SKTexture(imageNamed: "yellowbackground")
    }


    if (score >= 30) {
        self.backgroundNode.texture = SKTexture(imageNamed: "greenbackground")
    }

}

Infact inside the update method, when the score become >= 10, you are updating the texture EVERY FRAME.

This is absolutely wrong.

If you want to update the texture just do it when you need to change it (e.g. when the score goes from 9 to 10, or from 19 to 20 or from 29 to 30.).

But there's no point in updating the texture when it stays at 10 of when it goes from 10 to 11 since you will update it with another texture which is the same.

How can you do it?

Just create a shouldUpdateTexture: Bool = false property, then each time you update the score, if the score goes from 9 to 10 or 19 to 20 or 29 to 30 then turn shouldUpdateTexture to true.

Finally inside the update method check if shouldUpdateTexture is true, in this case set it back to false and update the texture.

Full code

class GameScene: SKScene {

    private var score = 0 {
        didSet {
            shouldUpdateTexture = oldValue < 10 && score >= 10 || oldValue < 20 && score >= 20 || oldValue < 30 && score >= 30
        }
    }
    private var shouldUpdateTexture = false
    private var backgroundNode = SKSpriteNode(imageNamed: "defaultbackground")

    
    override func update(currentTime: CFTimeInterval) {
        if shouldUpdateTexture {
            shouldUpdateTexture = false
            switch score {
            case 10...19: backgroundNode.texture = SKTexture(imageNamed: "orangebackground")
            case 20...29: backgroundNode.texture = SKTexture(imageNamed: "yellowbackground")
            case 30...Int.max: backgroundNode.texture = SKTexture(imageNamed: "yellowbackground")
            default: break
            }
        }
    }

}

That's it.

Update

As @Knight0fDragon pointed out, it would probably be faster moving the code I put into the update method into the didSet.

Pros: this way you don't need to perform a boolean equality check every frame

Cons: if you change the score value multiple times within the same frame you will perform multiple loads of the texture.

like image 21
Luca Angeletti Avatar answered Jan 14 '23 15:01

Luca Angeletti