grahamcracker1234
grahamcracker1234

Reputation: 611

How do I add an animatable CIFilter to my SKScene

I really wish to add a CIFilter to my SKScene. That I can do quite easily in fact by putting the following into my didMove(to view: SKView) function of my GameScene Class:

let grayscaleFilter = CIFilter(name: "CIColorControls", withInputParameters: ["inputBrightness" : 0.0, "inputContrast" : 1.1, "inputSaturation" : 0.0])!
grayscaleFilter.name = "grayscale"
self.filter = grayscaleFilter
self.shouldEnableEffects = true

Now my issue come with attempting to animate it, to my knowledge the only way to efficiently animate CIFilters is with Core Animation. I know that Core Animations can only be added to CALayer so I tried the following code in my didMove(to view: SKView) function of my GameScene Class:

let grayscaleFilter = CIFilter(name: "CIColorControls", withInputParameters: ["inputBrightness" : 0.0, "inputContrast" : 1.1, "inputSaturation" : 0.0])!
grayscaleFilter.name = "grayscale"
let grayscaleAnimation = CABasicAnimation(keyPath: "filters.grayscale.inputSaturation")
grayscaleAnimation.fromValue = 0.0
grayscaleAnimation.toValue = 1.0
grayscaleAnimation.duration = 5
view.isMultipleTouchEnabled = false
view.layer.filters?.append(grayscaleFilter)
view.layer.add(grayscaleAnimation, forKey: "grayscaleAnimation")

I get no results at all. No animations. No filter at all. Does anybody know a way to do this in a way that works, and could anyone explain why my current method doesn't work. Any help is appreciated, thanks in advance.

Upvotes: 1

Views: 421

Answers (1)

Peter Parker
Peter Parker

Reputation: 2201

Here is a way I know of that works, using only SpriteKit (no Core Animation). First you create the filter, store it:

let filter = CIFilter(name: "CIExposureAdjust", withInputParameters: ["inputEV": 0])
self.filter = filter // store it
scene.filter = filter // so the whole scene will be filtered
scene.shouldEnableEffects = true

Then you create an SKAction that changes a property of the filter.

let duration = 1.0
let a = SKAction.customAction(withDuration: duration) { (node, elapsed) in
    let percent = elapsed / CGFloat(duration)
    // Animate inputEV from 0 to 2.5
    self.filter.setValue(percent * 2.5, forKey: "inputEV")
}
a.timingMode = .easeIn
scene.run(a)

And if you don't want to filter the whole scene, you can use an SKEffectNode in the same way. But instead of assigning the filter to the scene, you assign it to the SKEffectNode. However, you need to add this line in the SKAction block, after updating the filter's properties:

self.myEffectNode.shouldEnableEffects = true

Without that line in the SKAction block, the animation won't work. I regard this as a bug. You don't have to do this if you're setting the filter on the whole scene though.

Upvotes: 2

Related Questions