Elephantinc
Elephantinc

Reputation: 49

Spritekit silhouette effect when passing in front of object

I'm working on a sprite kit game and I'm trying to create a particular silhouette lighting effect whenever objects pass in front of a particular object (in this case a red sun). I want anything in front of it to appear black, and everything else to appear as normal (so if an object is partially in front, only that part should be black). But I'm not sure how to do it in an efficient way. I've tried playing around with different SKBlendModes but so far haven't had much success. The major challenge seems to be achieving the effect whilst preserving the colour of the sun behind.

I'm not sure if it's possible to achieve this with the existing SKBlendModes, or if something else will be required.

I've attached a diagram to illustrate what I'm going for and an image of the current best fit.

Target

Best fit so far

Upvotes: 2

Views: 444

Answers (2)

0x141E
0x141E

Reputation: 12753

You can create the silhouette effect with an SKCropNode, a mask node, and a background node. An SKCropNode selectively mask pixels of the sprite nodes in its node tree. Here, it will provide the silhouette effect by hiding/revealing portions of a black circle.

First, create the following objects:

  1. A shape node (yellow circle) with its zPosition set to -1
  2. A sprite node (black circle) with the same size as the yellow one
  3. A crop node (SKCropNode) and a mask node (SKNode)

Next,

  1. Add the yellow circle at the center of the scene
  2. Add the black sprite as a child of the crop node
  3. Assign the mask node to the maskNode property of the crop node
  4. Add the crop node at the center of the scene

Since the mask node currently has no children, the black circle will be completely hidden and the entire yellow circle beneath it will be visible. As we add sprites to the mask node, the pixels of the black circle that intersect with the sprite will be unmasked (shown in black), producing the silhouette effect.

All that's left is to add a "presentation" node that will be displayed when the mask node's sprite is outside of the black circle. The presentation node will mirror the mask node's sprite's position and rotation. This should be done in didSimulatePhysics to ensure that the presentation and mask-node sprites are synchronized after the physics and actions have been updated.

But wait...if the presentation node moves over the yellow circle, won't it eclipse the Sun? Not if we set its presentation node's zPosition to -2.

The following shows an implementation of the above:

enter image description here

Upvotes: 3

CloakedEddy
CloakedEddy

Reputation: 1995

This problem was interesting to me, so I came up with a simple solution. It's not perfect nor does it scale very well, but the basics fulfill your need I believe and it may point others in the right direction.

I don't use any special SKBlendMode. Instead, I am using a duplicate object for the object which moves in front of the sun. This duplicate object is cropped using an SKCropNode with a duplicate of the sun as the maskNode. So, my node tree is as follows:

  • SKSpriteNode (sun) (the white sun)
  • SKSpriteNode (object1) (the rotating purple square with alpha 0.25)
  • SKCropNode
    • maskNode: SKSpriteNode (sunDuplicate = [sun copy])
    • SKSpriteNode (object1duplicate, same size as object1 but with [SKColor blackColor])

Inside the update method, align the duplicate with the original to ensure animation is properly forwarded.

- (void)update:(CFTimeInterval)currentTime {
  self.object1duplicate.position = self.object1.position;
  self.object1duplicate.zRotation = self.object1.zRotation;
}

As I said, this doesn't scale very well since you'll manually have to add duplicate objects and keep track of them for each object moving in front of the sun object. Perhaps SKShader can be of more elegant use.

Rendered output from an iPad Air

Upvotes: 2

Related Questions