Physics forces not affecting dynamic body Scenekit - ios

My scene contains;
A static body at the root of the scene
_firstNode = [[SCNNode alloc] init];
_firstNode.position = SCNVector3Make(0, 0, 0);
_firstNode.geometry = [SCNSphere sphereWithRadius:950];
_firstNode.geometry.firstMaterial.diffuse.contents = [UIColor greenColor];
_firstNode.physicsBody = [SCNPhysicsBody staticBody];
[[scene rootNode] addChildNode:_firstNode];
A dynamic body with it's pivot property at (0,-1000,0) with a SCNAction that permanently rotates it around the static body.
_secondNode = [[SCNNode alloc] init];
_secondNode.position = SCNVector3Make(0, 0, 0);
_secondNode.pivot = SCNMatrix4MakeTranslation(0, -1200, 0);
_SecondNode.physicsBody = [SCNPhysicsBody dynamicBody];
_secondNode.geometry = [SCNSphere sphereWithRadius:2];
_secondNode runAction:[SCNAction repeatActionForever:[SCNAction rotateByX:-1 y:0 z:0 duration:10.0]]];
[[scene rootNode] addChildNode:_secondNode];
When I try apply;
[_secondNode.physicsBody applyTorque:SCNVector4Make(0, 1, 0, 1) impulse:YES];
There is no effect on the node as if it was declared as a static body.

If I am reading what you want correctly, set your second node as so...
_secondNode.position = SCNVector3Make(0,-1200,0);
This will off set it from the firstNode
Then set its pivot point like so...
_secondNode.pivot = _firstNode.pivot;
This sets the pivot to the same spot as your firstNode and allows it to rotate around that point. This will only work as long as your firstNode pivot property has not been changed.
Lastly set your action like so...
SCNAction rotateByX:M_PI*2 y:0 z:0 duration:10.0
Noticed I changed your x setting to M_PI*2 which is equal to 360 degrees. You can use "-M_PI" to rotate the opposite way.

For physics to simulate the node needs a physicsShape assigned, which is achieved with;
_secondNode.physicsBody.physicsShape = [SCNPhysicsShape shapeWithGeometry:[SCNSphere sphereWithRadius:2] options:nil];

Related

SceneKit nodesInsideFrustumWithPointOfView: unexpectedly returning empty

I am using the following method to determine which SCNNodes are visible by the camera.
[self.scnView nodesInsideFrustumWithPointOfView:cameraNode];
However the returned array is always empty.
I set up the scene up as follows:
-(void)setupScene{
scene = [SCNScene scene];
cameraNode = [SCNNode node];
cameraNode.camera = [SCNCamera camera];
[scene.rootNode addChildNode:cameraNode];
cameraNode.position = SCNVector3Make(0, 0, 0);
[scene.rootNode addChildNode:cameraNode];
self.scnView.scene = scene;
self.scnView.showsStatistics = YES;
self.scnView.backgroundColor = [UIColor clearColor];
}
At a random time, after the scene is created, I add a SCNNode to the scene:
testnode = [Testnode createNode];
testnode.position = SCNVector3Make(0, 0, -10);
[self.scnView.scene.rootNode addChildNode:testnode];
On my device, the node "testnode" is visible on my screen yet nodesInsideFrustumWithPointOfView: returns nothing.
EDIT: I tried changing the point of view to a spot light object and test whether "testnode" is inside its frustum. Here is what I see on screen: http://imgur.com/a/C3XGu Yet the array still returns empty. The testnode is the white cube.
Adding this for better visibility. Thank Crashalot for this.
Basically, if the SCNNode you are trying to detect using [scnView nodesInsideFrustumWithPointOfView:] and [scnView isNodeInsideFrustum: withPointOfView:] is the child of a node with empty geometry, it won't be detected.
In my case, I added a plane geometry and set the material to transparent:
SCNNode *emptynode = [SCNNode node];
node.geometry = [SCNPlane planeWithWidth:1.0f height:2.0f];
node.geometry.firstMaterial.transparency = 0.0f;

How to prevent the camera from entering a SCNNode - Scenekit iOS

I have a problem whit the camera in SceneKit, by default i can move, rotate, zoom my camera whit this line :
myView.allowsCameraControl = YES;
But my camera can pass through walls and floor (which allows me to see the underside of the stage ) .
My first request: is it possible to apply constraints to the camera (position, rotation) ?
My second request: I thought making a cube encompassing stage and do my collision detection between my cube and my camera, but it does not work ...
code for viewDidLoad:
SCNView *myView = (SCNView *)self.view3D;
myView.scene = [SCNScene sceneNamed:#"art.scnassets/Pointe Marrin 3 def 3 def 2.dae"];
myView.scene.physicsWorld.contactDelegate = self;
myView.scene.physicsWorld.gravity = SCNVector3Make(0, 0, 0);
cubeLimite = [myView.scene.rootNode childNodeWithName:#"Cube" recursively:YES];
camera = [myView.scene.rootNode childNodeWithName:#"Caméra" recursively:YES];
cubeLimite.physicsBody = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeDynamic shape:nil];
cubeLimite.physicsBody.mass = 0;
cubeLimite.categoryBitMask = SCNPhysicsCollisionCategoryDefault;
cubeLimite.physicsBody.collisionBitMask = SCNPhysicsCollisionCategoryAll;
//test camera
CGFloat boxSide = 0.001;
SCNBox *box = [SCNBox boxWithWidth:boxSide
height:boxSide
length:boxSide
chamferRadius:0];
SCNNode *boxNode = [SCNNode nodeWithGeometry:box];
SCNPhysicsShape *shape = [SCNPhysicsShape shapeWithGeometry:boxNode.geometry options:nil];
camera.physicsBody = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeDynamic shape:shape];
camera.physicsBody.mass = 0;
camera.categoryBitMask = SCNPhysicsCollisionCategoryDefault;
camera.physicsBody.collisionBitMask = SCNPhysicsCollisionCategoryAll;
Code for delegate :
- (void)physicsWorld:(SCNPhysicsWorld *)world
didBeginContact:(SCNPhysicsContact *)contact{
NSLog(#"Contact debut");
}
- (void)physicsWorld:(SCNPhysicsWorld *)world
didUpdateContact:(SCNPhysicsContact *)contact{
NSLog(#"Contact milieu");
}
- (void)physicsWorld:(SCNPhysicsWorld *)world didEndContact:(SCNPhysicsContact *)contact{
NSLog(#"Contact fin");
}
The delegate :
SCNPhysicsContactDelegate
is declared in the .h file
Ideas ?
thank you beforehand
It's not possible, as far as I know, to constrain the default camera when allowsCameraControl is enabled.
The answer to Rotate SCNCamera node looking at an object around an imaginary sphere outlines an approach that might work for you, depending on what constraints you need to build.

physics body not inline with shape?

for some reason when i apply a physics body around the skshapenode it places the physics body more to the left and downwards of the actual shape even though the parameters of the physics body comes from the shape. does anyone know why? thanks.
-(SKShapeNode*) createGround1
{
//create a rectangle
ground1 = [SKShapeNode shapeNodeWithRect:CGRectMake(0, 0, 300, 19)];
ground1.position = CGPointMake(800, 500);
//applies physics to rectangle
ground1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize: CGSizeMake(ground1.frame.size.width, ground1.frame.size.height)];
ground1.physicsBody.allowsRotation = false;
ground1.physicsBody.dynamic = false;
ground1.physicsBody.restitution = 0.0;
return ground1;
}
This happens because the anchor points are different. You need to define a center point for your physics body like this:
ground1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(ground1.frame.size.width, ground1.frame.size.height) center:CGPointMake(150, 9.5)];

Node moving when pivot is inside the geometry of another node

My scene contains;
A static body at the root of the scene
_firstNode = [[SCNNode alloc] init];
_firstNode.position = SCNVector3Make(0, 0, 0);
_firstNode.geometry = [SCNSphere sphereWithRadius:950];
_firstNode.geometry.firstMaterial.diffuse.contents = [UIColor greenColor];
_firstNode.physicsBody = [SCNPhysicsBody staticBody];
[[scene rootNode] addChildNode:_firstNode];
A dynamic body with it's pivot property at (0,-1200,0)
_secondNode = [[SCNNode alloc] init];
_secondNode.position = SCNVector3Make(0, 0, 0);
_secondNode.pivot = SCNMatrix4MakeTranslation(0, -1200, 0);
_secondNode.physicsBody = [SCNPhysicsBody dynamicBody];
_secondNode.physicsBody.physicsShape = [SCNPhysicsShape shapeWithGeometry:[SCNSphere sphereWithRadius:2] options:nil];
[[scene rootNode] addChildNode:_secondNode];
Since both nodes are children of the rootNode the pivot is located in the centre of the static node. The idea is to run a SCNAction that rotates _secondNode around the _firstNode about it's X axis and applyTorque as needed to rotate about it's Y axis.
That code is fine for the moment, the problem is that while _firstNode remains static, it is pushing _secondNode.pivot to the surface of _firstNode's geometry. However simply taking around _firstNode's physicsBody away doesn't solve the problem either.
Sounds like it is getting too much friction as it is trying to spin. Try setting your angularDamping down to 0. Its default is 0.1. The angularDamping simulates the amount of friction as an object spins. A value of 0 simulates no friction (spin for ever). A value of 1.0 simulates 100% friction or static basically.
Set it like so...
yourNode.physicsBody.angularDamping = 0

Why won't this SKPhysicsJointPin keep these 2 sprites together?

I clearly don't understand the SKPhysicsJoint very well, but there is so little info on the web yet, other than the Apple docs of course. What is wrong with the following code, which I would think should keep the head and neck permanently joined - my intention is that they act like 2 pieces of paper with a pin, so that they can rotate a bit, but not just totally come apart. When I run this code, they fall to the bottom of the SKScene they're in, hit the ground, then the head falls off the body.
Maybe the joint is not moving WITH them or something, it's just staying in place while they move??
self.head = [SKSpriteNode spriteNodeWithImageNamed:#"head.png"];
self.head.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.head.size];
self.head.physicsBody.mass = 0.05;
self.head.physicsBody.dynamic = YES;
self.chest = [SKSpriteNode spriteNodeWithImageNamed:#"chest_neck"];
self.chest.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.chest.size];
self.chest.physicsBody.mass = 0.05;
self.chest.physicsBody.dynamic = YES;
self.leftLeg = [SKSpriteNode spriteNodeWithImageNamed:#"left_leg"];
self.leftLeg.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.leftLeg.size];
self.leftLeg.physicsBody.mass = 10;
self.leftLeg.physicsBody.dynamic = YES;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
self.head.position = CGPointMake(282, 220);
self.chest.position = CGPointMake(282, 130);
self.leftLeg.position = CGPointMake(282, 10);
} else {
self.head.position = CGPointMake(512, 380);
self.chest.position = CGPointMake(512, 290);
self.leftLeg.position = CGPointMake(512, 10);
}
[self addChild:self.head];
[self addChild:self.chest];
[self addChild:self.leftLeg];
self.chestJointPinAnchor = CGPointMake(self.chest.position.x, self.chest.position.y+39);
self.chestJointPin = [SKPhysicsJointPin jointWithBodyA:self.head.physicsBody bodyB:self.chest.physicsBody anchor:self.chestJointPinAnchor];
[self.physicsWorld addJoint:self.chestJointPin];
This is because you set sprite's position after you set up its physicsBody property.
I haven't discovered any mention of that in documentation, but I broke my head last weekend trying to figure out why my manually created rope works, but recursively one doesn't.
SpriteNodes' position MUST be set before physicsBody.
So, just reorder your code somehow like that:
self.head = [SKSpriteNode spriteNodeWithImageNamed:#"head.png"];
self.chest = [SKSpriteNode spriteNodeWithImageNamed:#"chest_neck"];
self.leftLeg = [SKSpriteNode spriteNodeWithImageNamed:#"left_leg"];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
self.head.position = CGPointMake(282, 220);
self.chest.position = CGPointMake(282, 130);
self.leftLeg.position = CGPointMake(282, 10);
} else {
self.head.position = CGPointMake(512, 380);
self.chest.position = CGPointMake(512, 290);
self.leftLeg.position = CGPointMake(512, 10);
self.head.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.head.size];
self.head.physicsBody.mass = 0.05;
self.head.physicsBody.dynamic = YES;
self.chest.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.chest.size];
self.chest.physicsBody.mass = 0.05;
self.chest.physicsBody.dynamic = YES;
self.leftLeg.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.leftLeg.size];
self.leftLeg.physicsBody.mass = 10;
self.leftLeg.physicsBody.dynamic = YES;
}
Oh, I've noticed, you've already found an answer by yourself... Could you please mark your question as answered then.
That seems about right.
A pin joint allows both bodies to rotate around the joint's anchor point. A real world example is an analog clock. Or a bicycle's pedals. Or a car wheel's axle.
One thing you have to know is that bodies connected through a joint will not collide with each other. You can use the pin joint limits though to prevent the head from doing a full 360 spin around the pin joint anchor.
Okay, so I found out that this is an actual bug in Sprite Kit. The fix is to set the sprite's position before setting its physicsBody. I did that and it worked perfectly, as expected.
Additionally, not invalidating what was said above, but apparently the physics engine assumes the scene's anchorPoint is left at (0, 0). If you change it to something else, the physics engine will still treat it like (0, 0).

Resources