HOME / 第 5 部分 · 3D 游戏开发 / 第 52 课
MODULE 5 · 3D 游戏开发
第 52 课 · 3D 物理与碰撞
🎯 本课学习目标
- 掌握 3D 物理体与碰撞形状
- 用 Area3D 做触发器
- 实现拾取与检测
3D 世界也要有物理。本课把 2D 的物理知识迁移到 3D。
📖 详细教学步骤
3D 物理体概念
| 节点 | 2D 对应 | 用途 |
|---|---|---|
| StaticBody3D | StaticBody2D | 地面/墙(不动) |
| CharacterBody3D | CharacterBody2D | 角色(代码控制) |
| RigidBody3D | RigidBody2D | 受力物体(箱子) |
| Area3D | Area2D | 触发器/感应区 |
碰撞形状实操
- 给物理体加子节点
CollisionShape3D - 检查器 Shape → 新建 3D 形状
- 常用:
BoxShape3D(箱子/墙)、SphereShape3D(球)、CapsuleShape3D(人形)、CylinderShape3D(柱子) - 调整尺寸贴合模型
💡 3D 碰撞形状同样要「贴合模型」,人形用胶囊最佳。
做一堵墙实操
- 添加
StaticBody3D,命名 Wall - 子节点 MeshInstance3D + BoxMesh(墙的模型)
- 子节点 CollisionShape3D + BoxShape3D(碰撞,尺寸与墙一致)
- 玩家撞上去会被挡住
💡 很多 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 做射线检测。