粒子系統在預設情況下,是不受外力作用,也不受重力或其他力場影響,行為完全由參數控制。但實際上,即使是火焰,也會受到氣流(流體力學的力場)的影響,因此必要時可開啟物理模擬,讓效果更逼真。開啟方式如下:
粒子系統.isAffectedByGravity = true // 受重力影響
// 或
粒子系統.isAffectedByPhysicsFields = true // 受其他力場影響
重力與(其他)力場可分別設定,一旦設定,整個粒子系統都會受到影響。SceneKit 場景中預設會有重力,所以一旦開啟受重力影響(isAffectedByGravity),會立刻生效(粒子往下掉);若開啟受其他力場影響(isAffectedByPhysicsFields),則還需另外設定力場。在上一課物理模擬中,我們尚未用過力場,本節就來試試看。
設定物理力場要用 SCNPhysicsField 物件,其中的 Field 譯為「場」,意思是一片區域或空間,例如操場、曬穀場,在此指受到某些力持續作用的範圍,故稱力場。半導體中有個名詞叫「場效電晶體(FET)」, F代表Field,同樣的意思。
SceneKit 支援以下幾種物理力場,使用 SCNPhysicsField 的類型方法來產出:
1. SCNPhysicsField.drag() — 拖曳力場(流體或空氣阻力)
2. SCNPhysicsField.vortex() — 旋轉力場、渦流場
3. SCNPhysicsField.radialGravity() — 放射狀重力場
4. SCNPhysicsField.linearGravity() — 線性重力場
5. SCNPhysicsField.noiseField() — 雜訊力場
6. SCNPhysicsField.turbulenceField() — 亂流、紊流場
7. SCNPhysicsField.spring() — 彈力場
8. SCNPhysicsField.electric() — 電場
9. SCNPhysicsField.magnetic() — 磁場
10. SCNPhysicsField.customField() — 客製化力場
力場產出之後,還需調整相關屬性,所有屬性都有預設值,說明如下表:
| # | 力場屬性 | 預設值 | 說明 |
| 1 | halfExtent | (無限, 無限, 無限) | 力場半徑(以節點位置為中心,但預設力場範圍並非圓球形,而是立方形)。例如,若節點位於原點(0, 0, 0),halfExtent = (1, 1, 1),則力場有效範圍為(-1, -1, -1) ~ (1, 1, 1) |
| 2 | scope | .insideExtent | 指定力場作用於halfExtent範圍內或範圍外 .insideExtent 範圍內 .outsideExtent 範圍外 |
| 3 | usesEllipsoidalExtent | false | 是否改為橢圓球或圓球形範圍(預設為立方形範圍,較好計算) |
| 4 | offset | (0, 0, 0) | 力場中心偏移(相對於節點中心) |
| 5 | direction | (0, -1, 0) | 力場中心軸(適用部分力場) |
| 6 | strength | 1 (依力場種類而異) | 力場強度(倍數) |
| 7 | falloffExponent | 0-2 (依力場種類而異) | 衰減指數(數值越大,衰減越快),僅對某些會衰減的力場有效 |
| 8 | minimumDistance | 近乎0 | 力場開始衰減的距離(minimumDistance以內不衰減,僅對某些力場有效) |
| 9 | isActive | true | 力場是否生效 |
| 10 | isExclusive | false | 是否獨佔,若true,則排除其他力場作用 |
| 11 | categoryBitMask | -1 (所有類別) | 適用於同類的物理模擬節點(參考補充6碰撞處理說明) |
不同力場,會對粒子系統產生不同的視覺效果,本節以渦流SCNPhysicsField.vortex()來示範,做一個類似龍捲風的效果。vortex 是漩渦、渦流或旋風的意思,和 vertex 幾何頂點英文很像,請勿搞混。
力場的程式碼比起粒子系統簡單多了,只要調整一兩個參數,然後將力場附加到某個節點即可:
let 螺旋力場 = SCNPhysicsField.vortex()
螺旋力場.strength = 0.5
暴風眼節點.physicsField = 螺旋力場
自此,該節點附近(預設為無限遠)的粒子系統或物理節點都會持續受力場影響,產生一定的加速度,作用越久,速度越快。
最後做出來的效果如下,形成漏斗狀的文字風暴,像是某些秘笈或寶藏從地底噴發出來的感覺:

完整範例程式如下:
// 6-5c 文字風暴:粒子系統+物理模擬
// Created by Heman, 2024/05/16
import SceneKit
import SwiftUI
struct 粒子系統3: View {
let 化學教室 = SCNScene()
let 助教 = 代理程式()
func 空間座標系(尺寸半徑: CGFloat) -> SCNNode {
let x軸 = SCNTube(innerRadius: 0, outerRadius: 0.01, height: 尺寸半徑 * 2.0)
let x軸節點 = SCNNode(geometry: x軸)
x軸節點.rotation = SCNVector4(x: 0, y: 0, z: 1, w: .pi / -2.0)
let y軸 = SCNTube(innerRadius: 0, outerRadius: 0.01, height: 尺寸半徑 * 2.0)
let y軸節點 = SCNNode(geometry: y軸)
let z軸 = SCNTube(innerRadius: 0, outerRadius: 0.01, height: 尺寸半徑 * 2.0)
let z軸節點 = SCNNode(geometry: z軸)
z軸節點.rotation = SCNVector4(x: 1, y: 0, z: 0, w: .pi / 2.0)
let 座標原點 = SCNNode(geometry: SCNSphere(radius: 0.02))
座標原點.addChildNode(x軸節點)
座標原點.addChildNode(y軸節點)
座標原點.addChildNode(z軸節點)
return 座標原點
}
var body: some View {
SceneView(scene: 化學教室, options: [.autoenablesDefaultLighting, .allowsCameraControl], delegate: 助教)
.onAppear {
let 地面 = SCNFloor()
地面.reflectivity = 0.0
地面.materials.first?.diffuse.contents = UIImage(named: "格線")
let 地面節點 = SCNNode(geometry: 地面)
let 文字風暴 = SCNParticleSystem()
文字風暴.birthRate = 1000
文字風暴.particleLifeSpan = 5
文字風暴.particleImage = UIImage(named: "百字碑")
文字風暴.imageSequenceRowCount = 10
文字風暴.imageSequenceColumnCount = 10
文字風暴.imageSequenceInitialFrame = 50
文字風暴.imageSequenceInitialFrameVariation = 100
文字風暴.particleColor = UIColor.orange
文字風暴.particleSize = 0.2
文字風暴.particleSizeVariation = 0.38
文字風暴.emitterShape = SCNSphere(radius: 0.3)
文字風暴.emissionDuration = 1.0
文字風暴.particleVelocity = 1.0
文字風暴.isAffectedByPhysicsFields = true
let 暴風眼 = SCNSphere(radius: 0.1)
let 暴風眼節點 = SCNNode(geometry: 暴風眼)
暴風眼節點.position.y = 0.5
暴風眼節點.addParticleSystem(文字風暴)
let 螺旋力場 = SCNPhysicsField.vortex()
print(螺旋力場.direction, 螺旋力場.strength)
螺旋力場.strength = 0.5
暴風眼節點.physicsField = 螺旋力場
文字風暴.acceleration = SCNVector3(0, 2.5, 0)
化學教室.rootNode.addChildNode(空間座標系(尺寸半徑: 10))
化學教室.rootNode.addChildNode(地面節點)
化學教室.rootNode.addChildNode(暴風眼節點)
化學教室.background.contents = UIColor.black
}
}
}
class 代理程式: NSObject, SCNSceneRendererDelegate {
func renderer(_ renderer: any SCNSceneRenderer, updateAtTime time: TimeInterval) {
renderer.showsStatistics = true
}
}
import PlaygroundSupport
PlaygroundPage.current.setLiveView(粒子系統3())
為了讓效果更好,除螺旋力場之外,還對粒子系統多加一個向上的加速度(反重力的效果),「文字風暴.acceleration = SCNVector3(0, 2.5, 0)」,可將這行程式碼取消,看看效果有何變化。
💡 註解
- 本節圖片用到前一節(6-5b)的「百字碑.png」以及上一課(6-4c)的「格線.png」。
- 物理力場的「力」,可分為超距力(如重力、電力、磁力)以及非超距力(如拉力、彈力、渦流、紊流),後者又稱接觸力,不過SceneKit只是模擬力學數據,並不需要實際接觸或介質。
- vortex 渦流、vertex 頂點、cortex 大腦皮質,三個英文單字很像,都不是常見的名詞,偶爾看到會容易搞混。字尾 tex 有編織品(textile)、紋理(texture)的意思。
- 在第4課6-4a提過,物理模擬的節點有三種本體,其中只有動態本體(dynamic)會受到力場影響,靜態(static)與活動(kinematic)本體不受力場影響。






































































































