skip to Main Content

I have a SceneKit scene in which the camera is stationary, and is positioned like this: cameraNode.position = SCNVector3(0.0, 0.0, 100.0). Other than that, the camera has the default configuration.

In the scene is a single, spherical SCNNode with a physics body.

Below the sphere is a flat plane, with a physics body, on which the sphere rolls around. The plane is positioned in the center of the scene, at SCNVector3(0.0, 0.0, 0.0).

What I need is for the scene to be surrounded by invisible "walls" that are positioned exactly at the edges of the screen. The sphere should bounce off these static physics bodies so it never leaves the screen.

I’ve tried placing one of these "walls" (an SCNNode with an SCNBox geometry) using the actual screen dimensions, but the positioning is incorrect; the node is apparently off screen. This is presumably because SceneKit coordinates are in meters, not pixels or whatever.

Question: How can I figure out the positioning of the "walls" so that they are fixed to the edges of the screen?

Thanks for your help!

2

Answers


  1. Chosen as BEST ANSWER

    So, obviously there's a mathematical solution to this problem, and that's the best way to do this. Unfortunately, I'm not very good at math, so I had to come up with another solution.

    First, I create the wall to be located at the top edge of the screen. It will begin at the center of the scene:

    let topEdge = SCNNode()
    let topEdgeGeo = SCNBox(width: MainData.screenWidth, height: 5.0, length: 5.0, chamferRadius: 0.0)
    
    topEdge.geometry = topEdgeGeo
    topEdge.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.kinematic, shape: SCNPhysicsShape.init(node: topEdge))
    topEdge.physicsBody?.categoryBitMask = CollisionTypes.kinematicObjects.rawValue
    topEdge.physicsBody?.collisionBitMask = CollisionTypes.dynamicObjects.rawValue
    topEdge.physicsBody?.isAffectedByGravity = false
    topEdge.physicsBody?.allowsResting = true
    topEdge.physicsBody?.friction = 0.0
    topEdge.position = SCNVector3(0.0, 0.0, 2.5)
    
    scnView.scene?.rootNode.addChildNode(topEdge)
    

    I then repeatedly reposition the wall a little bit farther up the y axis until it's no longer within the camera's viewport:

    var topWallIsOnScreen: Bool = scnView.isNode(topEdge, insideFrustumOf: scnView.pointOfView!)
    
    while topWallIsOnScreen {
       topEdge.position.y += 0.001
       topWallIsOnScreen = scnView.isNode(topEdge, insideFrustumOf: scnView.pointOfView!)
    }
    

    The end result is that the wall is positioned at the top edge of the screen. I was concerned about performance, but it seems to work just fine.


  2. Use the SCNSceneRenderer’s unprojectPoint(_:) method to convert left and right edges of the screen to 3D space coordinates and add two planes in those coordinates.

        let leftPoint = SCNVector3(scnView.bounds.minX, scnView.bounds.midY, 1.0)
        let righPoint = SCNVector3(scnView.bounds.maxX, scnView.bounds.midY, 1.0)
        
        let leftPointCoords = scnView.unprojectPoint(leftPoint)
        let rightPointCoords = scnView.unprojectPoint(righPoint)
        
        let rightPlane = SCNPlane(width: 100.0, height: 100.0)
        let leftPlane = SCNPlane(width: 100.0, height: 100.0)
        
        let rightPlaneNode = SCNNode(geometry: rightPlane)
        rightPlaneNode.eulerAngles = .init([0.0, .pi / 2, 0.0])
        rightPlaneNode.physicsBody = .init(type: .static, shape: nil)
        
        let leftPlaneNode = SCNNode(geometry: leftPlane)
        leftPlaneNode.physicsBody = .init(type: .static, shape: nil)
        leftPlaneNode.eulerAngles = .init([0.0, .pi / 2, 0.0])
        
        rightPlaneNode.position = rightPointCoords
        leftPlaneNode.position = leftPointCoords
        scene.rootNode.addChildNode(rightPlaneNode)
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search