A deterministic 2D rigid body physics engine in pure MoonBit: SAT collision detection, sequential impulse solver with warm starting, Coulomb friction, revolute and distance joints, sleeping, and SVG rendering.
let world = @moonphys.World::new()
// 静态地面
ignore(
world.add_body(
@moonphys.Body::fixed(@moonphys.Shape::box(10.0, 0.1)).at(0.0, -0.1),
),
)
// 一个自由下落的方块
ignore(world.add_body(@moonphys.Body::box(0.5, 0.5).at(0.0, 4.0)))
// 固定步长推进 2 秒
for _ in 0..<120 {
world.step(1.0 / 60.0)
}
println(world.body(1).position().y()) // 0.5:稳稳落在有厚度的地面上| 模块 | 内容 |
|---|---|
| 数学基座 | Vec2、Transform、向量运算符重载、Show 实现 |
| 形状 | 圆、任意凸多边形(逆时针),自动计算质心与转动惯量(平行轴定理) |
| 宽相 | sort-and-sweep:按 AABB 下界插入排序后扫描,稀疏场景近似线性;tie 按 body 下标打破 |
| 窄相 | SAT + reference/incident edge 裁剪(圆/圆、多边形/圆、多边形/多边形),返回多点接触 |
| 求解器 | 顺序冲量法 + 持久接触(Arbiter 按 feature id 迁移冲量做暖启动)+ Baumgarte 位置偏置 |
| 摩擦 | 库仑锥,切向冲量钳制在 ±μ·Pn |
| 恢复系数 | 只在法向相对速度超过阈值时生效,避免静止抖动 |
| 关节 | DistanceJoint(定长)、RevolveJoint(铰链),用 2×2 有效质量矩阵求解 |
| 睡眠 | 线/角速度低于阈值持续 0.5s 后休眠,接触对的动态体互相唤醒 |
| 确定性 | 无随机、无哈希迭代、固定步长 + 稳定排序 → 两次运行逐位一致(有测试) |
| 渲染 | render 包把世界快照输出为 SVG,带网格与配色,可逐帧拼成动画 |
moon add weopqrst/moonphysimport {
"weopqrst/moonphys" @moonphys,
}moon run cmd/main --target js > docs/index.html// 世界
let world = @moonphys.World::new() // 重力默认 (0, -9.81)
world.set_gravity(@moonphys.Vec2::new(0.0, -3.0)) // 低重力
world.set_velocity_iterations(10) // 迭代次数,越大越硬
world.set_sleep_enabled(true)
world.step(1.0 / 60.0) // 固定步长
world.run(120, 1.0 / 60.0) // 等价于循环 step
world.time() // 累计仿真时间
world.arbiter_count() // 当前接触对
world.snapshot() // 渲染/测试用的不可变快照
// 刚体
let body = @moonphys.Body::box(0.5, 0.5) // 动态方块
.at(0.0, 3.0) // 质心位置
.with_rotation(0.3) // 弧度
.with_velocity(1.0, 0.0)
.with_angular_velocity(-0.5)
.with_friction(0.6)
.with_restitution(0.2) // 弹性
.with_density(1.0) // 质量 = 密度 × 面积
.with_damping(0.05, 0.05) // 线/角阻尼
.with_allow_sleep(true)
.with_user_data(3) // 渲染配色用的自定义标记
ignore(world.add_body(body))
// 形状
@moonphys.Shape::box(0.5, 0.5) // 半宽半高
@moonphys.Shape::circle(0.4)
@moonphys.Shape::convex(vertices) // 逆时针凸多边形
// 力与冲量
body.apply_force(@moonphys.Vec2::new(0.0, 10.0)) // 本步有效
body.apply_impulse(@moonphys.Vec2::new(2.0, 0.0), body.position())
body.point_velocity(body.position())
// 查询
body.mass(); body.inertia(); body.aabb(); body.contains_point(p)
body.local_to_world(p); body.world_to_local(p)
// 关节
world.add_distance_joint(index_a, index_b) // 保持当前中心距
world.add_revolve_joint(index_a, index_b, @moonphys.Vec2::new(0.0, 5.0)) // 世界锚点
world.add_revolve_joint_local(a, b, local_a, local_b) // 各自的局部锚点moon testfn Arbiter::new(body_a : Int, body_b : Int, contacts : Array[ContactPoint], friction : Double, restitution : Double) -> Arbiterfn Arbiter::update(self : Arbiter, contacts : Array[ContactPoint], friction : Double, restitution : Double) -> Unitpub struct Body {
body_type : BodyType
shape : Shape
position : Vec2
rotation : Double
velocity : Vec2
angular_velocity : Double
force : Vec2
torque : Double
mass : Double
inv_mass : Double
inertia : Double
inv_inertia : Double
friction : Double
restitution : Double
linear_damping : Double
angular_damping : Double
allow_sleep : Bool
awake : Bool
sleep_time : Double
user_data : Int
world_aabb : Aabb
}pub enum BodyType {
Static
Dynamic
}fn DistanceJoint::new(body_a : Int, body_b : Int, local_anchor_a : Vec2, local_anchor_b : Vec2, length : Double) -> DistanceJointfn RevolveJoint::new(body_a : Int, body_b : Int, local_anchor_a : Vec2, local_anchor_b : Vec2) -> RevolveJointpub struct Vec2 {
x : Double
y : Double
}let LINEAR_SLOP : DoubleInstall
Download zipA deterministic 2D rigid body physics engine in pure MoonBit: SAT collision detection, sequential impulse solver with warm starting, Coulomb friction, revolute and distance joints, sleeping, and SVG rendering.