Qt3D中实体重叠时如何获取所有被拾取的实体?
解决Qt3D多重叠实体拾取全结果问题
核心思路
别盯着单个QObjectPicker的clicked信号了——这个信号本来就只会触发当前Picker绑定实体的回调。要拿到所有重叠实体的命中结果,得改从全局拾取事件里直接捞所有PickResult集合。
具体实现步骤
1. 确认拾取配置生效
先把QPickingSettings的结果模式和拾取方法配置对,这是基础:
# 假设你有Qt3DWindow实例window picking_settings = window.renderSettings().pickingSettings() picking_settings.setPickResultMode(Qt3DRender.QPickingSettings.PickAllPicks) picking_settings.setPickMethod(Qt3DRender.QPickingSettings.TrianglePicking) # 按需调整,也可以用BoundingVolumePicking
2. 重写Qt3DWindow的鼠标点击事件
自定义一个窗口子类,通过重写mousePressEvent直接捕获全局拾取结果:
from PySide2.Qt3DExtras import Qt3DWindow from PySide2.Qt3DRender import QPickEvent from PySide2.QtWidgets import QInputDialog class Custom3DWindow(Qt3DWindow): def mousePressEvent(self, event): # 获取当前鼠标位置的所有拾取结果 pick_event = self.pick(event.x(), event.y()) if pick_event and pick_event.hits(): # 提取所有命中的实体 hit_entities = [hit.entity() for hit in pick_event.hits() if hit.entity()] # 根据命中数量分支处理 if len(hit_entities) == 1: hit_entities[0].setSelected(True) else: # 弹出选择框让用户选目标实体(这里用QInputDialog示例) entity_labels = [f"实体ID: {ent.id}" for ent in hit_entities] selected_label, ok = QInputDialog.getItem(self, "选择实体", "多个实体被命中,请选择:", entity_labels) if ok: selected_idx = entity_labels.index(selected_label) hit_entities[selected_idx].setSelected(True) # 别忘了调用父类方法,保证其他事件正常流转 super().mousePressEvent(event)
3. 调整实体的QObjectPicker配置
不需要再给每个实体的QObjectPicker绑定clicked信号了——全局事件已经能拿到所有结果。如果需要保留Picker做hover之类的其他交互,可以留着但不用管clicked信号。
4. 关键细节
pick(x, y)方法返回的QPickEvent里,hits()就是所有命中的PickResult列表,前提是你已经开了PickAllPicks模式。- 从
PickResult的entity()方法能直接拿到对应实体对象,你可以给自己的NodeEntity加id、名称之类的自定义属性,方便用户区分选择。
替代方案:手动发射射线
如果不想重写窗口事件,也可以用QRayCaster手动发射射线获取结果:
from PySide2.Qt3DRender import QRayCaster from PySide2.QtCore import QPointF, QRectF # 初始化时给根实体添加射线发射器 ray_caster = Qt3DRender.QRayCaster() root_entity.addComponent(ray_caster) # 绑定鼠标点击事件触发射线 def on_mouse_click(event): camera = window.camera() # 计算鼠标位置对应的射线起点和方向 ray_origin = camera.unproject(QPointF(event.x(), event.y()), QRectF(0, 0, window.width(), window.height())) ray_direction = camera.viewVector() # 设置射线参数并发射 ray_caster.setOrigin(ray_origin) ray_caster.setDirection(ray_direction) ray_caster.setLength(1000.0) # 按需调整射线长度 ray_caster.trigger() # 监听射线命中结果变化 def handle_hits(hits): if hits: hit_entities = [hit.entity() for hit in hits if hit.entity()] # 后续选择逻辑和前面一致 ray_caster.hitsChanged.connect(handle_hits)
这种方式更灵活,适合需要手动控制射线发射时机的场景。
内容的提问来源于stack exchange,提问作者PePer
相关产品推荐
相关产品推荐

