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?
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.
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.
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.
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.
I built a small library to deal with this exact scenario! Here is SpriteBar: https://github.com/henryeverett/SpriteBar
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With