Box2DSharp三种刚体类型与碰撞过滤实战:static、kinematic、dynamic怎么选?
2026/8/24 10:59:55 网站建设 项目流程

Box2DSharp三种刚体类型与碰撞过滤实战:static、kinematic、dynamic怎么选?

【免费下载链接】Box2DSharpA C# port of Box2D项目地址: https://gitcode.com/gh_mirrors/bo/Box2DSharp

Box2DSharp是一个基于 C# 的 Box2D 物理引擎移植项目,它为 2D 游戏和应用提供刚体模拟、碰撞检测和碰撞过滤能力。很多新手在创建世界后第一个困惑就是:刚体(Body)明明看起来差不多,为什么有的会动、有的纹丝不动?答案就在statickinematicdynamic三种刚体类型上。本文用一个清晰的对照表 + 实际用例,帮你 5 分钟搞懂刚体类型怎么选,以及碰撞过滤(Collision Filtering)如何控制"谁和谁能撞"。

一、三种刚体类型速查表

Box2DSharp 中的刚体类型定义在 src/Dynamics/Body.cs 的BodyType枚举中,源码注释给出了最权威的一句话解释:

刚体类型质量速度谁来驱动它典型用途
StaticBody静态手动移动地面、墙壁、不可破坏的障碍
KinematicBody运动学非零(用户设定)你写代码驱动传送带、自动门、玩家操控平台
DynamicBody动力学正数由力决定物理引擎求解小球、箱子、角色、子弹

一句话记忆法:

  • 🏔️static= 摆在那儿,谁也推不动
  • 🎬kinematic= 像电影镜头一样,按你给的剧本走,但会挡住别人
  • 🏀dynamic= 完全交给物理引擎,重力、碰撞、力都影响它

二、static 静态刚体:场景的地基

静态刚体质量为零、速度为零,不参与受力计算,因此开销最小。地面、天花板、固定障碍物都应该用它。

在官方示例 test/Testbed.TestCases/CollisionFiltering.cs 中,地面就是这样创建的:默认BodyDef不指定类型时就是StaticBody,再挂上一个EdgeShape边形状作为地面线。

💡 注意:静态刚体可以被手动改位置(瞬移),但物理引擎不会模拟它的运动,所以不要用"逐帧挪动静态刚体"来模拟滑动平台——那是 kinematic 的活。

三、kinematic 运动学刚体:按剧本移动的演员

运动学刚体是新手最容易用错的一种:它质量为零,不受力和碰撞的影响,但你可以通过SetLinearVelocity给它指定速度,然后由求解器带着它移动。

项目自带了一个教科书级的演示 test/Testbed.TestCases/BodyTypes.cs:一个悬挂的平台可以被按键切换成 dynamic / static / kinematic 三种类型实时对比。切到 kinematic 后,平台在PreStep()中按自己设定的速度来回移动,撞到球会把球带走,但球却压不动它——这正是运动学刚体的精髓:

_platform.BodyType = BodyType.KinematicBody; _platform.SetLinearVelocity(new Vector2(-_speed, 0.0f));

典型场景:

  • 来回移动的关卡平台
  • 传送带表面(配合Friction摩擦力"推"物体)
  • 玩家直接控制位移的传送门、电梯

四、dynamic 动力学刚体:完全交给物理引擎

动力学刚体有正的质量(质量 = 密度 × 形状面积),它的速度完全由重力、外力、碰撞冲量共同决定。小球、箱子、角色都应该是 dynamic。

创建时的关键在FixtureDef.Density(密度),定义于 src/Dynamics/Fixture.cs。密度为 0 的 dynamic 刚体会被引擎自动改为质量 1,所以想"悬浮不掉落"时,优先考虑把GravityScale设为 0 或改用 kinematic,而不是玩密度的小聪明。

另外一个实用开关:BodyDef.Bullet。高速小物体容易"穿过"薄墙,把 dynamic 刚体标记为 Bullet 可以开启连续碰撞检测,但会增加计算开销,官方注释也提醒"应谨慎使用"。

五、碰撞过滤实战:谁和谁会发生碰撞?

刚体类型解决"怎么动",碰撞过滤解决"撞不撞"。每个 Fixture 都有一个Filter,包含三个字段(定义在 src/Dynamics/Fixture.cs):

字段类型含义
CategoryBitsushort(位掩码)是什么(占据若干位)
MaskBitsushort(位掩码)能碰到什么
GroupIndexshort同组特殊规则:>0 组内强制碰撞,<0 组内强制不碰

判定逻辑写在 src/Dynamics/DefaultContactFilter.cs 的ShouldCollide中,只有两步:

  1. 两个 shape 的GroupIndex相同且不为 0 → 正数碰、负数不碰,直接返回;
  2. 否则要求双向位与都非零A.Mask & B.Category != 0B.Mask & A.Category != 0,两个方向都"看得见"才算碰撞。

⚠️ 常见坑:只改了 A 的 Mask 却忘了 B 的 Mask,结果还是"双向不可见",以为过滤没生效。

一个完整的过滤配置思路

参考官方用例 test/Testbed.TestCases/CollisionFiltering.cs,它定义了四种类别位:

private const ushort DefaultCategory = 0x0001; private const ushort TriangleCategory = 0x0002; private const ushort BoxCategory = 0x0004; private const ushort CircleCategory = 0x0008;

再配合小组SmallGroup = 1(同组必碰)和大组LargeGroup = -1(同组必不碰),就可以做出"小方块能穿过大三角形、但大三角形之间互相穿过"这类精细规则。

配置口诀:

  1. 先给每个逻辑类别分一个唯一的位(0x01、0x02、0x04……别复用);
  2. 每个类别的MaskBits写成"我能碰的所有类别"的按位或;
  3. 同角色之间需要强规则(必碰/必不碰)时,再上GroupIndex

六、选型决策清单

按下面的顺序问自己,基本不会选错:

  1. 它会受力、被重力影响、被碰撞推动吗? → 是:DynamicBody
  2. 它需要按脚本精确移动(平台、传送带),但不该被碰撞物顶走吗? → 是:KinematicBody
  3. 它永远静止不动吗? → 是:StaticBody(默认值,最省事)

再叠加过滤规则:

  • 只关心接触事件、不需要物理阻挡 → 把FixtureDef.IsSensor设为true(传感器)
  • 角色要和地面/道具/其他角色分通道碰撞 →CategoryBits+MaskBits
  • 同类物体必须(或必须不)互碰 →GroupIndex用 ±1

七、小结

Box2DSharp 的三种刚体类型本质上是"谁掌握控制权"的分工:static 归场景、kinematic 归代码、dynamic 归引擎。而CategoryBits/MaskBits/GroupIndex这套位掩码过滤机制,让你能在不改逻辑的前提下精确控制碰撞矩阵。理解了这张对照表和双向位与的判定规则,传送带、传感器触发器、多层角色碰撞这些常见需求就都能落地了。

上手建议:直接打开 test/Testbed.TestCases/ 下的BodyTypes.csCollisionFiltering.csSensors.cs三个用例运行调试,比看文档更快建立直觉。

【免费下载链接】Box2DSharpA C# port of Box2D项目地址: https://gitcode.com/gh_mirrors/bo/Box2DSharp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询