第二个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、创建场景
在创建场景时,我把除天体以外的SCNView
、SCNScene
、SCNNode
都放在了这初始化。方便流程了理解。
#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
这个几何图形有些特殊,如果是其他几何图形SCNSphere
、SCNBox
等,在沿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
坐标原点的位置。
代码写得比较冗余(实际上除了注释也就几十行代码),可以按照代码规范优化。但是目前是以学习为主,所以尽量把所有的流程都写出来,方便理解。