Here's my setup, using Sprite Kit. First, I create a simple sprite node within a SKScene, like so:
let block = SKSpriteNode(color: UIColor.redColor(), size: CGSizeMake(90, 160))
block.zPosition = 2
block.shadowCastBitMask = 1
addChild(block)
Then add a light node to the scene:
let light = SKLightNode()
light.categoryBitMask = 1
light.falloff = 1
addChild(light)
Sure enough, the block now casts a nice little shadow:
Now I fade the block by manipulating its alpha value, for example by running an action:
let fadeOut = SKAction.fadeAlphaTo(0.0, duration: 5.0)
block.runAction(fadeOut)
Here's the awkward situation: while the block becomes more and more translucent, the shadow stays exactly the same. This is how it looks like just a moment before the end of the action:
And once the alpha drops to 0.0 entirely, the shadow suddenly disappears, from one frame to the next.
It would be much nicer, however, to have the shadow slowly become weaker and weaker, as the object casting it becomes more and more transparent.
Is an effect like this possible with Sprite Kit? If so, how would you go about it?
This is a little tricky because the shadow cast by an SKLightNode
isn't affected by the node's alpha
property. What you need to do is fade out the alpha channel of the shadowColor
property of the SKLightNode
at the same time you're fading out your block
.
The basic steps are:
shadowColor
and that color's alpha channel for reference.SKAction.customActionWithDuration
which:
shadowColor
to its original color but with the new alpha channel.Example:
let fadeDuration = 5.0 // We're going to use this a lot
// Grab the light's original shadowColor so we can use it later
let shadowColor = light.shadowColor
// Also grab its alpha channel so we don't have to do it each time
let shadowAlpha = CGColorGetAlpha(shadowColor.CGColor)
let fadeShadow = SKAction.customActionWithDuration(fadeDuration) {
// The first parameter here is the node this is running on.
// Ideally you'd use that to get the light, but I'm taking
// a shortcut and accessing it directly.
(_, time) -> Void in
// This is the original alpha channel of the shadow, adjusted
// for how much time has past while running the action so far
// It will go from shadowAlpha to 0.0 over fadeDuration
let alpha = shadowAlpha - (shadowAlpha * time / CGFloat(fadeDuration))
// Set the light's shadowColor to the original color, but replace
// its alpha channel our newly calculated one
light.shadowColor = shadowColor.colorWithAlphaComponent(alpha)
}
// Make the action to fade the block too; easy!
let fadeBlock = SKAction.fadeAlphaTo(0.0, duration: fadeDuration)
// Run the fadeBlock action and fadeShadow action in parallel
block.runAction(SKAction.group([fadeBlock, fadeShadow]))
The following is one way to ensure that the shadow and block fade-in/fade-out together. To use this approach, you will need to declare light and block as properties of the class.
override func didEvaluateActions() {
light.shadowColor = light.shadowColor.colorWithAlphaComponent(block.alpha/2.0)
}
EDIT: Here's how to implement the above.
class GameScene: SKScene {
let light = SKLightNode()
let block = SKSpriteNode(color: UIColor.redColor(), size: CGSizeMake(90, 160))
override func didMoveToView(view: SKView) {
/* Setup your scene here */
block.zPosition = 2
block.shadowCastBitMask = 1
block.position = CGPointMake(100, 100)
addChild(block)
light.categoryBitMask = 1
light.falloff = 1
addChild(light)
let fadeOut = SKAction.fadeAlphaTo(0.0, duration: 5.0);
let fadeIn = SKAction.fadeAlphaTo(1.0, duration: 5.0);
block.runAction(SKAction.sequence([fadeOut,fadeIn,fadeOut]))
}
override func didEvaluateActions() {
light.shadowColor = light.shadowColor.colorWithAlphaComponent(block.alpha/2.0)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With