6

The effect that I am trying to achieve is having a circle of light in an area of darkness. The effect is similar to that in pokemon games, when you are in a dark cave and only have limited vision surrounding you. From what I have tried and read, I have been unable to create a mask over nodes in sprite kit that has alpha levels. The masks I manage to create all have a hard edge, and basically just crop. Reading on the apple developer page about the SKCropNode, which has the maskNode property, it says "If the pixel in the mask has an alpha value of less than 0.05, the image pixel is masked out." This unfortunately sounds to me like the pixels will either be completely masked out or completely included, with no alpha values in between. If what I am trying to say has been hard to follow, here is an image of what I have achieved: https://www.dropbox.com/s/y5gbk8qvuq4ynh0/iOS%20Simulator%20Screen%20shot%20Jan%2020%2C%202014%201.06.23%20PM.png

and here is an image of what I would like to achieve: https://www.dropbox.com/s/wtwfdi1mjs2n8e6/iOS%20Simulator%20Screen%20shot%20Jan%2020%2C%202014%201.05.54%20PM.png

The way that I managed to get the above result, was I masked out the hard edge circle and then just added an image that has a gradient going from black on the outside to transparent on the inside. The reason this approach doesn't work is because I need to have multiple circles, and with the method I just mentioned, when the circles intersect the darkness on the outside of the transparent circle can be seen.

In conclusion, what I need is a way to have a circle that starts dark in the center, and then fades out. Then, have it so where the circle is dark, the image behind it can be seen, and where the circle is transparent, the image behind it cannot be seen. Again, sorry if what I am saying is difficult to follow. Here is the code I am using. Some of it was found from other posts.

SKSpriteNode *background = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(500, 500)];
    background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));

    SKCropNode *cropNode = [[SKCropNode alloc] init];
    SKNode *area = [[SKNode alloc] init];

    int x = 65; //radius of the circle

    _circleMask = [[SKShapeNode alloc ]init];
    CGMutablePathRef circle = CGPathCreateMutable();
    CGPathAddArc(circle, NULL, 0, 0, x/2, 0, M_PI*2, YES);
    _circleMask.path = circle;
    _circleMask.lineWidth = x*2;
    _circleMask.strokeColor = [SKColor whiteColor];
    _circleMask.name=@"circleMask";
    _circleMask.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));

//Here is where I just added in the gradient circle To give the desired appearance, but this isn't necessary to the code
    //_circleDark = [SKSpriteNode spriteNodeWithImageNamed:@"GradientCircle"];
    //_circleDark.position = [cropNode convertPoint:_circleMask.position fromNode:area];

    [area addChild:_circleMask];

    [cropNode setMaskNode:area];
    [cropNode addChild:background];
    //[cropNode addChild:_circleDark];

    [self addChild:cropNode];

This method has also allowed me to move the circles around, revealing different parts of the image behind it, which is what I want. To do this I just set it to change the _circleMask.position when the user taps the screen.

Also, just to make this clear in case anyone was confused, the black is just the background color of the scene, the picture is on top of that, and then the circle is part of the mask node.

mlrsquirrels
  • 61
  • 1
  • 4
  • Looking at the "Masking an Image with an Image Mask" section of: https://developer.apple.com/library/ios/documentation/graphicsimaging/conceptual/drawingwithquartz2d/dq_images/dq_images.html This is pretty much exactly what I need, if someone can figure out how to apply it to nodes and not just images. – mlrsquirrels Jan 21 '14 at 02:35
  • have you managed to figure that out ? – DevAno1 Oct 19 '15 at 16:10

2 Answers2

0

A very simple (and maybe less... or more performant) version of this would be to simply add a SKSpriteNode on top which has your vignette on a transparent background. In other words, if viewed in Photoshop, you would see a decreasing amount of checkerboard visible in the circle as you go from the center out, eventually displaying solid black. When the PNG image is used in your app, this transparency will be preserved when the two sprites are composited.

joshd
  • 1,626
  • 14
  • 17
  • I already tried something similar to what you are saying. The problem with this is that I need multiple circles of "light", so I can't just have an image with a circle in the center that is moved around, because if there are multiple circles they need to move independently. And if I do have them move independently, it looks something like this when they intersect: https://www.dropbox.com/s/3xptnv6bsgoa57x/iOS%20Simulator%20Screen%20shot%20Jan%2020%2C%202014%201.28.42%20PM.png – mlrsquirrels Jan 21 '14 at 01:43
  • You may want to have a look at SKEffectNode. The performance hit is pretty substantial but you may be able to find something that works well that isn't too heavy. – joshd Jan 21 '14 at 02:45
0

I have an idea... I hope it would help.

Make a PNG with the gradient you want from white to black with no transparency.

Use a separate sprite node with the png for each light you want and add them all to a SKEffectNode or SKCropNode node. It doesn't matter which since they are both rendered in a separate context. Set each sprite node to screen blending mode.

Then, when adding the parent SKEffectNode or SKCropNode to the scene, set it to multiply blend mode.

In the end, the screening will merge the "lights" together nicely, while the multiply will make the white area transparent.

mredig
  • 1,736
  • 1
  • 16
  • 29