Hay*_*gan 3 core-graphics ios sprite-kit
我目前正在开发一个 SpriteKit 项目,需要创建一颗彗星,它的尾巴会在屏幕上呈现动画效果。在这方面我对 SpriteKit 遇到了严重的问题。
尝试1。它:
对屏幕上的正方形进行动画处理,同时被线/SKCropNode 剪切
func makeCometInPosition(from: CGPoint, to: CGPoint, color: UIColor, timeInterval: NSTimeInterval) {
... (...s are (definitely) irrelevant lines of code)
let path = CGPathCreateMutable()
...
let line = SKShapeNode(path:path)
line.lineWidth = 1.0
line.glowWidth = 1.0
var squareFrame = line.frame
...
let square = SKShapeNode(rect: squareFrame)
//Custom SKTexture Extension. I've tried adding a normal image and the leak happens either way. The extension is not the problem
square.fillTexture = SKTexture(color1: UIColor.clearColor(), color2: color, from: from, to: to, frame: line.frame)
square.fillColor = color
square.strokeColor = UIColor.clearColor()
square.zPosition = 1.0
let maskNode = SKCropNode()
maskNode.zPosition = 1.0
maskNode.maskNode = line
maskNode.addChild(square)
//self is an SKScene, background is an SKSpriteNode
self.background?.addChild(maskNode)
let lineSequence = SKAction.sequence([SKAction.waitForDuration(timeInterval), SKAction.removeFromParent()])
let squareSequence = SKAction.sequence([SKAction.waitForDuration(1), SKAction.moveBy(CoreGraphics.CGVectorMake(deltaX * 2, deltaY * 2), duration: timeInterval), SKAction.removeFromParent()])
square.runAction(SKAction.repeatActionForever(squareSequence))
maskNode.runAction(lineSequence)
line.runAction(lineSequence)
}
Run Code Online (Sandbox Code Playgroud)
问题是,屏幕上出现 20-40 个其他节点后,就会发生奇怪的事情。屏幕上的一些节点消失,一些节点保留。另外,fps 和节点数(在 SKView 中切换且从未改变)
self.showsFPS = true
self.showsNodeCount = true
Run Code Online (Sandbox Code Playgroud)
从屏幕上消失。这让我认为这是 SpriteKit 的一个错误。已知SKShapeNode会导致问题。
尝试 2.我尝试将正方形从 SKShapeNode 更改为 SKSpriteNode(根据需要添加和删除与两者相关的行)
let tex = SKTexture(color1: UIColor.clearColor(), color2: color, from: from, to: to, frame: line.frame)
let square = SKSpriteNode(texture: tex)
Run Code Online (Sandbox Code Playgroud)
其余代码基本相同。这会产生类似的效果,并且没有性能/内存方面的错误。然而,SKCropNode 发生了一些奇怪的事情,看起来像这样

它没有抗锯齿功能,并且线条较粗。我尝试过更改抗锯齿、发光宽度和线宽。有一个最小宽度,由于某种原因不能改变,并且将发光宽度设置得更大可以做到这一点
。根据其他 stackoverflow 问题,maskNodes 在 alpha 中要么是 1,要么是 0。这很令人困惑,因为 SKShapeNode 可以具有不同的线条/发光宽度。
尝试 3。经过一些研究,我发现我也许能够使用剪切效果并使用 SKEffectNode 而不是 SKCropNode 保留线宽/发光。
//Not the exact code to what I tried, but very similar
let maskNode = SKEffectNode()
maskNode.filter = customLinearImageFilter
maskNode.addChild(line)
Run Code Online (Sandbox Code Playgroud)
这产生了与尝试 1 完全相同的效果。它创建了相同的线条和动画,但与其他节点/fps/nodeCount 发生了相同的错误。所以这似乎是 SKEffectNode 的错误,而不是 SKShapeNode 的错误。
我不知道如何通过尝试 1/3 或 2 绕过这些错误。
有谁知道我是否做错了什么,是否有绕过此问题的方法,或者完全针对我的问题的不同解决方案?
编辑:我考虑过发射器,但几秒钟内可能会有数百个彗星/其他节点进入,并且认为它们在性能方面不可行。在这个项目之前我没有使用过 SpriteKit,所以如果我错了请纠正我。
这看起来像是附加到彗星路径的自定义着色器的问题。如果您不熟悉SpriteKit 中的OpenGL 着色语言 (GLSL),它可以让您直接跳到 GPU 片段着色器,专门控制通过 SKShader附加到的节点的绘制行为。
方便的是,SKShapeNode有一个strokeShader属性,用于连接SKShader来绘制路径。连接到此属性时,除了该点的颜色值之外,着色器还会传递路径的长度和当前正在绘制的路径上的点。*
控制淡入淡出路径.fsh
void main() {
//uniforms and varyings
vec4 inColor = v_color_mix;
float length = u_path_length;
float distance = v_path_distance;
float start = u_start;
float end = u_end;
float mult;
mult = smoothstep(end,start,distance/length);
if(distance/length > start) {discard;}
gl_FragColor = vec4(inColor.r, inColor.g, inColor.b, inColor.a) * mult;
}
Run Code Online (Sandbox Code Playgroud)
要控制沿路径的淡入淡出,请使用两个名为 和 的 SKUniform 对象将起点和终点传递到自u_start定义着色器中u_end。这些对象在自定义 SKShapeNode 类 CometPathShape 初始化期间添加到自定义着色器,并通过自定义操作进行动画处理。
类 CometPathShape:SKShapeNode
class CometPathShape:SKShapeNode {
//custom shader for fading
let pathShader:SKShader
let fadeStartU = SKUniform(name: "u_start",float:0.0)
let fadeEndU = SKUniform(name: "u_end",float: 0.0)
let fadeAction:SKAction
override init() {
pathShader = SKShader(fileNamed: "controlFadePath.fsh")
let fadeDuration:NSTimeInterval = 1.52
fadeAction = SKAction.customActionWithDuration(fadeDuration, actionBlock:
{ (node:SKNode, time:CGFloat)->Void in
let D = CGFloat(fadeDuration)
let t = time/D
var Ps:CGFloat = 0.0
var Pe:CGFloat = 0.0
Ps = 0.25 + (t*1.55)
Pe = (t*1.5)-0.25
let comet:CometPathShape = node as! CometPathShape
comet.fadeRange(Ps,to: Pe) })
super.init()
path = makeComet...(...) //custom method that creates path for comet shape
strokeShader = pathShader
pathShader.addUniform(fadeStartU)
pathShader.addUniform(fadeEndU)
hidden = true
//set up for path shape, eg. strokeColor, strokeWidth...
...
}
func fadeRange(from:CGFloat, to:CGFloat) {
fadeStartU.floatValue = Float(from)
fadeEndU.floatValue = Float(to)
}
func launch() {
hidden = false
runAction(fadeAction, completion: { ()->Void in self.hidden = true;})
}
Run Code Online (Sandbox Code Playgroud)
...
SKScene 初始化 CometPathShape 对象、缓存并将它们添加到场景中。在update:场景中只需调用.launch()所选的 CometPathShapes。
游戏场景类:SKScene
...
override func didMoveToView(view: SKView) {
/* Setup your scene here */
self.name = "theScene"
...
//create a big bunch of paths with custom shaders
print("making cache of path shape nodes")
for i in 0...shapeCount {
let shape = CometPathShape()
let ext = String(i)
shape.name = "comet_".stringByAppendingString(ext)
comets.append(shape)
shape.position.y = CGFloat(i * 3)
print(shape.name)
self.addChild(shape)
}
override func update(currentTime: CFTimeInterval) {
//pull from cache and launch comets, skip busy ones
for _ in 1...launchCount {
let shape = self.comets[Int(arc4random_uniform(UInt32(shapeCount)))]
if shape.hasActions() { continue }
shape.launch()
}
}
Run Code Online (Sandbox Code Playgroud)
这将每个彗星的 SKNode 数量从 3 个减少到 1 个,从而简化了代码和运行时环境,并为通过着色器实现更复杂的效果打开了大门。我发现的唯一缺点是必须学习一些 GLSL。**
*在设备模拟器中并不总是正确。模拟器未将距离和长度值传递给自定义着色器。
**这以及 CGPath glsl 行为中的一些特质。路径构造正在影响淡入淡出的执行方式。看起来v_path_distance曲线段之间的混合不平滑。不过,只要小心构建曲线,这应该可行。
| 归档时间: |
|
| 查看次数: |
619 次 |
| 最近记录: |