Change SCNScene's sceneNamed programmatically - ios

I am trying to change a current sceneNamed with code but it seems my method has some problems. First, the new scene will be changed but I have to touch or rate the object to changing process happens.Second, it seems childNodeWithName doesn't change at all ! Here is my code :
- (void)load3DObjectName:(NSString*)name nodeName:(NSString*)nodeName zPhone:(CGFloat)positioniPhone zPad:(CGFloat)positioniPad{
SCNScene * scene = [SCNScene sceneNamed:name];
// retrieve the ship node
SCNNode *trex = [scene.rootNode childNodeWithName:nodeName recursively:YES];
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
trex.position = SCNVector3Make(0, 0, positioniPhone);
} else {
trex.position = SCNVector3Make(0, 0, positioniPad);
}
_the3DScence.scene = scene;
_the3DScence.autoenablesDefaultLighting = YES;
_the3DScence.allowsCameraControl = YES;
_the3DScence.backgroundColor = [UIColor colorWithRed:0.92 green:0.92 blue:0.92 alpha:1.00];
}
// Load default object :
- (void)viewDidLoad {
[self load3DObjectName:#"cube.dae" nodeName:#"cube1" zPhone:-40 zPad:-30];
}
//Trying to change the 3D object with button:
- (IBAction)nextObject:(id)sender {
[self load3DObjectName:#"redCube.dae" nodeName:#"cube2" zPhone:-40 zPad:-30];
}
- (IBAction)changeIt:(id)sender {
[self load3DObjectName:#"dayere.dae" nodeName:#"Sphere" zPhone:-40 zPad:-40];
}
Here is a source code :
https://www.dropbox.com/s/rrvbxmrb9wcrnoj/3D%20Objects%20Change.zip?dl=0
The code in the Dropbox version is not what I posted above. Here is the Dropbox version:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self load3DObjectName:#"cube.dae" nodeName:#"Cube" zPhone:-40 zPad:-40];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)load3DObjectName:(NSString*)name nodeName:(NSString*)nodeName zPhone:(CGFloat)positioniPhone zPad:(CGFloat)positioniPad{
SCNScene * scene = [SCNScene sceneNamed:name];
// retrieve the ship node
SCNNode *node = [scene.rootNode childNodeWithName:nodeName recursively:YES];
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) {
node.position = SCNVector3Make(0, 0, positioniPhone);
}
else {
node.position = SCNVector3Make(0, 0, positioniPad);
}
_the3DView.scene = scene;
_the3DView.autoenablesDefaultLighting = YES;
_the3DView.allowsCameraControl = YES;
_the3DView.backgroundColor = [UIColor colorWithRed:0.92 green:0.92 blue:0.92 alpha:1.00];
}
- (IBAction)changeIt:(id)sender {
[self load3DObjectName:#"dayere.dae" nodeName:#"Sphere" zPhone:-40 zPad:-40];
}

So I have take a look at your project. here is the issue: you don't have camera in you scenes. So I put camera for your each scenes manually at the same distance, and moved the nodes as desired. here is what it looks like now:
- (void)load3DObjectName:(NSString*)name nodeName:(NSString*)nodeName zPhone:(CGFloat)positioniPhone zPad:(CGFloat)positioniPad
{
SCNScene * scene = [SCNScene sceneNamed:name];
SCNNode *node = [scene.rootNode childNodeWithName:nodeName recursively:YES];
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
{
node.position = SCNVector3Make(0, 0, positioniPhone);
} else {
node.position = SCNVector3Make(0, 0, positioniPad);
}
SCNCamera *cam = [[SCNCamera alloc] init];
cam.xFov = 35;
cam.yFov = 35;
cam.zFar = 5000;
cam.zNear = 0;
SCNNode *camNode = [[SCNNode alloc] init];
camNode.camera = cam;
camNode.position = SCNVector3Make(0, 0, 400);
[scene.rootNode addChildNode:camNode];
_the3DView.pointOfView = camNode;
_the3DView.scene = scene;
_the3DView.autoenablesDefaultLighting = YES;
_the3DView.allowsCameraControl = YES;
_the3DView.backgroundColor = [UIColor colorWithRed:0.92 green:0.92 blue:0.92 alpha:1.00];
}
and I also changed the your object positions for testing purposes:
- (IBAction)changeIt:(id)sender
{
[self load3DObjectName:#"dayere.dae" nodeName:#"Sphere" zPhone:-340 zPad:-340];
}
and this one as well:
- (void)viewDidLoad
{
[super viewDidLoad];
[self load3DObjectName:#"cube.dae" nodeName:#"Cube" zPhone:-40 zPad:-40];
}
and last but not least, the screen shots:

It looks to me like your second round of code works fine. I notice that you're loading different scenes/nodes in the Dropbox sample than you are in the code you posted first. So that makes me think there's something in your DAE file that isn't what you expect it to be. Check for node names that are missing or misspelled. Add NSAssert calls after your childNodeWithName: and sceneNamed: calls, to make sure that you really did successfully load the scene and find the child node.
SCNScene * scene = [SCNScene sceneNamed:name];
NSAssert(scene, #"failed to load scene named %#", name);
// retrieve the ship node
SCNNode *node = [scene.rootNode childNodeWithName:nodeName recursively:YES];
NSAssert(node, #"couldn't fine child node named %#", nodeName);
If the NSAsserts fail, use your 3D editing tool to fix your Collada file.
Note also that when you replace the3DView.scene (aside: please don't bang instance variables directly, use accessor methods instead!), you're replacing the camera and lighting too. So you should expect a jerk in the camera positioning. Maybe you want to animate this? Or remove/replace nodes for the objects, without messing with camera and lighting?
When I run the code you posted to Dropbox, I see a screen full of red. When I zoom away, and rotate the camera, it looks like this:
After I tap the "Button" button, I see this:

Related

SceneKit Crash when move finger change scncamrea's position quickly

iPhoen8 Plus,iOS 11(15A372), Xcode9(9A235)
I load a scn file to show the 3D object.This is my code.This modelView will
as subView on a UIView, the UIView is the subView on UIScrollView.When I touch and move my finger, scnview's object will do some rotation. If I move quickly, it crashed.
// ModelView.m : SCNView
+ (instancetype)modelWithxxx {
ModelView *model = [[ModelView alloc] initWithFrame:MODELVIEWRECT];
model.scene = [SCNScene sceneNamed:#"xxx.scn"];
return model;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor clearColor];
self.allowsCameraControl = YES;
self.autoenablesDefaultLighting = YES;
}
return self;
}
It looks like ScnView's bug, I don't know what happen, I just load the .scn file and use cameracontrol.

Anti-gravity physics in SceneKit

I am trying to make a simple app where if you tap the screen a box is switched from floating to affected by gravity. I can't seem to find a way to make the box float in the air though.
This code here takes care of half the problem:
boxNode.physicsBody = [SCNPhysicsBody dynamicBody];
This causes the box to drop out of the air and hit a floor I created. Is there anything in SCNPhysicsBody that would do the opposite of this? Say, perhaps, cause objects to float or just sail off toward a ceiling?
Also, I've written this code:
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer {
if (myBool == false) {
myBool = true;
NSLog(#"true");
} else {
myBool = false;
NSLog(#"false");
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// touch recognizer
UITapGestureRecognizer *screenTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[self.view addGestureRecognizer:screenTap];
// create box
SCNBox *myBox = [SCNBox boxWithWidth:1.0 height:1.0 length:1.0 chamferRadius:0.1];
SCNNode *boxNode = [SCNNode nodeWithGeometry:myBox];
boxNode.position = SCNVector3Make(0.0, 0.0, 4.0);
[myScene.rootNode addChildNode:boxNode];
while (myBool == true) {
boxNode.physicsBody = [SCNPhysicsBody dynamicBody];
}
}
I'm not sure why the while loop doesn't work though. I was thinking it would detect that myBool has been changed and alter the physics of the boxNode, but it doesn't.
The viewDidLoad method is only called once when the view is loaded. If your app is initialised with myBool = false, then the while loop will never be run. However in this case if myBool was true the while loop would never stop executing, preventing the view from loading, and therefore preventing the user from tapping the view to trigger your gesture recogniser.
I haven't tested the below, but it should at least give you a starting point. The scene is created in the viewDidLoad as per your code, importantly the scene's physicsWorld has its gravity set to zero (this is -9.8 by default). Later on when the user taps the view we reset the gravity to its default value, this should cause the box to fall.
The header file GameViewController.h
#import <UIKit/UIKit.h>
#import <SceneKit/SceneKit.h>
#interface GameViewController : UIViewController {
SCNScene *myScene;
}
#end
and GameViewController.m
#import "GameViewController.h"
#implementation GameViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// create a new scene
myScene = [SCNScene scene];
// create box
SCNBox *myBox = [SCNBox boxWithWidth:1.0 height:1.0 length:1.0 chamferRadius:0.1];
SCNNode *boxNode = [SCNNode nodeWithGeometry:myBox];
boxNode.position = SCNVector3Make(0.0, 0.0, 4.0);
[myScene.rootNode addChildNode:boxNode];
boxNode.physicsBody = [SCNPhysicsBody dynamicBody];
//'disable' scene gravity
myScene.physicsWorld.gravity = SCNVector3Make(0, 0, 0);
SCNView *scnView = (SCNView *)self.view;
scnView.scene = myScene;
scnView.allowsCameraControl = YES;
scnView.autoenablesDefaultLighting = YES;
scnView.backgroundColor = [UIColor blackColor];
// add a tap gesture recognizer
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
NSMutableArray *gestureRecognizers = [NSMutableArray array];
[gestureRecognizers addObject:tapGesture];
[gestureRecognizers addObjectsFromArray:scnView.gestureRecognizers];
scnView.gestureRecognizers = gestureRecognizers;
}
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer {
myScene.physicsWorld.gravity = SCNVector3Make(0, -9.8, 0);
}
#end

Bonbons falling continuously from random positions

I have got the following problem in SpriteKit: My aim is to let bonbons fall continuously from the top of the screen. These bonbons can be collected by the player. Every 0.8 seconds or so, another bonbon should fall down and when the player collides with one of the bonbons, it should disappear. This is my code:
-(void)populate {
for (int i = 0; i < 2; i++) {
[self generate];
}
}
-(void)generate {
Y = (arc4random() % 280) - 140;
bonbon = [SKSpriteNode spriteNodeWithImageNamed:#"Bonbon.png"];
bonbon.size = CGSizeMake(20, 20);
bonbon.name = #"bonbon";
bonbon.physicsBody.categoryBitMask = bonbonCategory;
bonbon.position = CGPointMake(Y, 500);
bonbon.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:bonbon.size.width/2];
bonbon.physicsBody.dynamic = YES;
[bonbon addChild:bonbon];
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
if ([contact.bodyA.node.name isEqualToString: #"wallLeft"] || [contact.bodyB.node.name isEqualToString: #"wallLeft"])
{
[hero.physicsBody applyImpulse: CGVectorMake(100, 60)];
self.jumpDirection = YES;
}
else if ([contact.bodyA.node.name isEqualToString: #"bonbon"] || [contact.bodyB.node.name isEqualToString: #"bonbon"])
{
[world enumerateChildNodesWithName:#"bonbon" usingBlock:^(SKNode *node, BOOL *stop) {
PointsLabel *pointsLabel = (PointsLabel *)[self childNodeWithName:#"pointsLabel"];
[pointsLabel increment];
NSLog(#"didContactBonbon");
[bonbon removeFromParent];
NSLog(#"removedFromParent");
}];
}
else {
[hero.physicsBody applyImpulse: CGVectorMake(-100, 60)];
self.jumpDirection = NO;
}
}
I hope you understand my problem. Currently, no bonbons are falling at all. If you need more information, please do not hesitate to ask.
Greets
edit: I hope the formatting is better now. I am quite a newbie, i'm sorry for foolish mistakes :) This is in my MyScene.m, when is call the populate in the initWithSize method with [self populate]; I receive the message signal SIGABRT. What did I do wrong?
I think the problem is that you are adding your SKSpriteNode named bonbon to itself with :
- (void)generate {
...
bonbon = [SKSpriteNode spriteNodeWithImageNamed:#"Bonbon.png"];
...
[bonbon addChild:bonbon];
}
Try to add bonbon to your SKScene instance.
To use an SKView instance you have to create a UIViewController and give its view the SKView class. You can do that in your storyboard.
Then, in your viewController :
#implementation GameViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
// Configure the scene
SKScene *scene = [SKScene sceneWithSize:self.view.bounds.size];
// You can add objects to your scene
[scene addChild:bonbon];
// Present the scene.
[skView presentScene:scene];
}
#end
This is a very simple example but if you want more information, I suggest you read this tutorial: Sprite Kit Tutorial for Beginners.

How can i make a node collide with the sides of moving scene?

Hi i wish to know how can my nodes can collide with a moving scene. I have a main node that cans move along the x axis and cans collide with other nodes. I want to make the non main nodes collide with the sides of the moving scene. How can i make that ? ( the camera only follow the main node).
I have an obstacle class.
in my MLObstacle.m
-(id)init
{
if(self = [super init])
{
int aux = arc4random()%3;
switch (aux) {
case 0:
self = [MLObstaculo spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake((aux+1)*10,(aux+1)*13 )];
break;
case 1:
self = [MLObstaculo spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake((aux+1)*10,(aux+1)*15 )];
break;
case 2:
self = [MLObstaculo spriteNodeWithColor:[UIColor redColor] size:CGSizeMake((aux+1)*10,(aux+1)*14 )];
break;
}
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.affectedByGravity = NO;
self.physicsBody.categoryBitMask = obstacleCategory;
self.physicsBody.contactTestBitMask = visibleSidesCategory;
self.physicsBody.collisionBitMask = visibleSidesCategory;
[self setDx:0.0];
[self setDy:2.4];
}
return self;
}
So i add Obstacles to my WorldClass
In my WorlGen.m
-(void)GenerateWorld
{
SKSpriteNode *Ground = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(self.scene.frame.size.width,100)];
Ground.position = CGPointMake(0, -self.scene.frame.size.height/2 + Ground.size.height/2);
Ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Ground.size];
Ground.physicsBody.dynamic = NO;
[self addChild:Ground];
for(int i = 0; i < 3; i++)
{
MLObstaculo * auxobs= [[MLObstaculo alloc]initObstaculo];
auxobs.position = CGPointMake((i+1)*50, Ground.position.y + Ground.size.height/2 +auxobs.size.height/2 );
[self.Obstaculos addObject:auxobs];
[self addChild:auxobs];
auxobs = NULL;
}
}
and then i add the world to my scene
Scene.m
-(id)initWithSize:(CGSize)size
{
if(self = [super initWithSize:size]
{
WorldGen * world = [[WorldGen alloc] init];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsWorld.contactDelegate = self;
self.physicsBody.categoryBitMask = VisibleSidesCategory
self.physicsBody.collisionBitMask = ObstacleCategory;
self.anchorPoint = CGPointMake(0.5,0.5);
[self addChild:world];
[world GenerateWorld];
}
}
Update...
-(void)didSimulatePhysics
{
[self centerOnNode:hero];
}
-(void)centerOnNode:(SKNode *)node
{
CGPoint pointinScene = [self convertPoint:node.position fromNode:node.parent];
world.position = CGPointMake(world.position.x -pointinScene.x , world.position.y);
}
I initalized all the category variables with this format ( static const uint32_t ObstacleCategory = 0x1 <<1; )
First edit by defining your collisionBitMask
-(id)init
{
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.size];
self.physicsBody.categoryBitMask = ObstacleCategory;
self.physicsBody.contactTestBitMask = VisibleSidesCategory;
self.physicsBods.collisionBitMask = VisibleSidesCategory;
}
Second make this steps in Xcode -> File -> New -> File -> C and C++ -> Header File
Give a name of Physics.h and add those lines in it:
typedef enum : uint8_t {
ObstacleCategory = 1,
VisibleSidesCategory = 2,
} ColliderType;
and then import Physics.h into all your classes (You don't need to alloc or init for Physics.h, just use ObstacleCategory and VisibleSidesCategory)
From this and another question's problem, I have finally determined there is an error in the API. Specifically, a node's anchorPoint is considered (0,0) during initialization if using a WithRect: or FromRect: based initializer(but not a RectOfSize: or center: based one), so you get unexpected behavior.
Thus, even when the anchorPoint of the Scene and other objects are set to (0.5,0.5), a node places its lower left corner relative to the parent node's midpoint, rather than placing the child's midpoint relative to the parent node's midpoint.
Specifically, this can be solved in any one of these three ways:
Use an anchorPoint of (0,0).
Create your nodes without using Rect based initializers. You can use shapeNodeWithRectOfSize: instead of shapeNodeWithRect: and bodyWithEdgeLoopFromPath: instead of bodyWithEdgeLoopFromRect:
Set the Rect's initial position as lower and to the left, like so:
node.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(-node.size.width/2, -node.size.height/2, node.size.width, node.size.height)];

isgl3d pod file did not show in iPad

I am trying to import POD file 3d object(export from Blender using) into my ipad using ISGL3D frameworks. I did not get any error but my ipad only show black screen. I try to debug line by line and it seems like is the camera problem.
Here are my code in HelloWorldView:
- (id) init {
if ((self = [super init])) {
container = [[self.scene createNode] retain];
// Import pod data
_podImporter = [Isgl3dPODImporter podImporterWithFile:#"ee.pod"];
Isgl3dLight * light = [Isgl3dLight lightWithHexColor:#"000000" diffuseColor:#"FFFFFF" specularColor:#"FFFFFF" attenuation:0.001];
light.position = iv3(0, 0, 2);
light.renderLight = YES;
[container addChild:light];
///Problem seems to start from below
[self.camera removeFromParent];
self.camera = [_podImporter cameraAtIndex:0];
[self.scene addChild:self.camera];
role01 = [_podImporter meshNodeWithName:#"Sphere"];
[vound addChild:role01];
[self schedule:#selector(tick:)];
}
return self;}
I tried to add just the 3d object with out the _podImporter camera and I get exception that it cannot locate my 3d object. Please help and thanks!
Took me a while to find out the problem:
I missed out this code:
[_podImporter buildSceneObjects];
So the proper code to get podImporter to work is
- (id) init {
if ((self = [super init])) {
container = [[self.scene createNode] retain];
// Import pod data
_podImporter = [Isgl3dPODImporter podImporterWithFile:#"ee.pod"];
[_podImporter buildSceneObjects];///<--- Put it here or anywhere before the camera code
Isgl3dLight * light = [Isgl3dLight lightWithHexColor:#"000000" diffuseColor:#"FFFFFF" specularColor:#"FFFFFF" attenuation:0.001];
light.position = iv3(0, 0, 2);
light.renderLight = YES;
[container addChild:light];
[self.camera removeFromParent];
self.camera = [_podImporter cameraAtIndex:0];
[self.scene addChild:self.camera];
role01 = [_podImporter meshNodeWithName:#"Sphere"];
[vound addChild:role01];
[self schedule:#selector(tick:)];
}
return self;}

Resources