Search code examples
iosswiftxcodescenekitscene

Continuously rendering new child node objects on SceneKit scene in Swift


I got a question about a Scene Kit project I'm doing. So I have this scene where I randomly spawn cubes and add them to my scene root node, the thing is that they are rendered at the beginning, but quickly they stop being rendered, so I don't see these new objects being spawned, I do see them again when I tap on the screen (so when I make an action on the scene or whatever) OR sometimes some of them are randomly rendered and I don't know why

I have tried setting rendersContinuously to true but this does not change anything.

Here is the cube spawning thread :

DispatchQueue.global(qos: .userInitiated).async {
            while true {
                self.spawnShape()
                sleep(1)
            }
 }

Here is how I add them to the child nodes :

let geometryNode = SCNNode(geometry: geometry)
geometryNode.simdWorldPosition = simd_float3(Float.random(in: -10..<10), 2, shipLocation+Float.random(in: 20..<30))

self.mainView.scene!.rootNode.addChildNode(geometryNode)

And here's the endless action applied to the camera and my main node :

ship.runAction(SCNAction.repeatForever(SCNAction.moveBy(x: 0, y: 0, z: 30, duration: 1)))
camera.runAction(SCNAction.repeatForever(SCNAction.moveBy(x: 0, y: 0, z: 30, duration: 1)))

The added geometryNode cubes stop being rendered unless I tap on screen

How can I force the rendering of these new child node objects on the scene even when I don't touch the screen?

Thank you

EDIT asked by ZAY:

Here is the beginning of my code basically:

    override func viewDidLoad() {
        super.viewDidLoad()
                
        guard let scene = SCNScene(named: "art.scnassets/ship.scn")
            else { fatalError("Unable to load scene file.") }

        let scnView = self.view as! SCNView
        self.mainView = scnView
        self.mainView.rendersContinuously = true
        
        self.ship = scene.rootNode.childNode(withName: "ship", recursively: true)!
        self.camera = scene.rootNode.childNode(withName: "camera", recursively: true)!
        
        self.ship.renderingOrder = 1
        
        self.ship.simdWorldPosition = simd_float3(0, 0, 0)
        self.camera.simdWorldPosition = simd_float3(0, 15, -35)
        
        self.mainView.scene = scene
        
        DispatchQueue.global(qos: .userInitiated).async {
            while true {
                self.spawnShape()
                sleep(1)
            }
        }
        
        ship.runAction(SCNAction.repeatForever(SCNAction.moveBy(x: 0, y: 0, z: 30, duration: 1)))
        camera.runAction(SCNAction.repeatForever(SCNAction.moveBy(x: 0, y: 0, z: 30, duration: 1)))
        
        let tap = UILongPressGestureRecognizer(target: self, action: #selector(tapHandler))
        tap.minimumPressDuration = 0
        self.mainView.addGestureRecognizer(tap)
    }

When I tap on screen, this code is called:

    func tapHandler(gesture: UITapGestureRecognizer) {
        let p = gesture.location(in: self.mainView)
        let turnDuration: Double = 0.3
        print("x: \(p.x) y: \(p.y)")

        if gesture.state == .began {
            if p.x >= self.screenSize.width / 2 {
                self.delta = 0.5
            }
            else {
                self.delta = -0.5
            }
            self.ship.runAction(SCNAction.rotateBy(x: 0, y: 0, z: self.delta, duration: turnDuration))
            return
        }

        if gesture.state == .changed {
            return
        }

        self.ship.runAction(SCNAction.rotateBy(x: 0, y: 0, z: -self.delta, duration: turnDuration))
    }

Basically it rotates my ship to right or left depending on which side of screen I tap, and it displays the coordinates where I tapped in the console. When I release, the ship goes back to the initial position/rotation.


Solution

  • DispatchQueue.global(qos: .userInitiated).async {
            while true {
                self.spawnShape()
                sleep(1)
            }
    }
    

    Aren't you blocking that shared queue by doing that? Use a Timer if you want to run something periodically.