Scenekit PhysicEngine follow rolling ball - ios

I want to follow a rotating sphere in Apple's SceneKit. I've added an LookAt Constraint to the camera and as the sphere falls down the cam aleays points to it but if the sphere rolls away the camera stays at its current position.
I want the cam to follow this sphere like in a third persond shooter with a predefined distance to it. If I make the cam a childNode of the sphere the cam "orbits" around it as the ball is rolling.
Any ideas how I can follow the ball with the cam?

It's pretty simple. You just need to change the camera node's position at each frame to the ball's presentationNode plus an offset to avoid being inside of it.
I'm not too familiar with Swift but the code would look something like this:
func renderer(aRenderer: SCNSceneRenderer, didSimulatePhysicsAtTime time: NSTimeInterval){
var ballP = ballNode.presentationNode.position
// Offset the camera up and on X:
var cameraP = SCNVector3(x: ballP.x+5, y: ballP.y+10, z: ballP.z)
cameraNode.position = cameraP
}

Related

Limit Scene Kit Camera Rotation around a single (y) axis in iOS

So I have a sphere in scene kit and currently, I am orbiting the sphere with the default .allowsCameraControl = true. I want to limit the orbit of the camera to only orbit the Y-axis (basically it will look like a globe spinning).
I have tried messing around with spinning the actual sphere with pan gestures, but that seemed to complicate things rather quickly and I would like to keep the default physics and gestures involved with .allowsCameraControl = true if at all possible.
Below is what I have currently. Thanks!
private func createSceneHelpers() {
scnView.allowsCameraControl = true
}
private func createCamera() {
cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 5)
myScene.rootNode.addChildNode(cameraNode)
}
You can take a look at the SCNCameraController class and its maximumVerticalAngle property. The SCNInteractionModeOrbitTurntable interaction mode is also of interest. Note that if the online documentation is lacking, the documentation in SceneKit's header file as more often up-to-date.

Position SCNNode in front of camera with just one axis of rotation

I'm trying to make an ARKit application where an SCNNode, in this case a box, is placed in front of the camera, facing the camera. As the user moves the camera around, objects are placed when a certain distance has been moved. This would leave you with a series of nodes facing the camera in a line, equally spaced.
I have this working to a certain extent, but my problem is with the rotation. I'm currently taking all axes of rotation, so as the user re-orients their phone, the rotation of the node matches. I want to restrict this to just the rotation around the y-axis. The ideal outcome is a domino-trail like look, with all the objects having the same x and z rotations, but potentially different y rotations.
I hope I've explained this clearly enough!
Here's the code I'm currently using:
func createNode(fromCameraTransform cameraTransform: matrix_float4x4) -> SCNNode {
let geometry = SCNBox(width: 0.02, height: 0.04, length: 0.01, chamferRadius: 0)
let physicsBody = SCNPhysicsBody(type: .dynamic, shape: SCNPhysicsShape(geometry: geometry))
physicsBody.mass = 1000
let node = SCNNode(geometry: geometry)
node.physicsBody = physicsBody
var translationMatrix = matrix_identity_float4x4
translationMatrix.columns.3.x = 0.05 // Moves the node down in world space
translationMatrix.columns.3.z = -0.1 // Moves the object away from the camera
node.simdTransform = simd_mul(cameraTransform, translationMatrix)
return node
}
I've tried different combinations of extracting values from the second column of the cameraTransform and setting them as eulerAngles, rotation and simdRotation, but to no avail.
I've also tried extracting values from the pointOfView of the current sceneView and assigning them to the same values as listed above, but again, no luck.
Any help would greatly appreciated!
I know a little bit about this, but am really just starting out with SceneKit and 3D transformations/matrices so be gentle with me!
I think I know what your trying to do, basically automatically drop each new domino so its evenly spaced following the camera.pointOfView trail.
You can update the new nodes euler angles y-axis to the same as the cameras pointofView eularAngles.y. So as you move the camera around the next node you are placing is always facing towards the camera (only rotating around the y-axis).
The renderer function updateAtTime below gets called everytime
the camera moves
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
// You set the camera’s pointOfView’s eularAngle for y-xis to the node you are
about to place.
node.eulerAngles.y = (sceneView.pointOfView?.eulerAngles.y)!
I had this working in a playground so it does work.
Edit: This solution above angle was having a gimbal lock problem (as you went around in a circle in would reset its angle back to the axis.
So I found this approach using SCNBillboardConstraint works without experiencing the gimbal lock problem as you go around in circle.
let yFreeConstraint = SCNBillboardConstraint()
yFreeConstraint.freeAxes = .Y
node.constraints = [yFreeConstraint]
node.eulerAngles = node.presentation.eulerAngles
node.position = position

Scaling an object after rotating in SceneKit

I am trying to set up a simple scene (one spherical node and the default camera) in a square SceneView. Currently I set up the scene as below:
let scene = SCNScene()
let planet = SCNSphere(radius: 1.0)
let planetNode = SCNNode(geometry: planet)
scene.rootNode.addChildNode(planetNode)
To certain views, I also rotate the node as such:
let rotationNode = SCNNode()
rotationNode.addChildNode(planetNode)
scene.rootNode.addChildNode(rotationNode)
rotationNode.rotation = (SCNVector4: SCNVector4(x: 0, y: 0, z: 1, w: some_amount_of_radians))
What I noticed, however, is the objects that get rotated are smaller than the ones that don't get rotated. I am not really sure what the ratio is, but it seems to be dependent on how much rotation is added, to a point.
In the below screenshot, Earth is rotated 45 degrees, and the other 2 are not rotated. If I rotated it 90 degrees instead, there is no difference, which leads me to believe there is a square bounding box around the sphere and the default camera is forcing its point of view to contain this box.
I have also tried to change the euler angles, position, and scale of the rotated nodes to compensate, but no transormations I apply seem to have any effect. Any pointers for solving this camera issue would be perfect.

How to position a SCNNode to cover the whole SCNView?

I am very new to SceneKit and your help will be really appreciated!
I have a 200x200 sized SCNView in my UIView, which is at the centre of super view.
I want to put a SCNCylinder inside, such that the SCNCylinder covers full SCNView. I read that all these views of Scenekit are defined in meters, so how do I form a relationship between the dimensions of my screen and the
SCNCylinder.
I tried:
var coinNode = SCNNode()
let coinGeometry = SCNCylinder(radius: 100, height: 2)
coinNode = SCNNode(geometry: coinGeometry)
coinNode.position = SCNVector3Make(0, 0, 0)
coinScene.rootNode.addChildNode(coinNode)
let rotate90AboutZ = SCNAction.rotateByX(-CGFloat(M_PI_2), y: 0.0, z: CGFloat(M_PI_2), duration: 0.0)
coinNode.runAction(rotate90AboutZ)
ibOutletScene.scene = coinScene
But this leaves a margin between my coinScene and the ibOutletScene. How do I remove this space?
I also tried adding Camera:
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3Make(0, 0, 100)
coinScene.rootNode.addChildNode(cameraNode)
But I see random behaviour with this and the coinNode gets hidden! How should I position my camera? Or is there any other way to remove extra space from my ibOutletScene?
Edit:
This is how it looks if I don't add camera. There is a margin between red scene and green coin. I tried multiple sizes for the coin, but I am unable to remove this margin unless I add a camera. But, If I add camera, I get another problem, mentioned below this screenshot.
If I don't add the camera, The rotation animation on coin works perfectly, but If I add camera,the rotation enlarges the and then becomes small again with the animation. How can I rotate it on its axis, without increasing the size?
I am using following code to rotate the coin:
The same code works fine without camera, but enlarges the coin after adding camera. Checkout the snapshot.
let rotate = SCNAction.rotateByX(0, y: -2 * CGFloat(M_PI_2), z: 0, duration: 2)
coinNode.runAction(rotate)
The random behavior might be caused by the last line in your first code snippet. You're starting an animation, and then adding the scene to the view.
Instead, build your scene, attach it to the view, and then start your animation. Setting a non-zero duration for the action will give you a more pleasing transition.
As for the extra space, it would help us understand if you post a screenshot. But you're going to have to do a bit of trigonometry.
It looks like you have a scene that you want to be blocked by a coin, that then rotates out of the way? Simulate that yourself with real objects. Put your eye down at the edge of your desk. Put a coin out a ways from your eye. How far does that coin have to be in order to block particular objects farther away on your desk?
In SceneKit, you can query the field of view of the SCNCamera. You know the size of your coin and the size of the view. Calculate the distance from the camera needed for the projected diameter of your coin to equal the width of your view. Put the coin there.

Character not moving in the correct direction in Scenekit

I am making a fist person camera in Scenekit and the character is not moving relative to the rotation. Instead it is moving relative to the world axis. I want my game to be so when the player swipes forward, the character moves forward on its own x axis. Not along the x axis of the world. I tried to compensate of this with sine and cosine but it did not work. Here is my code:
func lookGestureRecognized(gesture: UIPanGestureRecognizer) {
let velocity = gesture.velocityInView(sceneView)
let rotationAngle = heroNode.presentationNode().rotation.w * heroNode.presentationNode().rotation.y
var impulse = SCNVector3Make(Float(velocity.x)/50, 0, Float(velocity.y)/50)
impulse = SCNVector3(x: impulse.x * cos(rotationAngle), y: 0, z: impulse.z * sin(rotationAngle))
heroNode.physicsBody?.applyForce(impulse, impulse: true)
}
This is still making the character move in the wrong direction of the swipe. Does anyone know how to make the character move relative to its rotation?
Thanks, please ask for clarification if needed
the rotation property is made of a rotation axis and a rotation angle. If you want to retrieve the rotation along the x axis you can use the eulerAngles property.

Resources