今日更新總結:Unity 2D 粒子模擬系統全面優化

2 min read
一、模組化重構
拆分核心腳本
ParticleManager.cs
:物件池化管理粒子與標籤GravitationalSystem.cs
:萬有引力、合併/分裂邏輯EnergyTracker.cs
:動能統計與能量柱圖顯示SimulationUIBindings.cs
:延遲啟動與 UI 控制LogExporter.cs
:粒子狀態與碰撞事件日誌輸出SimulationController.cs
:流程調度、暫停/重置/倍率控制
延遲啟動
// SimulationController.cs private bool hasStarted = false; public void StartSimulation() { if (hasStarted) return; hasStarted = true; // 初始化模組… GenerateParticlesFromInput(); StartCoroutine(LogLoop()); } private void Update() { if (!hasStarted || isPaused) return; // 模擬邏輯… }
二、效能優化五部曲
1. 物理計算節流
// SimulationController.cs
private int physicsFrameCounter = 0;
public int physicsFrameThreshold = 2; // 每兩幀執行一次
private void FixedUpdate()
{
if (!hasStarted || isPaused) return;
if (++physicsFrameCounter % physicsFrameThreshold == 0)
gravitationalSystem.ApplyForces();
}
2. 全面物件池化
// ParticleManager.cs (節錄)
private Queue<GameObject> particlePool = new();
private Queue<TMP_Text> labelPool = new();
private void Awake()
{
for (int i = 0; i < initialParticlePoolSize; i++)
{
var p = Instantiate(particlePrefab, transform);
p.SetActive(false);
particlePool.Enqueue(p);
}
// 標籤池同理…
}
public void CreateParticle(Vector2 pos, Vector2 vel, float mass)
{
var p = particlePool.Count>0 ? particlePool.Dequeue() : Instantiate(particlePrefab, transform);
p.SetActive(true);
// 設定 Rigidbody2D、顏色、大小…
var lbl = labelPool.Count>0 ? labelPool.Dequeue() : Instantiate(labelPrefab, worldCanvas.transform).GetComponent<TMP_Text>();
lbl.gameObject.SetActive(true);
// 註冊到 activeParticles / activeLabels…
}
3. GC 分配優化
// LogExporter.cs (節錄)
private StringBuilder sbScreen = new();
private StringBuilder sbFile = new();
public void LogParticleStates()
{
sbScreen.Clear();
sbFile.Clear();
float t = Time.time;
foreach (var p in particleManager.GetActiveParticles())
{
// AppendLine 到 sbScreen / sbFile…
}
logOutputText.text = sbScreen.ToString();
File.AppendAllText(logFilePath, sbFile.ToString());
// collisionLog…
}
4. 尾部交換移除法
// ParticleManager.cs (節錄)
public void RemoveParticle(int idx)
{
int last = activeParticles.Count - 1;
if (idx < 0 || idx > last) return;
var p = activeParticles[idx];
var lbl = activeLabels [idx];
// 回收到池…
if (idx != last)
{
activeParticles[idx] = activeParticles[last];
activeLabels [idx] = activeLabels [last];
}
activeParticles.RemoveAt(last);
activeLabels .RemoveAt(last);
}
5. Coroutine 定時更新
// EnergyTracker.cs
private IEnumerator EnergyLoop()
{
while (true)
{
yield return new WaitForSeconds(energyLogInterval);
LogTotalKineticEnergy();
}
}
private void Start()
{
StartCoroutine(EnergyLoop());
}
三、UI 顯示控制
開始前:只顯示「粒子數量輸入框」「速度輸入框」「開始按鈕」。
按下開始:隱藏初始輸入欄,顯示完整控制面板(暫停、重置、速度滑桿、能量圖、日誌等)。
重置:清除所有粒子與標籤,重新顯示開始按鈕。
// SimulationUIBindings.cs (節錄)
public GameObject[] uiHideOnStart;
public GameObject[] uiShowAfterStart;
startButton.onClick.AddListener(()=>
{
controller.StartSimulation();
foreach(var go in uiHideOnStart) go.SetActive(false);
foreach(var go in uiShowAfterStart) go.SetActive(true);
startButton.gameObject.SetActive(false);
});
0
Subscribe to my newsletter
Read articles from 郭俊鑫 directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
