SceneKit自学之路(4)

第四个Demo,看了那么久无聊的,这次我想做点稍微有意思的。来做一个可以做动作的机器人吧。


RobotDemo

这个实例主要是对SceneKit中可视化场景设置和SCNAction进行了学习和使用。
先看效果:

gif.gif

很简单的一个例子,实现了机器人的手部运动,腿部运动,头部运动。移动实现了固定场景和固定任务两种方式。
一共就一百来行代码。接下来记录一下:

1、机器人建模

前文中说过,我没有也不知道去哪找开源的资源。所以所有例子都是用图形和颜色搭建。本例的机器人亦如此。
本例中,我不再和以前的Demo一样,为了理解Scene,把所有流程用代码梳理出来。而是直接通过storyboard+SceneKit Scene File,可视化地完成了所有的界面部分。

StoryBoard

storyboard中找到SceneKitView,拖到我们的控制器上,像使用UIView一样给他设置大小位置或约束。摆放按钮就不说了。


创建SceneKit Scene File文件

接下来创建SceneKit Scene File文件,并在里面构造出我们的机器人。


可视化完成机器人的创建

没玩过3D的人对这个玩意儿开始确实不习惯,多玩玩,基本的操作还是挺简单的。
设置位置

在右边的属性栏里可以设置位置。(也可以用拖的,但是设置更精确)
设置大小

设置大小。这个根据你选择的几何不同而不同,比如圆的话是设置半径。

设置颜色

在Diffuse设置颜色。有贴图的话也可以在这设置。


创建子节点

创建子节点的方式则是直接把一个Node,拖到另一个Node里,类似往UIView里放另一个UIView。

还有灯光,相机等等,都可以直接往里面甩。然后根据可视化得进行设置和位置变换。

2、初始化场景

接下来该敲敲代码了。

- (void)viewDidLoad {
    [super viewDidLoad];
    SCNScene *scene = [SCNScene sceneNamed:@"art.scnassets/showcase.scn"];
    self.scnView.scene = scene;
    self.scnView.autoenablesDefaultLighting = YES;
    self.robot = [scene.rootNode childNodeWithName:@"robot" recursively:NO];
    self.camera = [scene.rootNode childNodeWithName:@"camera" recursively:NO];
}

初始化场景,设置默认光源等。这里我们也可以自己拖,但是这个Demo里,光源不是重点,所以从简了。

3、手部运动

这里运动我只实现了最基本的抬手和放下。

-(IBAction)leftHand {
    SCNNode *leftHand = [_robot childNodeWithName:@"leftShoulder" recursively:NO];
    [self raiseHand:leftHand];
}

-(IBAction)rightHand {
    SCNNode *rightHand = [_robot childNodeWithName:@"rightShoulder" recursively:NO];
    [self raiseHand:rightHand];
}

分别获取到双手,使用抬手动作

-(void)raiseHand:(SCNNode *)hand {
    SCNAction *raiseAction = [SCNAction rotateToX:-2.5 y:0 z:0 duration:0.5];
    SCNAction *putAction = [SCNAction rotateToX:0 y:0 z:0 duration:0.5];
    SCNAction *sequenceAction = [SCNAction sequence:@[raiseAction,putAction]];
    [hand runAction:sequenceAction];
}

首先实现了一个抬起的动作raiseAction,这个动作要将手臂,沿-x轴,旋转2.5个单位。
复习一下,+ (SCNAction *)rotateToX:(CGFloat)xAngle y:(CGFloat)yAngle z:(CGFloat)zAngle duration:(NSTimeInterval)duration;

Creates an action that rotates the node to absolute angles in each of the three principal axes.
When the action executes, the node’s rotation property animates to the new angle. Calling this method is equivalent to calling rotateToX:y:z:duration:shortestUnitArc: and passing NO
for the shortestUnitArc
parameter.
This action is not reversible; the reverse of this action has the same duration but does not change anything.

将节点旋转到三个主轴上的绝对角度。这里稍微扩散一下,SCNAction中旋转的角度有好几种:这个方法旋转的是绝对角。但是在SceneKit Scene File中对应的旋转方法,旋转的又是欧拉角。这两者不一样,所以不要疑惑为什么明明设置的一样的值为什么动作不一样。


SceneKit Scene File中可视化创建旋转

言归正传。SceneKit的坐标系:


坐标系

我们想做一个从前向上的举手动作,就是要求手沿x轴转动。

我是这么理解这个旋转角度的:从正轴往负轴的方向。当value<0,为逆时针旋转,value>0时,为顺时针旋转。所以,我们可以推断出举手的动作,x<0,y=0,z=0,如果感觉一时没got到也没关系,多试几次就知道了。所以在这里,我们设置举手

SCNAction *raiseAction = [SCNAction rotateToX:-2.5 y:0 z:0 duration:0.5];

同理手放下

SCNAction *putAction = [SCNAction rotateToX:0 y:0 z:0 duration:0.5];

然后把两个动作串联起来

SCNAction *sequenceAction = [SCNAction sequence:@[raiseAction,putAction]];

就是完整的一次举手。接下来就执行就可以了

[hand runAction:sequenceAction];
4、头部运动

同理我们来设置点头和摇头的动作。
点头:

-(IBAction)nod {
    SCNNode *head = [_robot childNodeWithName:@"head" recursively:NO];
    SCNAction *raiseAction = [SCNAction rotateToX:0.3 y:0 z:0 duration:0.3];
    SCNAction *putAction = [SCNAction rotateToX:0 y:0 z:0 duration:0.3];
    SCNAction *sequenceAction = [SCNAction sequence:@[raiseAction,putAction]];
    SCNAction *repeatAction = [SCNAction repeatAction:sequenceAction count:2];
    repeatAction.timingMode = SCNActionTimingModeEaseInEaseOut;
    [head runAction:repeatAction];
}

这里新用到了repeatAction,顾名思义就是重复动作。
timingMode,是动作执行的一个速度枚举。有四种类型

/*! @enum SCNActionTimingMode
 @abstract The modes that an action can use to adjust the apparent timing of the action.
 */
typedef NS_ENUM(NSInteger, SCNActionTimingMode) {
    SCNActionTimingModeLinear,//匀速
    SCNActionTimingModeEaseIn,//一开始慢,慢慢加快
    SCNActionTimingModeEaseOut,//一开始快,逐渐变慢
    SCNActionTimingModeEaseInEaseOut//开始慢慢地,通过中间的时候加速,然后再次放缓
} API_AVAILABLE(macos(10.10), ios(8.0));

摇头:

-(IBAction)shakeHead {
    SCNNode *head = [_robot childNodeWithName:@"head" recursively:NO];
    SCNAction *leftAction = [SCNAction rotateToX:0 y:-0.3 z:0 duration:0.5];
    SCNAction *rightAction = [SCNAction rotateToX:0 y:0.3 z:0 duration:0.5];
    SCNAction *sequenceAction = [SCNAction sequence:@[leftAction,rightAction]];
    SCNAction *repeatAction = [SCNAction repeatAction:sequenceAction count:2];
    SCNAction *centerAction = [SCNAction rotateToX:0 y:0 z:0 duration:0.5];
    SCNAction *sequenceAction2 = [SCNAction sequence:@[repeatAction,centerAction]];
    sequenceAction2.timingMode = SCNActionTimingModeEaseInEaseOut;
    [head runAction:sequenceAction2];
}

这里主要是尝试了各种Action的组合,先左右摇两次,再还原。

4、腿部运动

腿部运动分为两个部分,一个是腿部位置变换,一个是机器人位置变换。
腿部不多说了,和上文基本相同。

-(void)takeStep:(SCNNode *)foot{
    SCNAction *raiseAction = [SCNAction rotateToX:0.5 y:0 z:0 duration:0.5];
    SCNAction *putAction = [SCNAction rotateToX:0 y:0 z:0 duration:0.5];
    SCNAction *sequenceAction = [SCNAction sequence:@[raiseAction,putAction]];
    SCNAction *repeatAction = [SCNAction repeatActionForever:sequenceAction];
    [foot runAction:repeatAction];
}

接下来说机器人位置变换,就是移动。

-(void)move:(SCNNode *)node {
    SCNAction *moveAction = [SCNAction moveByX:0 y:0 z:1 duration:0.5];
    SCNAction *repeatAction = [SCNAction repeatActionForever:moveAction];
    [node runAction:repeatAction];
}

就是另外一个SCNAction,朝着+z轴方向,永远的重复前进。


移动

但是有一个问题,机器人走着走着就走出了镜头。
怎么像第一人称的游戏一样,让人物始终在视角的中心呢?设置除人物外的所有节点朝相反方向移动吗?
并不用,我们让摄像头跟着机器人一起移动就可以了。
机器人移动:

[self move:_robot];

镜头移动:

[self move:_camera];

-move:方法在上文)

镜头跟随

想停止的时候移除动作即可。

[_robot removeAllActions];
[_camera removeAllActions];

顺便,立正

-(void)standUp:(SCNNode *)foot {
    [foot removeAllActions];
    SCNAction *standAction = [SCNAction rotateToX:0 y:0 z:0 duration:0.5];
    [foot runAction:standAction];
}

以上,全部完成。


记得比较啰嗦,因为本人也在学习阶段,Demo实际上也就100来行。如有不正确的地方,感谢斧正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容