In SpriteKit, SKCropNode has no effect over a SKShapeNode

左心房为你撑大大i 提交于 2019-12-18 13:24:43

问题


I've been trying to apply a mask to a SKShapeNode using SKCropNode, and so far without success. Thinking that it's a SpriteKit bug - Here is the code snippet:

SKNode* contentNode = [SKNode node];

// picture - use an image bigger than 50x50
SKSpriteNode *pictureNode = [SKSpriteNode spriteNodeWithImageNamed:@"tree"];

// triangle
SKShapeNode* triangleNode = [SKShapeNode node];
UIBezierPath* triangleNodeBezierPath = [[UIBezierPath alloc] init];
[triangleNodeBezierPath moveToPoint:CGPointMake(0.0, 0.0)];
[triangleNodeBezierPath addLineToPoint:CGPointMake(0.0, 100.0)];
[triangleNodeBezierPath addLineToPoint:CGPointMake(50.0, 100.0)];
[triangleNodeBezierPath closePath];

triangleNode.path = triangleNodeBezierPath.CGPath;
triangleNode.fillColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1];

// create a mask
SKSpriteNode *mask = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size: CGSizeMake(50, 50)]; //50 by 50 is the size of the mask

// create a SKCropNode
SKCropNode *cropNode = [SKCropNode node];

[cropNode addChild: contentNode];
[cropNode setMaskNode: mask];

[self addChild: cropNode];
[contentNode addChild:pictureNode]; // pictureNode is being cropped
[contentNode addChild:triangleNode]; // triangleNode is not

cropNode.position = CGPointMake( CGRectGetMidX (self.frame), CGRectGetMidY (self.frame));

Does anyone have a workaround about this issue? Thanks a lot!


回答1:


This had been bugging me for most of the day. I had planned to create a timer similar to the excellent TCProgressTimer by Tony Chamblee. However, as my application uses multiple progress timers I didn't want to have to design dozens of different sized sprites for use at different resolutions.

My solution was to convert SKShapeNode objects to SKSpriteNode objects. I ended up having to go back to basics and use Core Graphics to do the heavy lifting. This is a rather messy way of doing things, I'm sure, but I wanted quick results to dynamically create objects that would resemble the results obtained when using SKShapeNode.

I am only interested in making circle objects at present, so I did it like this:

-(SKSpriteNode *)createSpriteMatchingSKShapeNodeWithRadius:(float)radius color:(SKColor *)color {
    CALayer *drawingLayer = [CALayer layer];
    CALayer *circleLayer = [CALayer layer];
    circleLayer.frame = CGRectMake(0,0,radius*2.0f,radius*2.0f);
    circleLayer.backgroundColor = color.CGColor;
    circleLayer.cornerRadius = circleLayer.frame.size.width/2.0;
    circleLayer.masksToBounds = YES;


    [drawingLayer addSublayer:circleLayer];

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(circleLayer.frame.size.width, circleLayer.frame.size.height), NO, [UIScreen mainScreen].scale);
    CGContextSetAllowsAntialiasing(UIGraphicsGetCurrentContext(), TRUE);
    CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), [UIColor clearColor].CGColor);
    CGContextFillRect(UIGraphicsGetCurrentContext(), CGRectMake(0,0,circleLayer.frame.size.width,circleLayer.frame.size.height));
    [drawingLayer renderInContext: UIGraphicsGetCurrentContext()];

    UIImage *layerImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImage:layerImage]];


    return sprite;
}

The resulting sprite can now be masked by an SKCropNode as expected. As these sprites are all generated before the scene begins, I do not notice a performance hit. However, I would imagine this method is highly inefficient if you are generating multiple nodes on the fly.

I would be eager to hear solutions from other users. Hope that helps.

-DC




回答2:


I have the similar task in my app. I need to draw multiple irregular shapes based on user input and then use them as crop node's masks.

The solution that works for me is to:

  1. create a SKShapeNode with the required path

  2. retrieve a SKTexture from it using SKView method textureFromNode:crop:

  3. create a SKSpriteNode from that texture.

  4. use SKSprite node as a mask.




回答3:


Your title is correct. I've also discovered having a ShapeNode in the CropNode's hierarchy affects the children above it as well. I created a quick experiment. You can create a new game project and try it yourself. By commenting out one of the addChild:shapeContent lines you can see how it affects the cropping of the spaceship image.

As DoctorClod points out the current solution seems to be making sure all children of the cropNode are SpriteNodes.

-(void)didMoveToView:(SKView *)view {

SKSpriteNode* colorBackground = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(800, 600)];

SKSpriteNode *spaceshipImage = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"];

SKShapeNode* shapeContent = [SKShapeNode shapeNodeWithRect:CGRectMake(0, 0, 200, 100)];
shapeContent.fillColor = [SKColor greenColor];

SKSpriteNode *maskShape  = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:CGSizeMake(500, 100)];

SKCropNode *cropNode = [SKCropNode new];

[cropNode addChild:colorBackground];
[cropNode addChild:shapeContent];     // comment this one out
[cropNode addChild:spaceshipImage];
[cropNode addChild:shapeContent];     // or comment this one out

[cropNode setMaskNode:maskShape];

cropNode.position = CGPointMake(CGRectGetMidX (self.frame), CGRectGetMidY(self.frame));
[self addChild:cropNode];
}


来源:https://stackoverflow.com/questions/21816079/in-spritekit-skcropnode-has-no-effect-over-a-skshapenode

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!