Vision應(yīng)用程序構(gòu)建-02-將3D內(nèi)容添加到您的應(yīng)用程序中

為您的visionOS應(yīng)用程序添加深度和維度,并發(fā)現(xiàn)如何將應(yīng)用程序的內(nèi)容整合到人們的環(huán)境中。

概述

帶有立體顯示的設(shè)備讓人們以更真實(shí)的方式體驗(yàn)3D內(nèi)容。內(nèi)容似乎有真正的深度,人們可以從不同的角度觀看它,讓它看起來(lái)像是在他們面前。

在為visionOS構(gòu)建應(yīng)用程序時(shí),請(qǐng)考慮如何為應(yīng)用程序的界面增加深度。該系統(tǒng)提供了多種方式來(lái)顯示3D內(nèi)容,包括在現(xiàn)有窗口、卷和沉浸式空間中。選擇最適合您的應(yīng)用程序和您提供的內(nèi)容的選項(xiàng)。

Window
Volume
Immersive space

為傳統(tǒng)的2D窗口添加深度

Windows是應(yīng)用程序界面的重要組成部分。使用visionOS,應(yīng)用程序會(huì)自動(dòng)獲取具有visionOS外觀和感覺的材料,具有完全可調(diào)整大小的窗口,其間距為眼睛和手的輸入進(jìn)行了調(diào)整,并可以訪問自定義控件的突出顯示調(diào)整。


根據(jù)需要將深度效果整合到自定義視圖中,并使用3D布局選項(xiàng)在窗口中排列視圖。

? Apply a shadow(color:radius:x:y:) or visualEffect(_:) modifier to the view.
? Lift or highlight the view when someone looks at it using a hoverEffect(_:isEnabled:) modifier.
? Lay out views using a ZStack.
? Animate view-related changes with transform3DEffect(_:).
? Rotate the view using a rotation3DEffect(_:axis:anchor:anchorZ:perspective:) modifier.

除了提供更深入的2D視圖外,您還可以將靜態(tài)3D模型添加到2D窗口中。Model3D視圖加載USDZ文件或其他資產(chǎn)類型,并在您的窗口中按其內(nèi)在大小顯示。在應(yīng)用程序中已經(jīng)擁有模型數(shù)據(jù)的地方使用它,或者可以從網(wǎng)絡(luò)下載它。例如,購(gòu)物應(yīng)用程序可能會(huì)使用這種類型的視圖來(lái)顯示產(chǎn)品的3D版本。

使用RealityKit顯示動(dòng)態(tài)3D場(chǎng)景

RealityKit是蘋果用于構(gòu)建3D模型和場(chǎng)景的技術(shù),您可以在屏幕上動(dòng)態(tài)更新。在visionOS中,同時(shí)使用RealityKit和SwiftUI將應(yīng)用程序的2D和3D內(nèi)容無(wú)縫耦合。加載現(xiàn)有的USDZ資產(chǎn)或在Reality Composer Pro中創(chuàng)建場(chǎng)景,其中包含動(dòng)畫、物理、燈光、聲音和內(nèi)容的自定義行為。要在應(yīng)用程序中使用Reality Composer Pro項(xiàng)目,請(qǐng)將Swift軟件包添加到Xcode項(xiàng)目中,并在Swift文件中導(dǎo)入其模塊。有關(guān)更多信息,請(qǐng)參閱在Xcode項(xiàng)目中管理文件和文件夾。

當(dāng)您準(zhǔn)備在界面中顯示3D內(nèi)容時(shí),請(qǐng)使用RealityView。此SwiftUI視圖作為RealityKit內(nèi)容的容器,并允許您使用熟悉的SwiftUI技術(shù)更新該內(nèi)容。

以下示例顯示了使用RealityView顯示3D球體的視圖。視圖閉包中的代碼為球體創(chuàng)建一個(gè)RealityKit實(shí)體,將紋理應(yīng)用于球體的表面,并將球體添加到視圖的內(nèi)容中。

 struct SphereView: View {
    var body: some View {
        RealityView { content in
            let model = ModelEntity(
                         mesh: .generateSphere(radius: 0.1),
                         materials: [SimpleMaterial(color: .white, isMetallic: true)])
            content.add(model)
        }
    }
}

當(dāng)SwiftUI顯示您的RealityView時(shí),它會(huì)執(zhí)行一次您的代碼以創(chuàng)建實(shí)體和其他內(nèi)容。由于創(chuàng)建實(shí)體相對(duì)昂貴,視圖只運(yùn)行一次您的創(chuàng)建代碼。當(dāng)您想更新實(shí)體的狀態(tài)時(shí),請(qǐng)更改視圖的狀態(tài),并使用更新閉包將這些更改應(yīng)用于您的內(nèi)容。當(dāng)縮放屬性中的值發(fā)生變化時(shí),以下示例使用更新閉包來(lái)更改球體的大?。?/p>

struct SphereView: View {
    var scale = false


    var body: some View {
        RealityView { content in
            let model = ModelEntity(
                         mesh: .generateSphere(radius: 0.1),
                         materials: [SimpleMaterial(color: .white, isMetallic: true)])
            content.add(model)
        } update: { content in
            if let model = content.entities.first {
                model.transform.scale = scale ? [1.2, 1.2, 1.2] : [1.0, 1.0, 1.0]
            }
        }
    }
}

有關(guān)如何使用RealityKit創(chuàng)建內(nèi)容的信息,請(qǐng)參閱RealityKit。

響應(yīng)與RealityKit內(nèi)容的交互

要處理與RealityKit場(chǎng)景實(shí)體的交互:
?將手勢(shì)識(shí)別器附加到您的RealityView,并將targetedToAnyEntity()修飾符添加到其中。
?將InputTargetComponent附加到實(shí)體或其父實(shí)體之一。
?向支持交互的RealityKit實(shí)體添加碰撞形狀。

targetedToAnyEntity()修飾符在手勢(shì)識(shí)別器和您的RealityKit內(nèi)容之間架起了一座橋梁。例如,要識(shí)別某人何時(shí)拖動(dòng)實(shí)體,請(qǐng)指定DragGesture并向其添加修飾符。當(dāng)指定的手勢(shì)發(fā)生在實(shí)體上時(shí),SwiftUI執(zhí)行提供的閉包。

以下示例將點(diǎn)擊手勢(shì)識(shí)別器添加到上一個(gè)示例的球形視圖中。該代碼還將InputTargetComponentCollisionComponent組件添加到形狀中,以允許交互發(fā)生。如果您省略這些組件,視圖將不會(huì)檢測(cè)到與您的實(shí)體的交互。

struct SphereView: View {
    @State private var scale = false


    var body: some View {
        RealityView { content in
            let model = ModelEntity(
                mesh: .generateSphere(radius: 0.1),
                materials: [SimpleMaterial(color: .white, isMetallic: true)])


            // Enable interactions on the entity.
            model.components.set(InputTargetComponent())
            model.components.set(CollisionComponent(shapes: [.generateSphere(radius: 0.1)]))
            content.add(model)
        } update: { content in
            if let model = content.entities.first {
                model.transform.scale = scale ? [1.2, 1.2, 1.2] : [1.0, 1.0, 1.0]
            }
        }
        .gesture(TapGesture().targetedToAnyEntity().onEnded { _ in
            scale.toggle()
        })
    }
}

在Volume中顯示3D內(nèi)容

Volume是一種以三維方式增長(zhǎng)的窗口,以匹配其包含的內(nèi)容的大小。Windows和Volume都可以容納2D和3D內(nèi)容,并且在許多方面是相似的。然而,Windows剪輯3D內(nèi)容從窗口表面延伸得太遠(yuǎn),因此Volume是主要是3D內(nèi)容的更好選擇。

要?jiǎng)?chuàng)建Volume,請(qǐng)將WindowGroup場(chǎng)景添加到您的應(yīng)用程序中,并將其樣式設(shè)置為體積。此樣式告訴SwiftUI為3D內(nèi)容創(chuàng)建一個(gè)窗口。在Volume中包含您想要的任何2D或3D視圖。您還可以添加RealityView來(lái)使用RealityKit構(gòu)建內(nèi)容。以下示例使用存儲(chǔ)在應(yīng)用程序捆綁包中的一些氣球的靜態(tài)3D模型創(chuàng)建一個(gè)Volume:

struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            Model3D("balloons")
        }.windowStyle(style: .volumetric)
    }
}

Windows和Volume是顯示有界2D和3D內(nèi)容的便捷方式,但您的應(yīng)用程序不會(huì)控制該內(nèi)容在人周圍的位置。系統(tǒng)在顯示時(shí)設(shè)置每個(gè)窗口和音量的初始位置。該系統(tǒng)還添加了一個(gè)窗口欄,允許某人重新定位窗口或調(diào)整其大小。


有關(guān)何時(shí)使用Volume的更多信息,請(qǐng)參閱人機(jī)界面指南>Windows。

在一個(gè)人的環(huán)境中顯示3D內(nèi)容

當(dāng)您需要對(duì)應(yīng)用程序內(nèi)容的位置進(jìn)行更多控制時(shí),請(qǐng)將該內(nèi)容添加到ImmersiveSpace中。沉浸式空間為您的內(nèi)容提供了一個(gè)無(wú)界區(qū)域,您可以控制空間內(nèi)內(nèi)容的大小和位置。獲得用戶許可后,您還可以使用帶有沉浸式空間的ARKit將內(nèi)容集成到周圍環(huán)境中。例如,您可以使用ARKit場(chǎng)景重建來(lái)獲取家具和附近對(duì)象的網(wǎng)格,并讓您的內(nèi)容與該網(wǎng)格交互。

ImmersiveSpace是您與應(yīng)用程序的其他場(chǎng)景一起創(chuàng)建的場(chǎng)景類型。以下示例顯示了一個(gè)包含沉浸式空間和窗口的應(yīng)用程序:

@main
struct MyImmersiveApp: App {
    var body: some Scene {
        WindowGroup() {
            ContentView()
        }


        ImmersiveSpace(id: "solarSystem") {
            SolarSystemView()
        }
    }
}

如果您沒有在ImmersiveSpace聲明中添加樣式修飾符,系統(tǒng)將使用mixed樣式創(chuàng)建該空間。此樣式顯示您的內(nèi)容以及顯示該人周圍環(huán)境的傳遞內(nèi)容。其他樣式允許您在不同程度上隱藏直通。使用 immersionStyle(selection:in:)修飾符來(lái)指定您的空間支持哪些樣式。如果您指定了多個(gè)樣式,您可以使用修飾符的選擇參數(shù)在樣式之間切換。

警告:
注意您在使用混合風(fēng)格的沉浸式場(chǎng)景中包含多少內(nèi)容。填充屏幕很大一部分的內(nèi)容,即使該內(nèi)容部分是透明的,也可以防止人們看到周圍環(huán)境的潛在危險(xiǎn)。如果您想讓人沉浸在您的內(nèi)容中,請(qǐng)以完整的風(fēng)格配置您的空間。有關(guān)更多信息,請(qǐng)參閱在應(yīng)用程序中創(chuàng)建完全身臨其境的體驗(yàn)。

請(qǐng)記住設(shè)置您放置在沉浸式空間中的物品的位置。使用修飾符定位SwiftUI視圖,并使用其轉(zhuǎn)換組件定位RealityKit實(shí)體。SwiftUI最初將空間的起源放在人的腳下,但可以根據(jù)其他事件改變這個(gè)起源。例如,系統(tǒng)可能會(huì)移動(dòng)原點(diǎn),以適應(yīng)SharePlay活動(dòng),該活動(dòng)以空間角色顯示您的內(nèi)容。如果您需要將SwiftUI視圖和RealityKit實(shí)體相對(duì)定位,請(qǐng)使用RealityView內(nèi)容參數(shù)中的方法執(zhí)行任何所需的坐標(biāo)轉(zhuǎn)換。

要顯示您的ImmersiveSpace場(chǎng)景,請(qǐng)使用您從SwiftUI環(huán)境獲得的openImmersiveSpace操作打開它。此操作異步運(yùn)行,并使用提供的信息來(lái)查找和初始化您的場(chǎng)景。以下示例顯示了一個(gè)用太陽(yáng)系標(biāo)識(shí)符打開空間的按鈕:

Button("Show Solar System") {
    Task {
        let result = await openImmersiveSpace(id: "solarSystem")
        if case .error = result {
            print("An error occurred")
        }
    }
}

當(dāng)應(yīng)用程序顯示ImmersiveSpace時(shí),系統(tǒng)會(huì)隱藏其他應(yīng)用程序的內(nèi)容,以防止視覺沖突。當(dāng)您的空間可見時(shí),其他應(yīng)用程序保持隱藏狀態(tài),但在您關(guān)閉它時(shí)返回。如果您的應(yīng)用程序定義了多個(gè)空間,您必須在顯示其他空間之前關(guān)閉當(dāng)前可見的空間。如果您不關(guān)閉可見空間,當(dāng)您嘗試打開另一個(gè)空間時(shí),系統(tǒng)會(huì)發(fā)出運(yùn)行時(shí)警告。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容