Weird shaking with hinge joint in Scene Kit - ios

i'm trying to make a join, tank with turret, but because of this joint whole model is shaking, do you have any ideas what causing this behaviour?
Link to project: Project
this is how i made it:
SCNNode *tankNode = [extraScene.rootNode childNodeWithName:#"Main_Body" recursively:YES];
[tankNode setScale:SCNVector3Make(0.003, 0.003, 0.003)];
[tankNode setPosition:SCNVector3Make(0, 1, 0)];
[tankNode setPhysicsBody:[SCNPhysicsBody dynamicBody]];
[tankNode setRotation:SCNVector4Make(0, 1, 0, -M_PI_2)];
[scene.rootNode addChildNode:tankNode];
SCNNode *turretNode = [tankNode childNodeWithName:#"Turret" recursively:YES];
turretNode.physicsBody = [SCNPhysicsBody staticBody];
SCNNode *gunNode = [turretNode childNodeWithName:#"gun" recursively:YES];
gunNode.physicsBody = [SCNPhysicsBody staticBody];
SCNPhysicsHingeJoint *gunJoint = [SCNPhysicsHingeJoint jointWithBodyA:turretNode.physicsBody axisA:SCNVector3Make(0,0,1) anchorA:SCNVector3Make(0.1,0.1,0.1) bodyB:gunNode.physicsBody axisB:SCNVector3Make(0,0,1) anchorB:SCNVector3Make(0.1,0.1,0.1)];
[scene.physicsWorld addBehavior:gunJoint];
SCNPhysicsHingeJoint *turretJoint = [SCNPhysicsHingeJoint jointWithBodyA:turretNode.physicsBody axisA:SCNVector3Make(0,1,0) anchorA:SCNVector3Make(0.1,0.1,0.1) bodyB:tankNode.physicsBody axisB:SCNVector3Make(0,1,0) anchorB:SCNVector3Make(0.5,0.5,-0.1)];
[scene.physicsWorld addBehavior:turretJoint];

I think there are two problems:
you are are assigning a constraint between a node and one of its child node (turret and tank). This is unlikely to give the expected results since a child node position is relative to its parent (so let's say the constraint moves the parent node, it will also move the child node).
you are assigning a constraint between two static bodies (gun & turret) - this should have no effect

Im only guessing here but i'd imagine you are placing a joint inside tankNode's physics body due to the fact you have scaled the tankNode, but not its physics shape.
Remember scaling a SCNNode does not scale its physics shape.
If you need your tank to be scaled : ensure you scale the physics body as well -
SCNVector3 Scale=SCNVector3Make(0.003, 0.003, 0.003);
Then your physics shape should be scaled :
NSDictionary *options=[NSDictionary dictionaryWithObject:
[NSValue valueWithSCNVector3:Scale] forKey:SCNPhysicsShapeScaleKey];
Now create a physics body from the scaled shape :
SCNPhysicsShape *physicsGeometry=[SCNPhysicsShape shapeWithGeometry:tankNode.geometry options:options];
Finally add the physics shape to your tank
tankNode.physicsBody=[SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeDynamic shape:physicsGeometry];
Your physics body and Tank node are now the same size.
If you require physics on the cannon and turret, i.e. the cannon needs to collide, create its physics body the same way..
Your code would suggest that turret and gun are children of the tank. The best / easiest way is to parent the turret to the gun so the gun is a child of the turret, and then parent the turret to the tank. This way, moving the tank moves the gun, turret and tank, Moving the turret moves the turret and the gun, and moving the gun only moves the gun.
Also in your 3d authoring software don't forget to move the pivot to the required centre of rotation for each node (or set it using SCNNode.pivot property).
You won't even need to use joints at all in this circumstance if the parent / child hierarchy is correct :)
I hope this helps.

Related

Put a ball inside a rectangle trapped inside, with some velocity and restitution

In this scene:
let rectangle = SKShapeNode(rectangleOfSize: CGSize(300, 400))
rectangle.fillColor = UIColor.clearColor()
rectangle.strokeColor = UIColor.grayColor()
rectangle.strokeWidth = 10
self.addChild(rectangle)
I want to put a ball inside this rectangle, trapped inside, with some velocity and restitution, that will be bouncing and changing the vector direction when collides with one of the walls.
I am not 100% sure if you can do that with SKShapeNodes and physics bodies because the physics body will apply to the whole shape node (with center).
Since you want to make the rectangle visible you can could either make a SKSpriteNode subclass with 4 SKSpriteNodes sides and than shape them as a rectangle.
Alternatively you could just draw a rectangle border in your program of choice, just make sure the centre is nothing basically, and export as PNG. Than just create a SKSpriteNode with the image.
let rectangle = SKSpriteNode(imageNamed: "Rectangle")
let texture = SKTexture(imageNamed: "Rectangle")
rectangle.physicsBody = SKPhysicsBody(texture: texture, size: texture.size())
rectangle.physicsBody?.collisionBitMask = 1
Now if you have drawn the rectangle correctly it will only have a physics body on the 4 sides since you did not draw a centre.
Than you simply create your ball and give it a physics body like above, with the same collision bitMask. Position it in the centre of the rectangle and give it an impulse or something. It should bounce around as you wish without leaving the rectangle.
If you are unsure about physics bodies than you need to read a bit about it, e.g
http://www.techotopia.com/index.php/A_Swift_iOS_8_Sprite_Kit_Collision_Handling_Tutorial

Can I set anchor point of SKPhysicsJoint outside node?

Let's say I have two circles respectively at (0,0) and (0,1).
I have a SKPhysicsJoint between them and it is working good, now I want to separate them with a distance of 2 on runtime, meaning while physics are working in-game. How can I achieve this?
I've tried setting anchor points to (0,0) and (0,2) but something is bugged, although I see the joint it doesn't have any affect.
I want the circles smoothly push each other, as if the length of the spring has increased.
Everything works if I make a circle 'teleport' to a distance of 2 and then anchor the spring to it, but making a physics object teleport cause other bugs as you can guess.
Before adding the joint I 'teleported' the second object to the desired position, then added the joint and then 'teleported' back to the original position.
Here is the code piece:
SKSpriteNode* node1 = [_bodies objectAtIndex:loop];
SKSpriteNode* node2 = [_bodies objectAtIndex:loop-1];
CGPoint prev1 = node1.position;
CGPoint prev2 = node2.position;
node1.position = [((NSValue*)[positions objectAtIndex:loop]) CGPointValue];
node2.position = [((NSValue*)[positions objectAtIndex:loop-1]) CGPointValue];
[self AttachPoint:node1 secondPoint:node2 pos1:node1.position pos2:node2.posiiton] ;
node1.position = prev1;
node2.position = prev2;
it is working as it is, but I'm not sure how efficient this is. I wish SKPhysicsJointSpring had a 'length' parameter that can be changed over time.

Block Won't Fall After Initial Gravity Effects End

I have a simple scene there is a floor, block, camera and light.
The floor is static
SCNNode* floor = [SCNNode node];
SCNPhysicsBody *staticBody = [SCNPhysicsBody staticBody];
floor.physicsBody = staticBody;
[[scene rootNode] addChildNode:floor];
The block is dynamic
SCNNode *block = [SCNNode node];
block.position = SCNVector3Make(-10, 45, -20)
block.geometry = [SCNBox boxWithWidth:5 height:5 length:5 chamferRadius:0];
block.physicsBody = [SCNPhysicsBody dynamicBody];
The block's starting position is above the floor, so when the app runs, the block falls to the floor as expected.
I have added motion manager that will move the floor down, simulating the floor being pulled out from under the block by translating negative changes in Y to the block and this is where the trouble begins.
if(userAcceleration.y < 0)
{
SCNVector3 vector = floor.position;
vector.y += userAcceleration.y*10.0;
floor.position = vector;
}
If the motion occurs before the block has come to rest, I can shake the device and can keep the block moving around as expected.
However, if the motion occurs after the block has come to a rest for a few seconds, the block will only rise (higher and higher with each motion) above the floor never to fall again.
Why does the gravity affect appear to stop?
Is there something that turns off gravity that I need to checking during the motion check? or is there something else I am missing?
It might sound silly, but have you set the playing property of your renderer to true?
If that doesn't work, my hack is to add an empty SCNNode and make it rotate forever with a SCNAction. It keeps the scene rendering/calculations going even if nothing is moving.
EDIT: I forgot about the allowResting property. It's probably what is causing your problem. Just turn it off for your object!
You can read more about it here: https://developer.apple.com/library/prerelease/ios/documentation/SceneKit/Reference/SCNPhysicsBody_Class/index.html#//apple_ref/occ/instp/SCNPhysicsBody/allowsResting

Swift Spritekit dissipating Gravity field

I have a game in swift using spritekit. If you tap the screen it will create a radial gravity field and pull all the other objects in. I create the gravity field like so
var fieldNode = SKFieldNode.radialGravityField();
fieldNode.falloff = 0.5;
fieldNode.strength = 1;
fieldNode.animationSpeed = 0.5;
It works but my problem is that I only want a sprite to be affected only when it is when it within a certain distance to the centre of the radial gravity, and i will have more than 1 sprite. The way I see it is that there are 2 ways to do it, 1. When a sprite is too far turn off the radial gravity for that sprite or 2. Make the radial gravity dissipate after a certain radius. There is also an overall gravity for the scene.
So the main question is:
How can I either turn off 1 gravity for a sprite OR make a radial gravity dissipate ?
A field node's region property determines its area of effect. The associated SKRegion object lets you define a circular region by its radius.
You can also use the fieldBitMask on a physics body and the categoryBitMask on a field to selectively control which fields affect which bodies.
Try using:
let radius: CGFloat = 1000.0
gravityField.strength = Float(pow(radius, 2)) * pow(10, -3)

Edge based SKPhysicsBody moves over time

I am using the following code to put a floor in my SpriteKit based game:
var floorNode = SKSpriteNode()
var floorBody = SKPhysicsBody(edgeFromPoint: CGPointMake(self.frame.minX, self.frame.minY), toPoint: CGPointMake(self.frame.maxX, self.frame.minY))
floorNode.physicsBody = floorBody
addChild(floorNode)
This places a floor at the bottom of the screen as expected. However as I add more things to the scene, one end of the floor eventually sinks down and everything in the scene that is resting on it slides off into the abyss.
I am at a complete loss here since Apple's documentation says that "an edge-based body does not have mass or volume, and is unaffected by forces or impulses in the system. Edge-based bodies are used to represent volume-less boundaries or hollow spaces in your physics simulation."
Anyone else seen this kind of behavior?
The easiest way to define floor for your scene, without adding any nodes to it:
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
where self is your SKScene.
If you don't want ceiling on the scene, define physicsBody as this:
[SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(self.frame.origin.x,
self.frame.origin.y,
self.frame.size.width,
self.frame.size.height*5)];

Resources