How would I add a gaussian blur to all nodes (there\'s no fixed number of nodes) in an SKScene in SpriteKit? A label will be added on top of the scene later, this will be m
I was trying to do this same thing now, in May 2020 (Xcode 11 and iOS 13.x), but wasn't unable to 'animate' the blur radius. In my case, I start with the scene fully blurred, and then 'unblur' it gradually (set inputRadius
to 0).
Somehow, the new input radius value set in the custom action block wasn't reflected in the rendered scene. My code was as follows:
private func unblur() {
run(SKAction.customAction(withDuration: unblurDuration, actionBlock: { [weak self] (_, elapsed) in
guard let this = self else { return }
let ratio = (TimeInterval(elapsed) / this.unblurDuration)
let radius = this.maxBlurRadius * (1 - ratio) // goes to 0 as ratio goes to 1
this.filter?.setValue(radius, forKey: kCIInputRadiusKey)
}))
}
I even tried updating the value manually using SKScene.update(_:)
and some variables for time book-keeping, but the same result.
It occurred to me that perhaps I could force the refresh if I "re-assingned" the blur filter to the .filter
property of my SKScene (see comments in ALL CAPS near the end of the code), to the same effect, and it worked.
The full code:
class MyScene: SKScene {
private let maxBlurRadius: Double = 50
private let unblurDuration: TimeInterval = 5
init(size: CGSize) {
super.init(size: size)
let filter = CIFilter(name: "CIGaussianBlur")
filter?.setValue(maxBlurRadius, forKey: kCIInputRadiusKey)
self.filter = filter
self.shouldEnableEffects = true
self.shouldRasterize = false
// (...rest of the child nodes, etc...)
}
override func didMove(to view: SKView) {
super.didMove(to: view)
self.unblur()
}
private func unblur() {
run(SKAction.customAction(withDuration: unblurDuration, actionBlock: { [weak self] (_, elapsed) in
guard let this = self else { return }
let ratio = (TimeInterval(elapsed) / this.unblurDuration)
let radius = this.maxBlurRadius * (1 - ratio) // goes to 0 as ratio goes to 1
// OBTAIN THE FILTER
let filter = this.filter
// MODIFY ATTRIBUTE
filter?.setValue(radius, forKey: kCIInputRadiusKey)
// RE=ASSIGN TO SCENE
this.filter = filter
}))
}
}
I hope this helps someone!