將模型添加到場(chǎng)景中 - 在您的環(huán)境中顯示3D內(nèi)容

在最后幾節(jié)中奏路,我們能夠檢測(cè)到一個(gè)平面并顯示一個(gè)焦點(diǎn)方塊阔蛉,以幫助我們?yōu)槟P椭付ㄒ粋€(gè)位置弃舒。我們也熟悉了熱門測(cè)試和世界變換。現(xiàn)在状原,我們擁有顯示虛擬對(duì)象所需的所有工具聋呢。在本教程中,我們將學(xué)習(xí)如何檢索模型并使用按鈕的觸發(fā)器將其呈現(xiàn)在場(chǎng)景中颠区。一旦顯示坝冕,我們將隱藏焦點(diǎn)方塊。

下載

要學(xué)習(xí)本教程瓦呼,您需要Xcode 9或更高版本喂窟,以及Focus Square的最終Xcode項(xiàng)目测暗。您可以下載本節(jié)的最終Xcode項(xiàng)目,以幫助您與自己的進(jìn)度進(jìn)行比較磨澡。

基本視圖

Main.Storyboard中碗啄,我們已經(jīng)提到ARSCNView默認(rèn)放在視圖控制器的頂部。但是稳摄,如果沒有UIView作為基礎(chǔ)稚字,則僅限于您可以在用戶界面上執(zhí)行的操作。為了能夠正確添加我們的按鈕厦酬,我們必須刪除當(dāng)前的 ARSCNView并首先從對(duì)象庫添加UIView作為底層胆描。接下來,選擇相同的ARKit SceneKit View并將其放回UIView之上仗阅。調(diào)整大小以填充整個(gè)視圖控制器昌讲。

約束

然后,單擊Storyboard編輯器左下角的第四個(gè)圖標(biāo)减噪,將新約束添加到場(chǎng)景視圖中短绸。定義約束以確保您的用戶界面適應(yīng)不同的屏幕尺寸或設(shè)備方向。設(shè)置為0頂部筹裕,醋闭,底部。確保它們都被約束到視圖而不是安全區(qū)域朝卒,然后單擊Add Constraints证逻。安全區(qū)域是凹口下方和主頁指示器上方的邊距,通常是屏幕的可見部分抗斤。此外瑟曲,請(qǐng)確保未選中“ 限制到邊距”

如果被限制在安全區(qū)域而不是超級(jí)視圖豪治,這就是看起來的樣子洞拨,顯然,這看起來并不好看负拟。

橫屏約束安全區(qū)

重新 Outlet

請(qǐng)記住烦衣,一個(gè)IBOutletsceneView鏈接到ARSCNView?因?yàn)槲覀儎h除了舊的ARSCNView掩浙,所以它打破了這個(gè)Outlet花吟。我們需要重新考慮新的。為此厨姚,請(qǐng)打開“ 助理”編輯器衅澈,該圖標(biāo)看起來像兩個(gè)交織在一起的圓圈。現(xiàn)在谬墙,我們并排放置兩個(gè)分屏今布,非常適合連接经备。在右側(cè),我們有ViewController.swift部默,在那里我們可以找到該出口的聲明侵蒙。單擊并拖動(dòng)左側(cè)的圓圈,它應(yīng)該是第15行傅蹂,然后釋放到ARSCNView上》坠耄現(xiàn)在,關(guān)閉助理編輯份蝴。

添加按鈕

我們想在視圖中添加一個(gè)按鈕犁功,用作在場(chǎng)景中添加模型的觸發(fā)器。從對(duì)象庫中婚夫,將UIButton拖動(dòng)到場(chǎng)景視圖的頂部浸卦。在“ 屬性”檢查器中,刪除“ 按鈕”標(biāo)題并將圖像設(shè)置為“ 按鈕/添加”请敦。

約束到底部20但這次是在安全區(qū)域,并取消選中Constrain到邊距储玫。然后侍筛,將鼠標(biāo)懸停在左側(cè)的“ 對(duì)齊”圖標(biāo)上,并在“容器”中選中“水平”以在屏幕中水平居中撒穷。

添加按鈕功能

我們剛剛在屏幕上添加了按鈕匣椰,但它根本沒有做任何事情。當(dāng)我們觸摸它時(shí)端礼,讓按鈕執(zhí)行某些操作∏菪Γ現(xiàn)在,打開Assistant編輯器并控制將故事板中的按鈕拖到ViewController類蛤奥。代碼中的順序并不重要佳镜,因?yàn)槲覀兩院髸?huì)移動(dòng)此函數(shù)。原因是我們不能在擴(kuò)展類中執(zhí)行此操作凡桥。將Connection更改為Action蟀伸,將其命名為addObjectButtonTapped。保持原樣缅刽。完成后啊掏,關(guān)閉“ 助理”編輯器

@IBAction func addObjectButtonTapped(_ sender: Any) {
    print("Add button tapped")
}

讓我們運(yùn)行應(yīng)用程序來查看我們的新按鈕衰猛。但在此之前迟蜜,評(píng)論一些印刷品陳述是明智的。轉(zhuǎn)到updateFocusSquare()并注釋掉這些代碼行啡省。

// print("Focus square hits a plane")

// print("Focus square does not hit a plane")

對(duì)象添加文件

讓我們創(chuàng)建另一個(gè)swift文件娜睛,以便在場(chǎng)景中添加模型髓霞。右鍵單擊視圖控制器+ ARSCNViewDelegate.swift并選擇新建文件...。然后微姊,選擇Swift File酸茴,單擊Next。稱之為ViewController + ObjectAddition兢交,然后是Create薪捍。

導(dǎo)入套件(Kits)

與往常一樣,用以下框架替換Foundation配喳。然后酪穿,向ViewController添加擴(kuò)展。

import UIKit
import SceneKit
import ARKit

extension ViewController {}

檢索模型

在擴(kuò)展內(nèi)部晴裹,創(chuàng)建一個(gè)新函數(shù)來檢索我們選擇的模型是一個(gè)很好的主動(dòng)被济。此函數(shù)僅在此文件中使用,因此我們將采用fileprivate涧团。將有一個(gè)String類型的參數(shù)只磷,它將有兩個(gè)名稱。在函數(shù)外部使用的那個(gè)被命名泌绣,而在函數(shù)內(nèi)使用的是名稱钮追。它將返回一個(gè)可選的SCNNode

fileprivate func getModel(named name: String) -> SCNNode? {}

與飛船場(chǎng)景類似阿迈,我們將使用我們指定的名稱調(diào)用場(chǎng)景元媚。然后,檢索該場(chǎng)景SketchUp的父節(jié)點(diǎn)苗沧。我們遞歸設(shè)置為false以返回具有該名稱的直接子節(jié)點(diǎn)刊棕。如果為true,它將解析所有節(jié)點(diǎn)待逞,直到找到它為止甥角。我們知道SketchUp是場(chǎng)景中唯一的節(jié)點(diǎn),所以在我們的情況下识樱,真實(shí)的不準(zhǔn)確蜈膨。之后,我們將變量名稱分配給模型的名稱牺荠。最后翁巍,此函數(shù)將在調(diào)用時(shí)返回模型。

let scene = SCNScene(named: "art.scnassets/\(name)/\(name).scn")!
guard let model = scene.rootNode.childNode(withName: "SketchUp", recursively: false) else {return nil}
model.name = name
return model

可選:PIVOT POINT FIX

如果您需要將模型的軸心點(diǎn)修改為所有3軸的中心休雌,那么您可以在此處執(zhí)行此操作灶壶。您可以使用以下公式。別客氣杈曲。

let min = model.boundingBox.min
let max = model.boundingBox.max

model.pivot = SCNMatrix4MakeTranslation(
    min.x + (max.x - min.x) / 2,
    min.y + (max.y - min.y) / 2,
    min.z + (max.z - min.z) / 2)

顯示模型

我們剛剛完成了這個(gè)功能驰凛,現(xiàn)在胸懈,我們準(zhǔn)備在點(diǎn)擊按鈕時(shí)在場(chǎng)景中顯示我們的模型。讓我們轉(zhuǎn)到ViewController.swift并剪切動(dòng)作函數(shù)addObjectButtonTapped并將其粘貼到這里以將其全部放在一個(gè)地方恰响。

我們首先確保焦點(diǎn)方塊首先存在趣钱,因?yàn)樗辉跈z測(cè)到表面時(shí)才出現(xiàn)在屏幕上。

guard focusSquare != nil else {return}

我們選擇展示的模型是iPhoneX胚宦。因此首有,我們將使用getModel函數(shù)檢索該模型。如果由于某種原因它失敗了枢劝,我們將打印一條消息給我們井联。然后,讓我們用一個(gè)小消息將它添加到場(chǎng)景中您旁。

let modelName = "iPhoneX"
guard let model = getModel(named: modelName) else {
    print("Unable to load \(modelName) from file")
    return
}

sceneView.scene.rootNode.addChildNode(model)
print("\(modelName) added successfully")

運(yùn)行應(yīng)用程序烙常。您將意識(shí)到該設(shè)備不僅站起來而且漂浮在空中。當(dāng)然鹤盒,我們已經(jīng)在場(chǎng)景中添加了我們的模型蚕脏,我們還沒有把它放在表面上。所以侦锯,讓我們這樣做驼鞭。

命中測(cè)試

顯然,我們將再次使用命中測(cè)試率触,方法與之前相同终议。

let hitTest = sceneView.hitTest(screenCenter, types: .existingPlaneUsingExtent)
guard let worldTransformColumn3 = hitTest.first?.worldTransform.columns.3 else {return}
model.position = SCNVector3(worldTransformColumn3.x, worldTransformColumn3.y, worldTransformColumn3.z)

翻轉(zhuǎn)設(shè)備

要將電話平放在桌子上汇竭,請(qǐng)打開iPhoneX.scn葱蝗。在“ 節(jié)點(diǎn)”檢查器中,將x Euler Angle重置為0细燎。

讓我們?cè)僭囈淮巍两曼,F(xiàn)在,我們的設(shè)備看起來更像是在房間里玻驻。

縮放模型

如果您選擇了其他型號(hào)悼凑,您可能已經(jīng)注意到尺寸不合適。因此璧瞬,我們將擴(kuò)展它們中的每一個(gè)户辫。我們?cè)?strong>iPhoneX的場(chǎng)景編輯器中完成了它。現(xiàn)在嗤锉,我們?cè)谶@里撤消它并代之以編碼渔欢。讓我們?yōu)樗羞吔鐚⒈壤呕氐?strong>1。

回到ViewController + ObjectAddition并在getModel函數(shù)中瘟忱,我們首先為比例聲明一個(gè)變量奥额,然后根據(jù)模型設(shè)置不同的值苫幢。在我們的情況下,使用[switch]控制流來匹配我們?cè)O(shè)置的許多條件是完美的垫挨。switch語句必須是詳盡的韩肝,這就是為什么有一個(gè)默認(rèn)情況來涵蓋所有其他方案。

var scale: CGFloat

switch name {
    case "iPhoneX":         scale = 0.025
    case "iPhone6s":        scale = 0.025
    case "iPhone7":         scale = 0.0001
    case "iPhone8":         scale = 0.000008
    case "iPhone8Plus":     scale = 0.000008
    case "iPad4":           scale = 0.0006
    case "MacBookPro13":    scale = 0.0029
    case "iMacPro":         scale = 0.0245
    case "AppleWatch":      scale = 0.0000038
    default:                scale = 1
}

在返回之前將模型縮放到我們之前分配的值九榔。

model.scale = SCNVector3(scale, scale, scale)

場(chǎng)景模型

知道我們?cè)趫?chǎng)景中有多少模型會(huì)很高興哀峻。在ViewController.swift中,將一個(gè)新的類變量聲明為一個(gè)節(jié)點(diǎn)數(shù)組帚屉,我們將其初始化為空谜诫。

var modelsInTheScene: Array<SCNNode> = []

返回ViewController + ObjectAddition.swift,并在addObjectButtonTapped操作方法的末尾攻旦,您添加的每個(gè)模型追加到數(shù)組modelsInTheScene中喻旷。然后,打印該數(shù)組的計(jì)數(shù)牢屋。

modelsInTheScene.append(model)
print("Currently have \(modelsInTheScene.count) model(s) in the scene")

我們?nèi)绾芜\(yùn)行應(yīng)用程序并堅(jiān)果且预?

焦點(diǎn)方塊隱藏/顯示選項(xiàng)

當(dāng)我們?cè)谄聊簧巷@示模型時(shí),我們?nèi)匀豢吹浇裹c(diǎn)方塊干擾了我們漂亮的模型烙无。如果我們?cè)诎仓煤箅[藏它锋谐,你怎么說?

FocusSquare類中截酷,讓我們創(chuàng)建一個(gè)函數(shù)來為焦點(diǎn)方塊的表示設(shè)置動(dòng)畫涮拗。將隱藏和顯示兩種情況,因此隱藏值是布爾值迂苛。然后我們聲明一個(gè)SCNAction用于淡入淡出三热,淡出用于隱藏和淡入顯示。這些行動(dòng)將運(yùn)行根據(jù)是否隱藏是真還是假三幻,一前一后就漾。為此目的使用序列

func setHidden(to hidden: Bool) {
    var fadeTo: SCNAction
        
    if hidden {
        fadeTo = .fadeOut(duration: 0.5)
    } else {
        fadeTo = .fadeIn(duration: 0.5)
    }
        
    let actions = [fadeTo, .run({ (focusSquare: SCNNode) in
        focusSquare.isHidden = hidden
    })]
    runAction(.sequence(actions))
}

視圖觀點(diǎn)

下一步將有點(diǎn)棘手念搬。如果我們看到模型抑堡,我們希望隱藏焦點(diǎn)方塊,對(duì)吧朗徊?但是首妖,如果我們?cè)谄聊簧峡床坏饺魏蝺?nèi)容呢?我們?cè)俅涡枰鼇磉x擇下一個(gè)位置爷恳。我們?cè)谄聊簧峡吹降氖遣粩嘧兓挠欣拢晕覀冃枰?strong>updateFocusSquare()中實(shí)現(xiàn)它。在那里,讓我們將pointOfView設(shè)置為場(chǎng)景視圖的視角妒貌。

guard let pointOfView = sceneView.pointOfView else {return}

然后通危,讓我們將firstVisibleModel的定義作為場(chǎng)景中的第一個(gè)模型。我們正在使用第一個(gè)返回滿足條件的第一個(gè)元素的方法灌曙。如果節(jié)點(diǎn)從視角可見菊碟,它將返回true或false 。

let firstVisibleModel = modelsInTheScene.first { (node) -> Bool in
    return sceneView.isNode(node, insideFrustumOf: pointOfView)
}

隱藏/顯示焦點(diǎn)方塊

現(xiàn)在在刺,如果第一個(gè)模型是可見的而不是零逆害,則模型將在視圖中可見。請(qǐng)記住蚣驼,如果顯示模型魄幕,我們將隱藏焦點(diǎn)方塊,反之亦然颖杏。如果這兩個(gè)因子的值不相等纯陨,我們將改變焦點(diǎn)平方的isHidden值。

let modelsAreVisible = firstVisibleModel != nil

if modelsAreVisible != focusSquareLocal.isHidden {
    focusSquareLocal.setHidden(to: modelsAreVisible)
}

實(shí)際上留储,這一切都令人困惑翼抠。我們實(shí)際上沒有選擇,因?yàn)楣?jié)點(diǎn)具有isHidden的屬性获讳,并且不顯示一個(gè)for阴颖。好吧,不是我所知道的丐膝。

那么量愧,讓我們來看看這兩個(gè)場(chǎng)景。如果modelsAreVisible為true且focusSquareLocal.isHidden為false帅矗,則表示兩者都可見偎肃,然后使setHidden為true(與modelsAreVisible值相同)以隱藏焦點(diǎn)方塊。另一方面损晤,如果modelsAreVisible為false且focusSquareLocal.isHidden為true软棺,則兩者都無處可見红竭,然后setHidden為false以顯示焦點(diǎn)方塊尤勋。聽起來很合乎邏輯。有了它茵宪,讓我們最后一次運(yùn)行應(yīng)用程序最冰。

結(jié)論

經(jīng)過漫長的旅程,我們終于將我們的模型添加到我們的環(huán)境中稀火,好像它們屬于它暖哨。我們?cè)诒竟?jié)中也學(xué)到了其他有用的概念。我們?cè)诠适掳逯卸ㄖ屏宋覀兊囊晥D凰狞,并在代碼中播放動(dòng)畫篇裁。在下一課中沛慢,我們將使用虛擬對(duì)象本身。敬請(qǐng)關(guān)注达布。

原文: https://designcode.io/arkit-adding-models

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末团甲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子黍聂,更是在濱河造成了極大的恐慌躺苦,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件产还,死亡現(xiàn)場(chǎng)離奇詭異匹厘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)脐区,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門愈诚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人牛隅,你說我怎么就攤上這事扰路。” “怎么了倔叼?”我有些...
    開封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵汗唱,是天一觀的道長。 經(jīng)常有香客問我丈攒,道長哩罪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任巡验,我火速辦了婚禮际插,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘显设。我一直安慰自己框弛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開白布捕捂。 她就那樣靜靜地躺著瑟枫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪指攒。 梳的紋絲不亂的頭發(fā)上慷妙,一...
    開封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音允悦,去河邊找鬼膝擂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的架馋。 我是一名探鬼主播狞山,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼叉寂!你這毒婦竟也來了铣墨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤办绝,失蹤者是張志新(化名)和其女友劉穎伊约,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體孕蝉,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屡律,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了降淮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片超埋。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖佳鳖,靈堂內(nèi)的尸體忽然破棺而出霍殴,到底是詐尸還是另有隱情,我是刑警寧澤系吩,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布来庭,位于F島的核電站,受9級(jí)特大地震影響穿挨,放射性物質(zhì)發(fā)生泄漏月弛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一科盛、第九天 我趴在偏房一處隱蔽的房頂上張望帽衙。 院中可真熱鬧,春花似錦贞绵、人聲如沸厉萝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谴垫。三九已至,卻和暖如春蜡饵,著一層夾襖步出監(jiān)牢的瞬間弹渔,已是汗流浹背胳施。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來泰國打工溯祸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓焦辅,卻偏偏與公主長得像博杖,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子筷登,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 1 CALayer IOS SDK詳解之CALayer(一) http://doc.okbase.net/Hell...
    Kevin_Junbaozi閱讀 5,160評(píng)論 3 23
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫剃根、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,124評(píng)論 4 61
  • 我去買滿天星 唯一一株滿天星 長勢(shì)喜人的滿天星 當(dāng)時(shí)店員不在 我便想著大不了下次再來買 其實(shí)心里也知道 有些花不在...
    Zack要多喝熱水閱讀 234評(píng)論 0 0
  • 又是一個(gè)雨天前方,心情如這雨狈醉,無助凄涼』菹眨看盡事態(tài)繁華與落幕苗傅,正義的泯滅與人性的殘忍,獨(dú)自一個(gè)人在悲傷班巩,一如這雨渣慕,如訴如泣。
    大道自然123閱讀 296評(píng)論 0 0
  • 現(xiàn)在電商賣家都會(huì)有一種感覺抱慌,電商越來越難做了逊桦,這不僅僅是中小店鋪所面臨的苦惱,第一波入駐某寶的老店鋪也開始重新審視...
    Helloii閱讀 436評(píng)論 0 0