在本課一開始認識 RealityKit ECS 架構之後,接下來最重要的兩個物件,就是 RealityView 的「內容」與「模型個體」(ModelEntity),到現在應該已相當熟悉。

RealityView 「內容」的屬性

「內容」囊括所有虛擬個體,本身也是個物件,內含若干屬性與方法,我們已經用過「內容.add()」,其他屬性與方法並不多,但都相當重要,如下表:

# 內容的屬性/方法 說明 初次使用章節
1 add() 加入個體 6-6a
2 remove() 移除個體 -
3 subscribe() 訂閱(某些事件發布) 6-?
4 entities 所有已加入個體(陣列) 6-6c
5 camera 設定鏡頭(虛擬或實景) 6-?
6 environment 環景(Image-Based Lighting) 6-7c
7 renderingEffects 場景渲染效果選項 -
8 audioListener 空間音效的聆聽位置(預設同鏡頭) -

本節有兩個目標:一是用預設的幾何模型,組成一個實用場景「旋轉樓梯」,其中需要四元數 simd_quatf 計算旋轉角度;二是觀察「內容.entities」有哪些資訊。

仿照 6-6b 座標軸的做法,將「旋轉樓梯()」寫成一個共享函式,傳回模型個體。在左側欄「第6單元共享程式」之下,新增一個「6-6d 旋轉樓梯.swift」檔案,敲入以下程式:

// 6-6d 共享程式:旋轉樓梯
// Created by Heman, 2025/02/12
// Test Environment: iMac 2019 (macOS 15.3) + Swift Playground 4.6.1
import RealityKit

public func 旋轉樓梯(圓柱半徑 r: Float = 0.3, 
                 高 h: Float = 2.0, 
                 樓梯寬 w: Float = 0.5, 
                 樓梯數 n: Int = 20) -> ModelEntity {
    let 圓柱 = MeshResource.generateCylinder(height: h, radius: r)
    let 水泥材質 = SimpleMaterial(color: .gray, roughness: 1.0, isMetallic: false)
    let 圓柱體 = ModelEntity(mesh: 圓柱, materials: [水泥材質])
    圓柱體.name = "旋轉樓梯"
    
    let 橫板 = MeshResource.generateBox(size: [w, w * 0.08, w * 0.4], cornerRadius: w * 0.02)
    let 橘色材質 = SimpleMaterial(color: .orange, roughness: 0.2, isMetallic: false)
    let 樓梯板 = ModelEntity(mesh: 橫板, materials: [橘色材質])
    樓梯板.position.y = -h * 0.5
    圓柱體.addChild(樓梯板)
    
    let 樓梯間距 = h / Float(n)          // 樓梯間距 0.1m
    for i in 0...n {
        let 新樓板 = 樓梯板.clone(recursive: false)
        新樓板.name = "樓板#\\(i)"
        樓梯板.addChild(新樓板)
        
        let 旋轉弧度 = Float.pi * 0.1 * Float(i)    // 旋轉間距 .pi * 0.1 = 18°
        新樓板.orientation = simd_quatf(angle: 旋轉弧度, axis: [0, 1, 0])
        新樓板.position.x = cos(旋轉弧度) * (r + w * 0.4)  // 樓梯寬*0.4:一半再小一點
        新樓板.position.y = 樓梯間距 * Float(i)
        新樓板.position.z = -sin(旋轉弧度) * (r + w * 0.4)
    }
    return 圓柱體
}

參數標籤(argument label) v.s. 參數名稱(parameter name)

在上面宣告「旋轉樓梯()」時,我們用了一個新語法:參數標籤(label),也就是在函式的參數名稱前面再加一個名字:

public func 旋轉樓梯(圓柱半徑 r: Float = 0.3, 
                 高 h: Float = 2.0, 
                 樓梯寬 w: Float = 0.5, 
                 樓梯數 n: Int = 20) -> ModelEntity { }

圓柱半徑、高、樓梯寬、樓梯數是參數標籤,用來充分表達參數的意義,可以讓其他模組看到;而 r, h, w, n 等參數名稱則只用於函式內部,講求簡單方便,自己看懂就行。

一旦用了參數標籤,當我們呼叫此函式時,只能用參數標籤,不能再用參數名稱:

let 一號梯 = 旋轉樓梯(高: 4.5)
// 不能用:
let 一號梯 = 旋轉樓梯(h: 4.5)    // 語法錯誤!

所以參數標籤又稱為外部名稱。若參數標籤為底線 “_”,表示呼叫時前置標籤可省略,這從第1單元就已經用過。

個體.clone(): class v.s. struct

另外,有一行程式碼相當重要,需要特別說明: