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.
Related
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;
I have a SceneKit game that I'm developing. In iOS 9, when SceneKit's depth of field is turned on, SKNodes on the 2D Overlay scene are blurred.
This code even blurs the 2D overlay node, but none of the others:
// focal blur.
[DDCamera sharedCamera].camera.zNear = 1;
[DDCamera sharedCamera].camera.zFar = 100;
[DDCamera sharedCamera].camera.focalDistance = 25;
[DDCamera sharedCamera].camera.focalSize = 25;
[DDCamera sharedCamera].camera.focalBlurRadius = 50;
DDCamera is a subclass of SCNNode that allows other classes to manipulate the camera. Its position is (0, 0, 25).
I put in a SKNode on the overlay scene, like this:
title = [SKLabelNode labelNodeWithText:[DDCommonFunctions getLocalizedStringFromKey:#"title"]];
title.fontColor = [UIColor whiteColor];
title.fontName = DDDefaultFont;
title.fontSize = 100.;
title.position = CGPointMake([DDCommonFunctions getScreenSize].width * .5, [DDCommonFunctions getScreenSize].height * 0.75);
title.name = #"title";
title.alpha = 1.;
title.zPosition = 0.;
[self addNode:title];
Every other node I put into the scene isn't blurry, but the only SKNode there is blurred. Why is this?
Also, why didn't this behavior occur in iOS 8.4.1?
EDIT: a screenshot:
EDIT 2: It works as expected in OpenGL. Why not Metal? How would I get the camera to focus on the 2D scene?
In my app I load models from different files (format is the same) and they have different geometry: big, small, wide, etc. I have object and camera position hard coded and for some cases I don't see anything because camera not point to object.
Maybe there is a way to normalise model before adding it to scene.
Update.
With Moustach answer I came up to following solution:
// import object from file
SCNNode *object = [importer load:path];
object.position = SCNVector3Make(0, 0, 0);
[scene.rootNode addChildNode:object];
// create and add a camera to the scene
SCNNode *cameraNode = [SCNNode node];
cameraNode.camera = [SCNCamera camera];
// to avoid view clipping
cameraNode.camera.automaticallyAdjustsZRange = YES;
// set camera position to front of object
SCNVector3 sphereCenter;
CGFloat sphereRadius;
[object getBoundingSphereCenter:&sphereCenter radius:&sphereRadius];
cameraNode.position = SCNVector3Make(sphereCenter.x, sphereCenter.y, sphereCenter.z + 2 * sphereRadius);
[scene.rootNode addChildNode:cameraNode];
Works well for me.
You can calculate the bounding box of your mesh, and scale it based on the number you get to make it the same size as other objects.
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];
I have the following code:
in my scene:
static const uint32_t enermyCategory = 0x1 << 0;
static const uint32_t fatherCategory = 0x1 << 1;
self.physicsWorld.contactDelegate = self;
//init ship
Ship *ship = [Ship getFather];
ship.position = CGPointMake(CGRectGetMaxX(self.frame) - ship.frame.size.width , ship.frame.size.height);
[self addChild: ship];
//init enermy
Enermy *ene = [[Enermy alloc] initWithImageNamed:enermyName gameScene:self];
ene.position = ship.position;
[self addChild:ene];
#pragma mark - Physics Delegate Methods
- (void)didBeginContact:(SKPhysicsContact *)contact{
NSLog(#"contact detected");
}
As you can see, I set both ship and energy at the same location to start with, so they will always collide.
For both classes, I have the following code in their init method:
//setup physics body
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.dynamic = NO;
self.physicsBody.categoryBitMask = enermyCategory; #shipCategory for ship
self.physicsBody.collisionBitMask = 0;
self.physicsBody.contactTestBitMask = shipCategory; #enermyCategory for ship
What I found is that the NSLog is never get called, so that the physical collision detection never works, I've read a lot from apple developer tutorials and it seems all the same of what they had, not sure why is not working. I can see the ship and energy images on screen collide each other.
self.physicsBody.dynamic = NO;
Static (non-dynamic) bodies do not generate contact events. Make them dynamic.
Also, I see you're passing gameScene to an instance of Enemy. If Enemy has a strong reference to game scene (an ivar) then you may be creating a retain cycle here (enemy retains scene, scene retains enemy).
In Sprite Kit you can simply use self.scene to access the scene and if you need the scene during init, move that code into a setup method and call [ene setup] right after adding it as child to perform setup steps involving the scene.
Having the physicsBody set to dynamic = NO doesn't affect contacts. The more likely case is that you are setting the same contactBitMask and categoryBitMask for both nodes. You should be setting the ship to have the following:
self.physicsBody.categoryBitMask = shipCategory;
self.physicsBody.collisionBitMask = 0;
self.physicsBody.contactTestBitMask = enermyCategory;
The enemy should have the following:
self.physicsBody.categoryBitMask = enermyCategory;
self.physicsBody.collisionBitMask = 0;
self.physicsBody.contactTestBitMask = shipCategory;
Bottom line: they should be swapped in the ship.