PhysicsDirectSpaceState3D.collide_shape
2026/4/14大约 4 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — PhysicsDirectSpaceState3D.collide_shape
PhysicsDirectSpaceState3D.collide_shape
定义
collide_shape 用一个自定义形状(球体、盒子、胶囊体等)去检测空间中碰到了哪些物理物体。不像射线只检测一条线,也不像点检测只检测一个点,你可以用任意形状去"探测"。
打个比方:射线检测像伸出一根手指去摸,点检测像用针尖戳一下,collide_shape 像你拿着一个特定形状的模具去按——球形的、方形的都行,看看模具和哪些物体重叠了。
在实际游戏开发中,collide_shape 常用于:检测攻击范围(剑的挥砍范围)、检测角色是否与特定区域重叠、爆炸伤害的范围检测等。
函数签名
C#
public Godot.Collections.Array<Dictionary> CollideShape(PhysicsShapeQueryParameters3D parameters, int maxResults = 32)GDScript
func collide_shape(parameters: PhysicsShapeQueryParameters3D, max_results: int = 32) -> Array[Dictionary]参数说明
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
parameters | PhysicsShapeQueryParameters3D | 是 | 形状查询参数,包含形状、位置、碰撞掩码等 |
maxResults | int | 否 | 最多返回几个结果,默认 32 |
PhysicsShapeQueryParameters3D 常用属性
| 属性 | 类型 | 说明 |
|---|---|---|
Shape / shape | Shape3D | 检测用的形状(SphereShape3D、BoxShape3D 等) |
Transform / transform | Transform3D | 形状的位置和旋转 |
CollisionMask / collision_mask | int | 碰撞层掩码 |
Exclude / exclude | Array | 排除的物体列表 |
返回值
Array[Dictionary] —— 返回碰撞结果数组,每个元素包含 collider、collider_id、rid 等键。无碰撞返回空数组。
代码示例
基础用法:用球体检测附近物体
C#
using Godot;
public partial class ShapeQueryTest : Node3D
{
public override void _PhysicsProcess(double delta)
{
var spaceState = GetWorld3D().DirectSpaceState;
var sphere = new SphereShape3D();
sphere.Radius = 5.0f;
var query = new PhysicsShapeQueryParameters3D();
query.Shape = sphere;
query.Transform = GlobalTransform;
query.Exclude = new Godot.Collections.Array<Rid> { GetRid() };
var results = spaceState.CollideShape(query);
GD.Print($"5 米范围内有 {results.Count} 个物体");
// 运行结果: 打印范围内检测到的物体数量
}
}GDScript
extends Node3D
func _physics_process(delta):
var space_state = get_world_3d().direct_space_state
var sphere = SphereShape3D.new()
sphere.radius = 5.0
var query = PhysicsShapeQueryParameters3D.new()
query.shape = sphere
query.transform = global_transform
query.exclude = [get_rid()]
var results = space_state.collide_shape(query)
print("5 米范围内有 %d 个物体" % results.size())
# 运行结果: 打印范围内检测到的物体数量实际场景:近战攻击范围检测
C#
using Godot;
public partial class MeleeWeapon : Node3D
{
[Export] public float ExAttackRange = 2.0f;
[Export] public float ExAttackDamage = 30.0f;
[Export] public float ExAttackAngle = 90.0f; // 扇形角度
public void PerformAttack()
{
var spaceState = GetWorld3D().DirectSpaceState;
var sphere = new SphereShape3D();
sphere.Radius = ExAttackRange;
var query = new PhysicsShapeQueryParameters3D();
query.Shape = sphere;
query.Transform = GlobalTransform;
query.CollisionMask = 2; // 敌人层
query.Exclude = new Godot.Collections.Array<Rid> { GetRid() };
var results = spaceState.CollideShape(query);
int hitCount = 0;
foreach (var result in results)
{
var collider = result["collider"].AsGodotObject() as Node3D;
if (collider == null) continue;
// 检查是否在攻击扇形范围内
Vector3 dirToTarget = (collider.GlobalPosition - GlobalPosition).Normalized();
float angle = Mathf.RadToDeg(GetForward().AngleTo(dirToTarget));
if (angle <= ExAttackAngle / 2.0f)
{
if (collider.HasMethod("TakeDamage"))
{
collider.Call("TakeDamage", ExAttackDamage);
hitCount++;
}
}
}
GD.Print($"近战攻击命中 {hitCount} 个敌人");
// 运行结果: 范围和角度内的敌人受到伤害
}
private Vector3 GetForward() => -GlobalBasis.Z;
}GDScript
extends Node3D
@export var attack_range: float = 2.0
@export var attack_damage: float = 30.0
@export var attack_angle: float = 90.0 # 扇形角度
func perform_attack():
var space_state = get_world_3d().direct_space_state
var sphere = SphereShape3D.new()
sphere.radius = attack_range
var query = PhysicsShapeQueryParameters3D.new()
query.shape = sphere
query.transform = global_transform
query.collision_mask = 2 # 敌人层
query.exclude = [get_rid()]
var results = space_state.collide_shape(query)
var hit_count = 0
for result in results:
var collider = result["collider"] as Node3D
if collider == null:
continue
# 检查是否在攻击扇形范围内
var dir_to_target = (collider.global_position - global_position).normalized()
var angle = rad_to_deg(get_forward().angle_to(dir_to_target))
if angle <= attack_angle / 2.0:
if collider.has_method("take_damage"):
collider.take_damage(attack_damage)
hit_count += 1
print("近战攻击命中 %d 个敌人" % hit_count)
# 运行结果: 范围和角度内的敌人受到伤害
func get_forward() -> Vector3:
return -global_basis.z进阶用法:用盒子形状检测物品拾取范围
C#
using Godot;
public partial class ItemPickup : CharacterBody3D
{
[Export] public Vector3 ExPickupBoxSize = new(2, 1, 2);
public override void _PhysicsProcess(double delta)
{
if (Input.IsActionJustPressed("interact"))
{
var spaceState = GetWorld3D().DirectSpaceState;
var box = new BoxShape3D();
box.Size = ExPickupBoxSize;
var query = new PhysicsShapeQueryParameters3D();
query.Shape = box;
query.Transform = GlobalTransform;
query.CollisionMask = 4; // 物品层
query.Exclude = new Godot.Collections.Array<Rid> { GetRid() };
var results = spaceState.CollideShape(query);
foreach (var result in results)
{
var item = result["collider"].AsGodotObject();
GD.Print($"拾取了:{item.Name}");
// 运行结果: 拾取范围内所有物品
if (item.HasMethod("Pickup"))
{
item.Call("Pickup");
}
}
}
}
}GDScript
extends CharacterBody3D
@export var pickup_box_size: Vector3 = Vector3(2, 1, 2)
func _physics_process(delta):
if Input.is_action_just_pressed("interact"):
var space_state = get_world_3d().direct_space_state
var box = BoxShape3D.new()
box.size = pickup_box_size
var query = PhysicsShapeQueryParameters3D.new()
query.shape = box
query.transform = global_transform
query.collision_mask = 4 # 物品层
query.exclude = [get_rid()]
var results = space_state.collide_shape(query)
for result in results:
var item = result["collider"]
print("拾取了:%s" % item.name)
# 运行结果: 拾取范围内所有物品
if item.has_method("pickup"):
item.pickup()注意事项
- 返回的是碰撞对,不是完整结果:每个结果只包含
collider、collider_id、rid,不包含碰撞点和法线。如果需要碰撞点信息,使用intersect_shape代替。 - 形状需要手动创建:你需要自己创建
SphereShape3D、BoxShape3D等形状对象并设置尺寸。 - 排除自身:别忘了把自身加入
Exclude列表,否则会检测到自己。 - 性能提示:大范围的形状查询可能比较耗时,避免每帧都做大面积查询。
