Part1前言

最常见的鼠标平移算法是平行于水平面(地面)的:无论相机视角如何,平移时,相机的世界Z值始终不变,因为绝大多数场景都是在观察地面上的物体,而人类的行走总是平行于地面的。但是本文要介绍的另一种小众的平移算法则平行于视锥体的截面,平移时,相机的本地X值始终不变,平移面始终垂直于相机的方向,这种鼠标平移算法适用于空间类的场景,比如观察一堆无人机的飞行轨迹。区别于“地面类”的平移,“空间类”平移算法还要求垂直同步。比如有如下的要求:鼠标左键按下后,通过光标位置的射线计算出光标点对应的场景位置,在平移过程中,该场景位置在屏幕上的投影点始终与光标在屏幕上的位置同步。这里要求光标与光标指向的物体同步移动,那么这条射线始终垂直于镜面(在现实中,镜面是球面,因此是垂直的),故曰垂直同步算法。


请取件| 基于UE5的鼠标平移垂直同步算法_鼠标事件

Part2实现原理

首先构建一个空间中的三角形:由视点、原光标点、新光标点组成的三角形。原光标点是鼠标按下时的光标点,新光标点是鼠标移动到的新的位置,原光标点和新光标点都在视锥体的近截平面上。鼠标按下时,沿鼠标方向发送一条射线,若没击中物体则结束,若击中物体则记录下所有参量(记为原光标),当鼠标按住移动时,每移动一个像素都通过此刻的新光标位置和原光标,利用相似三角形算出原光标的撞击点“应该”往哪个方向平移,最后让相机往相反的方向平移相同的距离即可。


请取件| 基于UE5的鼠标平移垂直同步算法_无人机_02

新视点 = 撞击点 - 新光标向量
新光标向量 = 新光标方向 * 新射线长
新射线长 / 新光标距 = 原射线长 / 原光标距

“光标距”指的是光标坐标到视点的距离,“新射线”是一条不存在的射线,相当于原视点到“新撞击点”的向量,或者”新视点“到原撞击点的向量。【就很绕】总之最终要求出新视点的位置,然后将视点移动过去即可。这种"射线垂直同步"算法适用于第一人称的pawn,蓝图其实很简单,需要2个鼠标事件来配合完成:当鼠标“左键按下事件”触发时,从视点沿光标方向发一条射线,在1km内尝试击中物体,在“line trace by channel”中勾选“trace complex”这样保证击中的位置更精确;击中后记录下4个关键信息:
1.是否击中,bool类型
2.撞击点坐标,vector类型
3.射线长度,float类型
4.光标距离,float类型

然后左键事件结束。因此射线只是在按下时发送,不用每帧都发。然后当鼠标平移事件触发时,先要判断左键是否释放,如果释放则结束事件,然后判断之前4个关键记录中“是否击中?”这一条件是否满足,如果没击中那也结束事件。(因此,拖拽无穷远处的天空是无法平移的)。好,当这2个条件都满足了以后,就可以平移了,根据之前的公式,利用那4个关键变量,再结合新光标距和新光标方向向量,就可以求出新的视点位置了。


请取件| 基于UE5的鼠标平移垂直同步算法_ue5_03

Part3代码开源

后续代码会在demo工程中开源出来​​https://github.com/inveta/demo​​希望大家持续关注

Part4Inveta团队

Inveta团队由研发、美术设计、建模等组成。​​团队开源项目:
https://github.com/inveta

请取件| 基于UE5的鼠标平移垂直同步算法_github_04