因为需最小闭环(刚体+碰撞检测+朴素积分),现成引擎抽象过重掩盖物理本质;核心必含rigidbody、aabb/circle、physicsworld三类;跨平台靠接口隔离;用std::chrono::high_resolution_clock统一高精度计时;碰撞修正须先检测再修正双方位置与速度,避免穿模。

为什么不用现成物理引擎而要自己写
因为你要的不是 Box2D 那种工业级方案,而是能快速嵌入、可调试、可定制的最小闭环:刚体 + 碰撞检测 + 朴素积分。现成引擎带太多抽象层(scene、body type、fixture hierarchy),反而掩盖了 dt、position 和 velocity 之间的真实耦合关系。自己写能控制每一步——比如你改一个 update() 的顺序,就能立刻看到穿透或抖动,这对理解物理模拟本质更直接。
核心结构必须包含的三个类
跨平台不等于写一堆宏,而是靠接口隔离平台差异。关键不是“怎么跨”,而是“哪些不该跨”。这三类必须存在:
-
RigidBody:只存position、velocity、mass、inertia,不涉及渲染或输入;所有物理更新只调用它的integrate(float dt)和applyForce(const Vec2&) -
AABB或Circle:仅提供collide(const AABB& other)和resolveCollision(RigidBody& other);不依赖 OpenGL/Vulkan/DirectX,也不含 transform stack -
PhysicsWorld:持有一个std::vector<:unique_ptr>></:unique_ptr>,暴露step(float dt);它负责遍历、粗筛(如用std::sort按 x 排序做 sweep-and-prune)、调用碰撞对,但不实现具体碰撞算法
Windows/macOS/Linux 下最简时间步处理
不同平台高精度计时 API 不同,但你不需要自己封装一套。C++11 起,std::chrono::high_resolution_clock 在所有主流平台(MSVC/GCC/Clang)都映射到最佳可用源:QueryPerformanceCounter(Windows)、clock_gettime(CLOCK_MONOTONIC)(Linux)、mach_absolute_time()(macOS)。别用 std::clock() 或 time(),它们分辨率不够,会导致 dt 跳变。
实操建议:
- 在
PhysicsWorld::step()开头记录auto t0 = std::chrono::high_resolution_clock::now(); - 步进结束后算差值:
auto dt = std::chrono::duration<float>(t1 - t0).count();</float> - 加个硬上限:
dt = std::min(dt, 1.0f/30.0f);防止卡顿时大步长炸飞物体
碰撞后位置修正的常见陷阱
很多自研引擎卡在这里:明明 collide() 返回 true,物体还是穿模。根本原因不是算法错,而是顺序和时机不对。典型错误包括:
- 在
integrate()之后才检测碰撞,此时位置已超限;正确做法是先检测、再根据穿透深度反向修正position,最后再积分 - 只修正 A 的位置,忽略 B 的响应;
resolveCollision()必须同时修改双方position和velocity(哪怕只是简单反射) - 用浮点除法算分离向量,遇到
mass == 0或极小值时产生 NaN;加一句if (totalMass
一个够用的分离逻辑示例(AABB):
void resolveCollision(RigidBody& a, RigidBody& b) {
Vec2 overlap = computeOverlap(a.aabb, b.aabb);
if (overlap.x == 0 && overlap.y == 0) return;
float totalMass = a.mass + b.mass;
a.position += overlap * (b.mass / totalMass);
b.position -= overlap * (a.mass / totalMass);
}
注意:这里没考虑旋转或摩擦,但已足够支撑平台跳跃原型。真要加角动量,得从 inertia 和 angularVelocity 开始补,别一开始就堆。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











