【UE4】基于Spline的测距功能
基于Spline的测距功能插件
一、功能分析
这里首先分析一下整个插件的功能部件
SplineActor—基于Spline的线条显示模块
Ranging—对整个插件功能的整体控制
DistancePanel—距离显示UI
Point—线上的点,集成DistancePanel
FunLib—高复用函数集合
资产状况:
由于使用屏幕坐标转世界坐标的方式实现测距功能可能及其复杂,所以这里使用比较直观简便的三维Spline来实现测距功能。
二、制作线上的点Point
1.Point的结构分析
Point是一个拥有StaticMesh和WedgitComponent组件的Actor,StaticMesh我这里使用Shape,Materials是自己做的一个发光材质。
2.MeshMeterial材质
Materials蓝图:
3.Point核心函数实现
Point主要实现一个函数两个事件
- 函数ShowDistance—负责距离显示
- 事件InitFontInfo—负责获取初始字体信息
- 事件ShowText—控制距离是否显示
在构造函数需要记录DistancePanel的初始位置信息和初始字体信息。
ConstructScript:记录DistancePanel的初始位置信息和初始字体信息
InitFontInfo:记录字体的初始信息
ShowText:设置显示字体的大小。
Tick函数:SetActorRotation设置点上的文字跟随摄像机旋转使之始终面向摄像机,GetMouseLocation->SetActorLocation->ShowText当SureLocation为false时即未确定点的位置时设置点跟随鼠标移动,当点跟随鼠标移动时不显示距离。
- FontInfo:是一个SlateFontInfoStructure结构体用于设置距离显示的字体样式。
- SureLocation:bool变量,确认点是否已经确定了位置坐标,当点没有确定位置坐标时,点将跟随鼠标移动。
- InitScale3D:Vector变量,保存DistancePanel的初始大小。
- SureAdsorb:bool变量,控制闭环吸附,当为true时,起点具有吸附功能,可以将终点吸附到起点位置实现闭环。
ShowDistance:将输入的距离信息显示出来。
至此点的设计完毕。
三、用于显示的Widget
创建一个UserWidget命名为DistancePanel,DistancePanel比较简单,CanvasPanel下就一个Text即可,只有一个函数UpdateTextScale,然后在EventConstruct中记录Text的初始大小。
EventConstruct:
UpdateTextScale:更新Text的大小,使Text跟随摄像机距离地板的远近变大变小,以保证Text的大小在视野中保持不变。
三、使用Spline制作线段
1.SplineActor结构分析
Spline是UE4的样条线组件,Spline是一组点和线的集合,但是Spline的点和线只有在编辑模式下可见,在运行模式下不可见,Spline可以通过选中其中的点按下Alt键并拖动鼠标来添加新的点。
我们需要一个Actor作为Spline的载体,创建一个Actor命名为Spline。Spline的组件结构为:
- StaticMesh:使用Shape和自定义的材质,这个Mesh作为起点使用。
- Spline:样条线组件,此Actor的核心组件。
- Sphere:球型触发器,用于起点吸附。
2.在编辑模式下实现Spline编辑
在编辑模式下实现样条线的编辑需要在构造函数中实现下面的逻辑:
核心函数分析:
AddSplineMeshComponent:这是一个自定义的封装函数,作用就是提高复用率,
StaticMesh决定线的样式,Meterial决定线的颜色;
AttachToMeshComponent:将添加的SplineMeshComponent组件设置StaticMesh为父节点;
Set Start and End:函数根据Get LocationandTangentatSplinePoint函数获取的起点和终点的位置和切角,将生成的SplineMeshComponent组件附着在上面。
这样我们的Spline样条线就被设置成了我们设定的模样,并且在运行时可见。
3.运行时动态添加Spline的点
由于在运行模式下无法像在编辑模式下通过Alt键拖动点来添加Spline的点,所以我们需要通过蓝图来实现。这项功能封装在AddPoint函数中。
AddPoint函数通过输入的坐标位置动态生成Spline的点
- AddSplinePoint:向Spline中添加新的点;
- SpawnActorPoint:目的是在Spline的生成的新点的位置处生成一个具象化的Point;
- Sequence的的Then0分支作用是当生成一个新的Point时,确定上一个Point的位置坐标;
- PointArray是一个Point类型的数组,用于存储生成的Point的引用,SplineMeshComponentArray是一个SplineMeshComponent类型的数组,用于存储生成的SplineMeshComponent组件的引用,两个数组的作用是方便之后对Point和SplineMeshComponent的操作。
- AddSplineMeshComponent、AttachToComponent和SetSartandEnd函数作用和构造函数中一样;
- UpddateTotaldistance函数用于更新距离显示,具体实现在后面介绍。
AddPoint函数在AddPointEvent事件中调用。
- GetMouseLocation是FunLib库中的一个函数负责获取鼠标坐在的屏幕坐标转换成空间坐标。
4.实时更新样条线
实时更新样条线的功能封装在UpdateCurrentSplinePoint函数中。
UpdateCurrentSplinePoint函数在SplineActor的Tick函数中调用,每帧删除前一个Spline的点,在新的坐标位置下添加一个新的Spline的点,由于Point是跟随鼠标移动的,所以通过这个操作在宏观上的表现就是Spline的点在跟随鼠标一点,之所以使用这种方式,是因为Spline中的点似乎没办法直接修改位置。
每帧设置好位置之后再重新渲染一遍Mesh组件,就达到如下效果了:
这样在运行状态下编辑Spline样条线就制作完成了。
四、实时更新距离
实时更新距离的功能封装在UpdateTotalDistance函数下。
- Ranging变量就是Ranging类型,存储Ranging的引用,在重新计算总距离前先将存储中距离的变量TotalDistance清零;
- 然后一次取PointArray中的Point来计算Point与Point之间的距离,0号索引的Point较为特殊需要与其他索引的Point分开计算,因为0号索引的Point需要与SplineActor的位置计算距离;
- ShowDistance函数封装在Point类中,负责将输入的距离显示出来。
显示总距离专门创建了一个DistancePanel来显示。
- HiddenTotalDistance控制总距离是否显示。
到这里基本的功能就基本实现了,下面实现一些必要的附加功能。
五、封装Ranging类
由于SplineActor类是插件的核心类,不宜对外开放调用接口,且SplineActor类自身拥有Mesh,直接拖入场景中会显示Mesh,效果不佳,所以在SplineActor之外再封装一成没有Mesh的Ranging是十分必要的,有Ranging类提供对外调用的接口。
Ranging类的封装函数和变量:
SetupRanging:启动测距,在鼠标所在位置生成SplineActor;
- AddPoint:封装SplineActor中的AddPointEvent事件;
- EndRanging:结束测距,封装ActorSpline的DeleteLastPoint函数,函数的具体实现在之后介绍;
Remove:删除所有的点线,封装SplineActor的RemoveAllPoint函数,函数的具体实现在之后介绍;
SplineActor:存储SplineActor的引用;
TotalDistance:存储总距离的值;
DistanceUint:显示总距离时的单位;
K:存储SplineActor中DistancePanel随相机距离变化大小的变化倍率;我这里设定的值为0.0002。
文字随相机距离变化的函数实现封装在Ranging的UpdeteDistancePanelScale事件中,事件在Ranging的Tick函数中调用。
六、保持Point的DistancePanel组件的大小不变
为了保证观感效果,Point的显示距离的DistancePanel组件的大小应该跟随相机的远近保持保持一定的大小,以保证相机贴近地面时,文字不会过大,相机原理地面时文字不过过小而看不见。
实现原理就在Ranging的UpdateDistancePanelScale事件中。
七、返回上一步功能
当我们确定点的位置时会出现位置确定错误的情况,所以返回上一步的功能也是十分必要的,具体实现在SplineActor的DeleteLastPoint函数中。
原理是移除上一个Spline的点和其匹配的Point、SplineMeshComponent并删除数组中对应的元素,然后更新一次距离,如果剩下最后一个点时,再撤回就直接将SplineActor删除并把显示总距离的DistancePanel移除,防止再创建SplineActor时再生成一个DiatancePanel而出现两个DistancePanel。
八、移除所有的点
当测距完成后需要清除所有的点,所以此功能也是必要的,具体实现在SplineActor的RemoveAllPoint中。
九、起点吸附功能
起点吸附是为了实现闭环。具体实现在SplineActor的EventActorBeginOverlap事件中。
原理就是当SplineActor的Sphere触发器检测到Point时将此Point的坐标设置到起点的坐标处。
九、整体效果预览
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!