Reputation: 243
Hello fellow developers,
I'm trying to change the color of a circle slowly in SpriteKit, making a transition from the original color to the new one with some time between them, transitioning from one color to the other visually. The problem that I have is that my code doesn't make that change slowly, it simply changes like if I directly assigned the new color. Here's my actual code:
func changeColor(c: UIColor) {
var r:CGFloat = 0
var g:CGFloat = 0
var b:CGFloat = 0
var a:CGFloat = 0
c.getRed(&r, green: &g, blue: &b, alpha: &a)
var r1:CGFloat = 0
var g1:CGFloat = 0
var b1:CGFloat = 0
var a1:CGFloat = 0
circle.fillColor.getRed(&r1, green: &g1, blue: &b1, alpha: &a1);
let wait = SKAction.waitForDuration(0.4)
r = (r - r1)/10
g = (g - g1)/10
b = (b - b1)/10
for(var i = 0; i < 10; i++) {
let cN = UIColor(red: (r1 + r*CGFloat(i)), green: (g1 + g*CGFloat(i)), blue: (b1 + b*CGFloat(i)), alpha: 1.0)
circle.fillColor = cN
circle.strokeColor = cN
runAction(wait)
}
}
I really belive that the problem is in "runAction(wait)", but I'm not sure. I'd like to know if there's a default function that does what I want, but I highly doubt it.
Upvotes: 0
Views: 566
Reputation: 126127
The problem is that you're trying to do all of this "live" in a for
loop. All the code in your function runs in between frames of animation, so that won't work.
To make this clearer, let's ignore the wait
part for a moment. When you run a function that looks like this:
func changeColor() { // unrolled loop and pseudocode colors for clarity
circle.fillColor = blue
circle.fillColor = red
circle.fillColor = green
}
...SpriteKit does change the circle's color three times—immediately—but it isn't drawing the results. Whatever color you set it to most recently appears only after all your code finishes running and SpriteKit draws the screen.
When you use SKAction
s, what you're doing is lining up instructions for SpriteKit to follow over time, which includes instructions for tasks that take more than one animation frame. But the non-SKAction
calls in your code are still the same as they were before—they make immediate changes, the end result of which you see when the next frame draws.
So, when your code looks like this:
func changeColor() { // unrolled loop and pseudocode colors for clarity
circle.fillColor = blue
circle.runAction(SKAction.waitForDuration(0.4))
circle.fillColor = red
}
... the order of events is like this:
So, the first thing to do is to get your color changes to stop being between-frames changes and add them to the over-time task list — that is, change color via an SKAction
. There's a colorizeWithColor
action, but I'm not sure whether it applies to shape nodes (with their separate fill/stroke colors). Try it, and if that fails you can try a runBlock
action that directly sets fillColor
/strokeColor
.
Back to pseudocode to talk about the sequencing problem, though...
func changeColor() { // unrolled loop and pseudocode color actions
circle.runAction(mySetBlueColorAction)
circle.runAction(SKAction.waitForDuration(0.4))
circle.runAction(mySetRedColorAction)
}
If you run this you'll notice there's still a problem here. Just calling runAction
adds separate actions that run (roughly) in parallel. So your circle is still changing colors twice, but both changes are happening at the same time. (Which one wins? I'm not sure there's a determinate answer.) And at the same time as your circle is getting confused about its color, it's also starting to wait 0.4 seconds for... what exactly?
A waitForDuration
action makes sense only in the context of a sequence
action—for it to be meaningful, there needs to be something after in in sequence to wait for.
So, if what you want to happen sounds like this sequence:
... then what you need is a sequence action describing what you want to happen in order:
let sequence = SKAction.sequence([
mySetBlueColorAction,
SKAction.waitForDuration(0.4),
mySetRedColorAction,
SKAction.waitForDuration(0.4),
mySetGreenColorAction,
// etc.
])
Upvotes: 4