Revolve a SCNode object in the 3D Space using Scen

2020-02-24 04:02发布

问题:

I am trying to learn my way around SceneKit and as an example, I thought I'd try to build a solar system using the SDK. I am able to add SCNode objects and configure its material properties like pattern and lighting. Also, I am able to rotate the planets, but I can't seem to understand how do I "revolve" them along a particular path.

My code so far looks like:

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

I read somewhere in AppleDocs that you need to add planets in the Sun's co-ordinate system and not in the root node but I am not sure if I understood that properly anyway.

Let me know how do you get about doing this.

回答1:

SceneKit doesn't provide an animate-along-path API. There are several ways to set up something like a solar system, though.

  1. Ever seen an orrery? This is the model that bit in the SCNNode documentation is talking about; it's also what the WWDC presentation does on the "Scene Graph Summary" slide. The trick is that you use one node to represent the frame of reference for a planet's revolution around the sun — like the armature that holds a planet in an orrery, if you make this node rotate around its central axis, any child node you attach to it will follow a circular path. The relevant code for this is in ASCSceneGraphSummary.m in that sample code project (condensed to just show node hierarchy setup here):

    // 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. You can't make an animation that follows a specific path you model in 3D space, but you could make a keyframe animation that interpolates between several positions in 3D space. The (untested) code below moves a node around a square shape in the xz-plane... add more points to get a rounder shape.

    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"];
    
  3. Use physics! The SCNPhysicsField class models radial gravity, so you can just drop a gravity field into a scene, add some physics bodies for planets, and sit back and watch your solar system cataclysmically destroy itself come to life! Getting realistic behavior here takes a lot of trial and error — you'll need to set an initial velocity for each planet tangential to what you expect its orbit to be, and tweak that velocity to get it right. Researching some ephemeris data for our solar system might help.