Files
XericUIActionVessel/Runtime/BubbleLayout/BubblePhysicsSolver.cs

98 lines
2.9 KiB
C#

using System.Collections.Generic;
using UnityEngine;
namespace XericUI.BubbleLayout
{
/// <summary>
/// 2D 物理力解算器 —— 使用半隐式欧拉积分进行速度/位置更新
/// </summary>
public static class BubblePhysicsSolver
{
/// <summary>
/// 对气泡元素列表执行一步物理积分,使用每元素自身的 mass 值
/// </summary>
/// <param name="items">气泡元素列表</param>
/// <param name="deltaTime">时间步长</param>
/// <param name="damping">速度阻尼系数 (0=无阻尼, 1=完全停止)</param>
public static void Integrate(List<BubbleItemData> items, float deltaTime, float damping = 0.9f)
{
for (int i = 0; i < items.Count; i++)
{
var item = items[i];
// 保存上一帧状态
item.prevVelocity = item.velocity;
item.prevForce = item.force;
// 半隐式欧拉积分: 力 → 加速度 → 速度 → 位置
float itemMass = Mathf.Max(item.mass, 0.001f);
Vector2 acceleration = item.force / itemMass;
// v_new = v_old * damping + a * dt
item.velocity = item.velocity * (1f - damping) + acceleration * deltaTime;
// 动量 = m * v
item.momentum = item.velocity * itemMass;
// p_new = p_old + v_new * dt
item.position += item.velocity * deltaTime;
// 角动量积分
item.rotation += item.angularMomentum * deltaTime;
// 清除本帧力(力只持续一帧,需要每帧重新施加)
item.force = Vector2.zero;
}
}
/// <summary>
/// 计算两个位置之间的排斥力(用于气泡排挤)
/// </summary>
/// <param name="posA">元素A中心位置</param>
/// <param name="sizeA">元素A尺寸</param>
/// <param name="posB">元素B中心位置</param>
/// <param name="sizeB">元素B尺寸</param>
/// <param name="stiffness">排斥刚度</param>
/// <returns>施加在A上的排斥力</returns>
public static Vector2 CalculateRepulsionForce(
Vector2 posA, Vector2 sizeA,
Vector2 posB, Vector2 sizeB,
float stiffness = 1f)
{
Vector2 delta = posA - posB;
Vector2 halfSizes = (sizeA + sizeB) * 0.5f;
// 计算X方向重叠量
float overlapX = halfSizes.x - Mathf.Abs(delta.x);
// 计算Y方向重叠量
float overlapY = halfSizes.y - Mathf.Abs(delta.y);
// 仅在重叠时产生力
if (overlapX <= 0f || overlapY <= 0f)
return Vector2.zero;
// 选择最小重叠方向进行排斥
Vector2 force;
if (overlapX < overlapY)
{
float sign = delta.x > 0f ? 1f : -1f;
force = new Vector2(sign * overlapX * stiffness, 0f);
}
else if (overlapY < overlapX)
{
float sign = delta.y > 0f ? 1f : -1f;
force = new Vector2(0f, sign * overlapY * stiffness);
}
else
{
// 重叠相等时双向排斥
float signX = delta.x > 0f ? 1f : -1f;
float signY = delta.y > 0f ? 1f : -1f;
force = new Vector2(signX * overlapX * stiffness * 0.5f, signY * overlapY * stiffness * 0.5f);
}
return force;
}
}
}