【5/8 更新】2d 粒子模擬系統-萬有引力還原、全系統速度上限、能量門檻設定

郭俊鑫郭俊鑫
2 min read

1. 純粹萬有引力還原,移除阻尼與軟化

  • 動力學:回到經典 F=G m1m2/r2F = G\,m_1m_2/r^2,不再在分母加 ε 軟化,也不做任何速度阻尼。

  • 程式碼

      // GravitationalSystem.ApplyForces()
      float forceMag = gravitationalConstant * (rbA.mass * rbB.mass) / (dist * dist);
      Vector2 force    = forceMag * dir.normalized;
      rbA.AddForce(force);
      rbB.AddForce(-force);
    

2. 全系統速度上限(systemSpeedLimit)

  • 需求:不只是初速限制,而是讓所有粒子在任何時刻都無法超過指定速度。

  • 實作:在 SimulationController.FixedUpdate() 的力運算後呼叫:

      private void ClampVelocities()
      {
          foreach (var p in particleManager.GetActiveParticles())
          {
              var rb = p.GetComponent<Rigidbody2D>();
              if (rb.velocity.magnitude > systemSpeedLimit)
                  rb.velocity = rb.velocity.normalized * systemSpeedLimit;
          }
      }
    
  • 初始化:由使用者在 UI 輸入框設定 systemSpeedLimit,並在 GenerateParticlesFromInput() 一併讀取。


3. 能量門檻動態設定

  • 門檻欄位:在 GravitationalSystem 中新增

      public float mergeEnergyThreshold; // 合併時總動能 ≤ 此值
      public float splitEnergyThreshold; // 分裂時總動能 ≥ 此值
    
  • 同步速度上限SimulationController 讀取 systemSpeedLimit 後,計算「最大動能」Kmax⁡=12mvmax⁡2K_{\max} = \tfrac12 m v_{\max}^2,並依 UI 參數 mergeThresholdFactorsplitThresholdFactor 自動更新:

      float maxEnergy = 0.5f * systemSpeedLimit * systemSpeedLimit;
      gravitationalSystem.mergeEnergyThreshold = mergeThresholdFactor * maxEnergy;
      gravitationalSystem.splitEnergyThreshold = splitThresholdFactor * maxEnergy;
    

4. 嚴格守恆驗證:合併與分裂

  • 合併

      private void MergeParticles(...)
      {
          // 計算合併前質量與動量
          float mA = rbA.mass, mB = rbB.mass;
          Vector2 pBefore = mA*rbA.velocity + mB*rbB.velocity;
    
          // 更新質量與速度
          float mTotal = mA + mB;
          rbA.mass     = mTotal;
          rbA.velocity = pBefore / mTotal;
    
          // 視覺更新 ← 調整大小與顏色
          // …
    
          // 驗證質量與動量守恆
          Debug.Assert(Mathf.Approximately(rbA.mass, mTotal));
          Vector2 pAfter = rbA.mass * rbA.velocity;
          Debug.Assert((pAfter - pBefore).sqrMagnitude < 1e-6f);
    
          particleManager.RemoveParticle(j);
      }
    
  • 分裂

      private void FragmentParticles(...)
      {
          float mB = rbB.mass;
          Vector2 pBefore = mB * rbB.velocity;
    
          // 移除原粒子,產生兩個子粒子(各半質量、動量分配)
          // …
    
          // 驗證守恆
          float mAfter = fragMass*2f;
          Vector2 pAfter = fragMass*v1 + fragMass*v2;
          Debug.Assert(Mathf.Approximately(mAfter, mB));
          Debug.Assert((pAfter - pBefore).sqrMagnitude < 1e-6f);
      }
    
  • 如此一來,每次合併/分裂都會在 Console 中檢測並確保質量、動量嚴格不變。


0
Subscribe to my newsletter

Read articles from 郭俊鑫 directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

郭俊鑫
郭俊鑫