You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Godot 4中如何通过射线检测获取鼠标指向的3D坐标?

问题分析与修复方案

你遇到的核心问题是射线检测几乎无法命中3D物体,导致ScreenPointToRay()返回空向量,同时角色移动逻辑也存在用法错误,以下是具体修复点:

1. 射线检测失效的原因及修复

碰撞层/掩码不匹配

射线检测默认只会检测与自身碰撞掩码匹配的物体。如果地面或可点击物体的碰撞层未被射线掩码覆盖,就会检测不到目标。

  • 修复:创建射线参数时明确指定要检测的碰撞层(比如检测第1层,对应地面):
    var params = PhysicsRayQueryParameters3D.create(rayOrigin, rayEnd)
    params.collision_mask = 0b1 # 按需调整,对应你场景中物体的碰撞层
    

射线长度不足

你设置的射线长度为200,若场景中相机到目标物体的距离超过这个值,射线就无法命中。

  • 修复:增大射线长度,或针对平面地面用更高效的投射计算:
    # 针对平面地面的替代方案(假设地面y坐标为0)
    var ground_y = 0
    var world_pos = camera.project_position(mousePos, camera.global_position.distance_to(Vector3(0, ground_y, 0)))
    

相机获取方式不可靠

get_tree().root.get_camera_3d()可能返回非当前活跃的相机(多相机场景下),改用视口获取更稳定:

var camera = get_viewport().get_camera_3d()

2. 移动逻辑错误

move_and_collide()需要传入位移向量,而非目标位置。你当前传入目标坐标会导致角色瞬间跳至错误位置。

  • 修复:计算当前位置到目标位置的差值作为位移:
var target_pos = Vector3(snapped(motion.x, 2), 0, snapped(motion.z, 2))
var displacement = target_pos - get_child(0).global_position
get_child(0).move_and_collide(displacement)

另外,Godot中没有snappedf函数,应使用snapped()方法,建议直接对Vector3整体处理。

修正后的完整代码

extends Node3D

func get_mouse_world_position() -> Vector3:
    var viewport = get_viewport()
    var camera = viewport.get_camera_3d()
    if not camera:
        return Vector3.ZERO
    
    var mouse_pos = viewport.get_mouse_position()
    var space_state = get_world_3d().direct_space_state
    
    # 配置射线检测参数
    var ray_origin = camera.project_ray_origin(mouse_pos)
    var ray_end = ray_origin + camera.project_ray_normal(mouse_pos) * 1000 # 增大射线长度
    var params = PhysicsRayQueryParameters3D.create(ray_origin, ray_end)
    params.collision_mask = 0b1 # 按需调整碰撞层
    
    var hit_result = space_state.intersect_ray(params)
    if hit_result.has("position"):
        return hit_result["position"]
    
    #  fallback:未检测到物体时,投射到固定高度地面
    return camera.project_position(mouse_pos, camera.global_position.distance_to(Vector3(0, 0, 0)))

func _process(delta):
    if Input.is_action_pressed("L_Click"):
        var target_world_pos = get_mouse_world_position()
        # 对齐到2单位网格
        var snapped_target = Vector3(
            snapped(target_world_pos.x, 2),
            0, # 固定角色y轴高度
            snapped(target_world_pos.z, 2)
        )
        # 计算位移并移动
        var player = get_child(0)
        var displacement = snapped_target - player.global_position
        player.move_and_collide(displacement)

额外检查项

  • 确保地面/可点击物体添加了CollisionShape3D或CollisionPolygon3D,且碰撞形状正确。
  • 检查物体的collision_layer是否与射线的collision_mask匹配(默认均为0b1,若修改过需对应)。

内容的提问来源于stack exchange,提问作者romaji9

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 12:23:10