mcpField
G
godot-physics

godot-physics

github.com/gamedev-skills开发5.4k7.0k 次安装1 天前更新
代码仓库

关于 godot-physics

面向 Godot 4.x 开发者的 2D 与 3D 物理技能指南,涵盖物理体、碰撞、重叠检测和射线查询,并说明 Jolt Physics 的差异。

主要功能

  1. 01选择物理体类型
  2. 02配置碰撞层与遮罩
  3. 03使用 Area 检测重叠
  4. 04为 RigidBody 施加力与冲量
  5. 05执行射线查询
  6. 06了解 Jolt Physics 差异

适用场景

  1. 01设置墙体与敌人的碰撞关系
  2. 02制作触发区或受击框
  3. 03驱动物理模拟物体
  4. 04实现视线或地面检测

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill godot-physics

安装后在 Claude Code 中输入/godot-physics确认已加载。

附带文件references/bodies-and-queries.md

详细介绍

Godot 物理(4.x,2D + 3D)

选择合适的物理体,设置碰撞层/掩码,检测重叠并进行射线检测。 这些概念适用于 2D 和 3D(替换 2D/3D 后缀即可)。目标版本为 Godot 4.7。在 3D 中,使用 Godot 4.6 或更高版本创建的项目默认运行 Jolt Physics; 较旧项目则继续使用 GodotPhysics3D,除非有人切换过。2D 始终使用 Godot 自带的 2D 引擎。

适用场景

  • 选择物理体类型,设置碰撞层/掩码以控制哪些对象会发生碰撞,检测重叠(触发器、受击框), 对 RigidBody 施加力/冲量,或进行射线检测以检查视线/地面时使用。

不适用场景: 运动学角色控制器(move_and_slide)→ godot-2d-movement;瓦片碰撞设置 → godot-tilemap;调整物理的手感(时间步长、质量、抖动)→ physics-tuning。

核心工作流

  1. 对于 3D,先检查运行的是哪个引擎,再调试行为:项目设置 > 物理 > 3D > 物理引擎(project.godot 中的 [physics] 3d/physics_engine="Jolt Physics")。 API 相同,但关节、射线检测面索引、运动学接触和碰撞边距的行为各不相同(参见注意事项)。
  2. 选择物理体类型:
    • StaticBody — 永不移动(地板、墙壁)。会发生碰撞,但不参与模拟。
    • RigidBody — 完全模拟(重力、力、弹跳)。不要直接设置它的 position;应施加力/冲量,或设置 linear_velocity。
    • CharacterBody — 由脚本驱动的运动学物体(参见 godot-2d-movement)。
    • Area — 检测重叠并可施加重力/阻尼;不会产生实体碰撞。 每个物理体都需要一个 CollisionShape(或 CollisionPolygon)子节点。
  3. 配置碰撞层和掩码。 物理体属于其碰撞层,并检测其 碰撞掩码中的对象。只有当一个物理体的碰撞层包含在另一个物理体的碰撞掩码中时,两者才会交互。为了清晰起见,请在项目设置 > 层名称中为碰撞层命名。
  4. 使用 Area 信号(body_entered、area_entered)检测重叠。
  5. 使用力/冲量驱动 RigidBody,或重写 _integrate_forces 以获得完全控制。
  6. 使用 RayCast2D/3D 节点进行射线检测(每帧轮询),或从代码发起一次性空间状态查询。

模式

1. 碰撞层与掩码(从代码设置)
# Player is on layer 1, scans layers 2 (walls) and 3 (enemies).
func _ready() -> void:
    set_collision_layer_value(1, true)    # I am on layer 1
    set_collision_mask_value(2, true)     # I collide with things on layer 2
    set_collision_mask_value(3, true)     # ...and layer 3
    # Bit-field forms also exist: collision_layer = 1; collision_mask = 0b110
2. 将 Area2D 用作触发器/受击框
extends Area2D                            # e.g. a damage zone

func _ready() -> void:
    body_entered.connect(_on_body_entered)
    area_entered.connect(_on_area_entered)

func _on_body_entered(body: Node2D) -> void:
    if body.has_method("take_damage"):
        body.take_damage(10)

func _on_area_entered(area: Area2D) -> void:
    print("Overlapped area: ", area.name)
3. 对 RigidBody3D 施加力和冲量
extends RigidBody3D

func push(direction: Vector3) -> void:
    apply_central_impulse(direction * 8.0)     # instantaneous velocity change

func _physics_process(_delta: float) -> void:
    apply_central_force(Vector3.FORWARD * 4.0) # continuous force (per tick)
    # Never set `position` on a RigidBody to move it; use forces/impulses or
    # set linear_velocity. Use freeze=true if you must hold it in place.
4. 两种射线检测方式
# A) RayCast2D node: enable it, then poll after physics has updated.
@onready var ray: RayCast2D = $RayCast2D    # set target_position in the editor

func _physics_process(_delta: float) -> void:
    if ray.is_colliding():
        var hit := ray.get_collider()
        var point := ray.get_collision_point()

# B) One-shot query from code (no node needed).
func ground_under(global_from: Vector2) -> Dictionary:
    var space := get_world_2d().direct_space_state
    var query := PhysicsRayQueryParameters2D.create(global_from, global_from + Vector2(0, 64))
    query.collision_mask = 1                 # only layer 1
    return space.intersect_ray(query)        # {} if nothing hit, else collider/position/normal

注意事项

  • 混淆碰撞层和掩码是最常见的错误。碰撞层 =“我是什么”;碰撞掩码 =“我检测什么”。 若要让 A 检测到 B,B 的碰撞层必须包含在 A 的碰撞掩码中。检测可以是单向的。
  • 通过 position 移动物理体 RigidBody 会与求解器冲突,并导致穿透/抖动。 应使用冲量/力、设置 linear_velocity,或冻结它(freeze)。若要瞬移,请设置位置, 并在 _integrate_forces 中将速度归零。
  • 如果既未设置监测(monitoring),也未设置可监测(monitorable),或碰撞层/掩码不重叠, Area 就不会触发信号。要让 Area 检测其他对象,必须启用 monitoring;monitorable 则允许其他对象检测它。
  • 如果 enabled 为 false,或在物理更新前读取数据,RayCast2D/3D 可能会读取到过时数据或没有数据——请在 _physics_process 中读取,并在同一帧内移动射线后调用 force_raycast_update()。
  • 忘记添加 CollisionShape(或将其留空)意味着物理体永远不会发生碰撞。
  • 快速移动的物体会穿透薄墙;请在 RigidBody 上启用连续碰撞检测(CD)(continuous_cd),或使用基于射线检测的检查。
  • intersect_ray 会排除自身物理体吗? 设置 query.exclude = [self.get_rid()](类型为 Array[RID],而不是节点数组)以跳过自身命中。
Jolt Physics(3D)的差异
  • 在 Jolt 下,intersect_ray() / RayCast3D 的结果中,face_index 始终为 -1。 如果需要该索引,请启用 Project Settings > Physics > Jolt Physics 3D > Queries > Enable Ray Cast Face Index;这会使 ConcavePolygonShape3D 的内存占用大约增加 25%。
  • 切换物理引擎后,只有一个物理体的关节会出现限制反转。 Jolt 将单个物理体视为 node_b,并将 node_a 视为世界;GodotPhysics3D 则始终将其视为 node_a。将物理体分配到 Jolt 预期的槽位,或者使用 Physics > Jolt Physics 3D > Joints > World Node,以兼容旧项目。
  • 关节软限制属性在 Jolt 下不起作用(例如 Pin、Hinge 和 ConeTwist 关节上的 bias/softness/ relaxation;Slider 和 Generic6DOF 关节上的限制柔度/恢复系数/阻尼)。设置为非默认值时,Godot 会发出警告。
  • 冻结的运动学 RigidBody3D 不会报告与静态或运动学物理体的接触,即使 max_contacts_reported > 0 也是如此。如果游戏逻辑依赖这些接触,请启用 Physics > Jolt Physics 3D > Simulation > Generate All Kinematic Contacts。
  • Jolt 下的形状 margin 会缩小形状,而不是为其增加填充,因此尺寸保持准确,但形状较小时,形状查询可能返回异常法线;有效边距由 Physics > Jolt Physics 3D > Collisions > Collision Margin Fraction 决定。
  • Jolt 下的 Area3D 现在会为 SoftBody3D 触发 body_entered(GodotPhysics3D 从未如此)。如果需要旧行为,请使用图层/掩码过滤软体。

参考资料

  • 关于 _integrate_forces、关节、单向碰撞、PhysicsServer 直接访问、形状查询(intersect_shape)以及 3D move_and_collide,请阅读 references/bodies-and-queries.md。
  • Jolt 相关内容:Godot 文档“使用 Jolt Physics” (https://docs.godotengine.org/en/stable/tutorials/physics/using_jolt_physics.html)。

相关技能

  • godot-2d-movement — 运动学 CharacterBody2D 控制器。
  • godot-tilemap — 瓦片碰撞形状及其图层。
  • physics-tuning — 与引擎无关的手感调校:时间步长、质量、阻力、CCD。
  • godot-3d-essentials — 这些物理体所在的 3D 场景设置。

评论

支持 Markdown