带圆角的渐变进度条 SpriteKit Swift

Gradient progress bar with rounded corners SpriteKit Swift

我正在尝试在 SpriteKit 中构建一个带有圆角的渐变进度条,但我完全卡在了这一点上。我已经尝试了 SKCropNode、SKShapeNodes 等的不同组合,但我似乎无法让它工作。 感谢任何帮助,亲切的问候!

它是关于 SKCropNode + 它的 maskNode 属性。来自文档:

SKCropNode is a container node that you use to crop other nodes in the scene. You add other nodes to a crop node and set the crop node's maskNode property. For example, here are some ways you might specify a mask:

An untextured sprite that limits content to a rectangular portion of the scene.

A textured sprite that works as a precise per-pixel mask.

A collection of child nodes that form a unique shape.

You can animate the shape or contents of the mask to implement interesting effects such as hiding or revealing.

所以,一个简单的例子是这样的:

    class GameScene: SKScene {
        
        override func sceneDidLoad() {
            
            super.sceneDidLoad()
            createProgressBar()
        }
        
        private func createProgressBar(){
            
            let barFrame = CGRect(x: 0, y: 0, width: 300, height: 15)
            
            if let cgImage = createImage(frame: barFrame) {
                
                let texture = SKTexture(cgImage: cgImage)
                
                let sprite = SKSpriteNode(texture: texture)
                let cropNode = SKCropNode()
                let mask = SKSpriteNode(color: .gray, size: barFrame.size)
                
                cropNode.addChild(sprite)
                cropNode.maskNode = mask
                
                sprite.anchorPoint = CGPoint(x: 0.0, y: 0.5)
                mask.anchorPoint = CGPoint(x: 0.0, y: 0.5)
                
                var counter:Double = 0
                let action = SKAction.run {[weak self, sprite] in
                    guard let `self` = self, counter < 100 else {
                    sprite?.removeAction(forKey: "loop")
                      return
                
                  }
                    
                    counter += 1
                    let newWidth = self.getWidth(percents: counter, spriteWidth: barFrame.width)
                    print("Bar width \(newWidth), percentage \(counter)")
                    
                    mask.size = CGSize(width: newWidth, height: barFrame.height)
                }
                let wait = SKAction.wait(forDuration: 0.05)
                let sequence = SKAction.sequence([wait, action])
                
                let loop = SKAction.repeatForever(sequence)
                
                
                
                addChild(cropNode)
                cropNode.position = CGPoint(x: self.frame.width / 2.0, y: self.frame.height / 2.0)
                
                sprite.run(loop, withKey: "loop")
                
                
            }
        }
        
        private func getWidth(percents:Double, spriteWidth:Double)->Double{
            
            let onePercent = spriteWidth / 100.0
            
            return onePercent * percents
        }
        
        
        private func createImage(frame barFrame:CGRect) -> CGImage?{
            
            if let ciFilter = CIFilter(name: "CILinearGradient"){
                
                let ciContext = CIContext()
                
                ciFilter.setDefaults()
                
                let startColor  = CIColor(red: 0.75, green: 0.35, blue: 0.45, alpha: 1)
                let endColor    = CIColor(red: 0.45, green: 0.35, blue: 0.75, alpha: 1)
                
                let startVector = CIVector(x: 0, y: 0)
                let endVector   = CIVector(x: barFrame.width, y: 0)
                
                ciFilter.setValue(startColor,   forKey: "inputColor0")
                ciFilter.setValue(endColor,     forKey: "inputColor1")
                ciFilter.setValue(startVector,  forKey: "inputPoint0")
                ciFilter.setValue(endVector,    forKey: "inputPoint1")
                
                if let outputImage = ciFilter.outputImage {
                    let  cgImage = ciContext.createCGImage(outputImage, from: CGRect(x: 0, y: 0, width: barFrame.width, height: barFrame.height))
                    return  cgImage
                    
                }
            }
            
            return nil
      

  }
}

现在因为这只是一个例子,我不会一直去实现这个权利,但你可以用可设计和可检查的属性制作一个 class,优化代码,使其可重用等。但是这里显示了总体思路。

您使用SKCropNode在其中添加进度条,并使用maskNode属性随着百分比的增加显示进度条。我还提供了一种以编程方式创建纹理的方法,但您可以只使用 .png 文件。

此处仅使用裁剪节点来产生渐变(因为我们不想缩放图像,而是逐部分显示)。显然,如果进度条只有一种颜色,则不需要裁剪节点。

这是最终结果: