SceneKit自学之路(2)

第二个Demo,没有用到什么新的东西。算是对学习的东西一个巩固吧。


该实例模拟了一个简单的天体运动。大概分了以下几步来实现。

- (void)viewDidLoad {
    [super viewDidLoad];
    //创建场景
    SCNView *universeView = [self makeScene];
    //创建太阳
    SCNNode *sunNode = [self makeSun];
    [universeView.scene.rootNode addChildNode:sunNode];
    //太阳自转
    [self rotation:sunNode];
    //创建地球
    SCNNode *earthNode = [self makeEarth];
    //地球自转
    [self rotation:earthNode];
    //地球公转
    [sunNode addChildNode:earthNode];
    //添加月亮
    SCNNode *moonNode = [self makeMoon];
    //月亮自转
    [self rotation:moonNode];
    //月亮公转
    [earthNode addChildNode:moonNode];
}
1、创建场景

在创建场景时,我把除天体以外的SCNViewSCNSceneSCNNode都放在了这初始化。方便流程了理解。

#pragma mark - 初始化Scene
-(SCNView *)makeScene {
    //创建SCNView
    SCNView *universeView = [[SCNView alloc]initWithFrame:self.view.frame];
    [self.view addSubview:universeView];
    [universeView setBackgroundColor:[UIColor lightGrayColor]];
    //创建SCNScene
    SCNScene *scene = [SCNScene new];
    universeView.scene = scene;
    //创建cameraNode(用于调整镜头,必须添加camera,否则添加的node总是会适应scene的大小,无法观测调整大小效果)
    SCNNode *cameraNode = [SCNNode node];
    cameraNode.camera = [SCNCamera camera];
    [scene.rootNode addChildNode:cameraNode];
    cameraNode.position = SCNVector3Make(0, 0,250);
    cameraNode.camera.zFar = 2000;
    return universeView;
}

在这里需要强调的是camera这个Node非常重要,要实现一个完整的功能,几乎必不可少。所以接下来的一篇,我将单独专门研究camera。

2、创建太阳

在上一篇的HelloWorld中,我发现SCNText这个几何图形有些特殊,如果是其他几何图形SCNSphereSCNBox等,在沿Y轴旋转时,都是围着中心旋转。但是SCNText是围着左边旋转。
分析后发现其他的几何图形,他们的坐标轴都是在自己的中心,但是SCNText是在自己的左下角。设置alignmentMode都没用。那如何让SCNText实现围着中心旋转呢?
搜索引擎了两天没有得到结果。我最终采用的方法是类似html的div方式。把SCNText用其他的几何图形包一层,然后让包裹它的几何图形旋转。需要注意的地方是我们需要将SCNText放在parentNode的中心。
我封装了这个一个方法

- (void)recenterText:(SCNNode *)node {
    SCNText *sceneText = (SCNText *)node.geometry;
    SCNVector3 min = SCNVector3Zero;
    SCNVector3 max = SCNVector3Zero;
    [sceneText getBoundingBoxMin:&min max:&max];
    sceneText.alignmentMode = kCAAlignmentCenter;
    CGFloat textHeight = max.y - min.y;
    CGFloat textWidth = max.x - min.x;
    SCNVector3 position = SCNVector3Make(-textWidth/2 , -textHeight/3*2 , 0);
    node.position = position;
}

上面的position的y值,我认为应该是-textHeight/2就行了,但是实际效果却偏高了一点,目测调整为-textHeight/3*2,原因还没想通。

#pragma mark - 初始化Sun
-(SCNNode *)makeSun {
    //创建太阳
    SCNNode *sunNode = [SCNNode new];
    SCNSphere *sphere = [SCNSphere sphereWithRadius:40];
    sphere.firstMaterial.diffuse.contents = [UIColor colorWithRed:1.0 green:0 blue:0 alpha:0.8];
    sunNode.geometry = sphere;
    //把太阳设置为发光源
    sunNode.geometry.firstMaterial.lightingModelName = SCNLightingModelConstant;
    SCNNode *omniLightNode = [SCNNode node];
    omniLightNode.light = [SCNLight light];
    omniLightNode.light.type = SCNLightTypeOmni;
    omniLightNode.light.color = [UIColor whiteColor];
    [sunNode addChildNode:omniLightNode];
    //给他一个Title
    SCNNode *textNode = [SCNNode new];
    SCNText *text = [SCNText textWithString:@"太阳" extrusionDepth:2];
    text.font = [UIFont systemFontOfSize:32];
    text.firstMaterial.diffuse.contents = [UIColor whiteColor];
    textNode.geometry = text;
    [sunNode addChildNode:textNode];
    [self recenterText:textNode];
    return sunNode;
}

我没有素材,就用颜色+文字表达了。给几何图形设置颜色的方法是text.firstMaterial.diffuse.contents = [UIColor redColor];也可以设置firstMateri.almultiply.contents等,了解更多可以查看一下SCNMaterial类。我没计划深入了解贴图,所以这里先跳过。

3、太阳自转
#pragma mark - 自转
-(void)rotation:(SCNNode *)node {
    SCNAction *customAction = [SCNAction rotateByX:0 y:1 z:0 duration:1];
    SCNAction *repeatAction = [SCNAction repeatActionForever:customAction];
    [node runAction:repeatAction];
}
4、初始化地球/月亮

地球和月亮的初始化方法和自转方法与太阳大同小异。

5、地球/月亮公转

实际上每个天体的旋转速度都是不一样的,在Demo中我们忽略这些。
地球是围着太阳旋转的,我们直接把earthNode加入sunNode的子节点,earthNode就跟着sunNode一起旋转了。

[sunNode addChildNode:earthNode];

假如要实现公转速度与太阳自转速度不相等,只需要再加一个公转速度的节点,并将地球节点设为它的子节点就好了。
还要注意地球与太阳的位置

earthNode.position = SCNVector3Make(120, 0, 0);

这个position就是node的坐标原点相对parentNode坐标原点的位置。


代码写得比较冗余(实际上除了注释也就几十行代码),可以按照代码规范优化。但是目前是以学习为主,所以尽量把所有的流程都写出来,方便理解。

效果图

你可能感兴趣的:(SceneKit自学之路(2))