HOME / 第 5 部分 · 3D 游戏开发 / 第 56 课
MODULE 5 · 3D 游戏开发

第 56 课 · 3D 拾取与交互

⏱ 预计时长 35 分钟📊 难度 进阶📚 模块 第 5 部分 · 3D 游戏开发

🎯 本课学习目标

「按 E 开门」「点击拾取」——本课用射线 + 输入实现通用 3D 交互。

📖 详细教学步骤

交互思路概念

通用交互三步:

  1. 每帧从准星发射射线,记录「当前指向的对象」
  2. 指向可交互物时显示提示(高亮/文字提示)
  3. 按交互键(E)调用对象的 interact() 函数

每帧检测指向对象代码

在玩家脚本里:

GDSCRIPTextends CharacterBody3D

var focused: Node3D = null   # 当前指向的可交互对象
@onready var cam: Camera3D = $CameraRig/Camera3D

func _process(delta):
    var center = get_viewport().get_visible_rect().size / 2
    var from = cam.project_ray_origin(center)
    var to = from + cam.project_ray_normal(center) * 3.0
    var query = PhysicsRayQueryParameters3D.create(from, to)
    var hit = get_world_3d().direct_space_state.intersect_ray(query)

    # 更新焦点
    var new_focus: Node3D = null
    if hit and hit.collider.has_method("interact"):
        new_focus = hit.collider
    if new_focus != focused:
        if focused and focused.has_method("set_focus"):
            focused.set_focus(false)   # 取消上一个高亮
        focused = new_focus
        if focused and focused.has_method("set_focus"):
            focused.set_focus(true)    # 高亮当前对象

交互执行代码

按 E 触发:

GDSCRIPTfunc _unhandled_input(event):
    if event.is_action_pressed("interact") and focused:
        focused.interact()

门的交互脚本代码

可交互物体统一实现 interact():

GDSCRIPTextends StaticBody3D

var opened: bool = false

func interact():
    print("开门!")
    opened = not opened
    # 开门动画:绕 Y 轴转 90°
    var tween = create_tween()
    tween.tween_property(self, "rotation:y",
        deg_to_rad(90) if opened else 0.0, 0.8)

高亮效果(set_focus)代码

指向时高亮物体:

GDSCRIPTfunc set_focus(focused: bool):
    var mat: StandardMaterial3D = $MeshInstance3D.material_override
    if focused:
        mat.emission_enabled = true
        mat.emission = Color(0.5, 0.8, 1.0)
        mat.emission_energy_multiplier = 0.8
    else:
        mat.emission_enabled = false

提示文字(HUD)代码

指向可交互物时显示「按 E 开门」:

GDSCRIPTfunc _process(delta):
    ...
    # 更新 HUD 提示
    if focused:
        hud.prompt.text = "[E] " + focused.prompt_text
        hud.prompt.visible = true
    else:
        hud.prompt.visible = false
⚠️
交互没触发?

检查:①物体有碰撞形状;②has_method("interact") 存在;③射线长度够(3 米内才可交互)。

⚠️
高亮不消失?

切换焦点时记得调用旧对象的 set_focus(false)。

🧪 动手试一试

🏋️ 课后练习

做一个「开门 + 捡宝物」房间:门按 E 打开,宝物按 E 拾取并消失、HUD 提示文字切换。

门和宝物都实现 interact() 与 set_focus();玩家统一射线驱动。

💡

所有可交互物实现统一接口(interact/set_focus/prompt_text),交互系统可无限扩展。

💡

射线长度=交互距离,3 米是常见手感。

🎓 本课小结

通用 3D 交互:每帧射线找焦点 → 高亮 + HUD 提示 → 按 E 调用 interact();对象统一实现 interact/set_focus 接口。