SKSpriteNode - 如何创建一个带圆角的节点?

38

有没有办法使SKSpriteNode的角变圆?我正在尝试创建类似于填充颜色的方块的Tile。

SKSpriteNode *tile = [SKSpriteNode spriteNodeWithColor:[UIColor colorWithRed:0.0/255.0
                                                                           green:128.0/255.0
                                                                            blue:255.0/255.0
                                                                           alpha:1.0] size:CGSizeMake(30, 30)];

我该如何使它拥有圆角?

谢谢!


1
请注意:你最好让图片拥有透明的角落。- https://stackoverflow.com/a/39983382/294884 - Fattie
8个回答

53

要获得一个圆角节点,您可以使用2种方法,每种方法都需要使用SKShapeNode。

第一种方法是使用SKShapeNode,并将其路径设置为圆角矩形,如下所示:

SKShapeNode* tile = [SKShapeNode node];
[tile setPath:CGPathCreateWithRoundedRect(CGRectMake(-15, -15, 30, 30), 4, 4, nil)];
tile.strokeColor = tile.fillColor = [UIColor colorWithRed:0.0/255.0
                                                    green:128.0/255.0
                                                     blue:255.0/255.0
                                                    alpha:1.0];

另一个方法使用精灵节点、裁剪节点和带有圆角矩形作为裁剪节点掩码的SKShapeNode:

SKSpriteNode *tile = [SKSpriteNode spriteNodeWithColor:[UIColor   colorWithRed:0.0/255.0
                                                                           green:128.0/255.0
                                                                            blue:255.0/255.0
                                                                           alpha:1.0] size:CGSizeMake(30, 30)];
SKCropNode* cropNode = [SKCropNode node];
SKShapeNode* mask = [SKShapeNode node];
[mask setPath:CGPathCreateWithRoundedRect(CGRectMake(-15, -15, 30, 30), 4, 4, nil)];
[mask setFillColor:[SKColor whiteColor]];
[cropNode setMaskNode:mask];
[cropNode addChild:tile];

如果您的瓷砖只有一种颜色,我建议您采用第一种方法。


即使SKShapeNode目前存在不一致性,第一种方法似乎是可靠的,并且很容易实现。 - Toby
10
SKShapeNode现在具有一个'rectOfSize:,cornerRadius:'的初始化程序。 - Charlie Martin
如果您的瓦片使用纹理,您仍然可以采用第一种方法,通过设置SKShapeNode的fillTexture。 - Craig Grummitt
如果您想将圆角矩形设置为单一颜色,请调用“setSetStrokeColor:”函数。 - rizzes
请注意,新的cornerRadius初始化程序将使运行iOS 8以下版本的设备崩溃。Xcode不应该警告您这些问题吗? - Crashalot
我必须说,这完全错误。在任何游戏引擎或系统中,如果你想要一个圆形的精灵,你只需要写一行代码就能得到它。SKShapeNode在这里真的与问题无关,它是用于完全不相关的问题的。 - Fattie

22

在 Swift 3 中,您可以使用以下方法创建:

let tile = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 30, height: 30), cornerRadius: 10)

我该如何为这个SKShapeNode设置物理体? - iGetIt

8
希望这可以帮到您:
SKSpriteNode *make_rounded_rectangle(UIColor *color, CGSize size, float radius)
{
    UIGraphicsBeginImageContext(size);
    [color setFill];
    CGRect rect = CGRectMake(0, 0, size.width, size.height);
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius];
    [path fill];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    SKTexture *texture = [SKTexture textureWithImage:image];
    return [SKSpriteNode spriteNodeWithTexture:texture];
}

5
这受到了被接受的答案的启发,但使用了更易读的方式创建SKShapeNode,并修复了围绕裁剪周围的恼人像素线。看起来像是一个小细节,但可能会让某人浪费几分钟时间。
CGFloat cornerRadius = 15;
SKCropNode *cropNode = [SKCropNode node];
SKShapeNode *maskNode = [SKShapeNode shapeNodeWithRectOfSize:scoreNode.size cornerRadius:cornerRadius];
[maskNode setLineWidth:0.0];
[maskNode setFillColor:[UIColor whiteColor]];
[cropNode setMaskNode:maskNode];
[cropNode addChild:scoreNode];

很抱歉要重复一遍,但说实话,为了任何在这里搜索的人,请不要这样做!你只需要裁剪PNG图片,就这么简单。 - Fattie
@Fattie 我的回答是通用的,可以裁剪任何节点。我没有使用PNG格式,如果我使用了,我肯定不想去触碰每一个文件。所以它仍然有其价值。 - Git.Coach

5

真的很简单......

class YourSprite: SKSpriteNode {

    func yourSetupFunction() {

          texture = SKTexture( image: UIImage(named: "cat")!.circleMasked! )

这真的很简单。

你绝对不能使用SKShapeNode。

使用SKShapeNode会造成严重的性能问题,它不是正确的解决方案,难度非常大,并且SKShapeNode的目的与此问题毫无关系。

enter image description here

看看所有这些小猫咪!

circleMasked的代码就是这么简单:

(所有处理图像的项目都需要用到这种方法。)

extension UIImage {
    
    var isPortrait:  Bool    { return size.height > size.width }
    var isLandscape: Bool    { return size.width > size.height }
    var breadth:     CGFloat { return min(size.width, size.height) }
    var breadthSize: CGSize  { return CGSize(width: breadth, height: breadth) }
    var breadthRect: CGRect  { return CGRect(origin: .zero, size: breadthSize) }
    
    var circleMasked: UIImage? {
        
        UIGraphicsBeginImageContextWithOptions(breadthSize, false, scale)
        defer { UIGraphicsEndImageContext() }
        
        guard let cgImage = cgImage?.cropping(to: CGRect(origin:
            CGPoint(
                x: isLandscape ? floor((size.width - size.height) / 2) : 0,
                y: isPortrait  ? floor((size.height - size.width) / 2) : 0),
            size: breadthSize))
        else { return nil }
        
        UIBezierPath(ovalIn: breadthRect).addClip()
        UIImage(cgImage: cgImage, scale: 1, orientation: imageOrientation)
            .draw(in: breadthRect)
        return UIGraphicsGetImageFromCurrentImageContext()
    }

// classic 'circleMasked' stackoverflow fragment
// courtesy Leo Dabius /a/29047372/294884
}

这就是全部内容了。

虽然这个功能做很出色,而且看起来你已经穿过了一系列的玻璃墙才找到了这个解决方案,但是它如何帮助制作圆角矩形作为遮罩呢? - Confused
嗨@Confused。我完全不知道你的意思是什么?你能解释一下你在问什么吗?我会尽力帮助你。 - Fattie
你使用了一个圆形,我经常开玩笑地称之为完全圆角的正方形。OP正在询问圆角正方形/矩形。如果是圆角矩形/正方形,你会如何施展你的魔法?就像玻璃墙一样,我在谈论你如何超越框架思考来解决问题。 - Confused
嗨@Confused - 现在我明白你的问题了。幸运的是,很容易修改UIBezierPath以提供圆角而不是“圆形”。我会尽快尝试将其放入,但这非常容易:这里有一个优秀作家的绝佳示例:https://dev59.com/s1wY5IYBdhLWcg3w7rr6#41962342 - Fattie

4

Swift 4:

在将精灵添加到其父级之前,可以创建一个很好的解决方案。我已经制作了一个小扩展,您可以根据需要进行更正:

extension SKSpriteNode {
    func addTo(parent:SKNode?, withRadius:CGFloat) {
        guard parent != nil else { return }
        guard  withRadius>0.0 else {
            parent!.addChild(self)
            return
        }
        let radiusShape = SKShapeNode.init(rect: CGRect.init(origin: CGPoint.zero, size: size), cornerRadius: withRadius)
        radiusShape.position = CGPoint.zero
        radiusShape.lineWidth = 2.0
        radiusShape.fillColor = UIColor.red
        radiusShape.strokeColor = UIColor.red
        radiusShape.zPosition = 2
        radiusShape.position = CGPoint.zero
        let cropNode = SKCropNode()
        cropNode.position = self.position
        cropNode.zPosition = 3
        cropNode.addChild(self)
        cropNode.maskNode = radiusShape
        parent!.addChild(cropNode)
    }
}

使用方法:

let rootNode = SKSpriteNode(color: .white, size: self.size)
rootNode.alpha = 1.0
rootNode.anchorPoint = CGPoint.zero
rootNode.position = CGPoint.zero
rootNode.zPosition = 1
rootNode.name = "rootNode"
rootNode.addTo(parent: self, withRadius: 40.0)

1
太好了!但是当我将radiusShape添加到我的SKSpriteNode时,它无法居中于mySKSpriteNode! - Mohammad Reza Koohkan

2

来自类参考:

"SKSpriteNode 是一个绘制纹理图像、彩色正方形或混合颜色的纹理图像的节点。"

似乎最简单的方法是绘制一个带有圆角的块,然后使用以下其中一个类方法:

  • spriteNodeWithImageNamed:
  • spriteNodeWithTexture:
  • spriteNodeWithTexture:size:

5
Sprite Kit 不是一个绘图 API,而是一个用于管理预定义像素块(也称为精灵)移动的引擎。 - rickster

1
这是一个基于被接受答案的第二种解决方案的Swift 3代码片段。
func createPlayerRoundedNode(){
    let tile = SKSpriteNode(color: .white, size: CGSize(width: 30, height: 30))
    tile.zPosition = 3
    tile.name = "tile node"
    let cropNode = SKCropNode()
    cropNode.zPosition = 2
    cropNode.name = "crop node"
    let mask = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 30, height: 30), cornerRadius: 10)
    mask.fillColor = SKColor.darkGray
    mask.zPosition = 2
    mask.name = "mask node"
    cropNode.maskNode = mask
    self.addChild(cropNode)
    cropNode.addChild(tile)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接