1. 项目概述与核心价值
最近在带几个刚入门的UE5新人,发现他们最头疼的不是怎么摆模型、调材质,而是如何与场景里的物体进行“有效沟通”。比如,你想让玩家框选一群士兵,然后指挥他们冲锋,这个看似简单的“框选”功能,就成了第一道坎。很多教程要么讲得太浅,只告诉你拖几个节点;要么直接上C++,对蓝图新手不太友好。所以,我决定把带新人时反复打磨的这套“蓝图框选Actor”方案整理出来,它不只是一个功能实现,更是一套理解UE5输入、渲染、碰撞检测联动的思维模型。
这个教程的目标很明确:让你仅用蓝图,就实现一个手感扎实、性能可靠、可扩展性强的鼠标框选系统。完成后,你将得到一个完整的项目文件,里面包含了从创建HUD绘制框选框,到处理鼠标事件,再到精准筛选被框选Actor的所有逻辑。更重要的是,你会明白每一步“为什么”要这么做——比如为什么要在HUD里画框,而不是在PlayerController里?框选检测时,Box和Sphere检测该怎么选?这些决策背后的考量,才是新手成长为熟手的关键。
无论你是想做RTS、策略游戏,还是需要场景编辑工具,这个功能都是基石。接下来,我会假设你已经有UE5的基础操作知识(比如打开编辑器、创建蓝图类),但完全没接触过框选逻辑。我们从最根本的设计思路开始拆解。
2. 核心设计思路与架构拆解
在动手写任何蓝图之前,我们必须先想清楚:一个完整的框选流程,在UE5里究竟是怎么跑起来的?如果脑子里没有这张“数据流图”,直接对着节点生搬硬套,出了问题根本不知道从哪查起。
2.1 框选流程的数据流全景图
一个标准的鼠标框选,其核心流程可以概括为四个阶段:输入捕获 -> 视觉反馈 -> 物理检测 -> 结果处理。在UE5的蓝图框架下,这四个阶段分别由不同的“责任人”来承担:
- 输入捕获阶段:主角是PlayerController。它像是一个总指挥,专门负责接收来自玩家鼠标、键盘的原始输入信号。当玩家按下鼠标左键时,这个信号首先被PlayerController拿到。它需要记录下“按下那一刻”鼠标在屏幕上的位置(一个2D坐标)。
- 视觉反馈阶段:主角是HUD。总指挥知道了起点,但玩家需要看到自己框选的范围。HUD就像一个透明的画布,覆盖在游戏画面上方,专门负责绘制UI和这类临时性的几何图形。它根据PlayerController传来的起点和当前鼠标位置,实时绘制一个矩形框,给玩家最直接的视觉反馈。
- 物理检测阶段:主角是PlayerController或GameMode。当玩家松开鼠标左键,框选结束时,我们需要知道这个2D的屏幕矩形框,对应在3D游戏世界里罩住了哪些物体。这个过程需要将2D屏幕坐标转换为3D世界中的检测体(比如一个立方体范围),然后使用碰撞查询去检测范围内的Actor。
- 结果处理阶段:主角是被选中的Actor和管理它们的系统。检测到目标后,通常需要通知这些Actor:“你被选中了!”(例如高亮显示),同时可能还有一个全局的选择管理器来记录当前选中的所有单位,以便后续进行群体移动或释放技能。
为什么要这么分工?这是UE框架的职责分离原则。PlayerController处理输入逻辑,HUD处理绘制,碰撞检测是引擎的基础服务。如果把绘制逻辑塞进PlayerController,或者用HUD去做复杂的碰撞查询,代码会变得混乱且难以维护。理解这个架构,是避免后面踩坑的基础。
2.2 关键蓝图类的职责与选型
基于上面的流程,我们需要创建和修改几个关键的蓝图类:
- MyPlayerController:继承自
PlayerController。我们将在这里处理鼠标按下、移动、抬起的输入事件,存储框选的起点和终点坐标,并在框选结束时发起物理检测。 - MyHUD:继承自
HUD。我们将重写它的DrawHUD函数,在这个函数里,根据MyPlayerController提供的起点和当前鼠标位置,使用绘制函数画出一个矩形框。 - SelectableActor:这是一个接口或父类。并非所有Actor都能被框选(比如背景天空球)。我们最好创建一个
Selectable接口(蓝图接口),里面定义诸如OnSelected、OnDeselected这样的函数。然后让所有希望被框选的单位(如BP_Character_Soldier)实现这个接口。这样,检测逻辑可以只关注实现了该接口的Actor,更清晰高效。
为什么用接口而不是直接检测所有Actor?这是性能与设计清晰度的双重考量。场景中可能有成百上千个Actor,但只有几十个是可选的单位。如果每帧都检测所有Actor,会做大量无用功。通过接口,我们可以在碰撞查询时指定特定的接口类,引擎会帮我们过滤,大幅提升效率。同时,这也是一种良好的契约设计,任何想要获得框选能力的Actor,只需实现几个规定的方法即可,扩展性很强。
2.3 框选检测的核心算法选择
当框选结束时,我们手里有一个2D的屏幕矩形。如何找到3D世界中被这个矩形“罩住”的Actor?主要有两种主流思路:
- 视锥体剔除 + 包围盒检测:将屏幕矩形的四个角,通过摄像机矩阵反推出四条射线,这四条射线在3D空间中构成一个平截头体(Frustum)。然后,检查每个候选Actor的包围盒(Bounding Box)是否与这个视锥体相交。这种方法在RTS游戏中非常经典,检测精度高,但计算量相对较大,需要自己处理视锥体构建和相交测试。
- 射线投射 + 体积检测:这是本教程采用的方法,更直观且易于用蓝图实现。其原理是:
- 在框选起点和终点位置,从屏幕向世界各发射一条射线(
Deproject Screen to World)。 - 根据这两条射线获得的世界空间起点,可以构造出一个3D的盒子(Box)或胶囊体(Capsule)。
- 使用
OverlapMultiBy...或BoxOverlapActors等节点,对这个3D体积进行重叠检测,返回所有在其中的Actor。
- 在框选起点和终点位置,从屏幕向世界各发射一条射线(
我们选择第二种方法(射线+体积检测)的原因:对于新手来说,蓝图节点支持完善,逻辑直观易懂。Deproject和Overlap节点都是现成的,不需要我们手动进行复杂的矩阵运算。在中小规模单位(几百个)的场景下,性能完全足够。第一种方法更适合C++实现或超大规模单位选择的极端优化场景。
3. 逐步实现:从零搭建框选系统
理论清晰了,现在开始动手。我会按照创建蓝图、绑定输入、绘制反馈、实现检测的顺序,一步步带你把系统搭起来。请打开你的UE5项目(建议用空白模板或ThirdPerson模板),跟着操作。
3.1 创建核心蓝图类与设置
首先,创建我们需要的蓝图类。
- 在内容浏览器中右键,选择“蓝图类”。
- 在弹出窗口中,搜索并选择“PlayerController”作为父类,命名为
BP_MyPlayerController。 - 同样方法,创建以“HUD”为父类的蓝图,命名为
BP_MyHUD。 - 接着,创建一个蓝图接口:右键 -> 蓝图 -> 蓝图接口,命名为
BPI_Selectable。
接下来,修改项目设置,让游戏使用我们新建的类。
- 点击菜单栏的“编辑” -> “项目设置”。
- 在“项目”分类下找到“地图和模式”。
- 在“默认模式”中,将“默认Pawn类”设为你的角色(如ThirdPerson示例中的
BP_ThirdPersonCharacter),将“玩家控制器类”设为BP_MyPlayerController,将“HUD类”设为BP_MyHUD。 - 保存设置。
这样,游戏运行时就会自动使用我们自定义的控制器和HUD了。
3.2 处理鼠标输入事件
打开BP_MyPlayerController,我们需要添加几个变量来记录框选状态。
添加变量:
bIsSelecting(布尔型):是否正在框选(鼠标左键按下未松开)。SelectionStart(Vector2D):框选开始的屏幕坐标。SelectionEnd(Vector2D):框选当前的屏幕坐标。CurrentSelectionBox(Box Extent 或 Vector): 用于存储计算出的3D检测框范围(可选,可临时计算)。
绑定输入事件:
- 在事件图表中,右键搜索“Enable Input”节点并调用,确保控制器能接收输入。
- 右键搜索“InputAction”或“InputAxis”事件。我们需要绑定三个:
LeftMouseButton(Pressed):鼠标左键按下事件。MouseX和MouseY(Axis Events):鼠标移动事件。LeftMouseButton(Released):鼠标左键释放事件。
实现输入逻辑:
- 在LeftMouseButton (Pressed)事件后:
- 设置
bIsSelecting = true。 - 使用
Get Mouse Position节点获取当前鼠标位置,存入SelectionStart。 - 同时,也将这个值赋给
SelectionEnd(因为刚开始框选,起点和终点相同)。
- 设置
- 在MouseX和MouseY(Axis Events) 事件后:
- 先做一个分支(Branch),判断
bIsSelecting是否为真。只有正在框选时才更新终点。 - 如果为真,再次使用
Get Mouse Position,更新SelectionEnd变量。 - 这里有个关键点:我们需要把最新的
SelectionEnd告诉HUD,让它重绘框选框。可以通过一个自定义事件(如UpdateSelectionBox)来传递坐标,或者直接设置HUD的公开变量。为了降低耦合,我常用一个简单方法:在PlayerController里定义一个事件OnSelectionBoxUpdated(带两个Vector2D参数),然后在HUD里绑定这个事件。这里我们先采用设置HUD变量的方式。
- 先做一个分支(Branch),判断
- 在LeftMouseButton (Released)事件后:
- 设置
bIsSelecting = false。 - 此时,
SelectionStart和SelectionEnd构成了一个完整的矩形。调用我们即将编写的“执行框选检测”函数(例如PerformSelection)。 - 检测完成后,应该清空或重置
SelectionStart和SelectionEnd,并通知HUD停止绘制(可以将SelectionEnd设置为一个无效值,如(-1,-1))。
- 设置
- 在LeftMouseButton (Pressed)事件后:
注意事项:输入坐标的坐标系
Get Mouse Position返回的坐标是相对于视口(Viewport)的像素坐标,原点(0,0)在左上角。而HUD绘制时使用的也是这个坐标系,所以可以直接使用。但在后续进行屏幕到世界的反投影时,需要将像素坐标转换为标准化设备坐标(NDC),即X和Y范围在[-1, 1]或[0, 1]之间。Deproject Screen to World节点通常接受的是视口坐标,但内部会处理这个转换。我们只需确保传入的坐标是正确的视口坐标即可。
3.3 在HUD中绘制框选框
打开BP_MyHUD。HUD的绘制发生在每一帧的DrawHUD事件中。我们需要在这里根据PlayerController传来的坐标画一个矩形。
添加变量来接收绘制信息:
SelectionBoxStart(Vector2D)SelectionBoxEnd(Vector2D)bDrawSelectionBox(布尔型)
从PlayerController获取数据:
- 在事件图表中,我们需要在游戏开始时获取到PlayerController的引用。可以在
Event BeginPlay节点后,使用Get Player Controller节点,然后类型转换为BP_MyPlayerController,将返回的引用保存到一个变量中,比如MyPC。 - 在
BP_MyPlayerController中更新SelectionEnd时,除了更新自己的变量,还应调用HUD上的一个自定义事件(如UpdateSelectionBox),将起点和终点坐标传过来,并设置bDrawSelectionBox为真。
- 在事件图表中,我们需要在游戏开始时获取到PlayerController的引用。可以在
重写DrawHUD逻辑:
- 在
BP_MyHUD的事件图表中,找到或重写Receive Draw HUD事件(在UE5中,通常是Event Receive Draw HUD节点)。 - 在这个事件后,先判断
bDrawSelectionBox是否为真,并且起点和终点不能相同(避免画一个点)。 - 如果为真,使用
Draw Rect节点来绘制矩形。Rect Position: 矩形的左上角坐标,应该是SelectionBoxStart和SelectionBoxEnd中X和Y较小的值。可以用Min of Float分别计算X和Y的最小值。Rect Size: 矩形的宽和高,是终点坐标减起点坐标的绝对值。用Abs (Float)节点计算SelectionBoxEnd.X - SelectionBoxStart.X和SelectionBoxEnd.Y - SelectionBoxStart.Y。Color & Opacity: 设置一个半透明的颜色,比如淡蓝色(R:0.2, G:0.5, B:1.0, A:0.3)。
- 当PlayerController在鼠标释放后通知HUD停止绘制时,将
bDrawSelectionBox设为假。
- 在
实操心得:绘制优化
DrawHUD每帧都会调用,里面的绘制操作要尽量轻量。Draw Rect本身开销不大。但要避免在DrawHUD里做复杂的计算(比如每帧都去计算矩形的最小/最大值)。最好在收到PlayerController坐标更新时,就提前计算好矩形的Position和Size,存为变量,在DrawHUD里直接使用。此外,只有当bDrawSelectionBox为真时才执行绘制逻辑,这也是一个重要的性能优化点。
3.4 实现框选检测逻辑
这是最核心的一步,在BP_MyPlayerController的PerformSelection函数中实现。我们的目标是:根据屏幕矩形,找到所有被框住的、实现了BPI_Selectable接口的Actor。
屏幕坐标到世界空间的转换:
- 我们需要将屏幕矩形的两个对角点(起点和终点)转换到3D世界空间。但注意,屏幕坐标是一个2D点,对应世界中的一条射线(从摄像机出发,穿过该屏幕点)。我们真正需要的是这条射线与某个“平面”的交点,来作为3D检测体积的角点。
- 通常,我们选择与地面平行的平面(比如Z=0的高度)。使用
Deproject Screen to World节点。它需要Player Controller(自身)、Screen Position(屏幕坐标)和World Position & World Direction(输出)。我们主要需要World Direction(射线方向)。 - 为了得到一个世界位置,我们需要定义一个检测平面。假设我们的可选中单位都在大致相同的高度(比如Z=100),我们可以计算射线与这个水平面的交点。公式是:
WorldOrigin + WorldDirection * T,其中T =(PlaneZ - OriginZ) / DirectionZ。在蓝图中,这需要一些向量计算。
构建3D检测体积:
- 一个更稳健且蓝图友好的方法是:我们不直接计算精确的角点,而是利用起点和终点在世界空间中的“意义”。我们可以从起点和终点分别发射射线,击中我们关心的“选择平面”,得到两个3D点(
WorldPointA和WorldPointB)。这两个点不一定能直接构成一个立方体的对角点,因为它们的Z值可能相同(如果平面是水平的)。 - 我们可以用这两个点的X和Y坐标来定义底面的矩形,然后赋予这个矩形一个高度(比如从地面向上200单位),从而构造出一个3D的盒子(Box)。
- 盒子的中心:
( (WorldPointA.X + WorldPointB.X)/2, (WorldPointA.Y + WorldPointB.Y)/2, PlaneZ + (BoxHeight/2) ) - 盒子的半高范围(Box Extent):
( Abs(WorldPointA.X - WorldPointB.X)/2, Abs(WorldPointA.Y - WorldPointB.Y)/2, BoxHeight/2 )
- 一个更稳健且蓝图友好的方法是:我们不直接计算精确的角点,而是利用起点和终点在世界空间中的“意义”。我们可以从起点和终点分别发射射线,击中我们关心的“选择平面”,得到两个3D点(
执行重叠检测:
- 有了盒子的中心(
Box Location)和范围(Box Extent),我们就可以进行物理检测了。 - 使用
Overlap Multi By Profile或Box Overlap Actors节点。推荐使用Box Overlap Actors,因为它更直观。 - 你需要指定:
World Context Object: 通常用Get World。Box Pos和Box Extent: 上面计算出的值。Object Types: 选择你想要检测的物体类型,比如WorldDynamic(可移动物体)。Actor Class Filter: 这里留空,因为我们用接口过滤。Actors to Ignore: 可以忽略玩家控制的Pawn自身。
- 这个节点会返回一个Actor数组,包含了所有与这个盒子重叠的Actor。
- 有了盒子的中心(
接口过滤与通知:
- 遍历返回的Actor数组。
- 对每个Actor,使用
Does Implement Interface节点,检查它是否实现了BPI_Selectable接口。 - 如果实现了,将其添加到一个最终的选择结果数组(
SelectedActors)中。 - 然后,可以调用该Actor上接口定义的函数,例如
On Selected,通知它已被选中。同时,也可以取消之前选中但现在不在列表中的Actor的选中状态(这需要一个记录上次选中列表的变量)。
关键细节:处理反向框选玩家框选时,可以从左往右拖,也可以从右往左拖。我们计算矩形
Position和Size时用了Min和Abs,这能正确处理。但在计算世界空间盒子的Box Extent时,Abs(WorldPointA.X - WorldPointB.X)同样保证了范围始终为正。这是构建检测体积时必须注意的,否则会得到一个负向的范围,导致检测失败。
3.5 为Actor添加可选中功能
最后,让我们创建一个可被框选的示例单位。
- 创建一个新的角色蓝图,比如
BP_SelectableUnit,基于Character或简单的Actor。 - 打开这个蓝图,在“类设置”中,点击“已实现的接口”旁边的“添加”按钮,选择
BPI_Selectable。 - 这会自动在事件图表中生成接口函数的定义(如
Event On Selected和Event On Deselected)。 - 在
Event On Selected事件后,你可以添加任何被选中时的反馈,例如:- 动态改变材质颜色(创建一个动态材质实例,设置自发光颜色)。
- 在头顶显示一个选中圈(附加一个粒子系统或静态网格体)。
- 播放一个音效。
- 在
Event On Deselected事件后,恢复原来的状态。
现在,将这个单位拖到场景中多个。运行游戏,用鼠标左键拖拽框选,你应该能看到半透明的蓝色选框,并且被框住的单位会触发你设置的选中反馈(比如变亮)。
4. 性能优化与高级技巧
基础功能跑通后,我们得考虑效率和实用性。直接每帧或每次框选都做全场景的BoxOverlap,在单位数量多的时候可能会成为性能瓶颈。
4.1 优化检测查询
使用对象通道(Object Channel)进行粗筛:
- 在项目设置的“碰撞”中,新建一个对象通道,命名为
Selectable。 - 为你所有可选中单位的网格体,在它们的碰撞设置中,将“对象类型”设为
WorldDynamic,并在“碰撞预设”中,确保它们对Selectable通道有响应(或者自定义一个预设)。 - 在
Box Overlap Actors节点中,将Object Types参数只勾选Selectable。这样,物理引擎在检测时,会首先过滤掉所有不属于这个通道的物体,大幅减少需要精细检测的对象数量。
- 在项目设置的“碰撞”中,新建一个对象通道,命名为
分帧处理或异步检测:
- 如果一次框选可能选中数百个单位,遍历数组并逐个调用接口函数可能会造成帧率卡顿。
- 可以将选中单位的处理(如高亮)分散到多帧中进行。使用
Delay节点或更优雅的Timeline、Async节点来实现。 - 一个简单的方法是:在
PerformSelection函数中,将最终选中的Actor数组保存起来。然后,每帧(在Tick事件中)处理其中固定数量(比如10个)的单位,直到处理完毕。
空间数据结构(进阶):
- 对于超大规模的场景(如成千上万个可交互单位),可以考虑使用空间划分数据结构,如四叉树(2D)或八叉树(3D),将单位按位置组织起来。
- 框选时,只需检测与选框相交的那些空间格子里的单位,而不是检测全部。这需要较多的C++编码或利用引擎已有的
Navigation Mesh、Grid等系统进行近似管理。对于纯蓝图项目,如果单位数量在几百以内,前面的优化通常已足够。
4.2 提升交互体验
框选框的视觉美化:
- 基础的
Draw Rect矩形框比较生硬。可以在HUD的绘制中,使用Draw Line或Draw Texture来绘制一个带边框和半透明填充的更好看的选框。 - 例如,用四条
Draw Line画出边框,再用Draw Rect填充半透明色。边框的颜色可以根据框选状态变化(比如按下时红色,拖动时蓝色)。
- 基础的
添加框选音效:
- 在鼠标按下开始框选时,播放一个简短的开始音效。
- 在鼠标释放完成框选时,如果选中了单位,播放一个“单位已选中”的反馈音效;如果没选中任何单位,播放一个取消音效或无声。
实现追加选择与取消选择:
- 标准的RTS操作是:单击取消当前选择并选择单个单位;按住Ctrl框选可以追加到当前选择;按住Shift框选可以取消已选中的单位。
- 这需要在
BP_MyPlayerController中维护一个CurrentSelection数组。 - 在
PerformSelection中,先检测是否按下了Ctrl或Shift键(使用Is Input Key Down节点)。 - 如果按下了Ctrl,将新选中的单位追加到
CurrentSelection数组中(注意去重)。 - 如果按下了Shift,遍历新选中的单位,如果它已经在
CurrentSelection中,则调用其On Deselected并将其移除;否则,将其加入并调用On Selected。 - 如果都没按,则先清空
CurrentSelection并取消所有之前单位的选中状态,再处理新选中的单位。
处理摄像机边缘滚动与框选:
- 在RTS游戏中,常常需要将鼠标移动到屏幕边缘来移动摄像机。这可能会和框选操作冲突。
- 一个常见的处理方法是:当鼠标左键按下并拖动时,优先视为框选操作,此时禁用或降低边缘滚动的灵敏度。可以在PlayerController的Tick中,根据
bIsSelecting变量来决定是否处理边缘滚动输入。
5. 常见问题与调试技巧实录
即使跟着步骤做,也难免会遇到一些“坑”。这里记录了几个我教学和开发中最常被问到的问题和解决方法。
5.1 框选框绘制不出来
- 检查HUD类绑定:确保在项目设置中,
BP_MyHUD已正确设置为默认HUD类。 - 检查绘制条件:在
BP_MyHUD的DrawHUD事件中,打印bDrawSelectionBox、SelectionBoxStart和SelectionBoxEnd的值,确保在拖拽鼠标时这些值被正确更新且有效。 - 检查坐标计算:
Draw Rect的Position和Size计算错误会导致矩形画在屏幕外。打印计算后的Rect Position和Rect Size,确保它们是在屏幕范围内的合理值(如Position为正数,Size不为负)。 - 检查绘制顺序:确保
Draw Rect节点确实被连接到DrawHUD的执行引脚上,并且前面没有导致流程中断的分支。
5.2 框选检测不到任何Actor
- 检查检测体积的位置和大小:这是最常见的问题。在
PerformSelection函数中,在调用Box Overlap Actors之前,使用Draw Debug Box节点(需要启用游戏中的调试绘制),将计算出的Box Location和Box Extent画出来。运行游戏并框选,看看这个绿色的调试盒子是否确实包裹住了你想要选择的单位。- 如果盒子位置完全不对(比如在天上或地下),说明从屏幕坐标到世界坐标的转换出了问题。仔细检查
Deproject Screen to World节点的使用,以及计算与世界平面交点的公式。 - 如果盒子大小是0或者非常小,说明
WorldPointA和WorldPointB计算有误或距离太近。
- 如果盒子位置完全不对(比如在天上或地下),说明从屏幕坐标到世界坐标的转换出了问题。仔细检查
- 检查碰撞设置:
- 确保你想要选中的Actor的网格体有碰撞体。在静态网格体编辑器中检查碰撞是否已生成(简单碰撞或复杂碰撞)。
- 确保在
Box Overlap Actors节点中,设置的Object Types包含了你的Actor所属的类型(如WorldDynamic),并且你新建的Selectable通道(如果用了)已正确配置。 - 尝试将
Actor Class Filter暂时留空,看看是否能检测到任何Actor(即使是不可选的)。如果能,说明是接口过滤的问题;如果不能,说明是碰撞检测本身的问题。
- 检查接口实现:在框选检测的循环中,添加一个调试字符串输出,打印每个被检测到的Actor名称,以及
Does Implement Interface节点的返回结果。确认你的单位蓝图确实正确添加了BPI_Selectable接口。
5.3 框选性能低下,游戏卡顿
- 缩小检测范围:确保你的检测盒子大小是合理的。如果玩家在屏幕角落轻轻一点,你构建的盒子却覆盖了整个地图,那性能肯定好不了。可以通过限制
Box Extent的最大值来避免这种情况。 - 启用对象通道过滤:如前所述,使用自定义的
Selectable对象通道是减少检测对象数量最有效的方法之一。 - 减少检测频率:确保
PerformSelection函数只在鼠标释放时调用一次,而不是在拖拽过程中每帧调用。 - 优化被选中单位的反馈:如果
On Selected事件里执行了昂贵的操作(如加载材质、生成粒子),也可能造成卡顿。考虑使用更轻量级的反馈,或者将反馈效果延迟加载。
5.4 框选结果不准确(多选、漏选)
- 2D屏幕框与3D体积的映射误差:这是固有难点。我们构建的3D盒子是一个近似。对于高度差异很大的单位(空中单位和地面单位),或者摄像机俯角很大时,可能会漏选。可以考虑:
- 构建一个更厚的检测盒子(增加
Box Extent的Z值)。 - 使用多个不同高度的检测平面进行多次检测(性能开销增大)。
- 对于重要的单位,使用
Overlap Multi By Channel并指定ECC_Visibility通道,模拟从摄像机发射一束通过屏幕矩形的“视锥体”进行检测(更精确但更复杂)。
- 构建一个更厚的检测盒子(增加
- 单位包围盒不准:如果单位的碰撞体(用于Overlap检测的)比其视觉模型小很多,就容易漏选。确保视觉网格体的碰撞体能够较好地包裹住模型。对于复杂模型,可以使用多个简单碰撞体(Box、Capsule)组合,或者使用
Complex Collision As Simple(性能开销大,慎用)。
整个项目文件的核心逻辑就搭建完毕了。我提供的完整项目文件会包含以上所有步骤的蓝图,以及一个带有选中高亮效果的示例单位。你可以直接打开学习,或者作为模板集成到你自己的项目中。记住,理解每一步背后的“为什么”,比复制粘贴节点更重要。这套框选框架就像一套乐高积木,你可以在此基础上,轻松地添加音效、更酷的视觉效果、单位编组、命令队列等高级功能,打造属于你自己的游戏交互体验。