How to create progress bar in sprite kit?

后端 未结 11 890
失恋的感觉
失恋的感觉 2020-12-28 17:56

I want to create my own progress bar in Sprite Kit.
I figured I will need to images - one fully empty progress bar and filled progress bar.
I have those images, I pu

相关标签:
11条回答
  • 2020-12-28 18:33

    I built a small library to deal with this exact scenario! Here is SpriteBar: https://github.com/henryeverett/SpriteBar

    0 讨论(0)
  • 2020-12-28 18:35

    Quite simply: you need a frame image (optional) and a "bar" image. The bar image out to be a single, solid color and as high as you need it and 1 or 2 pixels wide. A SKShapeNode as bar will do as well.

    Just making the bar and animating is simply a matter of changing the SKSpriteNode's size property. For example to make the bar represent progress between 0 and 100 just do:

    sprite.size = CGSizeMake(progressValue, sprite.size.height);
    

    Update the size whenever progressValue changes.

    You'll notice the image will increase in width to both left and right, to make it stretch only to the right change the anchorPoint to left-align the image:

    sprite.anchorPoint = CGPointMake(0.0, 0.5);
    

    That is all. Draw a frame sprite around it to make it look nicer.

    0 讨论(0)
  • 2020-12-28 18:35

    Swift 4:

    ( my answer 2 -> make a complex progress bar using textures)

    To make a complex progress bar based to texture and colors you can subclass a simple SKNode. About this case, SpriteKit for now (swift v4.1.2) doesn't have a method to directly cutting a SKTexture. We need to use another method called texture(from:crop:)

    class SKProgressImageBar: SKNode {
        var baseSprite: SKSpriteNode!
        var coverSprite: SKSpriteNode!
        var originalCoverSprite: SKSpriteNode!
        override init() {
            super.init()
        }
        convenience init(baseImageName:String="", coverImageName:String="", baseColor: SKColor, coverColor: SKColor, size: CGSize ) {
            self.init()
            self.baseSprite = baseImageName.isEmpty ? SKSpriteNode(color: baseColor, size: size) : SKSpriteNode(texture: SKTexture(imageNamed:baseImageName), size: size)
            self.coverSprite = coverImageName.isEmpty ? SKSpriteNode(color: coverColor, size: size) : SKSpriteNode(texture: SKTexture(imageNamed:coverImageName), size: size)
            self.originalCoverSprite = self.coverSprite.copy() as! SKSpriteNode
            self.addChild(baseSprite)
            self.addChild(coverSprite)
            self.coverSprite.zPosition = 2.0
        }
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        func setProgress(_ value:CGFloat) {
            print("Set progress bar to: \(value)")
            guard 0.0 ... 1.0 ~= value else { return }
            self.coverSprite.texture = self.originalCoverSprite.texture
            let originalSize = self.baseSprite.size
            var calculateFraction:CGFloat = 0.0
            self.coverSprite.position = self.baseSprite.position
            if value == 1.0 {
                calculateFraction = originalSize.width
            } else if 0.01..<1.0 ~= value {
                calculateFraction = originalSize.width * value
            }
    
            let coverRect = CGRect(origin: self.baseSprite.frame.origin, size: CGSize(width:calculateFraction,height:self.baseSprite.size.height))
            if let parent = self.parent, parent is SKScene, let parentView = (parent as! SKScene).view {
                if let texture = parentView.texture(from: self.originalCoverSprite, crop: coverRect) {
                    let sprite = SKSpriteNode(texture:texture)
                    self.coverSprite.texture = sprite.texture
                    self.coverSprite.size = sprite.size
                }
                if value == 0.0 {
                    self.coverSprite.texture = SKTexture()
                    self.coverSprite.size = CGSize.zero
                }
                if value>0.0 && value<1.0 {
                    let calculateFractionForPosition = originalSize.width - (originalSize.width * value)
                    self.coverSprite.position = CGPoint(x:(self.coverSprite.position.x-calculateFractionForPosition)/2,y:self.coverSprite.position.y)
                }
            }
        }
    }
    

    Usage:

    some texture just to make an example:

    baseTxt.jpeg:

    coverTxt.png:

    Code:

    self.energyProgressBar = SKProgressImageBar.init(baseImageName:"baseTxt.jpeg", coverImageName: "coverTxt.png", baseColor: .white, coverColor: .blue, size: CGSize(width:200,height:50))
    //self.energyProgressBar = SKProgressImageBar.init(baseColor: .white, coverColor: .blue, size: CGSize(width:200,height:50))
    self.addChild(self.energyProgressBar)
    self.energyProgressBar.position = CGPoint(x:self.frame.width/2, y:self.frame.height/2)
    let wait = SKAction.wait(forDuration: 2.0)
    let action1 = SKAction.run {
        self.energyProgressBar.setProgress(0.7)
    }
    let action2 = SKAction.run {
        self.energyProgressBar.setProgress(0.0)
    }
    let action3 = SKAction.run {
        self.energyProgressBar.setProgress(1.0)
    }
    let action4 = SKAction.run {
        self.energyProgressBar.setProgress(0.5)
    }
    let action5 = SKAction.run {
        self.energyProgressBar.setProgress(0.1)
    }
    let sequence = SKAction.sequence([wait,action1,wait,action2,wait,action3,wait,action4,wait,action5])
    self.run(sequence)
    

    Output:

    with colors:

    with textures:

    0 讨论(0)
  • 2020-12-28 18:41

    I would recommend looking into SKCropNode. For a visual aid how SKCropNode works, look it up in the Apple Programming Guide. I have read through the entire document multiple times and it is a particularly good read.

    SKCropNode is basically an SKNode which you add to your scene, but its children can be cropped by a mask. This mask is set in the maskNode property of the SKCropNode. In this way, you only need one texture image. I would subclass SKCropNode to implement functionality to move or resize the mask, so you can easily update its appearance.

    @interface CustomProgressBar : SKCropNode
    
    /// Set to a value between 0.0 and 1.0.
    - (void) setProgress:(CGFloat) progress;
    
    @end
    
    @implementation CustomProgressBar
    
    - (id)init {
      if (self = [super init]) {
        self.maskNode = [SKSpriteNode spriteNodeWithColor:[SKColor whiteColor] size:CGSizeMake(300,20)];
        SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:@"progressBarImage"];
        [self addChild:sprite];
      }
      return self;
    }
    
    - (void) setProgress:(CGFloat) progress {
      self.maskNode.xScale = progress;
    }
    
    @end
    

    In your scene:

    #import "CustomProgressBar.h"
    
    // ...
    
    CustomProgressBar * progressBar = [CustomProgressBar new];
    [self addChild:progressBar];
    
    // ...
    
    [progressBar setProgress:0.3];
    
    // ...
    
    [progressBar setProgress:0.7];
    

    Note: this code doesn't move the mask (so the sprite will be cropped on either side) but I'm sure you get the idea.

    0 讨论(0)
  • 2020-12-28 18:43

    A simple class using two sprite nodes

    class PBProgressBar: SKNode {
    private var baseNode : SKSpriteNode!
    private var progressNode : SKSpriteNode!
    private var basePosition: CGPoint!
    var progress: CGFloat!
    init(progress: CGFloat = 0.45, position: CGPoint = CGPoint.zero) {
        super.init()
        self.progress = progress
        self.basePosition = position
        configureProgress()
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    func configureProgress() {
        baseNode = SKSpriteNode(color: .white, size: CGSize(width: 10, height: 100))
        baseNode.anchorPoint = CGPoint.zero
        let heightFraction = baseNode.size.height * progress
        baseNode.position = basePosition
        progressNode = SKSpriteNode(color: .blue, size: CGSize(width: 10, height: heightFraction))
        progressNode.anchorPoint = CGPoint.zero
        baseNode.addChild(progressNode)
        self.addChild(baseNode)
    }
    

    }

    0 讨论(0)
提交回复
热议问题