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 中文教程

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容