21

I have a moving black image on a dark screen, to make it easier to see I would like to add in a white glow to the image. This is my code for the moving image:

   Ghost = SKSpriteNode(imageNamed: "Ghost1")
Ghost.size = CGSize(width: 50, height: 50)
Ghost.position = CGPoint(x: self.frame.width / 2 - Ghost.frame.width, y: self.frame.height / 2)

Ghost.physicsBody = SKPhysicsBody(circleOfRadius: Ghost.frame.height / 1.4)
Ghost.physicsBody?.categoryBitMask = PhysicsCatagory.Ghost
Ghost.physicsBody?.collisionBitMask = PhysicsCatagory.Ground | PhysicsCatagory.Wall
Ghost.physicsBody?.contactTestBitMask = PhysicsCatagory.Ground | PhysicsCatagory.Wall | PhysicsCatagory.Score
Ghost.physicsBody?.affectedByGravity = false
Ghost.physicsBody?.isDynamic = true
Ghost.zPosition = 2

self.addChild(Ghost)

I'm not sure how or what to use to add in a glow, if you need more information please ask.

Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
Oren Edrich
  • 674
  • 1
  • 7
  • 23

2 Answers2

36

I created this extension to add a glow effect to an SKSpriteNode

Just add this to your project

extension SKSpriteNode {

    func addGlow(radius: Float = 30) {
        let effectNode = SKEffectNode()
        effectNode.shouldRasterize = true
        addChild(effectNode)
        let effect = SKSpriteNode(texture: texture)
        effect.color = self.color
        effect.colorBlendFactor = 1
        effectNode.addChild(effect)
        effectNode.filter = CIFilter(name: "CIGaussianBlur", parameters: ["inputRadius":radius])
    }
}

Now given an SKSpriteNode

let sun = SKSpriteNode(imageNamed: "sun")

all you have to do it

sun.addGlow()

enter image description here

Alexander Volkov
  • 7,904
  • 1
  • 47
  • 44
Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
  • 5
    This is an amazing answer, beyond an answer. This is a generous provision of a service plus insight, and another amazing demonstration of the power inherent to thinking in terms of extensions. THANK YOU!!! And that's not enough. Add !!!^3. A performance query: I've tinkered with effects, and finished up manually baking them into textures to use as sprites. Does this blur effect update every frame. Or do they "bake" and simply operate as a bitmap/sprite addition unless the underlying object (upon which they're applied) changes? – Confused Nov 01 '16 at 16:46
  • 1
    @Confused: Very good point. I just added this line to my extension `effectNode.shouldRasterize = true`. Now the blur effect is calculated only the first time and then cached for the next frames. The cache is automatically invalidated when something changes inside `effectNode` (but it's not gonna happen with this code). – Luca Angeletti Nov 01 '16 at 16:53
  • THANK YOU, again. That's clarified it for me. I wasn't sure if `effectNodes` do intelligent, self aware optimisation of themselves, or not. I think, sometimes, they do. But only guessing from looking at odd changes to framerates. I don't often see a correlation between the framerates shown onscreen and the debug tools in Xcode. But shouldRasterize... should force it to do that optimisation/prevention of constant recalculation. I hope. Cheers! – Confused Nov 01 '16 at 16:58
  • @appzYourLife You ever attempt to use an SKLightNode instead of an SKEffectNode? You may see some performance gain in the matter – Knight0fDragon Nov 01 '16 at 18:08
  • will this add a specific color for the glow or will it make what ever color the image is, brighter? – Oren Edrich Nov 01 '16 at 20:35
  • @OrenEdrich: This only add the glow effect around the original sprite. It does not change how the original sprite is represented. – Luca Angeletti Nov 01 '16 at 20:36
  • Is there a way add a white glow around a back spritenode? – Oren Edrich Nov 01 '16 at 20:45
  • @OrenEdrich: In this case use an SKTexture with white color at this line `effectNode.addChild(SKSpriteNode(texture: texture))` – Luca Angeletti Nov 01 '16 at 20:48
  • Great. I'm not sure we're exactly to add the extension. Do I add in right after the code for the Spritenode? – Oren Edrich Nov 01 '16 at 22:09
  • Wherever you're doing the coding (whichever file in your project you do this creation and addition of the glow) you can put the extension right at the top, just underneath the import of SpriteKit. There is a way to make extensions universal for your project, too. But I'm not sure how to do that. I always put them at the top of the file I use them in to make sure I remember to use them ;) @OrenEdrich – Confused Nov 02 '16 at 03:04
  • Thank you, i added in the extension to the top, but I'm not sure what to replace the (texture: texture))) with to make it a white color instead – Oren Edrich Nov 02 '16 at 03:15
  • Put extensions at the bottom of your class, not the top. The idea is better readability. You enter a class file, chances are you want to see the code for that class, not extensions of other classes that this class uses. Also, as far as I am aware, once you make an extension it is usable throughout the project. There is no form of a private extension. – Knight0fDragon Nov 03 '16 at 11:24
  • 4
    @Knight0fDragon: You can restrict the visibility of an extension to the file where it is declared using `fileprivate` – Luca Angeletti Nov 03 '16 at 15:28
  • @appzYourLife, ok, looks like you can declare them private, but by default they are public, thanks for letting me know – Knight0fDragon Nov 03 '16 at 15:35
  • This is great! how would you alter this to be more generic to any kind of SKNode? for example an SKLabelNode, SKShapeNode, etc? – Dave Kliman Dec 30 '16 at 18:32
  • also, what if you want to remove the effect? – Dave Kliman Dec 31 '16 at 13:39
  • 2
    @DaveKliman: You can assign a `name` to the effectNode `effectNode.name = "GlowEffect"`. Later one you can retrieve it and make it hidden `childNode(withName: "GlowEffect")?.isHidden = true` – Luca Angeletti Dec 31 '16 at 13:46
  • 1
    @appzYorLife hm. i just tried to add this code to a simple bit of code that allows users to drag cards around on the screen, but firstly it basically only blurs them and doesn't really make them look "lit up," and secondly, oddly, once they are blurred, they won't drag around anymore. not sure why. – Dave Kliman Dec 31 '16 at 14:23
5

Just to add to this, you can perform this on any type of SKNode by first rendering its contents using the texture(from:SKNode) method available on an SKView instance.

Example:

extension SKNode
{
    func addGlow(radius:CGFloat=30)
    {
        let view = SKView()
        let effectNode = SKEffectNode()
        let texture = view.texture(from: self)
        effectNode.shouldRasterize = true
        effectNode.filter = CIFilter(name: "CIGaussianBlur",withInputParameters: ["inputRadius":radius])
        addChild(effectNode)
        effectNode.addChild(SKSpriteNode(texture: texture))
    }
}
prolific8
  • 108
  • 1
  • 6
  • When I apply this to a moving SKNode, it stops for a while. I guess it's because it's the performance issue. – Bagusflyer Apr 30 '20 at 11:17