需手动编译Jolt源码,启用CMake并关闭断言;链接pthread/winmm;初始化必调RegisterDefaultAllocator和RegisterTypes;单位用米,质量用CalculateMassAndInertia计算;设固定时间步0.02f,禁用休眠,正确注册ContactListener。

怎么把 Jolt Physics 集进 C++ 项目里
Jolt 不是开箱即用的库,它不提供预编译二进制,也不支持 pkg-config 或 vcpkg 默认集成。你得自己编译源码、管理依赖、处理 CMake 链接细节——跳过这步直接 find_package(Jolt) 肯定失败。
实操建议:
- 从 GitHub 克隆官方仓库:
git clone https://github.com/jrouwe/JoltPhysics.git,别用 release zip(缺 CMakeLists.txt 根配置) - 用
JoltPhysics/CMakeLists.txt作为 project root 运行 CMake,不是进Build目录手动建空工程 - 必须开启
JPH_ENABLE_ASSERTS=OFF(默认 ON),否则 Release 模式下断言宏会炸掉——尤其在JPH_OBJECT_LAYER或JPH_BROADPHASE_LAYER配错时静默崩溃 - 链接时除了
Jolt库,还得显式链接pthread(Linux/macOS)和winmm.lib(Windows),漏一个就报 undefined reference 到GetTickCount64或clock_gettime
初始化 Jolt 时最常崩在哪几个地方
初始化失败通常不抛异常,而是后续调用 BodyInterface::CreateBody 返回空指针,或 PhysicsSystem::Update 直接 abort——因为底层内存池/分配器没配对。
关键检查点:
立即学习“C++免费学习笔记(深入)”;
-
JPH::RegisterDefaultAllocator()必须在任何 Jolt 类型创建前调用,且只能调一次;重复调用会导致malloc冲突 -
JPH::Factory::sInstance = new JPH::Factory();和JPH::RegisterTypes()缺一不可,否则BodyCreationSettings构造时会触发 nullptr dereference - 如果你用了自定义分配器(比如基于 arena),务必确保
JPH::TempAllocatorImpl的 buffer size ≥ 1MB,默认 10MB 是安全值,但小了会在碰撞检测阶段 malloc 失败
Jolt 的 Body 和 Shape 怎么配才不飘不穿模
Jolt 对刚体质量、转动惯量、形状缩放非常敏感,参数稍偏就会漂移、抖动或穿透。它不像 Bullet 那样自动归一化,所有值都按 SI 单位硬算。
稳妥做法:
- 所有尺寸单位统一用「米」:1 个 Unity 单位 = 1 米,别用厘米或像素;
JPH::BoxShape的半边长传Vec3(0.5f, 0.5f, 0.5f)表示 1m³ 立方体 - 静态物体设
EMotionType::Static,动态物体设EMotionType::Dynamic,千万别用EMotionType::Kinematic当“可移动但不受力”的替代——它不参与碰撞响应,只是强行插值位置 - 质量不能为 0 或负数;
BodyCreationSettings中mMassProperties建议用CalculateMassAndInertia(shape, density)算,密度给1000.0f(水密度)比硬写1.0f更稳 - 碰撞层(
ObjectLayer)和宽相层(BroadPhaseLayer)必须成对注册,否则PhysicsSystem::AddBody成功但完全不检测碰撞
为什么 Update 后位置没变 / 碰撞回调没触发
常见假象:代码看似跑通,但物体悬空不动,或者 ContactListener 一个回调都没进来——大概率是时间步或调度逻辑没对上。
排查重点:
-
PhysicsSystem::Update的inDeltaTime不能传 0 或超大值(如 1.0f),推荐固定步长:0.02f(50Hz),配合帧同步;传deltaTime浮点误差累积会导致数值发散 -
ContactListener子类必须重写OnContactAdded,且该实例要通过physicsSystem->SetContactListener注册,不是构造完就自动生效 - 动态刚体默认休眠(
CanSleep = true),静止 0.5 秒后进入 sleep 状态,不再更新位置——加body->SetAllowSleeping(false)或手动body->Activate()唤醒 - 记得每帧调
BodyInterface::GetWorldTransform拿最新位姿,别缓存Body::GetPosition结果,它返回的是内部状态快照,非世界坐标
最麻烦的其实是多线程——Jolt 默认启用 job system,但如果你没初始化 JobSystemThreadPool,或者主线程直接调 Update 而没等 job 完成,物理状态就永远滞后一帧。这个点没人提,但实际踩中率极高。



















