Swift : 动画 SKSpriteNode 渐变
Swift : Animate SKSpriteNode Gradient
我想做的事情对我来说似乎很简单,但我想不出一种方法来实现它,因为我对 iOS 还很陌生。
我非常想让我的背景具有渐变效果,通过非常缓慢地淡入和淡出来改变颜色。
我在 github 上找到它并用它来创建两个具有不同颜色的渐变节点,唯一的问题是我似乎无法为其 alpha 设置动画或让它以任何方式淡入淡出.
https://github.com/braindrizzlestudio/BDGradientNode
这是我所做的:
//Blueish
let color1 = UIColor(red: 0.965, green: 0.929, blue: 0.667, alpha: 1)
let color2 = UIColor(red: 0.565, green: 0.71, blue: 0.588, alpha: 1)
let color3 = UIColor(red: 0.259, green: 0.541, blue: 0.529, alpha: 1)
let colors = [color1, color2, color3]
//Redish
let color4 = UIColor(red: 1, green: 0.518, blue: 0.769, alpha: 1)
let color5 = UIColor(red: 0.859, green: 0.22, blue: 0.541, alpha: 1)
let color6 = UIColor(red: 0.737, green: 0, blue: 0.314, alpha: 1)
let colors2 = [color4, color5, color6]
let blending : Float = 0.3
let location1 : CGFloat = 0.5
let locations : [CGFloat] = [location1]
let startPoint = CGPoint(x: 0.3, y: 0.0)
let endPoint = CGPoint(x: 0.6, y: 0.8)
let size = CGSize(width: self.size.width, height: self.size.height)
let texture = SKTexture(imageNamed: "White Background")
myGradientNode = BDGradientNode(linearGradientWithTexture: texture, colors: colors, locations: nil, startPoint: startPoint, endPoint: endPoint, blending: blending, keepTextureShape: true, size: size)
myGradientNode2 = BDGradientNode(linearGradientWithTexture: texture, colors: colors2, locations: nil, startPoint: startPoint, endPoint: endPoint, blending: blending, keepTextureShape: true, size: size)
myGradientNode2?.blending = 0
self.addChild(myGradientNode2!)
self.addChild(myGradientNode!)
我觉得有更简单的方法,也许不用这个。
你现在在很多游戏应用程序中都看到了这一点,如果有人能帮助我实现这个令人惊叹的设计,那将会很有帮助!
这是一个可能的完整解决方案(在 Swift 2.2 中),您可以根据自己的喜好进行调整(包括对您的应用有意义的错误处理) .我将以相反的顺序排列代码,从能够编写的目标开始:
// assuming:
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
if let bg = HorizontalGradientNode(
size: scene.size,
fadingFrom: [.AliceBlue, .BurlyWood],
to: [.AliceBlue, .CornflowerBlue],
wait: 1,
duration: 2)
{
scene.addChild(bg)
}
这将向场景添加一个节点,封装两个子渐变精灵节点,其中最上面的一个将永远淡入淡出。 HorizontalGradientNode.init
接受两个 CGColor
数组。它们每个可以包含 两种或更多 种颜色!命名的CGColor
s (AliceBlue
, CornflowerBlue
, etc.)可以通过以下方式构造:
public extension CGColor {
public static func withHex(hex: Int, alpha: CGFloat = 1) -> CGColor {
let x = max(0, min(hex, 0xffffff))
let r = CGFloat((x & 0xff0000) >> 16) / 0xff
let g = CGFloat((x & 0x00ff00) >> 8) / 0xff
let b = CGFloat( x & 0x0000ff) / 0xff
return CGColorCreateGenericRGB(r, g, b, alpha)
}
public static var AliceBlue: CGColor { return .withHex(0xF0F8FF) }
public static var BurlyWood: CGColor { return .withHex(0xDEB887) }
public static var CornflowerBlue: CGColor { return .withHex(0x6495ED) }
// see named colours at: http://www.w3schools.com/colors/colors_names.asp
}
HorizontalGradientNode
的最简单版本如下:
class HorizontalGradientNode : SKNode {
init?(size: CGSize, fadingFrom colors1: [CGColor], to colors2: [CGColor], wait: NSTimeInterval, duration: NSTimeInterval) {
guard
let grad1 = CGImage.withHorizontalGradient(size: size, colors: colors1),
let grad2 = CGImage.withHorizontalGradient(size: size, colors: colors2)
else
{
return nil
}
let bg1 = SKSpriteNode(texture: SKTexture(CGImage: grad1))
let bg2 = SKSpriteNode(texture: SKTexture(CGImage: grad2))
bg2.alpha = 0
super.init()
addChild(bg1)
addChild(bg2)
bg2.runAction(
.repeatActionForever(
.sequence(
[
.waitForDuration(wait),
.fadeInWithDuration(duration),
.waitForDuration(wait),
.fadeOutWithDuration(duration)
]
)
)
)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
这个CGImage.withHorizontalGradient(size:, colors:)
可以实现如下:
public extension CGImage {
public static func withHorizontalGradient(size size: CGSize, colors: [CGColor]) -> CGImage? {
guard colors.count >= 2 else { return nil }
let locations: [CGFloat] = colors.indices.map{
CGFloat([=13=]) / CGFloat(colors.count - 1)
}
guard let gradient = CGGradientCreateWithColors(nil, colors, locations) else {
return nil
}
let start = CGPoint(x: size.width / 2, y: size.height)
let end = CGPoint(x: size.width / 2, y: 0)
return CGContext.rgb(size)?
.draw(linearGradient: gradient, start: start, end: end)
.image
}
}
利用 CGContext
的点语法扩展,例如:
public extension CGContext {
public static func rgb(size: CGSize) -> CGContext? {
let sp = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)
let bi = CGImageAlphaInfo.PremultipliedLast.rawValue
return CGBitmapContextCreate(nil, Int(ceil(size.width)), Int(ceil(size.height)), 8, 0, sp, bi)
}
public var image: CGImage? { return CGBitmapContextCreateImage(self) }
public func draw(linearGradient
gradient: CGGradient?,
start: CGPoint,
end: CGPoint,
options: CGGradientDrawingOptions = [.DrawsBeforeStartLocation, .DrawsAfterEndLocation]
) -> CGContext
{
CGContextDrawLinearGradient(self, gradient, start, end, options)
return self
}
}
如果你想在 playground 中尝试这个,你可以将所有这些粘贴进去,并以以下代码片段(对于 OS X)作为前缀,确保你在助理编辑器中有 playground 的时间轴:
import XCPlayground
import Cocoa
import SpriteKit
let scene = SKScene(size: CGSize(width: 400, height: 400))
let view = SKView(frame: CGRect(origin: .zero, size: scene.size))
XCPlaygroundPage.currentPage.liveView = view
view.presentScene(scene)
在我最初回答后的评论中@OriginalAlchemist 询问是否可以修改 HorizontalGradientNode
以循环显示两组以上的颜色。这是一种方法(假设我的第一个答案中的 CGImage.withHorizontalGradient(size:colors:)
可用):
class HorizontalGradientNode : SKNode {
init(size: CGSize, gradientColorSets: [[CGColor]], wait: NSTimeInterval, duration: NSTimeInterval) {
super.init()
var i = 0
let setNextGradientTexture = SKAction.customActionWithDuration(0) { (node, _) in
guard let sprite = node as? SKSpriteNode else { return }
let colors = gradientColorSets[i]
i = (i + 1) % gradientColorSets.count
guard let image = CGImage.withHorizontalGradient(size: size, colors: colors) else { return }
sprite.texture = SKTexture(CGImage: image)
sprite.size = sprite.texture!.size()
}
let sprite1 = SKSpriteNode()
let sprite2 = SKSpriteNode()
sprite1.runAction(setNextGradientTexture)
sprite2.alpha = 0
sprite2.runAction(
.repeatActionForever(
.sequence(
[
setNextGradientTexture,
.waitForDuration(wait),
.fadeInWithDuration(duration),
.runBlock({ sprite1.runAction(setNextGradientTexture) }),
.waitForDuration(wait),
.fadeOutWithDuration(duration)
]
)
)
)
addChild(sprite1)
addChild(sprite2)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
用法:
// assuming
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
let gradientColorSets: [[CGColor]] = [
[.AliceBlue, .BurlyWood], // note that you can have more than two colours per set
[.BurlyWood, .CornflowerBlue],
[.CornflowerBlue, .ForestGreen],
[.ForestGreen, .AliceBlue]
]
let bg = HorizontalGradientNode(size: scene.size, gradientColorSets: gradientColorSets, wait: 1, duration: 2)
scene.addChild(bg)
@OriginalAlchemist 还要求 iOS 兼容版本的 CGColors
:
public extension CGColor {
public static func withHex(hex: Int, alpha: CGFloat = 1) -> CGColor {
let x = max(0, min(hex, 0xffffff)) // allows force unwrapping of the return value
let r = CGFloat((x & 0xff0000) >> 16) / 0xff
let g = CGFloat((x & 0x00ff00) >> 8) / 0xff
let b = CGFloat( x & 0x0000ff) / 0xff
let cp = CGColorSpaceCreateDeviceRGB()
return CGColorCreate(cp, [r, g, b, alpha])!
}
public static var AliceBlue: CGColor { return .withHex(0xF0F8FF) }
public static var BurlyWood: CGColor { return .withHex(0xDEB887) }
public static var CornflowerBlue: CGColor { return .withHex(0x6495ED) }
public static var ForestGreen: CGColor { return .withHex(0x228B22) }
// see named colours at: http://www.w3schools.com/colors/colors_names.asp
}
我想做的事情对我来说似乎很简单,但我想不出一种方法来实现它,因为我对 iOS 还很陌生。
我非常想让我的背景具有渐变效果,通过非常缓慢地淡入和淡出来改变颜色。
我在 github 上找到它并用它来创建两个具有不同颜色的渐变节点,唯一的问题是我似乎无法为其 alpha 设置动画或让它以任何方式淡入淡出. https://github.com/braindrizzlestudio/BDGradientNode 这是我所做的:
//Blueish
let color1 = UIColor(red: 0.965, green: 0.929, blue: 0.667, alpha: 1)
let color2 = UIColor(red: 0.565, green: 0.71, blue: 0.588, alpha: 1)
let color3 = UIColor(red: 0.259, green: 0.541, blue: 0.529, alpha: 1)
let colors = [color1, color2, color3]
//Redish
let color4 = UIColor(red: 1, green: 0.518, blue: 0.769, alpha: 1)
let color5 = UIColor(red: 0.859, green: 0.22, blue: 0.541, alpha: 1)
let color6 = UIColor(red: 0.737, green: 0, blue: 0.314, alpha: 1)
let colors2 = [color4, color5, color6]
let blending : Float = 0.3
let location1 : CGFloat = 0.5
let locations : [CGFloat] = [location1]
let startPoint = CGPoint(x: 0.3, y: 0.0)
let endPoint = CGPoint(x: 0.6, y: 0.8)
let size = CGSize(width: self.size.width, height: self.size.height)
let texture = SKTexture(imageNamed: "White Background")
myGradientNode = BDGradientNode(linearGradientWithTexture: texture, colors: colors, locations: nil, startPoint: startPoint, endPoint: endPoint, blending: blending, keepTextureShape: true, size: size)
myGradientNode2 = BDGradientNode(linearGradientWithTexture: texture, colors: colors2, locations: nil, startPoint: startPoint, endPoint: endPoint, blending: blending, keepTextureShape: true, size: size)
myGradientNode2?.blending = 0
self.addChild(myGradientNode2!)
self.addChild(myGradientNode!)
我觉得有更简单的方法,也许不用这个。 你现在在很多游戏应用程序中都看到了这一点,如果有人能帮助我实现这个令人惊叹的设计,那将会很有帮助!
这是一个可能的完整解决方案(在 Swift 2.2 中),您可以根据自己的喜好进行调整(包括对您的应用有意义的错误处理) .我将以相反的顺序排列代码,从能够编写的目标开始:
// assuming:
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
if let bg = HorizontalGradientNode(
size: scene.size,
fadingFrom: [.AliceBlue, .BurlyWood],
to: [.AliceBlue, .CornflowerBlue],
wait: 1,
duration: 2)
{
scene.addChild(bg)
}
这将向场景添加一个节点,封装两个子渐变精灵节点,其中最上面的一个将永远淡入淡出。 HorizontalGradientNode.init
接受两个 CGColor
数组。它们每个可以包含 两种或更多 种颜色!命名的CGColor
s (AliceBlue
, CornflowerBlue
, etc.)可以通过以下方式构造:
public extension CGColor {
public static func withHex(hex: Int, alpha: CGFloat = 1) -> CGColor {
let x = max(0, min(hex, 0xffffff))
let r = CGFloat((x & 0xff0000) >> 16) / 0xff
let g = CGFloat((x & 0x00ff00) >> 8) / 0xff
let b = CGFloat( x & 0x0000ff) / 0xff
return CGColorCreateGenericRGB(r, g, b, alpha)
}
public static var AliceBlue: CGColor { return .withHex(0xF0F8FF) }
public static var BurlyWood: CGColor { return .withHex(0xDEB887) }
public static var CornflowerBlue: CGColor { return .withHex(0x6495ED) }
// see named colours at: http://www.w3schools.com/colors/colors_names.asp
}
HorizontalGradientNode
的最简单版本如下:
class HorizontalGradientNode : SKNode {
init?(size: CGSize, fadingFrom colors1: [CGColor], to colors2: [CGColor], wait: NSTimeInterval, duration: NSTimeInterval) {
guard
let grad1 = CGImage.withHorizontalGradient(size: size, colors: colors1),
let grad2 = CGImage.withHorizontalGradient(size: size, colors: colors2)
else
{
return nil
}
let bg1 = SKSpriteNode(texture: SKTexture(CGImage: grad1))
let bg2 = SKSpriteNode(texture: SKTexture(CGImage: grad2))
bg2.alpha = 0
super.init()
addChild(bg1)
addChild(bg2)
bg2.runAction(
.repeatActionForever(
.sequence(
[
.waitForDuration(wait),
.fadeInWithDuration(duration),
.waitForDuration(wait),
.fadeOutWithDuration(duration)
]
)
)
)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
这个CGImage.withHorizontalGradient(size:, colors:)
可以实现如下:
public extension CGImage {
public static func withHorizontalGradient(size size: CGSize, colors: [CGColor]) -> CGImage? {
guard colors.count >= 2 else { return nil }
let locations: [CGFloat] = colors.indices.map{
CGFloat([=13=]) / CGFloat(colors.count - 1)
}
guard let gradient = CGGradientCreateWithColors(nil, colors, locations) else {
return nil
}
let start = CGPoint(x: size.width / 2, y: size.height)
let end = CGPoint(x: size.width / 2, y: 0)
return CGContext.rgb(size)?
.draw(linearGradient: gradient, start: start, end: end)
.image
}
}
利用 CGContext
的点语法扩展,例如:
public extension CGContext {
public static func rgb(size: CGSize) -> CGContext? {
let sp = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)
let bi = CGImageAlphaInfo.PremultipliedLast.rawValue
return CGBitmapContextCreate(nil, Int(ceil(size.width)), Int(ceil(size.height)), 8, 0, sp, bi)
}
public var image: CGImage? { return CGBitmapContextCreateImage(self) }
public func draw(linearGradient
gradient: CGGradient?,
start: CGPoint,
end: CGPoint,
options: CGGradientDrawingOptions = [.DrawsBeforeStartLocation, .DrawsAfterEndLocation]
) -> CGContext
{
CGContextDrawLinearGradient(self, gradient, start, end, options)
return self
}
}
如果你想在 playground 中尝试这个,你可以将所有这些粘贴进去,并以以下代码片段(对于 OS X)作为前缀,确保你在助理编辑器中有 playground 的时间轴:
import XCPlayground
import Cocoa
import SpriteKit
let scene = SKScene(size: CGSize(width: 400, height: 400))
let view = SKView(frame: CGRect(origin: .zero, size: scene.size))
XCPlaygroundPage.currentPage.liveView = view
view.presentScene(scene)
在我最初回答后的评论中@OriginalAlchemist 询问是否可以修改 HorizontalGradientNode
以循环显示两组以上的颜色。这是一种方法(假设我的第一个答案中的 CGImage.withHorizontalGradient(size:colors:)
可用):
class HorizontalGradientNode : SKNode {
init(size: CGSize, gradientColorSets: [[CGColor]], wait: NSTimeInterval, duration: NSTimeInterval) {
super.init()
var i = 0
let setNextGradientTexture = SKAction.customActionWithDuration(0) { (node, _) in
guard let sprite = node as? SKSpriteNode else { return }
let colors = gradientColorSets[i]
i = (i + 1) % gradientColorSets.count
guard let image = CGImage.withHorizontalGradient(size: size, colors: colors) else { return }
sprite.texture = SKTexture(CGImage: image)
sprite.size = sprite.texture!.size()
}
let sprite1 = SKSpriteNode()
let sprite2 = SKSpriteNode()
sprite1.runAction(setNextGradientTexture)
sprite2.alpha = 0
sprite2.runAction(
.repeatActionForever(
.sequence(
[
setNextGradientTexture,
.waitForDuration(wait),
.fadeInWithDuration(duration),
.runBlock({ sprite1.runAction(setNextGradientTexture) }),
.waitForDuration(wait),
.fadeOutWithDuration(duration)
]
)
)
)
addChild(sprite1)
addChild(sprite2)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
用法:
// assuming
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
let gradientColorSets: [[CGColor]] = [
[.AliceBlue, .BurlyWood], // note that you can have more than two colours per set
[.BurlyWood, .CornflowerBlue],
[.CornflowerBlue, .ForestGreen],
[.ForestGreen, .AliceBlue]
]
let bg = HorizontalGradientNode(size: scene.size, gradientColorSets: gradientColorSets, wait: 1, duration: 2)
scene.addChild(bg)
@OriginalAlchemist 还要求 iOS 兼容版本的 CGColors
:
public extension CGColor {
public static func withHex(hex: Int, alpha: CGFloat = 1) -> CGColor {
let x = max(0, min(hex, 0xffffff)) // allows force unwrapping of the return value
let r = CGFloat((x & 0xff0000) >> 16) / 0xff
let g = CGFloat((x & 0x00ff00) >> 8) / 0xff
let b = CGFloat( x & 0x0000ff) / 0xff
let cp = CGColorSpaceCreateDeviceRGB()
return CGColorCreate(cp, [r, g, b, alpha])!
}
public static var AliceBlue: CGColor { return .withHex(0xF0F8FF) }
public static var BurlyWood: CGColor { return .withHex(0xDEB887) }
public static var CornflowerBlue: CGColor { return .withHex(0x6495ED) }
public static var ForestGreen: CGColor { return .withHex(0x228B22) }
// see named colours at: http://www.w3schools.com/colors/colors_names.asp
}