在本課一開始提過,粒子系統是為了模擬自然界的一些現象,例如雲霧、火焰,這些現象在似有似無之間,運動軌跡變幻莫測,非常難以計算,甚至比登月軌道還難,如今透過大量算力,總算可以完美模擬出來。
粒子系統的應用場景非常多,但都必須客製化,要熟悉60多個參數並不容易,還好 RealityKit 提供6種內建特效作為模板,幫助我們很快做出想要的視覺特效。
本課最後一節範例,想從 ParticleEmitterComponent() 預設值來客製化粒子系統,利用其中一個關鍵參數 vortexStrength (渦流力場)來做出龍捲風特效,配合前面所學的動作動畫,模擬一個行進中的龍捲風。
實際效果如下,跟預期有點落差,變成是龍捲風+暴風雪的感覺:
不過實際程式倒是不難,只調整13個參數,其中幾個重要參數都有 “Variation”,能提供隨機性,讓特效看起來比較自然。
完整程式如下:
// 6-12e 客製化粒子系統:龍捲風
// Created by Heman Lu on 2025/06/23
// Minimum Requirement: macOS 15 or iPadOS 18 + Swift Playground 4.6
import SwiftUI
import RealityKit
struct 粒子系統: View {
var body: some View {
RealityView { 內容 in
內容.add(座標軸()) // 共享程式6-6b
// (1) 粒子系統
var 龍捲風 = ParticleEmitterComponent()
龍捲風.speed = 2.4 // ← 0.5
龍捲風.speedVariation = 1.0 // ← 0
龍捲風.emitterShapeSize = [0.05, 0.05, 0.05] // ← 0.1
龍捲風.mainEmitter.birthRate = 50000 // ← 100
龍捲風.mainEmitter.birthRateVariation = 10000
龍捲風.mainEmitter.lifeSpan = 3.0 // ← 1
龍捲風.mainEmitter.lifeSpanVariation = 2.0 // ← 0.2
龍捲風.mainEmitter.size = 0.02 // ← 0.02
龍捲風.mainEmitter.sizeVariation = 0.01 // ← 0
龍捲風.mainEmitter.color = .constant(.random(a: .white, b: .black))
龍捲風.fieldSimulationSpace = .local // ← .global
龍捲風.mainEmitter.vortexStrength = -36.0 // 逆時針旋轉
龍捲風.mainEmitter.noiseStrength = 2.5 // ← 0
// (2) 隱形個體:承載粒子系統
let 暴風眼 = Entity()
暴風眼.name = "暴風眼"
暴風眼.position = [0, -1.0, 0]
暴風眼.components.set(龍捲風)
內容.add(暴風眼)
// (3) 隱形個體:調節「暴風眼」的運動路徑
let 圓心 = Entity()
圓心.name = "圓心"
圓心.position = [0, -1, -2]
內容.add(圓心)
// (4) 令「暴風眼」繞著「圓心」轉動
let 繞圈轉 = OrbitEntityAction(
pivotEntity: .entityNamed("圓心"),
revolutions: 1.0)
if let 動畫 = try? AnimationResource.makeActionAnimation(
for: 繞圈轉,
duration: 51.0,
bindTarget: .transform,
repeatMode: .repeat
) {
暴風眼.playAnimation(動畫)
}
// (5) 令「圓心」在直線上來回移動
let 位移 = FromToByAction(by: Transform(translation: [-2, 0, -1]))
if let 動畫2 = try? AnimationResource.makeActionAnimation(
for: 位移,
duration: 19.0,
bindTarget: .transform,
repeatMode: .autoReverse
) {
圓心.playAnimation(動畫2)
}
if let 天空盒 = try? await EnvironmentResource(named: "威尼斯清晨") {
內容.environment = .skybox(天空盒)
}
}
.realityViewCameraControls(.orbit)
}
}
import PlaygroundSupport
PlaygroundPage.current.setLiveView(粒子系統())
程式用到一個新的物件 Entity(),這其實是 ModelEntity 的父類別(用 class 定義,會有階層關係),是最簡單的個體,只包含 Transform 與 SynchronizationComponent 兩個必要元件。
為什麼用 Entity() 而不用 ModelEntity() 呢?
實際上,所有個體都可以從 Entity() 加上不同元件而來:加上 ModelComponent 就變成模型個體,可顯示在場景中;加上 CollisionComponent 跟 PhysicsBodyComponent 就得到物理模擬效果。因此,用 Entity() 加上元件(參考補充(23) RealityKit 元件列表),我們可以任意組合新的個體。
範例中,我們用 Entity() 加上粒子元件(ParticleEmitterComponent),就變成最簡單的粒子特效,因為沒有 ModelComponent,所以個體是隱形的,只能看到粒子特效;而且因為 Entity() 包含座標變換元件,所以可配合動作動畫。