SpriteKit问题与Swift中的SKShader动画
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpriteKit问题与Swift中的SKShader动画相关的知识,希望对你有一定的参考价值。
对于我的生活,我无法理解为什么这个着色器动画没有运行。任何帮助表示赞赏。请参阅下面的代码...着色器正确显示,但没有动画。谢谢。
注意:Xcode 9.4.1,Swift 4.1,ios 11.4
import SpriteKit
class GameScene: SKScene {
static let marquee: SKShader = {
let shader = SKShader(
source: "void main() {" +
"float np = mod(a_path_phase + v_path_distance / u_path_length, 1.0);" +
"vec3 hsb = vec3(np, 1.0, 1.0);" +
"vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);" +
"vec3 p = abs(fract(hsb.xxx + K.xyz) * 6.0 - K.www);" +
"vec3 rgb = hsb.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsb.y);" +
"vec4 df = SKDefaultShading();" +
"gl_FragColor = vec4(rgb, df.z);" +
"}"
)
shader.attributes = [SKAttribute(name: "a_path_phase", type: .float)]
return shader
} ()
private let beat: TimeInterval = 0.05
private var phase: Float = 0
override func didMove(to view: SKView) {
let node = SKShapeNode(rectOf: CGSize(width: 256, height: 256), cornerRadius: 16)
node.fillColor = .clear
node.strokeColor = .white
node.strokeShader = GameScene.marquee
node.lineWidth = 8
node.glowWidth = 4
node.run(
SKAction.repeatForever(
SKAction.sequence(
[
SKAction.wait(forDuration: beat),
SKAction.run() { [weak self] in
if let weak = self {
weak.phase = fmodf(weak.phase + Float(weak.beat), 1)
node.setValue(SKAttributeValue(float: weak.phase), forAttribute: "a_path_phase")
}
}
]
)
)
)
addChild(node)
}
}
我也遇到了.setValue(forAttribute:)
的问题。它似乎没有用。经过调查,我的结论是......
我相信SKShapeNode.setValue(forAttribute:)
不起作用。
事实上SKSpriteNode.setValue(forAttribute:)
在相同的条件下工作正常,这让我得出结论,在SKShapeNode
中,几乎肯定是iOS中的一些错误。
事实上,无论我做什么,我可能在
SKAttribute
或SKShapeNode.fillShader
设置的任何SKShapeNode.strokeShader
s都将它们的值设置为0.0
。然而,
SKSpriteNode.shader
没有发生同样的情况。
所以我必须找到一种方法来规避这个bug。某种破解。幸运的是,就我而言,我确实找到了一种方法。
幸运的是,似乎也足以解决您的问题。
在这种情况下,您可以做的是将相位的值作为strokeColor
的一个组件传递,例如红色组件。
要做到这一点,你必须将相位标准化为0.0
和1.0
之间的值。您可以使用shader.uniform传递相位乘法器以帮助非规范化它。
然后从SKDefaultShading().x
(红色组件)中读取着色器内的相位值。
确保将
strokeColor
的alpha分量设置为1.0
,因为SKDefaultShading()
的颜色分量值已经被alpha预乘。
现在,因为你的strokeColor已经被用来传递相位,你可以将描边线的实际颜色作为vec4 shader.uniform传递。
- 在我的情况下,我也使用
u_time
来帮助我执行我的动画。像fract(u_time)
这样的东西非常有用。
以下是更正后的代码的外观:
>注意:有一个更简单的解决方案。看看这篇文章的结尾。
import SpriteKit
class GameScene: SKScene {
private typealias Me = GameScene
static let phaseMultiplier: Float = 1000
static let marquee: SKShader = {
let shader = SKShader(
source: "void main() {" +
"float red = SKDefaultShading().x;" +
"float phase = red * u_phase_multiplier;" +
"float np = mod(phase + v_path_distance / u_path_length, 1.0);" +
"vec3 hsb = vec3(np, 1.0, 1.0);" +
"vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);" +
"vec3 p = abs(fract(hsb.xxx + K.xyz) * 6.0 - K.www);" +
"vec3 rgb = hsb.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsb.y);" +
"vec4 df = u_stroke_color;" +
"gl_FragColor = vec4(rgb, df.z);" +
"}"
)
let white = vector_float4.init(1, 1, 1, 1)
shader.uniforms = [
SKUniform(name: "u_phase_multiplier", float: Me.phaseMultiplier),
SKUniform(name: "u_stroke_color", vectorFloat4: white)]
return shader
} ()
private let beat: TimeInterval = 0.05
private var phase: Float = 0
override func didMove(to view: SKView) {
let node = SKShapeNode(rectOf: CGSize(width: 256, height: 256), cornerRadius: 16)
node.fillColor = .clear
node.strokeColor = .white
node.strokeShader = Me.marquee
node.lineWidth = 8
node.glowWidth = 4
node.run(
SKAction.repeatForever(
SKAction.sequence(
[
SKAction.wait(forDuration: beat),
SKAction.run() { [weak self] in
if let weak = self {
weak.phase = fmodf(weak.phase + Float(weak.beat), 1)
let phase = weak.phase / Me.phaseMultiplier
node.strokeColor = SKColor.init(red: phase, green: 0, blue: 0, alpha: 1)
}
}
]
)
)
)
addChild(node)
}
}
注意:我没有编译上面发布的代码。我应该仅仅作为草图。
在这篇文章的第二天,我发现属性参数不应该在片段着色器中使用,只能在顶点着色器中使用。
看看StackOverflow问题:In WebGL what are the differences between an attribute, a uniform, and a varying variable?
然而,
SKAttribute
和SKAttributeValue
的SDK文档似乎暗示“SKAttributes”和实际着色器属性参数之间可能没有直接关系。它们似乎被命名为相同。
更简单的解决方案
然而,最终,从来没有任何需要使用像我在这里展示的那样的黑客。
所有需要做的就是通过
SKShader.uniformNamed("u_phase")?.floatValue = phase
设置统一的值,而不是像jmdecombe那样使用.setValue(forAttribute:)
,正确地说,后来指出。
我将在这里保留我的答案以供参考,因为它提出了一个解决问题的非常规方法。
以上是关于SpriteKit问题与Swift中的SKShader动画的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Swift 正确切换 SpriteKit 中的 SKScene?
在使用 Swift 的 SpriteKit 中的 didBeginContact(日志)中未检测到 contactTestBitMask
Swift:Spritekit 中的多个场景,从第二个和第一个场景的不同 Sprite 节点类型调用 touchesBegan