PhysicsDirectSpaceState3D.intersect_ray
2026/4/14大约 3 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — PhysicsDirectSpaceState3D.intersect_ray
PhysicsDirectSpaceState3D.intersect_ray
定义
intersect_ray 在 3D 空间中从一点发射一条射线,检测射线碰到的第一个物体。与 2D 版本原理相同,只是在三维空间中操作,起点和方向都是 Vector3。
打个比方:3D 射线就像在三维世界里发射一颗无限快的子弹——子弹沿直线飞行,碰到第一个物体就停下来,告诉你"打中了什么、打在哪里"。
在实际游戏开发中,3D 射线检测用于:FPS 游戏的射击判定、鼠标点击 3D 物体、AI 视线检测、第三人称相机的碰撞避免等。
函数签名
C#
public Dictionary IntersectRay(PhysicsRayQueryParameters3D parameters)GDScript
func intersect_ray(parameters: PhysicsRayQueryParameters3D) -> Dictionary参数说明
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
parameters | PhysicsRayQueryParameters3D | 是 | 射线查询参数,包含起点、终点等 |
PhysicsRayQueryParameters3D 常用属性
| 属性 | 类型 | 说明 |
|---|---|---|
From / from | Vector3 | 射线起点 |
To / to | Vector3 | 射线终点 |
CollisionMask / collision_mask | int | 碰撞层掩码 |
Exclude / exclude | Array | 排除的物体列表 |
返回值
Dictionary —— 碰撞时返回字典,包含 position(Vector3)、normal(Vector3)、collider(Object)、collider_id(int)、rid(RID)等键。未碰撞返回空字典。
代码示例
基础用法:3D 射线检测
C#
using Godot;
public partial class Raycast3DTest : Node3D
{
public override void _PhysicsProcess(double delta)
{
var spaceState = GetWorld3D().DirectSpaceState;
var query = PhysicsRayQueryParameters3D.Create(
GlobalPosition,
GlobalPosition + new Vector3(0, 0, -10) // 向前 10 米
);
var result = spaceState.IntersectRay(query);
if (result.Count > 0)
{
GD.Print($"碰到了:{result["collider"]},位置:{result["position"]}");
// 运行结果: 打印碰到的物体和碰撞位置
}
}
}GDScript
extends Node3D
func _physics_process(delta):
var space_state = get_world_3d().direct_space_state
var query = PhysicsRayQueryParameters3D.create(
global_position,
global_position + Vector3(0, 0, -10) # 向前 10 米
)
var result = space_state.intersect_ray(query)
if result:
print("碰到了:%s,位置:%s" % [result["collider"], result["position"]])
# 运行结果: 打印碰到的物体和碰撞位置实际场景:鼠标点击拾取 3D 物体
C#
using Godot;
public partial class ObjectPicker : Camera3D
{
[Export] public float ExRayLength = 1000.0f;
public override void _Input(InputEvent evt)
{
if (evt is InputEventMouseButton mouseBtn && mouseBtn.Pressed && mouseBtn.ButtonIndex == MouseButton.Left)
{
var spaceState = GetWorld3D().DirectSpaceState;
Vector3 from = ProjectRayOrigin(mouseBtn.Position);
Vector3 to = from + ProjectRayNormal(mouseBtn.Position) * ExRayLength;
var query = PhysicsRayQueryParameters3D.Create(from, to);
var result = spaceState.IntersectRay(query);
if (result.Count > 0)
{
var collider = result["collider"].AsGodotObject();
GD.Print($"点击了物体:{collider.Name}");
// 运行结果: 打印鼠标点击到的 3D 物体名称
}
}
}
}GDScript
extends Camera3D
@export var ray_length: float = 1000.0
func _input(evt):
if evt is InputEventMouseButton and evt.pressed and evt.button_index == MOUSE_BUTTON_LEFT:
var space_state = get_world_3d().direct_space_state
var from = project_ray_origin(evt.position)
var to = from + project_ray_normal(evt.position) * ray_length
var query = PhysicsRayQueryParameters3D.create(from, to)
var result = space_state.intersect_ray(query)
if result:
var collider = result["collider"]
print("点击了物体:%s" % collider.name)
# 运行结果: 打印鼠标点击到的 3D 物体名称进阶用法:FPS 射击检测
C#
using Godot;
public partial class FPSWeapon : Node3D
{
[Export] public float ExDamage = 25.0f;
[Export] public float ExRange = 100.0f;
private Camera3D _camera;
public override void _Ready()
{
_camera = GetParent<Camera3D>();
}
public override void _Input(InputEvent evt)
{
if (evt.IsActionPressed("shoot"))
{
Shoot();
}
}
private void Shoot()
{
var spaceState = GetWorld3D().DirectSpaceState;
Vector3 from = _camera.GlobalPosition;
Vector3 to = from + (-_camera.GlobalBasis.Z) * ExRange;
var query = PhysicsRayQueryParameters3D.Create(from, to);
query.Exclude = new Godot.Collections.Array<Rid> { GetRid() };
var result = spaceState.IntersectRay(query);
if (result.Count > 0)
{
var hitPos = (Vector3)result["position"];
GD.Print($"射击命中!位置:{hitPos}");
// 运行结果: 打印命中位置
// 如果命中的物体有受伤方法就调用
var collider = result["collider"].AsGodotObject();
if (collider.HasMethod("TakeDamage"))
{
collider.Call("TakeDamage", ExDamage);
// 运行结果: 命中的敌人受到 25 点伤害
}
}
else
{
GD.Print("射击未命中");
}
}
}GDScript
extends Node3D
@export var damage: float = 25.0
@export var range: float = 100.0
@onready var _camera: Camera3D = get_parent()
func _input(evt):
if evt.is_action_pressed("shoot"):
shoot()
func shoot():
var space_state = get_world_3d().direct_space_state
var from = _camera.global_position
var to = from + (-_camera.global_basis.z) * range
var query = PhysicsRayQueryParameters3D.create(from, to)
query.exclude = [get_rid()]
var result = space_state.intersect_ray(query)
if result:
var hit_pos = result["position"]
print("射击命中!位置:%s" % hit_pos)
# 运行结果: 打印命中位置
var collider = result["collider"]
if collider.has_method("take_damage"):
collider.take_damage(damage)
# 运行结果: 命中的敌人受到 25 点伤害
else:
print("射击未命中")注意事项
- 在
_PhysicsProcess中调用最安全:虽然在某些情况下_Input中也能用,但推荐在物理帧中调用以确保物理世界状态最新。 - 鼠标拾取用 Camera 的投影方法:
ProjectRayOrigin和ProjectRayNormal可以将屏幕坐标转换为 3D 射线。 - 排除自身:使用
query.Exclude排除玩家自身的 RID,避免射线碰到自己。 - 3D 射线方向:在 Godot 中,
-Z方向通常是"前方"。-camera.GlobalBasis.Z就是相机面朝的方向。
