Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create progress bar in sprite kit?

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 put filled one on top of empty one, they are regular SKSPriteNodes now I can't figure out how do I cut my filled image where I need?

How do I cut SKSpriteNode image at certain point? Maybe texture?

like image 917
Dvole Avatar asked May 09 '14 11:05

Dvole


5 Answers

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.

like image 144
CloakedEddy Avatar answered Nov 16 '22 11:11

CloakedEddy


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.

like image 42
LearnCocos2D Avatar answered Nov 16 '22 11:11

LearnCocos2D


that is my ProgressBar in swift :

import Foundation

import SpriteKit


class IMProgressBar : SKNode{

var emptySprite : SKSpriteNode? = nil
var progressBar : SKCropNode
init(emptyImageName: String!,filledImageName : String)
{
    progressBar = SKCropNode()
    super.init()
    let filledImage  = SKSpriteNode(imageNamed: filledImageName)
    progressBar.addChild(filledImage)
    progressBar.maskNode = SKSpriteNode(color: UIColor.whiteColor(),
        size: CGSize(width: filledImage.size.width * 2, height: filledImage.size.height * 2))

    progressBar.maskNode?.position = CGPoint(x: -filledImage.size.width / 2,y: -filledImage.size.height / 2)
    progressBar.zPosition = 0.1
    self.addChild(progressBar)

    if emptyImageName != nil{
        emptySprite = SKSpriteNode.init(imageNamed: emptyImageName)
        self.addChild(emptySprite!)
    }
}
func setXProgress(xProgress : CGFloat){
    var value = xProgress
    if xProgress < 0{
        value = 0
    }
    if xProgress > 1 {
        value = 1
    }
    progressBar.maskNode?.xScale = value
}

func setYProgress(yProgress : CGFloat){
    var value = yProgress
    if yProgress < 0{
        value = 0
    }
    if yProgress > 1 {
        value = 1
    }
    progressBar.maskNode?.yScale = value
}
required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

}

//How to use :

let progressBar = IMProgressBar(emptyImageName: "emptyImage",filledImageName: "filledImage")

or

let progressBar = IMProgressBar(emptyImageName: nil,filledImageName: "filledImage")

and add this progressBar to any SKNode :

self.addChild(progressBar)

//That's all.

like image 32
Tritmm Avatar answered Nov 16 '22 10:11

Tritmm


Assuming HealthBarNode is a subclass of SKSpriteNode with a public property health that varies between 0.0 and 1.0 and whose parental property texture is generated from the entire color bar image of width _textureWidth (a private property), you could do something like this:

- (void)setHealth:(CGFloat)fraction
{
    self.health = MIN(MAX(0.0, fraction), 1.0); // clamp health between 0.0 and 1.0
    SKTexture *textureFrac = [SKTexture textureWithRect:CGRectMake(0, 0, fraction, 1.0) inTexture:self.texture]; 
// check docs to understand why you can pass in self.texture as the last parameter every time

    self.size = CGSizeMake(fraction * _textureWidth, self.size.height);
    self.texture = textureFrac;
}

Setting the health to a new value will cause the health bar (added as a child to the main scene, say) to get cropped properly.

like image 3
Aky Avatar answered Nov 16 '22 12:11

Aky


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

like image 2
henryeverett Avatar answered Nov 16 '22 12:11

henryeverett