HOME / 第 5 部分 · 3D 游戏开发 / 第 52 课
MODULE 5 · 3D 游戏开发

第 52 课 · 3D 物理与碰撞

⏱ 预计时长 35 分钟📊 难度 入门📚 模块 第 5 部分 · 3D 游戏开发

🎯 本课学习目标

3D 世界也要有物理。本课把 2D 的物理知识迁移到 3D。

📖 详细教学步骤

3D 物理体概念

节点2D 对应用途
StaticBody3DStaticBody2D地面/墙(不动)
CharacterBody3DCharacterBody2D角色(代码控制)
RigidBody3DRigidBody2D受力物体(箱子)
Area3DArea2D触发器/感应区

碰撞形状实操

  1. 给物理体加子节点 CollisionShape3D
  2. 检查器 Shape → 新建 3D 形状
  3. 常用:BoxShape3D(箱子/墙)、SphereShape3D(球)、CapsuleShape3D(人形)、CylinderShape3D(柱子)
  4. 调整尺寸贴合模型

💡 3D 碰撞形状同样要「贴合模型」,人形用胶囊最佳。

做一堵墙实操

  1. 添加 StaticBody3D,命名 Wall
  2. 子节点 MeshInstance3D + BoxMesh(墙的模型)
  3. 子节点 CollisionShape3D + BoxShape3D(碰撞,尺寸与墙一致)
  4. 玩家撞上去会被挡住

💡 很多 3D 关卡会用「不可见碰撞 + 可见模型」分离的方式。

Area3D:拾取物品代码

3D 拾取与 2D 几乎一样:

GDSCRIPTextends Area3D

func _ready():
    body_entered.connect(_on_body_entered)

func _on_body_entered(body):
    if body.is_in_group("player"):
        print("玩家拾取了物品!")
        queue_free()

RigidBody3D 推箱子代码

3D 推箱子:

GDSCRIPTextends RigidBody3D

func _physics_process(delta):
    # 持续施加力
    apply_central_force(Vector3(10, 0, 0))

func kick(direction: Vector3):
    # 瞬间冲量
    apply_central_impulse(direction * 5)

射线检测(点击拾取)进阶

用射线检测「准星瞄准的物体」(第 55 课射击要用):

GDSCRIPTfunc cast_ray_from_camera():
    var cam = $Camera3D
    var from = cam.project_ray_origin(get_viewport().get_visible_rect().size / 2)
    var to = from + cam.project_ray_normal(get_viewport().get_visible_rect().size / 2) * 100
    var query = PhysicsRayQueryParameters3D.create(from, to)
    var result = get_world_3d().direct_space_state.intersect_ray(query)
    if result:
        print("射中:", result.collider.name)
⚠️
3D 物体穿墙?

碰撞体没加或形状太小。记得「可见模型」和「碰撞形状」是两回事,都要有。

⚠️
Area3D 检测不到?

确认被检测物体有碰撞形状,且 Area3D 有自己的 CollisionShape3D。

🧪 动手试一试

🏋️ 课后练习

搭一个小房间:地面 + 四面墙 + 一个可拾取的宝物(Area3D)+ 一个可推动的箱子(RigidBody3D)。

StaticBody3D 做墙,Area3D 做宝物,RigidBody3D 做箱子。

💡

调试 3D 碰撞:菜单 调试 → 可见碰撞形状。

💡

3D 单位是米,箱子 1 米见方、人 1.8 米高比较协调。

🎓 本课小结

3D 物理与 2D 一脉相承:Static/Character/Rigid/Area 四种体 + CollisionShape3D;Area3D 做拾取触发器;PhysicsRayQuery 做射线检测。