是否可以在 Sprite Kit 中使用圆形(SKShapeNode)作为蒙版?

Posted

技术标签:

【中文标题】是否可以在 Sprite Kit 中使用圆形(SKShapeNode)作为蒙版?【英文标题】:Is it possible to use a circle (SKShapeNode) as a mask in Sprite Kit? 【发布时间】:2013-11-26 22:05:28 【问题描述】:

我正在尝试在 Sprite Kit 项目中创建圆形蒙版。我像这样创建圆圈(将其定位在屏幕的中心):

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

SKShapeNode *circleMask = [[SKShapeNode alloc ]init];
CGMutablePathRef circle = CGPathCreateMutable();
CGPathAddArc(circle, NULL, CGRectGetMidX(self.frame), CGRectGetMidY(self.frame), 50, 0, M_PI*2, YES);
circleMask.path = circle;
circleMask.lineWidth = 0;
circleMask.fillColor = [SKColor blueColor];
circleMask.name=@"circleMask";

在代码的下方,我将其设置为cropNode 的掩码:

[cropNode setMaskNode:circleMask];

...但内容不是显示在圆圈内,而是显示为正方形。

是否可以使用SKShapeNode 作为蒙版,还是需要使用图像?

【问题讨论】:

【参考方案1】:

经过大量的咒骂、搜索和在 Xcode 中的实验,我有了一个非常老套的修复方法。

请记住,这是一个非常讨厌的 hack - 但您可以将其归咎于 Sprite Kit 对 SKShapeNode 的实现。向路径添加填充会导致以下情况:

为您的场景添加一个额外的节点 路径变得不可屏蔽 - 它出现在“遮罩”上方 使任何非SKSpriteNode 兄弟节点不可屏蔽(例如SKLabelNodes)

不是一个理想的状态。

受Tony Chamblee's progress timer的启发,“修复”是完全省去填充,只使用路径的描边:

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

SKShapeNode *circleMask = [[SKShapeNode alloc ]init];
CGMutablePathRef circle = CGPathCreateMutable();
CGPathAddArc(circle, NULL, CGRectGetMidX(self.frame), CGRectGetMidY(self.frame), 50, 0, M_PI*2, YES); // replace 50 with HALF the desired radius of the circle
circleMask.path = circle;
circleMask.lineWidth = 100; // replace 100 with DOUBLE the desired radius of the circle
circleMask.strokeColor = [SKColor whiteColor];
circleMask.name=@"circleMask";

[cropNode setMaskNode:circleMask];

如上所述,您需要将半径设置为通常的一半,并将线宽设置为半径的两倍。

希望苹果将来会关注这一点;目前,这个 hack 是我找到的最好的解决方案(除了使用图像,如果你的面具需要是动态的,这真的不起作用)。

【讨论】:

【参考方案2】:

是的,在当前的 Sprite-Kit 实现中不可能使用填充颜色的 shapenode。我认为这是一个错误。

但是!

您始终可以将形状渲染为纹理并将其用作遮罩!

例如,设 edge 是之前创建的 SKShapeNode。

首先,添加到视图之前将其渲染到纹理(在这种情况下,它将从其他节点清除)

SKTexture *Mask = [self.view textureFromNode:edge];
[self addChild:edge]; //if you need physics borders

第二,

SKCropNode *cropNode = [[SKCropNode alloc] init];
[cropNode setMaskNode:[SKSpriteNode spriteNodeWithTexture:Mask]];
[cropNode addChild: [SKSpriteNode spriteNodeWithTexture:rocksTiles size:CGSizeMake(w,h)]];
cropNode.position = CGPointMake(Mask.size.width/2,Mask.size.height/2);
//note, anchorpoint of shape in 0,0, but rendered texture is in 0.5,0.5, so we need to dispose it
[self addChild:cropNode];

【讨论】:

现在有些事情是可能的。 (ios 8.1): int deviceOSVersion= [[[UIDevice currentDevice] systemVersion] floatValue]; if (deviceOSVersion>=8.0) treeS.fillTexture=[SKTexture textureWithImage:[UIImage imageNamed:@"treeS"]];【参考方案3】:

由于我不能使用 SKShapeNode 作为掩码,我决定将其转换为 SKSpriteNode

这是我的 Swift 代码:

let shape : SKShapeNode = ... // create your SKShapeNode
var view : SKView = ... // you can get this from GameViewController

let texture = view.textureFromNode(shape)
let sprite = SKSpriteNode(texture: texture)
sprite.position = ...

cropNode.mask = sprite

它确实有效:)

【讨论】:

看起来您需要在场景中添加一个未使用的形状节点。我建议您考虑从CGPathUIBezierPath 创建UIImage。然后,您可以从图像生成纹理。 嗨 0x141E,实际上我没有将 shape(SKShapeNode)添加到场景中。唯一要保存的节点是sprite 这是一个比公认的更好的解决方案【参考方案4】:

对于这个问题有一个稍微尴尬的解决方案,我认为它比围绕这个问题的任何有争议的 hack 都稍微不那么令人讨厌。只需将您的 SKShapeNode 包装在 SKNode 中。我还没有测试这可能会导致什么样的性能损失,但鉴于 SKNode 是非绘图的,我希望它不会太显着。

let background = SKSpriteNode(imageNamed: "Background")
let maskNode = SKShapeNode(path: MyPath)
let wrapperNode = SKNode()
let cropNode = SKCropNode()

wrapperNode.addChild(maskNode)
cropNode.maskNode = wrapperNode
cropNode.addChild(background)

【讨论】:

【参考方案5】:

将掩码节点的 alpha 设置为 .0 即可:

circleMask.alpha = .0;

编辑: 似乎仅适用于 Mac OS。

【讨论】:

以上是关于是否可以在 Sprite Kit 中使用圆形(SKShapeNode)作为蒙版?的主要内容,如果未能解决你的问题,请参考以下文章

在 Sprite Kit 中混合颜色

如何在 Sprite-kit 中画一条线

如何在 Sprite Kit 中初始化所有场景?

如何在场景中放置场景 - Sprite Kit

Sprite Kit:为所有场景只创建一次节点

如何在Sprite-kit中画一条线