I've implemented zooming by rescaling a root "world" node housing all the objects in my game. However, with the smaller size of the world node & children, applying forces now has a greater effect.
Even if I scale forces by the same scaling as my world node, they are still huge and objects go flying.
I've seen some pretty hectic solutions around:
Scale the scene (but then overlays are scaled too)
Create a whole new invisible layer that obeys normal physics then a visible layer on top which you scale...
Is there a more straightforward approach to somehow just scale the physics world with the world node?
Yes, don't scale in order to zoom. Try resizing instead.
myScene.scaleMode = SKSceneScaleModeAspectFill;
And then while zooming:
myScene.size = CGSizeMake(myScene.size.width + dx, myScene.size.height + dy);
*Apple Documentation says:
Set the scaleMode property to SKSceneScaleModeResizeFill. Sprite Kit automatically resizes the scene so that it always matches the view’s size.
Related
When I add a new node with ARKit (ARSKView), the object is positioned base on the device camera. So if your phone is facing down or tilted, the object will be in that direction as well. How can I instead place the object base on the horizon?
For that, right after a new node's creation, use a worldOrientation instance property that controls the node's orientation relative to the scene's world coordinate space.
var worldOrientation: SCNQuaternion { get set }
This quaternion isolates the rotational aspect of the node's worldTransform matrix, which in turn is the conversion of the node's transform from local space to the scene's world coordinate space. That is, it expresses the difference in axis and angle of rotation between the node and the scene's rootNode.
let worldOrientation = sceneView.scene.rootNode.worldOrientation
yourNode.rotation = worldOrientation /* X, Y, Z, W components */
P.S. (as you updated your question) :
If you're using SpriteKit, 2D sprites you spawn in ARSKView are always face the camera. So, if the camera moves around a definite point of real scene, all the sprites must be rotated about their pivot point, still facing a camera.
Nothing can prevent you from using SceneKit and SpriteKit together.
At the moment, my code loads in a 3D model, creates a node using that model, and displays the node in the scene. Setting the scale/rotation (euler angles) of the node works fine. However, I'm trying to set the position of the node relative to the world origin, and I don't want the node to be attached to a plane.
I've tried setting node.position and node.worldPosition to no avail; although the position of the node changes, when the camera moves, the node doesn't stay static, but moves about with the camera. I'm new to using ARKit, so I'm probably doing something stupid, but I can't figure out what it is that I need to do, so any help would be much appreciated.
Edit:
The weird thing is that if I set the coordinates to say SCNVector3(0, 3, 0) it's fine, but if I go over a certain number of meters away it seems to fail. Is this expected
Firstly, poor world tracking can be caused by these common issues.
Poor ligthing. Causes low number of feature points available for tracking
Lack of texture Causes low number of feature points available for tracking
Fast Movement Causes blurry images which causes tracking to fail
However, what I believe is happening in your case (which is a little more tricky to debug)... is you are most likely placing the loaded model underneath the detected horizontal plane in the scene.
In other words, you may have positioned the SCNNode using a negative Y coordinate which places the node below the horizontal detected plane and causes the model to drift around as you change the cameraView
try setting the Y position of the node to either 0 or a small positive value like 0.1 metres
node.position = SCNVector3(0, 0, -1) // SceneKit/AR coordinates are in meters
sceneView.scene.rootNode.addChildNode(node)
z = -1 places the SCNNode 1 metre in front of your cameraView.
Note: I verified this issue myself using a playground I use for testing purposes.
content
first i have a 360 (equirectangular) image viewer by applying the image as a texture onto a sphere with the scenekit camera at the center.
then i enter a "drawing mode" where i can use my finger to draw on a transparent UIView
when i'm done, i take the drawing and apply it to my sphere as an annotation
problem (with video example)
the problem is in this 3rd step, the scale isn't saved correctly.
https://www.dropbox.com/s/a2l3vvx92sa3cgh/drawing_defect_trimmed_480p.mp4?dl=0
temporary solution
i was able to add a magic number to the expected scale which lessens the scaling problem, but it is still a little bit off and obviously suboptimal from a technical perspective.
e.g. “scale_used = expected_scale + magic_constant”
implementation details
I am projecting a UIView in front of a Scene Kit camera at some custom
distance in the Scene Kit world and trying to make it so the new
Scene Kit node will have exactly the same visual size.
the approach is to calculate the perspective projection of the item,
located in the world at drawingContentItem.distance using camera
zNear - “(screenHeight * distance / Float(zNear))”.
Then we assume that size of visible world of scene kit is from -1000
to 1000; and the view angle is 60 degrees; and calculate the ratio of
scene Kit near plane view to UIView - “(sceneScreenHeight /
nearPlaneHeightInWorlCoordinates)”.
that gives us the finalHeight of drawing in the world coordinates and
we use this to calculate the scale.
But it seems that there is some mistake in the formula and it causes
the need for the magical number. :(
Here is my setup:
SKScene with a node called world
to this world, I attach another node: vehicle
to this vehicle, I attach three nodes that make up the vehicle; a body and two wheels
the wheels are attached to the body via SKPhysicsJointPin specifying their anchors
Now, everything is fine until I zoom out of my world:
[_world runAction:[SKAction scaleTo:0.5 duration:0.75]];
My vehicle suddenly lifts off the wheels. It appears as if the same distances as in the not-zoomed-in-world are kept. All parts of the vehicle are properly scaled- except the distances to its parts.
Do I have to apply the zoom to my joints as well? Or do I need to reset the anchor of my joints?
Thanks for your help!
Physics don't scale. Changing a node's scale is a purely visual effect, it does not alter physics in any way.
Even if you manually update physics positions synchronized with a node's scale you'll find that you can't scale each body's shape without removing the previous body and replacing it with a corresponding body of the same shape, just scaled. During a scale action you would have to through away and create new bodies every frame, which will probably cause a serious framerate issue.
I am creating a spritenode, setting its position and changing its anchorpoint to (0, .5) and then creating a phyicsbody.
The physicsbody thinks my anchorpoint is still at (.5, .5), stupidly.
The same problem is referenced here, but unsolved: Physicsbody doesn't adhere to node's anchor point
The order I am doing things in is correct, it's just my physicsbody is stubborn.
The anchorPoint determines where the node's texture is drawn relative to the node's position. It simply does not affect physics bodies because it's a purely visual property (a texture offset).
For physics-driven nodes it is actually counter-productive to change the anchorPoint from its default because that will change the point around which the texture will rotate. And the physics body will usually also change the node's rotation.
So even if you were to move the physics body shape's vertices to match the sprite with a modified anchorPoint, the physics shape will be misaligned with the image as soon as the body starts rotating. And it'll seem to behave weird.
Plus whatever you want to achieve using anchorPoint you can more flexibly achieve by using the node hierarchy to your advantage. Use a SKNode as the physics node, and add a non-physics sprite node as child to that node and offset it the way you wanted the image to be offset by changing the sprite's anchorPoint.
You end up having two nodes, one invisible representing the physics body and one (or more) sprite(s) representing the visuals for the body but not necessarily tied to the body's center position.