9

I'm working on a metaballs effect in SpriteKit. I have an SKEffectNode with a shader that compresses the colour range of its buffer. This effectnode has a hundred-odd SKSpriteNode as its children. The effect works great for a couple of seconds, but suddenly, over the course of a few frames, all of the spritenodes vanish from the screen, and the nodecount drops from 100-odd down to 2 (I guess the 2 remaining are the effectnode itself, and the edge chain that forms the container to hold the balls?)

If I add the ball spritenodes directly to the rootnode of the scene, this does not happen. It's only when they are made children of the effectnode that they seem to get aggressively culled after a few seconds.

Interestingly, (and this is visible in the simulator if the frame rate is quite low) the SpriteNodes seem to get culled in the vertical order that they are currently displayed on the screen, from the top of the screen to the bottom.

If the view's showsPhysics property is set to true, I can see the SKSpriteNode's physics bodies are still there onscreen, and rolling around. But their visual representation has vanished, and the view's node count has been depleted. According to the docs showsNodeCount "shows physics bodies that are visible in the scene." So because the nodes are no longer visible, the node count has dropped.

Anyone know what could be causing this, or what tools I could use to debug it?

Standard SpriteKit iOS Swift 2.2 Xcode 7 template, replace the GameScene file with this:

import SpriteKit

class GameScene: SKScene {

    override func didMoveToView(view: SKView) {
        // create walls to contain balls
        let box = SKPhysicsBody(edgeLoopFromRect: CGRect(x: 0, y: 0, width: 800, height: 800))
        let boxNode = SKNode()
        boxNode.physicsBody = box
        addChild(boxNode)

        let radius: CGFloat = 5

        let texture = createMetaballTexture(radius)

        let centre = CGPoint(x: frame.size.width / 2, y: frame.size.height / 2)
        let rows = 12
        let columns = 10

        let metaballs = SKEffectNode()
        let shaderString = [
            "void main()",
            "{",
            "vec4 c = texture2D(u_texture, v_tex_coord);",
            "gl_FragColor = smoothstep(0.3, 0.6, c); ",
            "}"].joinWithSeparator("\n")
        let map = SKShader(source: shaderString)

        metaballs.shader = map
        for i in 0...(rows * columns) {
            let ball = SKSpriteNode(texture: texture)
            let body = SKPhysicsBody(circleOfRadius: radius)
            body.friction = 0.1
            body.restitution = 0.8
            ball.physicsBody = body
            ball.position = CGPoint(x: centre.x + CGFloat(i % columns) * radius, y: centre.y + CGFloat(i / rows) * radius)
            ball.blendMode = .Add

            metaballs.addChild(ball) // change this to "addChild(ball)", and the nodes don't get culled

        }

        addChild(metaballs)
        physicsWorld.speed = 0.2
        backgroundColor = SKColor.darkGrayColor()

    }

    func createMetaballTexture(radius: CGFloat) -> SKTexture {
        let metaballScale: CGFloat = 8
        let ellipseOrigin = (metaballScale / 2) - 1.5
        UIGraphicsBeginImageContext(CGSize(width: radius * metaballScale, height: radius * metaballScale))
        let context = UIGraphicsGetCurrentContext()
        SKColor.whiteColor().setFill()

        CGContextFillEllipseInRect(context, CGRect(x: radius * ellipseOrigin, y: radius * ellipseOrigin, width: radius * 3, height: radius * 3))

        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return SKTexture(CGImage: blur(image!))
    }

}

func blur(image: UIImage) -> CGImage {
    let gaussianBlurFilter = CIFilter(name: "CIGaussianBlur")
    let inputImage = CIImage(CGImage: image.CGImage!)
    gaussianBlurFilter?.setValue(inputImage, forKey:kCIInputImageKey)
    gaussianBlurFilter?.setValue(5, forKey: kCIInputRadiusKey)
    let outputImage = gaussianBlurFilter?.outputImage
    let context = CIContext(options: nil)
    return context.createCGImage(outputImage!, fromRect: inputImage.extent)
}
4

0 回答 0