VR+全景播放器+头控讲解-06

VR+全景播放器+头控讲解-01-知识储备
VR+全景播放器+头控讲解-02-创建球体
VR+全景播放器+头控讲解-03-渲染视频
VR+全景播放器+头控讲解-04-滑动手势
VR+全景播放器+头控讲解-05-伸缩画面
VR+全景播放器+头控讲解-06-头控实现
VR+全景播放器+头控讲解-07-分屏技术

学习目标

掌握头控部分布局
如何检测头控按钮被选中
如何实现悬停动画

在UIView上面布局我们可以使用UIButton UIView UIImageView等,但是是在3D场景中,我们不能使用UIView,我们要使用平面几何当视图使用,下面具体介绍一下

分析

243FA4BB-FAC5-4A93-A9DF-C3A1F447F010.png

提示几点

  1. 头控根节点可以放在根节点上中心或者球体中心都是可以的,但是考虑到后期我们要进行视频滤波,所以最好放到场景节点上
  2. 低头菜单出现,抬头菜单消失,这个要根据重力感应在X轴旋转决定的
  3. 当抬头时菜单总是出现在下方,一旦出现不会跟随照相机转动,当重力感应变化时,我们让头控节点的绕着y轴旋转

实现步骤

第一步.创建菜单节点

+(SCNNode*)createBackgroundNode{
    /// 创建一个背景色
    SCNNode* bgNode = [SCNNode node];
    bgNode.position = SCNVector3Make(0, CONTROL_DISTANCE/2, -CONTROL_DISTANCE);
    bgNode.geometry.firstMaterial.cullMode = SCNCullModeBack;
    bgNode.rotation = SCNVector4Make(1, 0, 0, atan(1/2.0));
    return bgNode;
    }
+(SCNNode*)createShadowNode{
    SCNNode *shadowNode = [SCNNode node];
    shadowNode.geometry= [SCNPlane planeWithWidth:MENU_MAX_LENGTH height:BUTTON_WIDHT+BUTTON_WIDHT/2.0];
    shadowNode.geometry.firstMaterial.diffuse.contents = BACKGROUND_MENU_SHOW_NAME;
    shadowNode.position = SCNVector3Make(0, 0, 0);
    shadowNode.geometry.firstMaterial.cullMode = SCNCullModeBack;
    return shadowNode;
}

+(SCNNode*)createPlayNode{
    SCNNode* playNode = [SCNNode node];
    playNode.geometry = [SCNPlane planeWithWidth:BUTTON_WIDHT*0.8 height:BUTTON_WIDHT*0.8];
    playNode.geometry.firstMaterial.diffuse.contents = ICON_PLAY_NAME;
    playNode.position = SCNVector3Make(-20, 0, 2);
    playNode.geometry.firstMaterial.cullMode = SCNCullModeBack;
    return playNode;
}
+(SCNNode*)createPreviousNode{
    /// 第六步-创建播放上一个视频的节点
    SCNNode* previousNode = [SCNNode node];
    previousNode.geometry = [SCNPlane planeWithWidth:BUTTON_WIDHT*0.8 height:BUTTON_WIDHT*0.8];
    previousNode.geometry.firstMaterial.diffuse.contents = ICON_PREVIOUS_NAME;
    previousNode.position = SCNVector3Make(-60, 0, 2);
    previousNode.geometry.firstMaterial.cullMode = SCNCullModeBack;
    return previousNode;
}
+(SCNNode*)createAfterNode{
    SCNNode *nextNode = [SCNNode node];
    nextNode.geometry  = [SCNPlane planeWithWidth:BUTTON_WIDHT*0.8 height:BUTTON_WIDHT*0.8];
    nextNode.geometry.firstMaterial.diffuse.contents = ICON_AFTER_NAME;
    nextNode.position = SCNVector3Make(20, 0, 2);
    nextNode.geometry.firstMaterial.cullMode = SCNCullModeBack;
    return nextNode;
}
+(SCNNode*)createMoreNode{
    SCNNode* moreNode = [SCNNode node];
    moreNode.geometry = [SCNPlane planeWithWidth:BUTTON_WIDHT*0.8 height:BUTTON_WIDHT*0.8];
    moreNode.geometry.firstMaterial.diffuse.contents = ICON_MENU_HIDDEN_NAME;
    moreNode.position = SCNVector3Make(60, 0, 2);
    moreNode.geometry.firstMaterial.cullMode = SCNCullModeBack;
    return moreNode;
}
+(SCNNode*)createHighVoiceNode{
    SCNNode* voiceHighNode = [SCNNode node];
    voiceHighNode.geometry = [SCNPlane planeWithWidth:BUTTON_WIDHT*0.8 height:BUTTON_WIDHT*0.8];
    voiceHighNode.geometry.firstMaterial.diffuse.contents = ICON_VOICE_HIGH_NAME;
    voiceHighNode.geometry.firstMaterial.cullMode = SCNCullModeBack;
    return voiceHighNode;
}
+(SCNNode*)createLowVoiceNode{
    SCNNode* voiceLowNode = [SCNNode node];
    voiceLowNode.geometry = [SCNPlane planeWithWidth:BUTTON_WIDHT*0.8 height:BUTTON_WIDHT*0.8];
    voiceLowNode.geometry.firstMaterial.diffuse.contents = ICON_VOICE_LOW_NAME;
    voiceLowNode.geometry.firstMaterial.cullMode = SCNCullModeBack;
    return voiceLowNode;
}

创建的方法基本一致,调节一下位置即可!

第二步 添加到先把功能按钮节点添加到头控背景节点上,然后将背景节点添加到头控根节点上去

[self.scene.rootNode addChildNode:self.controlNode];
[self.controlNode addChildNode:self.bgNode];
[self.bgNode addChildNode:self.shadowNode];
[self.bgNode addChildNode:self.playOrPauseNode];
[self.bgNode addChildNode:self.proviousNode];
[self.bgNode addChildNode:self.AfterNode];
[self.bgNode addChildNode:self.showMoreNode];
[self.bgNode addChildNode:self.lowVoiceNode];
[self.bgNode addChildNode:self.highVoiceNode];
[self.scene.rootNode addChildNode:self.eyeNode];

第三步 添加点控节点,将其放在添加到照相机节点上去,这样照相机转动的时候,它就能跟着转动,效果就是一直在屏幕中央

 [self.eyeNode addChildNode:self.dotNode];

第四步 抬头小时低头出现

-(void)controlEyeNodeInVR:(SCNVector3)vector{
// 向上抬头 并且local
if ( vector.x>0.8 &&self.bgNode.hidden==false){
    self.bgNode.hidden = true;
    self.dotNode.hidden = true;
}else if ( vector.x<0.8 &&self.bgNode.hidden){
    self.bgNode.hidden = false;
    self.dotNode.hidden = false;
    SCNVector3 eulerAngles = self.controlNode.eulerAngles;
   // eulerAngles.y = -vector.z;
    eulerAngles.z = vector.z;
    self.controlNode.eulerAngles = eulerAngles;
  }
}

第五步 如何检测点控射线和头控按钮相交
思路:

先将按钮转换到照相机节点上,点控射线是否和按钮区域相交,就是相当于头控坐标 x在范围 [-button.width/2,button.width/2]内, y在 [-button.height/2.button.heigth/2.0]内

/// 下面就举一个例子,其它的都是类似的
SCNVector3 proviousPosition = [self.bgNode convertPosition:self.proviousNode.position toNode:self.self.eyeNode];
if (proviousPosition.x > -BUTTON_WIDHT/2 && proviousPosition.x < BUTTON_WIDHT/2 && proviousPosition.y > -BUTTON_WIDHT/2 && proviousPosition.y < BUTTON_WIDHT/2 ){
    if (!self.controlDotIn.inPreviousNode&&!self.proviousNode.hidden){
    // 第一次进入 还没有离开
     // 开始执行悬停动画
     [self startAnimation];
    }
    _controlDotIn.inPreviousNode = YES;
    return;
}else if(_controlDotIn.inPreviousNode){
    // 检测到离开按钮
    return;
}

悬停动画的实现

第一步 创建动画行为

-(void)createAnimation{
/// 创建动画节点
self.animationNode = [SCNNode node];
self.animationNode.geometry = [SCNPlane planeWithWidth:CONTROL_DISTANCE/5.0 height:CONTROL_DISTANCE/5.0];
self.animationNode.hidden = true;
 // 控制点上增加动画节点
[self.dotNode addChildNode:self.animationNode];
__weak XJRenderView* weakSelf = self;
self.animationAction  = [SCNAction customActionWithDuration:3 actionBlock:^(SCNNode * _Nonnull node, CGFloat elapsedTime) {
    int time = (int) (elapsedTime *(weakSelf.gif.count-1)/3.0);
    node.geometry.firstMaterial.diffuse.contents = weakSelf.gif[time];
    if(time == weakSelf.gif.count-1){
        [weakSelf animationDidStop];
    }
}];
}
-(NSArray *)gif{
    if(!_gif){
        NSMutableArray *gif= [NSMutableArray arrayWithCapacity:GIF_LENGTH];
        for(int i = 0 ; i< GIF_LENGTH ;i++)
        {
            NSString *name = [NSString stringWithFormat:GIF_PREFIX_NAME_FORMAT,i];
            [gif addObject:name];
        }
        _gif = gif;
    }
    return _gif;
}

第二步 在执行的节点上执行动画行为

-(void)startAnimation{
    [self.animationNode runAction:self.animationAction];
}

本节讲解完毕
SceneKit 中文教程

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

推荐阅读更多精彩内容