为什么 SCNPhysicsBody 在设置 eulerAngles 时会重置位置? [英] Why SCNPhysicsBody resets position when set eulerAngles?

查看:64
本文介绍了为什么 SCNPhysicsBody 在设置 eulerAngles 时会重置位置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 SceneKit 为 tvOS 开发游戏,但遇到了问题.当我在对 PhysicsBody 施加脉冲之前设置节点的 eulerAngle 时,节点将重置为其原始位置.

I'm trying to use SceneKit to develop a game for tvOS, and I'm having an issue. When I set the node's eulerAngle before apply an impulse to the physicsBody the node is reset to his original position.

我期待看到节点在地板平面上移动,但在每次点击时,节点都会在施加脉冲之前移动到原点位置.

I was expecting to see the nodes moving around on the floor's plane, but on each tap the nodes are moved to the origin position before the impulse is applied.

我是这个框架的新手,所以我想知道错误在哪里.我正在使用带有 tvOS 9.0 和 XCode 7.1.1 的新 AppleTV

I'm new at the use of this framework, so I wonder where is the mistake. I'm using the new AppleTV with tvOS 9.0 and XCode 7.1.1

要重现它,您可以创建一个新的 xcode 项目(Game for tvOS)并将 GameViewController.m 替换为以下代码:

To reproduce it, you can create a new xcode project (Game for tvOS) and replace the GameViewController.m with this code:

#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 方法中),则问题不会出现.

If you comment the lines where the euler angles are set (at handleTap method) the problem doesn't appears.

推荐答案

我找到了问题的答案.

基于这个答案:https://stackoverflow.com/a/28921103/2845875

我意识到我必须在应用变换之前从场景树中删除节点,然后重新插入它们.

I realized that I must remove the nodes from the scene tree before apply the transformations, and then reinsert them.

以我的示例为例,代码如下:

To follow with my example, here is the code:

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];

这篇关于为什么 SCNPhysicsBody 在设置 eulerAngles 时会重置位置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆