SKPhysicsBody circleOfRadius area wrong - sprite-kit

Since Xcode 8.2.1 and iOS 10.2, I'm seeing (randomly) wrong area for SKPhysicsBody when constructed as circle. This is my code:
let boundingBox = (node.path)?.boundingBox
let radius = (boundingBox?.size.width)! / 2.0
node.physicsBody = SKPhysicsBody(circleOfRadius: radius)
print("bubbleRadius: \(radius), boundingBox: \(boundingBox), physicsArea: \(node.physicsBody?.area), physicsAreaShouldBe: \((CGFloat.pi * radius/100 * radius/100))")
Now random factor. I couldn't find any clue when this printed 'physicsArea' is correct and when it's wrong. Sometimes print shows this (where you will see that area of PhysicsBody is calculated differently?):
bubbleRadius: 50.0, boundingBox: Optional((-50.0, -50.0, 100.0,
100.0)), physicsArea: Optional(0.349065870046616), physicsAreaShouldBe: 0.785398163397448
And sometimes print shows this (where you can see that area is calculated normally - per my understanding of area of circle):
bubbleRadius: 50.0, boundingBox: Optional((-50.0, -50.0, 100.0,
100.0)), physicsArea: Optional(0.785398185253143), physicsAreaShouldBe: 0.785398163397448
radius is always the same, only area gets calculated differently sometimes. This makes my circles overlap each other in the scene because their body is smaler than it looks.
Any ideas what might this be? Thanks.

Related

SpriteKit: SKPhysicsJointLimit not respecting 'maxLength'

I'm trying to create a chain-like structure in SpriteKit and I'm having trouble understanding the behavior of SKPhysicsJointLimit's maxLength property. It seems not to do anything at all.
This question didn't solve my problem.
According to the documentation, maxLength is The maximum distance allowed between the two physics bodies connected by the limit joint.
However, my two nodes become oriented much farther apart than their maxLength value. It's true that I'm setting their initial positions to be farther apart than maxLength -- but I would expect the nodes to pull together during the simulation, as if tied together by a stretchy rope. Instead, the nodes remain far apart.
So, here's some code that sets a joint between two SKSpriteNodes.
let screen = UIScreen.main.bounds
let bodyA = SKSpriteNode(imageNamed: "box.png")
let bodyB = SKSpriteNode(imageNamed: "box.png")
bodyA.size = CGSize(width: 20, height: 20)
bodyB.size = CGSize(width: 20, height: 20)
bodyA.position = CGPoint(x: screen.width*0.4, y: screen.height*0.8)
bodyB.position = CGPoint(x: screen.width*0.6, y: screen.height*0.8)
bodyA.physicsBody = SKPhysicsBody(circleOfRadius: 20)
bodyB.physicsBody = SKPhysicsBody(circleOfRadius: 20)
addChild(bodyA)
addChild(bodyB)
let pinJoint = SKPhysicsJointLimit.joint(withBodyA: bodyA.physicsBody!, bodyB: bodyB.physicsBody!, anchorA: CGPoint(x: 0.5, y: 0.5), anchorB: CGPoint(x: 0.5, y: 0.5))
//This doesn't seem to do anything:
pinJoint.maxLength = 5.0
scene?.physicsWorld.add(pinJoint)
In the simulation, it's clear that there is a physics joint connecting the two nodes -- it's just that the nodes are much farther apart than they should be.
Why doesn't my maxLength value change the behavior of my two nodes, and how do I fix the problem? What am I not understanding?
Thanks for your input!
Be sure that the anchor points are in scene coordinates, as described in the documentation. The (0.5, 0.5) is likely intended to be "center of the sprite" or something like that, but that's not correct for a joint.

Can You Add SKPhysicsBody To Only One Side Of A Rectangular Node?

So let's say I have this rectangular SKNode. Can I add SKPhysicsBody to only one side of the rectangle i.e. only the top side, so that only the top side could detect collision and not the other sides (then the physics indicator blue line would only appear on the top and not anywhere else)?
I wanted to create a game, but I don't know whether something like this could work.
So if I could, how?
If I can't, is there a way around this issue?
I wanted to add a normal rectangle with physicsBody on only one side.
See the rectangle image here, the blue part is the place where I want the physicsBody
So I've tried adding an edgeBased physicsBody, but it doesn't seem to work (either the physicsBody didn't get created or it is in the wrong position).
let rectangle = SKSpriteNode(imageNamed: "Rectangle")
rectangle.size = CGSize(width: 128, height: 128)
rectangle.position = CGPoint(x: frame.midX, y: frame.midY)
rectangle.physicsBody = SKPhysicsBody(edgeFrom: CGPoint(x: rectangle.position.x - rectangle.size.width/2, y: rectangle.position.y + rectangle.size.width/2), to: CGPoint(x: rectangle.position.x + rectangle.size.width/2, y: rectangle.position.y + rectangle.size.width/2))
rectangle.physicsBody!.restitution = 0.0
rectangle.physicsBody!.categoryBitMask = physicsCategories.groundCategory
rectangle.physicsBody!.collisionBitMask = physicsCategories.squareCategory
addChild(rectangle)
Thanks!
You could, but it would also detect collision coming from the 'wrong' side i.e. with an object that has traveled through the node.
Physics bodies can be volume or edge based, but even an edge-based one that was only a pixel thick still has 4 sides.
If you could supply a drawing of exactly what you want, we could help better as there are probably several different approaches.

Swift SceneKit — physical blocks do not stick to each other

Blocks just crumble apart.
How can this problem be solved?
Initializing blocks:
var boxNode = SCNNode(geometry: SCNBox(width: 0.75, height: 0.15, length: 0.25, chamferRadius: 0))
boxNode.position = SCNVector3(x: x1, y: y, z: z1)
boxNode.geometry?.firstMaterial = SCNMaterial()
boxNode.geometry?.firstMaterial?.diffuse.contents = UIImage(named: "wood.jpg")
boxNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
boxNode.eulerAngles.y = Float(Double.pi / 2) * rotation
boxNode.physicsBody?.friction = 1
boxNode.physicsBody?.mass = 0.5
boxNode.physicsBody?.angularDamping = 1.0
boxNode.physicsBody?.damping = 1
picture
video
full code
I won't be able to tell you how to fix it as I have the exact same problem which I wasn't able to solve. However, as I played around I figured out a couple of things (which you may find useful):
The same problem hasn't happened to me in pure SceneKit, hence I think it's a bug in ARKit
Node with physics has to be added to the rootNode of the scene, otherwise odd stuff happens (elements passing through each other, gravity behaving in an inconsistent way)
If you pass nil as shape parameter, SceneKit will figure bounding box based on the geometry of the node. This hasn't worked properly for me so what I've done (using SceneKit editor) was to duplicate the geometry and then set it as a custom shape for the bounding box (have a look at the attached image)
Overall I've found physics simulation in SceneKit when used with ARKit to be extremely buggy and I spent a lot of time "tricking" it into working more-or-less how I wanted it to work.

Placing "SCNNode" at a given position and facing "SCNCamera" with offset around Y-axis

So,
I have the exact position I want to place the node at. If I test things with a sphere geometry I can place spheres in the world by telling the node:
node.simdPosition = position
(I provide the "position" as an input to the function).
That successfully places the object in the world exactly where I want it to go.
What I really want to do is placing a plane:
let plane = SCNPlane(width: 0.2, height: 0.3)
plane.cornerRadius = plane.width / 10
plane.firstMaterial?.diffuse.contents = UIColor.red
plane.firstMaterial?.specular.contents = UIColor.white
let node = SCNNode(geometry: plane)
Then telling it to be placed at the "position":
node.simdPosition = position
All this works with the plane as well. What I have problems with is the angle:
I want to tell the plane's node to be placed with a given "angle" (around Y) offset to the camera. I tried this but it's not working:
node.rotation = SCNVector4Make(0, 1, 0, currentFrame.camera.eulerAngles.z - angle)
So then, the question is, how can a node be placed at a certain position and at the moment it gets placed in the world, also have a certain Y angle offset from the perpendicular to the camera?
I was using the wrong Euler angle... (z)
This made it work:
node.eulerAngles = SCNVector3Make(0, cameraEulerAngles.y - Float(0.7), 0)

Swift plot coordinates on uiimageview with world map

I am trying to plot some coordinates on the earth on an UIImage which contains a map of the world. (I don't want to use maps)
See an example of the UIImageView below below:
As you see it's working out pretty well but the mapping from coordinates and X Y are incorrect!
Amsterdam's coordinates are: (52.36666489, 4.883333206) and the Center's are (0,0).
I've done the following things to try to make this happen but unfortunately this isn't working out:
I've tried first to 'normalize' the coordinates since latitude ranges from -90 to 90 and latitude -180 to 180. This is done by adding 90 to the real latitude and 180 to the real longitude which yiels the 'normalized' versions:
let normalizedLat = location.coordinate.latitude + 90.0.
let normalizedLng = location.coordinate.longitude + 180.0
After that I've calculated the scale factor where the normalizedLat and normalizedLng should scale with:
let heightScaleFactor = mapImageView.frame.height / 180.0
let widthScaleFActor = mapImageView.frame.width / 360.0
And 3. After that i've got the scaling factors I finally can calculate the coordinates by:
let x = Double(widthScaleFActor * CGFloat(normalizedLng))
let y = Double(heightScaleFactor * CGFloat(normalizedLat))
dot.frame = CGRect(x: x, y: y, width: Double(dot.frame.width), height: Double(dot.frame.height))
But for some strange reason Amsterdam is not on the Amsterdam spot and the Center is not on the Center spot.
I am quite sure that my calculations has gone wrong. Any ideas?
Remember, in iOS the origin is in the top-left, not the bottom-left. Positive-y goes down, not up.
You need to factor that in.
dot.frame = CGRect(x: x, y: mapImageView.frame.height - y, width: Double(dot.frame.width), height: Double(dot.frame.height))
Also note that the equator in your image is not in the middle. It's lower in the image so you need to add an additional offset in your calculation of the y value based on the equator's offset in the image.
dot.frame = CGRect(x: x, y: mapImageView.frame.height - y + equatorOffset, width: Double(dot.frame.width), height: Double(dot.frame.height))
It's also possible that your map projection doesn't have a simple linear latitude scale. 0-10 degrees might be 12 pixels while 10-20 degrees might be 11 pixels, etc. and 80-90 is only 3 pixels (or whatever).