# Mesh Collider 把 FBX / GLB 拖进 Godot 场景,**自动给这个实例加上碰撞体**。 **不复制模型、不生成新场景、不写任何文件。** 加进去的碰撞节点属于**你正在编辑的场景**,所以模型文件在项目里仍然只被引用一份。 面向 Godot **4.7.2**。 --- ## 2.0 改了什么 1.0 的版本会把模型"本地化"——把整棵节点树压平、把网格数据内嵌进一个新的 `_local.tscn`。实测代价是 **1.0–3.1× 的额外存储**,以及"模型重导出后场景里是旧几何而且不报错"的风险。 2.0 **删掉了全部本地化逻辑**。现在只做一件事:往实例上挂碰撞。 之所以这样可行,是因为一个容易被忽略的事实:**实例化的场景节点可以添加子节点**。你只是不能改它内部已有的节点,而注入的碰撞体是**新子节点** —— 它作为父场景自己的内容被序列化,不引用模型文件,也就没有膨胀。 --- ## 安装 1. 把 `addons/mesh_collider/` 复制到你项目的 `addons/` 下; 2. **项目 → 项目设置 → 插件**,启用 **Mesh Collider**; 3. 菜单出现在 **项目 → 工具** 下。 设置项在 **项目设置 → mesh_collider**。 --- ## 用法 **拖进去就行。** 插件会轮询当前编辑的场景,发现新拖入的模型实例就自动加碰撞,并且整个操作走编辑器的撤销系统 —— **Ctrl+Z 可以撤销,Ctrl+Shift+Z 可以重做**。 菜单里还有三个手动操作: | 菜单项 | 作用 | | --- | --- | | 给选中模型加碰撞 | 对当前选中的模型实例执行一次 | | 移除选中模型的碰撞 | 移除本插件加的那个碰撞体(同样可撤销) | | 设置 | 跳到项目设置页 | --- ## 生成的节点结构 ``` Chair_PC_04 ← 模型实例,仍然引用 res://.../chair.glb └─ CollisionBody (StaticBody3D) ← 本插件加的,属于你的场景 └─ Chair_PC_04Shape (CollisionShape3D) ``` 这个布局与 **Godot 自己**用 `-col` / `-convcol` 名字后缀导入时生成的完全一致。我实测过每种后缀的行为: | 后缀 | Godot 生成的形状 | | --- | --- | | `-col` / `_col` | 网格下挂 `StaticBody3D` → `ConcavePolygonShape3D`(三角网格) | | `-convcol` / `_convcol` | 同上,形状换成 `ConvexPolygonShape3D`(凸包) | | `-colonly` / `-convcolonly` | 网格同级生成 `@StaticBody3D@`,网格隐藏 | 保持一致是有意的:两种机制产出的东西对你后续编辑而言是同一种东西,可以混用、可以互相替换。 ### 那为什么不直接用 `-col`? `-col` **更省事,建议先用它** —— 能用就别引插件。插件的价值在这几种情况: - 要**按实例**调整碰撞(不同实例用不同形状/层); - 模型是买来的成品,不想改它的名字或导入设置; - 只想给**场景里某一个**实例加碰撞,而不是所有用这个模型的地方; - 想在编辑器里直接看到、选中、手动微调这些碰撞节点。 --- ## 设置 | 设置 | 默认 | 说明 | | --- | --- | --- | | `auto_add_collision` | `true` | 自动处理拖进场景的模型 | | `collision_shape` | `Convex Hull` | `Convex Hull` / `Trimesh` / `Box` / `Sphere` / `Capsule` / `Cylinder` | | `collision_body` | `StaticBody3D` | `StaticBody3D` / `RigidBody3D` / `Area3D` / `CharacterBody3D` | | `skip_meshes_with_collision` | `false` | 跳过已经位于物理体下的网格(模型自带碰撞时打开) | | `collision_layer` / `collision_mask` | `1` | 碰撞层 / 掩码 | | `model_extensions` | `fbx glb gltf dae obj blend …` | 识别为模型的后缀 | | `skip_dirs` | `addons/`, `.godot/` | 永不扫描的目录 | ### 形状怎么选 - **Convex Hull**(默认)—— 每个网格一个凸包。性能好,可用于会移动的物体;凹形会被"填平"。 - **Trimesh** —— 逐面精确,不做凸包简化。**只能用在静态物体上**,顶点多时开销较大。 - **Box / Sphere / Capsule / Cylinder** —— 最省性能。这几种形状会**按每个网格的实际位置自动偏移居中**。 ### 模型自带碰撞时 如果模型是用 `-col` 后缀导入的,它内部已经有碰撞体了。这时把 `skip_meshes_with_collision` 打开,插件会跳过那些已经有碰撞的子树,避免重复。 --- ## 脚本里直接用 核心库不依赖编辑器,运行时也能用: ```gdscript const MeshColliderLib := preload("res://addons/mesh_collider/mesh_collider.gd") # 运行时:直接挂上去 var body := MeshColliderLib.add_collision( model_instance, MeshColliderLib.ShapeType.CONVEX_HULL, MeshColliderLib.BodyType.STATIC) if body != null: print("shapes: ", MeshColliderLib.count_shapes(body)) # 检查 / 移除 MeshColliderLib.has_injected_collision(model_instance) # bool MeshColliderLib.has_any_collision(model_instance) # 子树里任何碰撞 MeshColliderLib.remove_collision(model_instance) # bool ``` 编辑器里请用插件(走撤销系统)。`add_collision` 是给运行时和工具脚本用的便捷包装;**放进场景保存时**记得节点要有 `owner`,否则不会被序列化。 --- ## 两个必须知道的限制 **1. 碰撞体不会跟着动画变形。** 碰撞是按模型**静止姿态的几何**烘焙的,不是按骨骼运行时变形后的形状。这是静态烘焙的固有性质,游戏里通常也是用胶囊/凸包代替。 需要碰撞跟随骨骼时,把 `collision_body` 改成 `RigidBody3D` / `CharacterBody3D`,并自己用骨骼驱动它。 **2. 模型重新导出后,碰撞体不会自动更新。** 碰撞体是你场景里的普通节点,Godot 不会因为模型变了就重建它。**换了模型要重新生成**:先「移除选中模型的碰撞」,再「给选中模型加碰撞」即可。 > 这一条正是 1.0 本地化方案想解决的问题,但它的代价(1–3× 存储 + 静默过期)不值得。原生 `-col` 方案没有这个问题,因为它把碰撞生成放在导入流程里 —— 这也是我建议优先考虑它的原因。 --- ## 已知取舍 - **检测靠轮询**(0.4 秒一次)。Godot 没有暴露"模型被拖进来"的信号,这是唯一可靠的做法。 - **已验证的 API 兼容性**:本插件只使用在 Godot 4.7.2 上实测过的 API。踩过的坑包括 `MeshInstance3D.skeleton` 是 `NodePath` 而不是 `Skeleton3D`、`ClassDB.can_instantiate()` 才是判断节点能否容纳子节点的正确方法、以及 GDScript 不允许 `const` 字典的值引用其他常量。 --- ## 工程结构 ``` demo_project/ ├─ addons/mesh_collider/ ← 插件本体(复制这个目录即可) │ ├─ plugin.cfg │ ├─ mesh_collider_plugin.gd 编辑器集成(检测 / 注入 / 撤销) │ ├─ mesh_collider.gd 核心逻辑(纯逻辑,不依赖编辑器) │ ├─ mesh_collider_settings.gd 项目设置 │ └─ README.md ├─ assets/models/ 测试模型(GLB + 带骨骼动画的 FBX) └─ tests/ 辅助脚本 ``` *Mesh Collider 2.0 · 面向 Godot 4.7.2*