2015-12-04 2 views
4

Я пытаюсь использовать SceneKit для разработки игры для tvOS, и у меня проблема. Когда я устанавливаю eulerAngle узла, прежде чем применить импульс к физическому модулю, узел будет сброшен в исходное положение.Почему SCNPhysicsBody сбрасывает положение при установке eulerAngles?

Я ожидал увидеть узлы, перемещающиеся по плоскости пола, но на каждом касании узлы перемещаются в исходное положение до применения импульса.

Я новичок в использовании этой структуры, поэтому я задаюсь вопросом, где ошибка. Я использую новый AppleTV с tvOS 9,0 и XCode 7.1.1

Чтобы воспроизвести его, вы можете создать новый проект Xcode (Игра для tvOS) и заменить GameViewController.m с этим кодом:

#import "GameViewController.h" 

SCNNode *ship; 
SCNNode *node; 
SCNNode *ground; 

@implementation GameViewController 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    // create a new scene 
    SCNScene *scene = [[SCNScene alloc] init]; 
    scene.physicsWorld.gravity = SCNVector3Make(0, -800, 0); 

    // create and add a camera to the scene 
    SCNNode *cameraNode = [SCNNode node]; 
    cameraNode.camera  = [SCNCamera camera]; 
    cameraNode.camera.zFar = 10000; 
    [scene.rootNode addChildNode:cameraNode]; 

    // place the camera 
    cameraNode.position = SCNVector3Make(0, 64, 64); 

    // create and add a light to the scene 
    SCNNode *lightNode = [SCNNode node]; 
    lightNode.light  = [SCNLight light]; 
    lightNode.light.type = SCNLightTypeOmni; 
    lightNode.position = SCNVector3Make(0, 10, 10); 
    [scene.rootNode addChildNode:lightNode]; 

    // create and add an ambient light to the scene 
    SCNNode *ambientLightNode = [SCNNode node]; 
    ambientLightNode.light  = [SCNLight light]; 
    ambientLightNode.light.type = SCNLightTypeAmbient; 
    ambientLightNode.light.color = [UIColor darkGrayColor]; 
    [scene.rootNode addChildNode:ambientLightNode]; 

    SCNGeometry *geometry; 
    SCNMaterial *material; 
    SCNNode *tempNode; 
    SCNPhysicsShape* shape; 
    SCNPhysicsBody* body; 

    //-- 
    SCNScene *loaded = [SCNScene sceneNamed:@"art.scnassets/ship.scn"]; 
    tempNode = [loaded.rootNode childNodeWithName:@"ship" recursively:YES]; 

    geometry = [SCNCylinder cylinderWithRadius:16 height:8]; 
    shape = [SCNPhysicsShape shapeWithGeometry:geometry options:nil]; 

    tempNode.physicsBody = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeDynamic shape:shape]; 
    tempNode.physicsBody.restitution  = 1; 
    tempNode.physicsBody.friction   = 0.25; 
    tempNode.physicsBody.categoryBitMask = 2; 
    tempNode.physicsBody.collisionBitMask = 1; 
    tempNode.position = SCNVector3Make(32, 32, 0); 
    [scene.rootNode addChildNode:tempNode]; 
    ship = tempNode; 

    //-- 
    geometry = [SCNCylinder cylinderWithRadius:16 height:8]; 

    material = [[SCNMaterial alloc] init]; 
    material.diffuse.contents = UIColor.yellowColor; 
    geometry.materials  = @[material]; 

    shape = [SCNPhysicsShape shapeWithGeometry:geometry options:nil]; 
    body = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeDynamic shape:shape]; 

    tempNode = [SCNNode nodeWithGeometry: geometry]; 
    tempNode.physicsBody     = body; 
    tempNode.physicsBody.restitution  = 1; 
    tempNode.physicsBody.friction   = 0.25; 
    tempNode.physicsBody.categoryBitMask = 2; 
    tempNode.physicsBody.collisionBitMask = 1; 
    tempNode.position = SCNVector3Make(0, 32, 0); 
    [scene.rootNode addChildNode:tempNode]; 
    node = tempNode; 

    //-- 
    geometry = [[SCNFloor alloc] init]; 

    material = [[SCNMaterial alloc] init]; 
    material.diffuse.contents = UIColor.blueColor; 
    geometry.materials  = @[material]; 

    shape = [SCNPhysicsShape shapeWithGeometry:geometry options:nil]; 
    body = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeKinematic shape:shape]; 

    tempNode = [SCNNode nodeWithGeometry: geometry]; 
    tempNode.physicsBody = body; 
    tempNode.physicsBody.categoryBitMask = 1; 
    [scene.rootNode addChildNode:tempNode]; 
    ground = tempNode; 

    //-- 
    SCNLookAtConstraint * constraint = [SCNLookAtConstraint lookAtConstraintWithTarget: ground]; 
    constraint.gimbalLockEnabled = YES; 
    cameraNode.constraints = @[constraint]; 

    // configure the SCNView 
    SCNView *scnView = (SCNView *)self.view; 
    scnView.scene = scene; 
    scnView.allowsCameraControl = NO; 
    //scnView.antialiasingMode = SCNAntialiasingModeMultisampling2X; 
    scnView.debugOptions = SCNDebugOptionShowPhysicsShapes; 
    scnView.showsStatistics = YES; 
    scnView.backgroundColor = [UIColor blackColor]; 

    // add a tap gesture recognizer 
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; 
    NSMutableArray *gestureRecognizers = [NSMutableArray array]; 
    [gestureRecognizers addObject:tapGesture]; 
    [gestureRecognizers addObjectsFromArray:scnView.gestureRecognizers]; 
    scnView.gestureRecognizers = gestureRecognizers; 
} 

- (void) handleTap:(UIGestureRecognizer*)gestureRecognize 
{ 
    float x = (rand()/(float)RAND_MAX) - 0.5f; 
    float y = (rand()/(float)RAND_MAX) - 0.5f; 
    float speed = (rand()/(float)RAND_MAX) * 300; 

    CGPoint velocity = CGPointMake(x, y); 
    float angle = [self AngleBetween:velocity And:CGPointMake(1, 0)] + M_PI_2; 

    [node.physicsBody applyForce:SCNVector3Make(velocity.x*speed, 0, velocity.y*speed) impulse:YES]; 
    [ship.physicsBody applyForce:SCNVector3Make(velocity.x*speed, 0, velocity.y*speed) impulse:YES]; 

    // if comment these lines the problem doesn't appears 
    node.eulerAngles = SCNVector3Make(0, angle, 0); 
    ship.eulerAngles = SCNVector3Make(0, angle, 0); 
} 

- (void)didReceiveMemoryWarning 
{ 
    [super didReceiveMemoryWarning]; 
} 

- (float) AngleBetween:(CGPoint)_origin And:(CGPoint)_destination 
{ 
    float dotProduct = (_origin.x * _destination.x) + (_origin.y * _destination.y); 
    float perpDotProduct = (_origin.x * _destination.y) - (_origin.y * _destination.x); 

    return -atan2f(-perpDotProduct, dotProduct); 
} 

@end 

Если вы прокомментируете линии, в которых установлены углы эйлеров (при методе handleTap), проблема не возникает.

ответ

3

Я нашел ответ для своей проблемы.

на основе этого ответа: https://stackoverflow.com/a/28921103/2845875

я понял, что я должен удалить узлы из дерева сцены, прежде чем применять преобразования, а затем снова вставьте их.

Чтобы следовать моему пример, вот код:

SCNNode *root = node.parentNode; 
[node removeFromParentNode]; 
[ship removeFromParentNode]; 

[node.physicsBody applyForce:SCNVector3Make(velocity.x*speed, 0, velocity.y*speed) impulse:YES]; 
[ship.physicsBody applyForce:SCNVector3Make(velocity.x*speed, 0, velocity.y*speed) impulse:YES]; 

node.eulerAngles = SCNVector3Make(0, angle, 0); 
ship.eulerAngles = SCNVector3Make(0, angle, 0); 

[root addChildNode:node]; 
[root addChildNode:ship]; 
3

Из SCNPhysicsBody документации Apple:

При изменении преобразования значения или любые из других свойств, компоненты преобразования, такие как положение и вращение узла, затронутого физикой, SceneKit сбрасывает физическое моделирование для этого узла.

Физика смоделированных значений в собственности presentationNode. Таким образом, в вашем случае путь к копированию позиции презентационного узла перед применением преобразования, а затем записать его обратно:

SCNVector3 nodePosition = [[node presentationNode] position]; 
SCNVector3 shipPosition = [[ship presentationNode] position]; 

[node.physicsBody applyForce:SCNVector3Make(velocity.x*speed, 0, velocity.y*speed) impulse:YES]; 
[ship.physicsBody applyForce:SCNVector3Make(velocity.x*speed, 0, velocity.y*speed) impulse:YES]; 

// if comment these lines the problem doesn't appears 
node.eulerAngles = SCNVector3Make(0, angle, 0); 
ship.eulerAngles = SCNVector3Make(0, angle, 0); 

[node setPosition: nodePosition]; 
[ship setPosition: shipPosition]; 
+0

какое простое решение. Спасибо друг! – mauris

+0

Hm. Я испытываю эту проблему, хотя я не назначил физическое тело своим узлам. – Ash