如何在Godot引擎3D游戏中检测对象的点击/触摸输入?
解决Godot 3D对象鼠标点击检测与屏幕位置获取问题
嘿,我来帮你搞定这个问题!首先得先掰扯清楚:Control节点是给2D UI组件用的,比如按钮、文本框这些,和3D场景里的对象点击完全不搭边,别在这上面浪费精力啦😉
下面分两部分给你讲清楚怎么实现需求:
一、3D对象的鼠标点击检测
要检测3D对象的点击,核心是利用Godot的碰撞检测系统,常用的有两种方法,按需选就行:
方法1:全局射线检测(适合批量检测多个对象)
这种方法是从相机向鼠标点击的方向发射一条“射线”,看这条射线撞到了哪个3D对象。适合需要在全局统一处理所有点击事件的场景:
extends Node3D # 可以挂在主场景根节点上 func _input(event: InputEvent) -> void: # 只处理鼠标左键按下的事件 if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed: # 获取当前场景的相机(这里假设你相机节点叫"Camera3D",按需修改) var camera = get_node_or_null("Camera3D") if not camera: camera = get_tree().root.get_camera() # 计算射线的起点和终点:从相机位置出发,沿着鼠标方向延伸足够远 var ray_origin = camera.project_ray_origin(event.position) var ray_direction = camera.project_ray_normal(event.position) var ray_end = ray_origin + ray_direction * 1000.0 # 1000是射线长度,根据你的游戏场景调整 # 执行射线碰撞检测 var space_state = get_world_3d().direct_space_state var hit_result = space_state.intersect_ray(RayCastParameters.create(ray_origin, ray_end)) # 如果检测到碰撞对象 if hit_result: var target_obj = hit_result["collider"] # 判断是不是你要的特定对象:可以通过节点名称、分组或者自定义脚本判断 if target_obj.name == "Player" or target_obj.is_in_group("InteractiveObjects"): print(f"成功点击到目标:{target_obj.name}") # 这里可以触发你的逻辑,比如打开交互面板、播放动画等
方法2:节点自带的_input_event函数(适合单个对象单独处理)
如果你的3D对象需要自己处理点击事件,可以直接在对象的脚本里重写_input_event函数,前提是对象必须有碰撞体组件(比如CollisionShape3D):
extends MeshInstance3D # 假设你的对象是MeshInstance3D,其他3D节点同理 func _ready(): # 确保对象可以被点击检测(默认是开启的,这里可以手动确认) input_pickable = true func _input_event(viewport: Viewport, event: InputEvent, shape_idx: int) -> void: # 检测左键点击 if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed: print("我被点击啦!") # 这里直接写当前对象的点击逻辑就行
二、获取3D对象在屏幕上的位置
不管用哪种点击检测方法,要获取对象的屏幕位置都很简单,用相机的unproject_position方法,把对象的全局坐标转换成屏幕像素坐标:
# 假设已经拿到了目标对象target_obj和相机camera var screen_position = camera.unproject_position(target_obj.global_position) print(f"对象在屏幕上的位置:X={screen_position.x}, Y={screen_position.y}")
划重点:unproject_position返回的Vector2中,x是屏幕横向像素,y是纵向像素,Godot的屏幕原点是左上角哦。
必看注意事项
- 所有要被点击的3D对象,必须添加碰撞体组件(CollisionShape3D/CollisionPolygon3D),并且碰撞形状要和模型匹配,否则射线检测不到。
- 如果用
_input_event,要确保节点的input_pickable属性是true(默认开启,要是没反应可以检查这个)。 - 如果你的场景有多个相机,记得获取当前正在渲染的相机(用
get_tree().root.get_camera()可以自动拿到当前激活的相机)。
内容的提问来源于stack exchange,提问作者Partin22




