Как создать индикатор выполнения в наборе спрайтов?

Я хочу создать свой собственный индикатор выполнения в Sprite Kit.
Я решил, что мне понадобится изображение - один полностью пустой индикатор выполнения и заполненный индикатор выполнения.
У меня есть эти образы, я накладываю заполненный поверх пустого, они регулярные SKSPriteNodes, теперь я не могу понять, как я могу нарезать свое заполненное изображение там, где мне нужно?

Как отрезать изображение SKSpriteNode в определенный момент? Может быть, текстура?

Ответ 1

Я бы рекомендовал посмотреть SKCropNode. Для наглядного пособия, как работает SKCropNode, посмотрите его в Руководстве по программированию Apple. Я прочитал весь документ несколько раз, и он особенно хорошо читается.

SKCropNode - это в основном SKNode, который вы добавляете к своей сцене, но его дети могут быть обрезаны маской. Эта маска устанавливается в свойстве maskNode для SKCropNode. Таким образом, вам нужно только одно изображение текстуры. Я бы подклассифицировал SKCropNode для реализации функциональности для перемещения или изменения размера маски, поэтому вы можете легко обновить ее внешний вид.

@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

В вашей сцене:

#import "CustomProgressBar.h"

// ...

CustomProgressBar * progressBar = [CustomProgressBar new];
[self addChild:progressBar];

// ...

[progressBar setProgress:0.3];

// ...

[progressBar setProgress:0.7];

Примечание: этот код не перемещает маску (поэтому спрайт будет обрезаться с обеих сторон), но я уверен, что вы поняли эту идею.

Ответ 2

Довольно просто: вам нужно изображение кадра (необязательно) и изображение "бар". Изображение штриха должно быть единым, сплошным цветом и столь же высоким, насколько вам нужно, и шириной 1 или 2 пикселя. Также будет работать SKShapeNode в качестве панели.

Просто создание панели и анимация - это просто вопрос изменения свойства размера SKSpriteNode. Например, чтобы сделать панель показателем прогресса между 0 и 100, выполните:

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

Обновляйте размер при изменении значения progressValue.

Вы заметите, что изображение будет увеличиваться по ширине как влево, так и вправо, чтобы растянуть его только вправо, изменив привязку к левому выравниванию изображения:

sprite.anchorPoint = CGPointMake(0.0, 0.5);

Вот и все. Нарисуйте кадр спрайта вокруг него, чтобы он выглядел лучше.

Ответ 3

Предполагая, что HealthBarNode является подклассом SKSpriteNode с общедоступным свойством health, который варьируется от 0.0 до 1.0 и родительское свойство texture генерируется из всего изображения цветной полосы шириной _textureWidth (частный свойство), вы можете сделать что-то вроде этого:

- (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;
}

Установка health в новое значение приведет к тому, что панель работоспособности (добавляется как дочерний элемент в основную сцену, скажем) будет правильно отрегулирована.

Ответ 4

Я построил небольшую библиотеку, чтобы справиться с этим точным сценарием! Вот SpriteBar: https://github.com/henryeverett/SpriteBar

Ответ 5

Нет "разрезания" изображения/текстуры.

Альтернативой тому, что предлагает Cocos, является создание нескольких текстур и их замена в ваш node в зависимости от состояния здоровья. Я сделал игру, где панель здоровья меняла текстуру каждые 10 пунктов (диапазон был 0-100). Однако после некоторых проб и ошибок я только что сделал то, что предложил Cocos.

enter image description here

Ответ 6

Я сделал это так, и он отлично работает.

Итак, сначала я объявил SKSpriteNode:

baseBar = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(CGRectGetMidX(self.frame)-40, self.frame.size.height/10)];
//The following will make the health bar to reduce from right to left
//Change it to (1,0.5) if you want to have it the other way
//But you'd have to play with the positioning as well
[baseBar setAnchorPoint:CGPointMake(0, 0.5)];
CGFloat goodWidth, goodHeight;
goodHeight =self.frame.size.height-(baseBar.frame.size.height*2/3);
goodWidth =self.frame.size.width-(10 +baseBar.frame.size.width);
[baseBar setPosition:CGPointMake(goodWidth, goodHeight)];
[self addChild:baseBar];

Затем я добавил "Frame" для панели с SKShapeNode без цвета заливки (clearcolour) и цвета штриха:

//The following was so useful
SKShapeNode *edges = [SKShapeNode shapeNodeWithRect:baseBar.frame];
edges.fillColor = [UIColor clearColor];
edges.strokeColor = [UIColor blackColor];
[self addChild:edges];

Когда я хотел уменьшить здоровье, я сделал следующее:

    if (playerHealthRatio>0) {
        playerHealthRatio -= 1;
        CGFloat ratio = playerHealthRatio / OriginalPlayerHealth;
        CGFloat newWidth =baseBar.frame.size.width*ratio;
        NSLog(@"Ratio: %f newwidth: %f",ratio,newWidth);
        [baseBar runAction:[SKAction resizeToWidth:newWidth duration:0.5]];
    }else{
//        NSLog(@"Game over");
    }

Простая, чистая и не сложная.

Ответ 7

это мой ProgressBar в быстрой:

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")
}

}

//Как использовать:

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

или

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

и добавьте этот progressBar в любой SKNode:

self.addChild(progressBar)

//Это все.