UE4中蓝图实现小地图——雷达图篇

前言

小地图是游戏中常见的组成部分。大概可以分为两到三种。本文根据在实习期间修改的一个蓝图小地图,将其中分为雷达图世界图两种。
其中,雷达图没有世界地图场景,以角色为中心,图上标注了敌我NPC的朝向、位置以及任务点等内容。
世界图只有角色,没有其他目标,有世界地图场景,世界地图不移动,标注了角色位置,并实时移动。

雷达图的实现

雷达图由画板两部分组成。其中,画板是最终的雷达图UI部件。

首先在 Content 目录下UI的相应位置建立 RadarMap 文件夹。再在 RadarMap 文件夹中建立 BP 文件夹和 Texture 文件夹。
在 BP文件夹 中,右键,建立新的User Interface / Widget Blueprint ,并命名为 UI_Point。双击打开。

Designer视图中,设置为 Fill Screen。在Canvas Panel 中添加 Image 控件,重命名为PointImg,并将其 Anchors 设置为四周,且四周的Offset均为0。如下图所示。
UE4中蓝图实现小地图——雷达图篇

在 Graph 视图中,在“The MyBlueprint tab”中的 Functions 中添加新的函数 SetColor,函数具有一个Input,称为 Color,类型为 Linear Color。

双击打开 SetColor 的蓝图Graph,拖拽左侧中的 Variables 中的 PointImg 进来,并给它添加“Set Color and Opacity”,把输入与该函数的输入相连即可。如下图所示。

UE4中蓝图实现小地图——雷达图篇

这么一来,地图的组成部分“点”就算基本完成了。

画板

画板是雷达图的核心内容。

首先,在BP文件夹下新建Widget Blueprint,命名为 UI_RMMovePanel。其中RM为RadarMap的缩写。

先来张图。

UE4中蓝图实现小地图——雷达图篇

图中,在Hierarchy Tab中,Image、BgImg 和 ViewPort 其实是可以合为一张 Texture 放进来的。它们表示的是中间雷达图上除了两个较大的带刻度的圆圈之外的其他内容。因为雷达图永远以角色为中心及方向的正向的,因此如果要使雷达图具有罗盘功能,即显示当前的飞机航向,则需要一个可以 Rotate 的 Ring (Image控件)。左侧这四个(其实可以是两个) Image 控件的Anchors 均为四周型,且四周的 Offset 均为 0 。整个UI的尺寸为 Custom,宽高均为400。

再来张该 Widget BluePrint 的 Graph 图。

UE4中蓝图实现小地图——雷达图篇

这里需要说明一下。该小地图是比较定制化了的,因此其中的 AimSys,是玩家角色飞机的瞄准系统,其父类为 Actor Component。对世界中其他 Actor 的捕捉、识别等,由该 AimSys 完成。其主要功能是获取世界场景中的所有敌机,并判断哪些是在本机的射程距离内的,哪些是被本机导弹系统已经锁定了的。因此,在其他应用场景下,这里并不是必须的,可以由负责相应功能的类实现。

这其中很重要的是 Points 变量。是个刚刚完成的 UI_Point 类变量的 Array,UE4的4.12及以前版本还没有其他的结构体,只有Array(数组)或者Element(单变量)。但是已经足够。

变量 ConvertRatio 用于控制对整个小地图的缩放。

事件图(Event Graph)里的逻辑比较简单。构建事件里,获取当前的角色(或者其他可以获取整个场景中其他目标的类,可以是GameState,或者NPCManager之类的。),并把它 提升为变量 (Promote to variable)。Tick时钟事件里,主要完成两件事,一是完成刚刚提到的罗盘方向刻度盘 ( Image 控件 Ring ) 的旋转,再一个,就是对整个地图上所有的点进行处理(Manage Points)。

那么,我们来看 Manage Points 函数里有些什么呢。。。。来张图。

UE4中蓝图实现小地图——雷达图篇

嗯,非常简单。先把所有的点全部移除。然后再绘制上所有需要绘制的点。

那么接下来,我们先看 Remove Points 函数。上图片~

UE4中蓝图实现小地图——雷达图篇

嗯嗯,还是很简单。如果 Points 数组中有值的话,那么就依次把 Points 里的那些值(Image 控件)从他们的父节点(MovePanel)上移除(相当于把“点”那张图片从整个雷达图的图片上去除)。完成后,将数组 Points 清空。

将画板清空之后再绘制上新的点,就完成了整个雷达图的实时更新。那么我们看 Draw Points 函数。来,图。

UE4中蓝图实现小地图——雷达图篇

看起来很乱很复杂。。但其实还是很简单。只有 Add Points 函数是重要的。在本项目中,只是重复调用Add Points 来添加颜色、纹理(style)或大小不同的目标点而已。而其他的几个 Branch, 则是用来判断角色的瞄准系统(相当于雷达探测器)有任何捕获内容,而并非空数组,以免产生运行时的 Warning 或者 Error。

这里有个数组变量 Visible Tgts,是来自 Aim Sys 的。是一个自定义结构体。具体如下图所示:

UE4中蓝图实现小地图——雷达图篇

所以,当瞄准系统从 GameState 或者 NPCManager 那里获取了所有的其他目标之后,将依次计算这些目标与玩家角色之间的距离,(Z轴)方位角(相当于从上向下俯视图里,目标与自己的连线 和 自己前进方向射线 的夹角),并根据距离和角度来得出目标的绘制等级(比如0为可见不可攻击的;1为可见可攻击的;2为当前导弹瞄准的)。至于这其中的具体算法,此处不再赘述。这不是这篇文章关注的点。(后来发现这个结构体基本上是个废弃的。。。Oh,no。只是,在雷达图里,并没有用到这些值,但是在 Aim Sys 中,是用到了的。)

我们还是来看 Add Points 函数。它有几个输入,分别是目标数组Tgts、点颜色Color、点样式Style、点尺寸Size、角色自身的Z轴旋转值(俯视图中的航向)Self ZRot,以及角色自身的位置Self Pos。So,我们来看内部图。

UE4中蓝图实现小地图——雷达图篇

哈哈。依然简单。只是把数组拆开,依次调用 Add Point 函数。而 Add Point 函数则比 Add Points 函数又多了两个变量,分别是目标位置 Tgt Pos 和目标Z轴旋转值(航向) Tgt ZRot。为了避免可能出现的运行时 Warning 或 Error,添加了对每个目标的有效性检验。

闲话少说,我们来看 Add Point 函数。直接上图。看图说话。

UE4中蓝图实现小地图——雷达图篇

嗯。。。这个函数内部就有点麻烦了。不过还算一目了然,首先创建一个点 Create Point,然后设置这个点的画笔 Set Brush from Texture。因为创建的点,是之前我们做好的 UI_Point,那个 Widget BluePrint 类有个变量是 Image 控件 PointImg,可以通过这样的函数来动态地设置该控件的纹理图,然后接下来设置这个控件的渲染方向 Set Render Angle,这是为了显示出目标的移动方向或者朝向。接下来,对整个 UI_Point 设置颜色 Set Color,设置大小 Set Size,以及 设置位置 Set Position。其中,设置大小和位置的时候需要把 UI_Point Slot as Canvas Slot。。(暂时我也没弄清楚为啥要这么做,只是知道应该这么做。)

那么,这里面还有两个自定义函数,分别是 Create Point 和 Location Convert。至于说 Set Render Angle那里为什么要用 Tgt ZRot - Self ZRot,为什么Set Position要减去尺寸的一半。就不再详述了,相信有点数学基础知识的都能想通。

嗯。现在我们来看最后剩下的这俩,Create Point 和 Location Convert。先看纯数学的 Location Convert 吧。请看图片。

UE4中蓝图实现小地图——雷达图篇

首先,这是个纯函数(Pure),也就是它不需要白色线的事件驱动,也不会产出驱动。然后,我们看,World Position 就是目标的世界坐标,减去本机自身的世界坐标,得到的将是一条由本机指向目标点的向量。那么,去掉这条向量的 z 值,只看 x y 值的话,那么就相当于是从上帝的视角俯视这个世界看到了雷达图一样。所以,除以 Convert Ratio,进行一定比例的缩小之后,绘制到雷达图上就可以了。但是,这里有两点需要注意,分别是图中用红色框框起来的两处。

首先是,刚刚那么减出来之后,得到了一个坐标,但是如果把它应用之后会发现,整个雷达图是歪的,意思就是说,如果我开着飞机,我的右前方有架敌机,那么在雷达图上,它出现在了我的飞机的左上角,或者左下角,或者右下角。反正不是正确的本应出现在的右上角。所以要对上面减完并且缩小后的向量绕着Z轴(0,0,1)旋转一下下,转到合适的角度(90*n)(这个可以不断地编译、运行、然后查看效果,再做相应修改)之后就可以啦。但是,需要注意的是,本机飞机也是在飞行,旋转的,所以要把本机的Z轴上的选转考虑在内。因此就最终得出了这样的, Self ZRot * (-1) - 90 的结果。

另外,要在 X Y 的值上分别加 200。其实,这里的200更好的方法是获取 RootCanvas 的尺寸,然后尺寸的 x y 值除以2,加到这里来。对,就是因为,如果不加这个200,那么雷达图相当于只有第一象限的那部分了。我们需要把雷达图上的原点,从图的左下角,挪到中心点上来。正是因为我们在创建这个 RMMovePanel 的时候,在 Design 视图里,把画布设置成了 Custom 的 400 * 400,所以,这里要分别加200 。

最后的最后,看看 Create Point。来,图。

UE4中蓝图实现小地图——雷达图篇

首先获取Player,然后创建一个上面做好了的 UI_Point 类,并将其提升为 Local Variable,并将其添加到 Root Canvas 下,作为子节点(这与 Remove Points 里的 Remove from the Parent对应),设置 ZOrder 为 1,嗯嗯。记一下这里,等会儿我们要出去看看视图里的 ZOrder。 最后把它添加进 Points 数组里,并且返回该点。

UE4中蓝图实现小地图——雷达图篇

回到视图窗口里,我们可以看到,之前把 Image、BgImg、ViewPort分开的意义了。我们把其他几项的眼睛闭上不看,只看ViewPort,和它的 ZOrder,意图很明显了。这是个遮罩,因为所有的点的 ZOrder 为1,上面那些 Image 控件的 ZOrder 为 0,所以,当目标点出了这个 ViewPort 的中间的圆圈空白范围后,将被它的四角的蓝色挡住了。

当然,有人会说,可以通过蓝图编程的方法实现遮罩,的确。我只是提供一种更懒的,更不需要计算机去计算圆圈判断是否显示的方法而已。

当然,还有人会说,要不要当目标跑出去之后,添加一个箭头,指向目标的位置。就像下图里地图最上方的箭头这样,它表示有个敌人在距离自己更加远的地方,已经远到出了小地图的可视范围了:

UE4中蓝图实现小地图——雷达图篇

这些都可以有。只要按照一定的规则调用 AddPoints 函数就可以了。

总结

其实最主要的思想就是,用一个 Widget Blueprint 类 作为 点,并且添加到另一个作为雷达图的 Widget Blueprint 的子节点里。并且不断地进行删除、重绘就能实现了。最终,要实现把 雷达图 放到飞机上,应该不是很难的事情,搜一下应该都会有。如果有需要,可以评论,视情况决定是否写篇这样的说明吧。

UE4中蓝图实现小地图——雷达图篇

这是雷达图最终的应用效果,显示在飞机的雷达仪表显示区。其中,带圈的绿色目标(朝右(W))的那个就是当前正在被瞄准的,也就是图中被红色框框住的这个。

欢迎关注我的微信公众号:VR_Tech。

公众号刚刚起步,还十分不完善。更多内容,敬请期待。

UE4中蓝图实现小地图——雷达图篇

UE4中蓝图实现小地图——雷达图篇