【问题标题】:SKShapeNode - Animate color changeSKShapeNode - 动画颜色变化
【发布时间】:2014-01-19 06:54:03
【问题描述】:

我正在使用 SpriteKit 开发游戏。我正在用 SKShapeNode 绘制一个形状。现在我想为其颜色变化设置动画,但 SKActions 不适用于 SKShapeNode。有什么方法可以做到这一点,还是我必须使用不同的方法?

谢谢。

编辑:

感谢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]];
}];

我只需要淡化两种特定的颜色,所以它不是一个通用的解决方案,但现在已经足够了。

谢谢

【问题讨论】:

    标签: ios objective-c sprite-kit


    【解决方案1】:

    为 Swift 4.2 更新

    这是您需要放入的通用代码和扩展文件或类似的东西。

    func lerp(a : CGFloat, b : CGFloat, fraction : CGFloat) -> CGFloat
    {
        return (b-a) * fraction + a
    }
    
    struct ColorComponents {
        var red = CGFloat(0)
        var green = CGFloat(0)
        var blue = CGFloat(0)
        var alpha = CGFloat(0)
    }
    
    extension UIColor {
        func toComponents() -> ColorComponents {
            var components = ColorComponents()
            getRed(&components.red, green: &components.green, blue: &components.blue, alpha: &components.alpha)
            return components
        }
    }
    
    extension SKAction {
        static func colorTransitionAction(fromColor : UIColor, toColor : UIColor, duration : Double = 0.4) -> SKAction
        {
            return SKAction.customAction(withDuration: duration, actionBlock: { (node : SKNode!, elapsedTime : CGFloat) -> Void in
                let fraction = CGFloat(elapsedTime / CGFloat(duration))
                let startColorComponents = fromColor.toComponents()
                let endColorComponents = toColor.toComponents()
                let transColor = UIColor(red: lerp(a: startColorComponents.red, b: endColorComponents.red, fraction: fraction),
                                         green: lerp(a: startColorComponents.green, b: endColorComponents.green, fraction: fraction),
                                         blue: lerp(a: startColorComponents.blue, b: endColorComponents.blue, fraction: fraction),
                                         alpha: lerp(a: startColorComponents.alpha, b: endColorComponents.alpha, fraction: fraction))
                (node as? SKSpriteNode)?.color = transColor
            }
            )
        }
    }
    

    用法:

    redSKSpriteNodeThatBecomesBlue.run(SKAction.colorTransitionAction(fromColor: .red, toColor: .blue, duration: 5))

    请注意,对于 SKShapeNode 或其他目的,您需要重写此行以满足您的需要:

    (node as? SKSpriteNode)?.color = transColor

    此解决方案最初受到 Paddy Collins 回答的极大启发。

    【讨论】:

    • 谢谢!这个很完美。
    • 除非在构建设置中将Exclusive Access to Memory 设置为No Enforcement,否则在swift 4 中会导致“同时访问...”错误。
    • 我会在晚上检查,谢谢提姆提姆。
    • @Tim 似乎问题在于旧实现使用相同的数组来存储所有输入输出颜色分量。请检查使用结构的更新版本。根据快速测试,现在似乎工作正常。
    【解决方案2】:

    使用customActionWithDuration:block: 并更改fillColorstrokeColor 属性。

    我认为着色操作不起作用,因为 SKShapeNode 没有 color 属性。值得尝试将此属性添加到子类或类别中的类,并将其重定向到 fillColorstrokeColor 或两者。

    【讨论】:

    • @Aaron 感谢您的编辑...在 iPad 上添加语法高亮实在是太烦人了
    • 是的,我希望 Apple 会添加一个 Markdown 键盘……但永远不会发生。
    • 如果我可以将选择弹出窗口(复制、粘贴等)设置为出现在选择下方而不是上方。它通常会阻止我点击编辑框中的任何降价图标,这就是为什么我在 ipad 上很少正确格式化的原因。
    • 此解决方案不会为颜色变化设置动画,它会立即发生。
    • bpn,您可能需要将持续时间设置为 3.0(秒),然后根据传递到块中的时间值将块中的值设置为颜色过渡的百分比跨度>
    【解决方案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;
    }
    

    【讨论】:

    • 非常有帮助。我将其转换为SKAction 上的类别,并在方法签名中包含duration 参数。
    • Here's a category version(使用 lerp 宏)。
    【解决方案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】:

      添加到 mogelbuster 的答案,这是添加到 GOR 的答案,这是添加到 Patrick Collins 的答案。

      斯威夫特 4

      func shapeColorChangeAction(from fromColor: UIColor, to toColor: UIColor, withDuration duration: TimeInterval) -> SKAction {
      
          func components(for color: UIColor) -> [CGFloat] {
              var comp = color.cgColor.components!
              // converts [white, alpha] to [red, green, blue, alpha]
              if comp.count < 4 {
                  comp.insert(comp[0], at: 0)
                  comp.insert(comp[0], at: 0)
              }
              return comp
          }
          func lerp(a: CGFloat, b: CGFloat, fraction: CGFloat) -> CGFloat {
              return (b-a) * fraction + a
          }
      
          let fromComp = components(for: fromColor)
          let toComp = components(for: toColor)
          let durationCGFloat = CGFloat(duration)
          return SKAction.customAction(withDuration: duration, actionBlock: { (node, elapsedTime) -> Void in
              let fraction = elapsedTime / durationCGFloat
              let transColor = UIColor(red: lerp(a: fromComp[0], b: toComp[0], fraction: fraction),
                                       green: lerp(a: fromComp[1], b: toComp[1], fraction: fraction),
                                       blue: lerp(a: fromComp[2], b: toComp[2], fraction: fraction),
                                       alpha: lerp(a: fromComp[3], b: toComp[3], fraction: fraction))
              (node as! SKShapeNode).fillColor = transColor
          })
      }
      

      【讨论】:

        【解决方案6】:

        我发现 GOR 上面的回答很有帮助,他发现 Paddy Collin 上面的回答很有帮助,所以我将它扩展为通过一系列颜色制作动画。

        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 的原始函数进行了一些更改以适应多种颜色,主要是封装 frgbatrgbalerp,因此该块不需要引用 self,并允许 @ 987654326@ 和 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))
        

        其中starSKShapeNode

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-12-15
          • 1970-01-01
          • 2013-08-15
          • 2011-04-26
          • 2011-05-05
          • 2014-05-12
          • 2015-08-11
          相关资源
          最近更新 更多