Подтвердить что ты не робот

SKShapeNode - изменение цвета анимации

Я работаю над игрой с SpriteKit. Я рисую форму с помощью SKShapeNode. Теперь я хочу оживить его изменение цвета, но SKActions не работает для SKShapeNode. Есть ли способ сделать это, или я должен использовать другой подход?

Спасибо.

EDIT:

Благодаря LearnCocos2D я смог придумать это быстрое (и совершенно не идеальное) решение.

int groundChangeInterval = 5;
SKColor *originalColor = [SKColor colorWithRed:0.92 green:0.87 blue:0.38 alpha:1.0];
SKColor *finalColor = [SKColor colorWithRed:0.29 green:0.89 blue:0.31 alpha:1.0];

CGFloat red1 = 0.0, green1 = 0.0, blue1 = 0.0, alpha1 = 0.0;
[originalColor getRed:&red1 green:&green1 blue:&blue1 alpha:&alpha1];

CGFloat red2 = 0.0, green2 = 0.0, blue2 = 0.0, alpha2 = 0.0;
[finalColor getRed:&red2 green:&green2 blue:&blue2 alpha:&alpha2];

SKAction *changeGroundColor = [SKAction customActionWithDuration:groundChangeInterval actionBlock:^(SKNode *node, CGFloat elapsedTime) {
    CGFloat step = elapsedTime/groundChangeInterval;

    CGFloat red3 = 0.0, green3 = 0.0, blue3 = 0.0;
    red3 = red1-(red1-red2)*step;
    green3 = green1-(green1-green2)*step;
    blue3 = blue1-(blue1-blue2)*step;

    [(SKShapeNode*)node setFillColor:[SKColor colorWithRed:red3 green:green3 blue:blue3 alpha:1.0]];
    [(SKShapeNode*)node setStrokeColor:[SKColor colorWithRed:red3 green:green3 blue:blue3 alpha:1.0]];
}];

Мне нужно было только угаснуть два разных цвета, поэтому это не универсальное решение, но этого достаточно.

Спасибо

4b9b3361

Ответ 1

Используйте customActionWithDuration:block: и измените свойства fillColor или strokeColor.

Я полагаю, что действия раскраски не будут работать, потому что SKShapeNode не имеет свойства color. Стоит попробовать добавить это свойство в класс в подкласс или категорию и перенаправить его на fillColor или strokeColor или и то, и другое.

Ответ 2

Я нашел Падди Коллинза ниже, чтобы помочь, поэтому я написал его в Swift для своей игры.

// In the class that calls colorTransitionAction
// Include these variables
// In my code its the class that aggregates the sprite
var frgba = [CGFloat(0.0), CGFloat(0.0), CGFloat(0.0), CGFloat(0.0)]
var trgba = [CGFloat(0.0), CGFloat(0.0), CGFloat(0.0), CGFloat(0.0)]

func lerp(a : CGFloat, b : CGFloat, fraction : CGFloat) -> CGFloat
{
    return (b-a) * fraction + a
}

func colorTransitionAction(fromColor : UIColor, toColor : UIColor, duration : Double = 0.4) -> SKAction
{
    fromColor.getRed(&frgba[0], green: &frgba[1], blue: &frgba[2], alpha: &frgba[3])
    toColor.getRed(&trgba[0], green: &trgba[1], blue: &trgba[2], alpha: &trgba[3])

    return SKAction.customAction(withDuration: duration, actionBlock: { (node : SKNode!, elapsedTime : CGFloat) -> Void in
        let fraction = CGFloat(elapsedTime / CGFloat(duration))
        let transColor = UIColor(red: lerp(self.frgba[0], b: self.trgba[0], fraction: fraction),
            green: lerp(self.frgba[1], b: self.trgba[1], fraction: fraction),
            blue: lerp(self.frgba[2], b: self.trgba[2], fraction: fraction),
            alpha: lerp(self.frgba[3], b: self.trgba[3], fraction: fraction))
        (node as! SKShapeNode).fillColor = transColor
        }
    )
}

Ответ 3

Вот моя реализация. Я думаю, что немного легче читать.

-(SKAction*)getColorFadeActionFrom:(SKColor*)col1 toColor:(SKColor*)col2 {

    // get the Color components of col1 and col2
    CGFloat r1 = 0.0, g1 = 0.0, b1 = 0.0, a1 =0.0;
    CGFloat r2 = 0.0, g2 = 0.0, b2 = 0.0, a2 =0.0;
    [col1 getRed:&r1 green:&g1 blue:&b1 alpha:&a1];
    [col2 getRed:&r2 green:&g2 blue:&b2 alpha:&a2];

    // return a color fading on the fill color
    CGFloat timeToRun = 0.3;

    return [SKAction customActionWithDuration:timeToRun actionBlock:^(SKNode *node, CGFloat elapsedTime) {

        CGFloat fraction = elapsedTime / timeToRun;

        SKColor *col3 = [SKColor colorWithRed:lerp(r1,r2,fraction)
                                        green:lerp(g1,g2,fraction)
                                         blue:lerp(b1,b2,fraction)
                                        alpha:lerp(a1,a2,fraction)];

        [(SKShapeNode*)node setFillColor:col3];
        [(SKShapeNode*)node setStrokeColor:col3];
    }];
}

double lerp(double a, double b, double fraction) {
    return (b-a)*fraction + a;
}

Ответ 4

Мне нужен непрерывный пульсирующий свет на моем полигоне. Я использовал runBlock: вместо customActionWithDuration:block:, но мог просто бесконечно работать с этим.

SKShapeNode *shape = [SKShapeNode shapeNodeWithPoints:points count:indices.count];
shape.fillColor = [[SKColor yellowColor] colorWithAlphaComponent:0.2];
shape.strokeColor = [SKColor clearColor];
[self addChild:shape];
shape.userData = @{@"minAlpha": @0, @"maxAlpha": @0.5, @"deltaAlpha": @.025}.mutableCopy;
SKAction *a = [SKAction runBlock:^{
  SKColor *color = shape.fillColor;
  CGFloat delta = [shape.userData[@"deltaAlpha"] floatValue];
  CGFloat w, alpha; [color getWhite:&w alpha:&alpha];
  shape.fillColor = [color colorWithAlphaComponent:alpha + delta];
  if ((delta < 0 && alpha <= [shape.userData[@"minAlpha"] floatValue]) ||
      (delta > 0 && alpha >= [shape.userData[@"maxAlpha"] floatValue])) {
    shape.userData[@"deltaAlpha"] = @(-delta);
  }
}];
SKAction *slice = [SKAction sequence:@[a, [SKAction waitForDuration:0.05]]];
SKAction *glow = [SKAction repeatActionForever:slice];
[shape runAction:glow];

Ответ 5

Я нашел ответ GOR выше полезного, который нашел ответ Падди Коллина выше полезного, поэтому я расширил его, чтобы оживить массив цветов.

extension SKAction {

func multipleColorTransitionAction(colors:[SKColor], duration:Double) -> SKAction {
    guard colors.count > 1 else { return SKAction.colorize(withColorBlendFactor: 1, duration: 0) }
    var colorActions:[SKAction] = []
    for i in 1..<colors.count {
        colorActions.append( colorTransitionAction(fromColor: colors[i-1] , toColor: colors[i], duration: duration/Double(colors.count)) )
    }
    colorActions.append(colorTransitionAction(fromColor: colors.last!, toColor: colors.first!, duration: duration/Double(colors.count)))
    return SKAction.sequence(colorActions)
}

func colorTransitionAction(fromColor : SKColor, toColor : SKColor, duration : Double = 0.4) -> SKAction {
    func lerp(_ a : CGFloat, b : CGFloat, fraction : CGFloat) -> CGFloat { return (b-a) * fraction + a }
    var frgba:[CGFloat] = [0,0,0,0]
    var trgba:[CGFloat] = [0,0,0,0]
    fromColor.getRed(&frgba[0], green: &frgba[1], blue: &frgba[2], alpha: &frgba[3])
    toColor.getRed(&trgba[0], green: &trgba[1], blue: &trgba[2], alpha: &trgba[3])

    return SKAction.customAction(withDuration: duration, actionBlock: { (node : SKNode!, elapsedTime : CGFloat) -> Void in
        let fraction = CGFloat(elapsedTime / CGFloat(duration))
        let transColor = UIColor(red:   lerp(frgba[0], b: trgba[0], fraction: fraction),
                                 green: lerp(frgba[1], b: trgba[1], fraction: fraction),
                                 blue:  lerp(frgba[2], b: trgba[2], fraction: fraction),
                                 alpha: lerp(frgba[3], b: trgba[3], fraction: fraction))
        (node as! SKShapeNode).fillColor = transColor
    })
}
}

Я изменил некоторые вещи о оригинальной функции GOR для размещения в нескольких цветах, в основном инкапсулируя frgba, trgba и lerp, чтобы блок не нуждался в ссылке на self, и чтобы разрешить frgba и trgba, чтобы иметь несколько экземпляров, захваченных несколькими блоками. Вот пример использования:

let theRainbow:[SKColor] = [.red,.orange,.yellow,.green,.cyan,.blue,.purple,.magenta]
let rainbowSequenceAction = SKAction.multipleColorTransitionAction(colors: theRainbow, duration: 10)
star.run(SKAction.repeatForever(rainbowSequenceAction))

Где star - SKShapeNode.