SceneKit Crash when move finger change scncamrea's position quickly - ios

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.

Related

Change SCNScene's sceneNamed programmatically

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:

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

Tilemap is not filling up entire scene

I'm using cocos2d with a viewcontroller in Xcode 5. I create a CCDirector as a child viewcontroller, and then add this to a UIView that is in the storyboard.
myViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
CCDirector* director = (CCDirector *)[self childViewControllers][0];
director.view.frame = CGRectMake(0, 0, 320, 176);
[_sceneView addSubview:[director view]];
}
The tilemap is created in IntroLayer.m.
-(id) initWithViewContoller:(MyCocosViewController *)viewController
{
if( (self=[super init])) {
_tileMap = [CCTMXTiledMap tiledMapWithTMXFile:#"apartmentWithWall.tmx"];
[self addChild:_tileMap z:-1];
_tileMap.scale = 1;
_bgLayer = [_tileMap layerNamed:#"Floorplan"];
self.wall = [_tileMap layerNamed:#"Walls"];
_wall.visible = NO;
}
And the scene is made in IntroLayer.m also
+(CCScene *) sceneWithViewController:(MyCocosViewController *)viewController
{
CCScene *scene = [CCScene node];
IntroLayer *layer = [[IntroLayer node] initWithViewContoller:viewController];
// add layer as a child to scene
[scene addChild: layer];
return scene;
}
This code successfully creates the tilemap and puts it into the view. However, the tilemap does not take up the whole scene - it seems to be only half sized. It is placed on the bottom left of the view, and the rest of the view is just black. If you touch on it, then the pixels are not scaled correctly - you can touch on the black part, and it still thinks this is part of the tilemap. This is what it looks like: http://i.imgur.com/hb0gMFI.png
The white and black floorplan on the bottom left should take up the entire scene.
So why isn't it just naturally scaled correctly? The tutorials I've looked at don't seem to have this problem.

Moving a frame half of the parent's size moves it all the way across (Coordinates doubled)?

I have no idea what is going on.
I am not doing anything special.
I have a UIViewController that I create programmatically, no storyboard.
Then I create a UIView with nothing special
#import "SettingsView.h"
#interface SettingsView()
#property UIImageView* bg;
#end
#implementation SettingsView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.bg = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"happy.png"]];
[self addSubview:self.bg];
}
return self;
}
-(void)layoutSubviews
{
self.bg.frame = self.frame;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
I instantiate it in the VC and when I want to show it I do:
-(void)showSettingsView
{
self.settings.frame = self.view.frame;
self.btnInfo.userInteractionEnabled = NO;
self.btnPause.userInteractionEnabled = NO;
self.btnSettings.userInteractionEnabled = NO;
self.view.userInteractionEnabled = NO;
[self.view addSubview:self.settings];
CGRect frame = self.settings.frame;
frame.origin.x = frame.size.width * 0.48;
frame.origin.y = 0;
self.settings.frame = frame;
}
Instead of seeing the image view roughly half way on the screen, it starts at exactly double (0.48 * 2 = 96%) of the screen.
I cannot understand why coordinates are doubled or something?
When I check the frame, I clearly see the origin at around 150 which is half the width of the iPhone.
What could be happening?
Thanks
Could it be that in...
-(void)layoutSubviews
{
self.bg.frame = self.frame;
}
you should be using bounds instead of frame of your self object...
-(void)layoutSubviews
{
self.bg.frame = self.bounds;
}

CCNode Hud, not acting as Hud but "scrolling with scene"

Attempting to simply have a scene where I have the gameplay, and the hud. Since I have it positioning the scene (which contains both layers), I assume this is why my Menu button is moving even though it's in the Hud Layer.
To fix it I would think that I would maybe have to change this line
[self setCenterOfScreen: mainChar.position];
to something like this:
[gameLayer setCenterOfScreen: mainChar.position];
But then I get this:
No visible #interface for CCNode declares the selector
'setCenterOfScreen:'
Here is the
GameScene (commented where I think issue is):
+ (GameScene *)scene
{
return [[self alloc] init];
}
// -----------------------------------------------------------------------
- (id)init
{
self = [super init];
if (!self) return(nil);
CGSize winSize = [CCDirector sharedDirector].viewSize;
self.userInteractionEnabled = YES;
self.theMap = [CCTiledMap tiledMapWithFile:#"AftermathRpg.tmx"];
self.contentSize = theMap.contentSize;
CCNode *gameLayer = [CCNode node];
gameLayer.userInteractionEnabled = YES;
gameLayer.contentSize = self.contentSize;
[self addChild:gameLayer];
[gameLayer addChild:theMap z:-1];
self.mainChar = [CCSprite spriteWithImageNamed:#"mainChar.png"];
mainChar.position = ccp(200,300);
[gameLayer addChild:mainChar];
// POSSIBLY WHY BOTH LAYERS ARE SHIFTING, RATHER THEN HUD STANDING STILL,
// I essentially want the gameLayer to be centered on screen, not both.
// I tried [gameLayer setCenterOfScreen: mainChar.position] but got error posted above
[self setCenterOfScreen: mainChar.position];
CCNode *hudLayer = [CCNode node];
hudLayer.userInteractionEnabled = YES;
hudLayer.position = ccp(winSize.width * 0.9, winSize.height * 0.9);
[self addChild:hudLayer];
CCButton *backButton = [CCButton buttonWithTitle:#"[ Menu ]" fontName:#"Verdana-Bold" fontSize:18.0f];
backButton.positionType = CCPositionTypeNormalized;
backButton.position = ccp(0.85f, 0.95f); // Top Right of screen
[backButton setTarget:self selector:#selector(onBackClicked:)];
[hudLayer addChild:backButton];
return self;
}
but then I just get:
Just confused on how I would set up a scene, to initially have 2 layers - one that keeps position in say the top right as a HUD, and the other that scrolls with scene for gameplay when telling it to.
This is related to the other question you asked here (Adding a Hud Layer to Scene Cocos2d-3) which I posted the answer to. In the future it is better to modify your original OP in your first question rather than create a new question that is almost the same.
Hope this helped.

Resources