使用SceneKit在3D空间中旋转SCNode对象

6

我正在尝试学习SceneKit,并以构建太阳系为例。我可以添加SCNode对象并配置其材质属性,如图案和灯光,也可以旋转行星,但我不知道如何沿着特定路径“公转”它们。

到目前为止,我的代码看起来像:

// create a new scene
SCNScene *scene = [SCNScene scene];

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

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

// create and add a 3d sphere - sun to the scene
SCNNode *sphereNode = [SCNNode node];
sphereNode.geometry = [SCNSphere sphereWithRadius:0.3];
[scene.rootNode addChildNode:sphereNode];

// create and configure a material
SCNMaterial *material = [SCNMaterial material];
material.diffuse.contents = [NSImage imageNamed:@"SunTexture"];
material.specular.contents = [NSColor whiteColor];
material.specular.intensity = 0.2;
material.locksAmbientWithDiffuse = YES;

// set the material to the 3d object geometry
sphereNode.geometry.firstMaterial = material;

// create and add a 3d sphere - earth to the scene
SCNNode *earthNode = [SCNNode node];
earthNode.geometry = [SCNSphere sphereWithRadius:0.15];
NSLog(@"Sun position = %f, %f, %f", sphereNode.position.x, sphereNode.position.y, sphereNode.position.z);
earthNode.position = SCNVector3Make(0.7, 0.7, 0.7);
NSLog(@"Earth position = %f, %f, %f", earthNode.position.x, earthNode.position.y, earthNode.position.z);
//earthNode.physicsBody = [SCNPhysicsBody dynamicBody];
[scene.rootNode addChildNode:earthNode];

SCNMaterial *earthMaterial = [SCNMaterial material];
earthMaterial.diffuse.contents = [NSImage imageNamed:@"EarthTexture"];

earthNode.geometry.firstMaterial = earthMaterial;

我在AppleDocs的某处看到,需要将行星添加到太阳的坐标系中,而不是根节点中,但我不确定我是否正确理解了这一点。

请告诉我如何进行此操作。

1个回答

14

SceneKit没有提供沿路径动画的API。但是有几种方法可以设置类似太阳系的东西。

  1. 你见过调星器吗?这个模型在SCNNode文档中提到;也是WWDC演示上"场景图摘要"幻灯片所做的事情。关键是,使用一个节点来表示行星绕太阳公转的参考框架 - 就像调星器中保持行星的架子一样 - 如果让这个节点绕其中心轴旋转,则附加到它的任何子节点都将沿着一个圆形路径运动。相关代码在该示例代码项目的ASCSceneGraphSummary.m中(在此仅缩略显示节点层次结构设置):

    // Sun
    _sunNode = [SCNNode node];
    _sunNode.position = SCNVector3Make(0, 30, 0);
    [self.contentNode addChildNode:_sunNode];
    
    // Earth-rotation (center of rotation of the Earth around the Sun)
    SCNNode *earthRotationNode = [SCNNode node];
    [_sunNode addChildNode:earthRotationNode];
    
    // Earth-group (will contain the Earth, and the Moon)
    _earthGroupNode = [SCNNode node];
    _earthGroupNode.position = SCNVector3Make(15, 0, 0);
    [earthRotationNode addChildNode:_earthGroupNode];
    
    // Earth
    _earthNode = [_wireframeBoxNode copy];
    _earthNode.position = SCNVector3Make(0, 0, 0);
    [_earthGroupNode addChildNode:_earthNode];
    
    // Moon-rotation (center of rotation of the Moon around the Earth)
    SCNNode *moonRotationNode = [SCNNode node];
    [_earthGroupNode addChildNode:moonRotationNode];
    
    // Moon
    _moonNode = [_wireframeBoxNode copy];
    _moonNode.position = SCNVector3Make(5, 0, 0);
    [moonRotationNode addChildNode:_moonNode];
    
  2. 您不能制作遵循您在3D空间中建模的特定路径的动画,但可以制作插值多个3D空间中的位置之间的关键帧动画。 下面是(未经测试的)代码,它将一个节点沿着xz平面上方形移动...添加更多点以获得更圆滑的形状。

  3. CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPaths:@"position"];
    anim.values = @[
        [NSValue valueWithSCNVector3:SCNVector3Make(0, 0, 1)],
        [NSValue valueWithSCNVector3:SCNVector3Make(1, 0, 0)],
        [NSValue valueWithSCNVector3:SCNVector3Make(0, 0, -1)],
        [NSValue valueWithSCNVector3:SCNVector3Make(-1, 0, 0)],
    ];
    [node addAnimation:anim forKey:"orbit"];
    
  4. 利用物理学!SCNPhysicsField类模拟了径向引力,所以你可以在场景中添加一个重力场,为行星添加一些物理体,然后坐下来观察你的太阳系活灵活现地展现出来!要获得逼真的行为需要大量的试验和错误 - 你需要为每个行星设置一个切向于预期轨道的初始速度,并调整该速度以使其正确。研究一些我们太阳系的星历数据可能会有所帮助。


作为一种解决方法,我实现了类似于您提到的第一点的东西。感谢详细的回复,实现物理学的想法听起来很令人兴奋。 - Gaurav Wadhwani
太棒了。真的...太棒了。 - Nerrolken
这非常有帮助。另外,如果您决定使用 Orrery 并且希望所有行星都有不同的路径,您可以在太阳中心添加隐藏节点并旋转它们的轴 ;) - TeaCupApp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接