Sprite Kit, masking node is aliased - ios

I'm experimenting a bit with Sprite Kit. I'm trying to mask an image with an alpha png. This is easy enough but I can't find information on how to mask an image with alpha values. Right now it renders the pixel or it doesn't.
From the docs on SKCropNode it says: If the pixel in the mask has an alpha value of less than 0.05, the image pixel is masked out.
So this is a logical outcome from the way I'm doing this right now:
CGPoint location = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
SKCropNode *cropNode = [SKCropNode node];
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
SKSpriteNode *maskImage = [SKSpriteNode spriteNodeWithImageNamed:#"mask5.png"];
[maskImage setName:#"mask"];
[cropNode setMaskNode:maskImage];
[cropNode addChild:sprite];
[cropNode setPosition:location];
[self addChild:cropNode];
The result is that the edges are jagged. Is there a way to mask the image in such way the edges are smooth?
Thanks!

No, the mask nodes are binary in the way they work, either they show or the hide. Apple provides an example in their documentation where they apply a blur filter to the masknode afterwards. A 1px box blur might do it for you.
Documentation -> Mask and blur effect

Related

cocos2d v3: physicsBody of a CCSprite is positioned incorrectly

I'm very new to cocos2d development.
What I am trying to implement is some sprite nodes with physics bodies. As far as I understand, you're supposed to add them to the scene like this ("physics" here is the name of CCPhysicsNode declared earlier):
[physics addChild:node];
instead of
[self addChild:node];
The second one is pretty self-explanatory, I never had any trouble with it. But with the first the position of collision shapes does not match the position of the actual sprite (as seen in debug drawing, and by offset I mean greatly offset, like 2x the actual position). This is how I declare and add the node:
CCSprite *sprite = [CCSprite spriteWithImageNamed:#"sprite.png"];
sprite.position = position;
sprite.physicsBody = [CCPhysicsBody bodyWithRect:[sprite boundingBox] cornerRadius:0];
sprite.physicsBody.collisionType = #"SomeCollisionType";
sprite.name = #"Name";
[physics addChild:sprite];
What am I doing wrong? Please explain me how to make the positions match. TIA.

Sprite kit - how to display half of sprite/texture/image

I am working on a Sprite Kit project in which I need to display in some cases only half of an existing image.
I tried making the frame of the sprite smaller, but it just stretches the image.
Is there any possibility to use a mask or something in order to display only half of the sprite/image/texture?
So, in order to show only half of the image, texture, sprite, someone would need to use a SKCropNode. The only sensible thing is to crop the sprite you need starting from half, not just cropping with a predefined size. This can be achieved by setting the mask node position.
1) create a SkSpriteNode with that texture/image:
// Obj-C
SKSpriteNode *skelet = [SKSpriteNode spriteNodeWithImageNamed:imageName];
// Swift
let skelet = SKSpriteNode(imageNamed: imageName)
2) create the SKCropNode:
// Obj-C
SKCropNode * cropNode = [SKCropNode node];
// Swift
let cropNode = SKCropNode()
3) create the mask
// Obj-C
SKSpriteNode *mask = [SKSpriteNode spriteNodeWithColor:[UIColor blackColor] size:CGSizeMake(skelet.frame.size.width/2, skelet.frame.size.height)];
// Swift
let mask = SKSpriteNode(color: .black, size: CGSize(width: skelet.frame.size.width/2, height: skelet.frame.size.height))
** set the mask position to the half of the result you need (you need half of the skelet -> set the mask position to the half "of the half of the skelet")
// Obj-C
mask.position = CGPointMake(skelet.frame.size.width/4, 0);
// Swift
mask.position = CGPoint(x: skelet.frame.size.width/4, y: 0)
The division by 4 is because you need the center of the mask to be not in the center of the skelet node, but moved to the half of a half of the skelet (reminder the mask node works with a default anchor point of 0.5 0.5 - so the zero point corresponds with the center of the skelet node).
4) add the needed elements to the crop node
// Obj-C
[cropNode addChild:skelet];
[cropNode setMaskNode:mask];
[self addChild:cropNode];
// Swift
cropNode.addChild(skelet)
cropNode.maskNode = mask
self.addChild(cropNode)

How to use a SKEmitterNode in sprite kit to achieve a "fog of war" effect?

I'm trying to use a SKEmitterNode to create a shader, kind of like in Pokemon when you are in a cave:
http://www.serebii.net/pokearth/maps/johto-hgss/38-route31.png
Here is the code I have so far :
NSString *burstPath =
[[NSBundle mainBundle] pathForResource:#"MyParticle" ofType:#"sks"];
SKNode *area = [[SKNode alloc] init];
SKSpriteNode *background = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:CGSizeMake(self.frame.size.width, self.frame.size.width)];
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
SKEmitterNode *burstNode =
[NSKeyedUnarchiver unarchiveObjectWithFile:burstPath];
burstNode.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
burstNode.particleBlendMode = SKBlendModeSubtract;
[area addChild:background];
[area addChild:burstNode];
[self addChild:area];
Here is the SKEmitterNode : http://postimg.org/image/60zflqjzt/
I've had two ideas.
The first one was to create a rectangular SKSpriteNode and remove the SKEmitterNode from the rectangular SKSpriteNode. That way, we have a black rectangle with a "hole" in the center, where we can see through.
The second one was to add the rectangular SKSpriteNode and the SKEmitter node to another SKNode (area), then set the particleBlendMode of the SKEmitterNode and finally set the alpha of the SKNode (area) in function of the color. For exemple, if the color of a pixel is black, the alpha value of that pixel will be 1.0 and another pixel is white, that other pixel's alpha value will be 0.0.
This question is a possible duplicate of How to create an alpha mask in iOS using sprite kit in some ways, but since no good answer has been given, I assume it isn't a problem.
Thank you very much.
These are not the nodes you are looking for! ;)
Particles can't be used to make a fog of war, even if you could make them behave to generate a fog of war it would be prohibitively slow.
Based on the linked screenshot you really only need an image with a "hole" in it, a transparent area. The image should be screen-sized and just cover up the borders to whichever degree you need it. This will be a non-revealing fog of war, or rather just the effect of darkness surrounding the player.
A true fog of war implementation where you uncover the world's area typically uses a pattern, in its simplest form it would just be removing (fading out) rectangular black sprites.
Now, with the powerful devices of this era (iPhone 12) is it possible to use 'SKEmitterNode' without lost too much frames per second.
You must build an SKS (SpriteKit Particle File) with this image:
Then, set your vars like this picture:
So, go to your code and add your particle with something like this example:
let fog = SKEmitterNode(fileNamed: "fog")
fog.zPosition = 6
self.addChild(fog)
fog.position.y = self.frame.midY
fog.particlePositionRange.dx = self.size.width * 2.5

How to create an alpha mask in iOS using sprite kit

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.
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.
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.

keep unmasking image on mask animation

I am trying to make an effect where at first the entire screen is masked out. As a ball moves across the screen, the ball unmasks the area that it is in AND the areas that it WAS in remain unmasked.
I have the following code:
CALayer * ball = [CALayer layer];
ball.bounds = CGRectMake(0, 0, 42, 42);
ball.position = [[[alphabet controls] objectAtIndex:0] CGPointValue];
ball.contents = (id)([UIImage imageNamed:#"done.png"].CGImage);
[self.layer addSublayer:ball];
[self.layer setMask:ball];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.path = path;
anim.repeatCount = HUGE_VALF;
anim.duration = 8.0;
[ball addAnimation:anim forKey:#"race"];
This animation masks the entire view and shows only what is behind the ball layer.
My question is : How can I keep unmasked the parts of the screen that were revealed?
Hmmm.
What you want is an image that contains all the pixels that the ball shape traveled through.
If you were animating the ball with frame-based animation, you could create a grayscale (or 1-bit) image and install that as the content of your mask layer. Then, as you moved the ball, you could draw it into your mask image with each frame.
I'm not sure how to get the same effect with Core animation.
You could make your mask a CAShapeLayer, create a CGPath that describes the entire path of your ball, and make that the path for your shape layer. If your ball is round, you could set the line thickness of the shape layer to your ball size. That would work. If you ball is an irregular shape, though, that approach wouldn't work. Quartz graphics on iOS doesn't have any way I know of to stroke a path with an arbitrary-shaped brush.

Resources