Arzie100 发表于 2022-5-11 09:47

(七)Cesium for Unreal创建航班追踪器(译)

原文请参考Cesium官方文档:Build a Flight Tracker with Cesium for Unreal
上一篇:(六)Cesium for Unreal在全球各地穿梭(译)
本教程将向您展示如何用可视化的方式查看从旧金山到哥本哈根的航班。



飞越美国加利福尼亚州旧金山

您将学习如何:

[*]将真实的航班数据导入虚幻引擎
[*]用数据和USplineComponent创建航班路线
[*]导入飞机模型并使飞机跟随航班轨迹
[*]在飞行中切换镜头视角
准备工作


[*]安装虚幻引擎(至少4.26或更高版本)。有关如何安装虚幻引擎的说明,请查阅虚幻引擎下载页面和安装虚幻引擎指南。
[*]安装Visual Studio 2019
[*]知道如何构建基本的Cesium for Unreal应用程序。查看Cesium for Unreal快速入门指南,以获取有关Cesium for Unreal插件的入门指南。
第一步:创建虚幻关卡

您可以从虚幻引擎市场上下载的Cesium for Unreal Samples的01_CesiumWorld关卡开始,也可以创建一个新关卡。如果用新的关卡,请确保关卡中至少有CesiumWorld Terrain和一些灯光(使用CesiumSunSky或您自己的自定义灯光)。查看快速入门指南,了解如何使用Cesium for Unreal创建新的关卡。
对于本教程,CesiumGeoreference Actor的Origin Longitude, Origin Latitude和Origin Height属性值分别设置为-122.390533、37.61779和0.0,这是航班始发地旧金山国际机场(SFO)的坐标。
第二步:添加PlaneTrack类

PlaneTrack类将包含处理航班数据的逻辑,还能生成代表航班轨迹的样条线上的点。

[*]点击虚幻编辑器左上角的菜单File -> New C ++ Class添加一个新的C++类。选择Actor作为父类。点击File -> Open Visual Studio,在Visual Studio中打开项目。


可以在Visual Studio项目的Source文件夹中找到新创建的C++文件。


2. 将以下代码片段添加到项目Build.cs文件中,该文件也可以在Source文件夹中找到:
// 添加Cesium for Unreal插件依赖
PrivateDependencyModuleNames.AddRange(new string[] { "CesiumRuntime" });

// 告诉虚幻引擎使用C++17
CppStandard = CppStandardVersion.Cpp17;


3. 在PlaneTrack类中添加一些成员变量,以存储航班数据和样条曲线,并将数据转换为适当的坐标系对应的坐标。导入必要的头文件,并将以下public变量添加到PlaneTrack.h头文件中:
...
// 添加头文件引用。确保在PlaneTrack.generated.h代码之前(否则会导致编译错误)
#include "Components/SplineComponent.h"
#include "CesiumGeoreference.h"
#include "Engine/DataTable.h"
...

public:
// 代表航班轨迹的样条线成员变量
UPROPERTY(BlueprintReadOnly, Category = "FlightTracker")
    USplineComponent* SplineTrack;

// Cesium工具类,包含很多有用的坐标转换相关的函数
UPROPERTY(EditAnywhere, Category = "FlightTracker")
    ACesiumGeoreference* CesiumGeoreference;

// 用于保存航班数据的虚幻引擎数据表
UPROPERTY(EditAnywhere, Category = "FlightTracker")
    UDataTable* AircraftsRawDataTable;
4. 打开PlaneTrack.cpp文件,在PlaneTrack构造函数中初始化SplineTrack成员变量:
APlaneTrack::APlaneTrack()
{
// 让该Actor每帧都执行Tick()方法,如果需要可以设置成false以提高性能
PrimaryActorTick.bCanEverTick = true;

// 初始化轨迹
SplineTrack = CreateDefaultSubobject<USplineComponent>(TEXT("SplineTrack"));
// 让样条线在运行模式下可见
SplineTrack->SetDrawDebug(true);
// 设置样条线的颜色
SplineTrack->SetUnselectedSplineSegmentColor(FLinearColor(1.f, 0.f, 0.f));
// 设置样条线的粗细或者宽度
SplineTrack->ScaleVisualizationWidth = 70.f;                           
}
ACesiumGeoreference和UDataTable成员变量将在虚幻引擎编辑器中初始化。
在继续下一步之前,请编译代码。在虚幻引擎编辑器中,单击顶部工具栏上的Compile按钮:


如果代码没有问题,您将在虚幻引擎主编辑器的右下角看到Compile Complete的消息。在本教程中,编译代码指的就是这个步骤。
第三步:导入真实的航班数据

接下来,您将使用从旧金山到哥本哈根的真实航班数据。该数据由FlightRadar24收集。在Cesium for Unreal中的高度,是相对于WGS84椭球的,以米为单位。这里对数据进行了预处理,将高度单位从相对于平均海平面的英尺,换算成了相对于椭球的米。您可以在此处下载转换后的数据。



微软电子表格中的航班数据

为了使PlaneTrack类能访问数据以执行坐标转换,您将使用虚幻引擎的DataTable将数据存储在项目中。在此步骤中,您将创建一个数据结构来表示航班数据的结构。

[*]在中PlaneTrack.h,将以下代码片段插入引入头文件的下方,以定义航班数据的结构:
USTRUCT(BlueprintType)
struct FAircraftRawData : public FTableRowBase
{
GENERATED_USTRUCT_BODY()

public:
    FAircraftRawData()
      : Longitude(0.0)
      , Latitude(0.0)
      , Height(0.0)
    {}

UPROPERTY(EditAnywhere, Category = "FlightTracker")
    double Longitude;
UPROPERTY(EditAnywhere, Category = "FlightTracker")
    double Latitude;
UPROPERTY(EditAnywhere, Category = "FlightTracker")
    double Height;
};
该结构包含三个成员变量:Longitude,Latitude,和Height。这些变量对应于上面原始数据表中的列名。另外请注意,该结构继承于FTableRowBase。 编译代码。
2. 将.csv数据文件拖放到虚幻引擎Content Browser中。在Choose DataTable Row Type下拉框中选择AircraftRawData:


点击Apply,然后Content Browser中双击新创建的UDataTable对象,以打开数据表:


故障排除:如果您收到了虚幻引擎无法导入的错误消息,请检查数据文件是否保存在项目文件夹中。如果数据文件保存在其他位置,可能会引发此错误。第四步:将坐标添加到航班轨迹中

在此步骤中,您将向PlaneTrack类添加更多代码,以完成剩下的创建样条线轨迹的功能。

[*]将以下头文件引用和方法定义代码添加到PlaneTrack.h中:
// 头文件
...
#include <glm/vec3.hpp>
#include "CesiumGeospatial/Ellipsoid.h"
#include "CesiumGeospatial/Cartographic.h"
...

public:
// 解析数据表并创建样条线轨迹的方法
UFUNCTION(BlueprintCallable, Category = "FlightTracker")
    void LoadSplineTrackPoints();
2. 在LoadSplineTrackPoints的方法体中添加以下代码。这是大部分计算要完成的地方。
void APlaneTrack::LoadSplineTrackPoints()
{
    if (this->AircraftsRawDataTable != nullptr && this->CesiumGeoreference != nullptr)
    {
      int32 PointIndex = 0;
      for (auto& row : this->AircraftsRawDataTable->GetRowMap())
      {
      FAircraftRawData* Point = (FAircraftRawData*)row.Value;
      // 获取经度/维度/高度值,转换成UE4坐标
      double PointLatitude = Point->Latitude;
      double PointLongitude = Point->Longitude;
      double PointHeight = Point->Height;

      // 计算UE里的坐标
      glm::dvec3 UECoords = this->CesiumGeoreference->TransformLongitudeLatitudeHeightToUe(glm::dvec3(PointLongitude, PointLatitude, PointHeight));
      FVector SplinePointPosition = FVector(UECoords.x, UECoords.y, UECoords.z);
      this->SplineTrack->AddSplinePointAtIndex(SplinePointPosition, PointIndex, ESplineCoordinateSpace::World, false);

      // 获取飞机的向上方向
      const CesiumGeospatial::Ellipsoid& Ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
      glm::dvec3 upVector = Ellipsoid.geodeticSurfaceNormal(CesiumGeospatial::Cartographic(FMath::DegreesToRadians(PointLongitude), FMath::DegreesToRadians(PointLatitude), FMath::DegreesToRadians(PointHeight)));

      // 计算飞机的UE4向上方向
      glm::dvec4 ecefUp(upVector, 0.0);
      const glm::dmat4& ecefToUnreal = this->CesiumGeoreference->GetEllipsoidCenteredToUnrealWorldTransform();
      glm::dvec4 unrealUp = ecefToUnreal * ecefUp;
      this->SplineTrack->SetUpVectorAtSplinePoint(PointIndex, FVector(unrealUp.x, unrealUp.y, unrealUp.z), ESplineCoordinateSpace::World, false);

      PointIndex++;
      }
      this->SplineTrack->UpdateSpline();
    }
}
编译代码。
3. 回到虚幻引擎编辑器,将航班轨迹添加到场景中。在Place Actors面板中,搜索Plane Track,将其拖放到场景中。
4. 选中PlaneTrack Actor,然后在Details面板中,找到Flight Tracker类别。将Cesium Georeference属性设置为场景中的Cesium Georeference,并将Aircrafts Raw Data Table属性设置为在步骤三中添加的数据表。


5. 打开关卡编辑。在这里,您将添加一些蓝图节点以填充样条曲线轨迹。


6. 查找Event BeginPlay节点。在运行模式的最开始,这个节点就会被调用。从World Outliner拖拽PlaneTrack Actor到Event Graph中,从这个节点拉出一根连线,搜索并添加Clear Spline Points节点。首次将样条线添加到场景时,默认情况下它有两个点。这两个点是随机的,在本教程中不需要,因此可以使用Clear Spline Points清除它们。
7. 从PlaneTrack对象节点,拖动另一跟连线并搜索Load Spline Track Points节点。连接Clear Spline Points和Load Spline Track Points节点,并将Event BeginPlay节点连接到Clear Spline Points节点。最终的蓝图拓扑如下所示:


单击蓝图编辑器左上角的Compile按钮。默认情况下样条线在运行时处于不可见状态,您可以通过点击编辑器主窗口,按键盘上的`键(通常在Esc键下面),输入命令:ShowFlag.Splines 1。要检查所有设置是否正确,请点击主编辑器顶部面板中的Play按钮。您应该能够看到由样条曲线连接的数据点,该样条曲线从旧金山国际机场的航站楼开始,如下所示:


译者注:也可以Event BeginPlay节点后添加Execute Console Command节点,Command输入ShowFlag.Splines 1:

第五步:添加飞机

完成航班轨迹的最后一步是添加跟随样条线轨迹的飞机。对于飞机网格体,可以使用任何您想要的模型。这是TurboSquid的波音787飞机模型,您可以免费下载。
本教程中的飞机模型是Sketchfab的波音737飞机模型。
译者注:翻译此教程时,该模型已经从Sketchfab下架了,您可以在Sketchfab搜索其他的波音787飞机模型。另外,可以使用Sketchfab plugin for Unreal Engine一键导入Sketchfab模型,省去下载fbx模型,再导入UE的麻烦。


[*]在内容浏览器中点击右键,再点击Add/Import —> Import to /Game//将飞机模型导入到内容浏览器中。您可以在Static Mesh Editor中检查模型:


2. 右键单击Content Browser的空白区域,然后选择Blueprint Class。当提示您选择父类时,选择Actor。将类命名为BP_Aircraft(BP代表Blueprint蓝图)。该类将包含使飞机沿着样条线轨迹移动的逻辑。
3. 双击新创建的BP_Aircraft蓝图,打开蓝图编辑器。单击左上角绿色的Add Component按钮,然后搜索Static Mesh。将其添加到组件列表中,并将其命名为AircraftMesh。


4. 选中此新组件后,在蓝图编辑器右侧找到Details面板。找到Static Mesh属性,然后在下拉菜单中找到并选择您先前导入的飞机网格体。
5. 单击蓝图编辑器顶部的Event Graph选项卡,打开Event Graph。单击右键Event Graph中的任意位置,然后搜索Custom Event节点。将此自定义事件命名为MoveAircraft。
6. 在Event Graph中再次单击右键,然后搜索Add Timeline节点。Timeline节点将用于随着时间移动飞机。


7. 双击Timeline节点以打开Timeline Editor。通过单击编辑器左上角的Add Float Track按钮来创建浮点数曲线,并为其命名。在本教程中,浮点数曲线被命名为Alpha。右键单击曲线,选择Add key to Curve,给时间轴添加关键帧。最终曲线如下图所示:


第一个关键帧时间= 0,值= 0,而第二个关键帧时间= 1,值=1。要为关键点分配更精确的值,请选中它并在左上角输入值:


选中Use Last Keyframe旁边的复选框。如果您希望动画在结束后立即循环播放,也可以勾选Loop复选框。
回到Event Graph,右键单击时间轴节点的Alpha输出引脚,选择Promote to variable菜单,将其提升为变量。这将在左侧面板的Components中添加一个新变量:


8. 点击My Blueprint面板中绿色的Add New按钮,给BP_Aircraft蓝图添加一个名为Duration的变量。该变量用于确定飞机沿轨迹飞行需要多长时间。将他的类型设置为浮点数float,然后单击它旁边的眼睛符号,以使其公开,并在虚幻引擎主编辑器中进行编辑。
同样,将下列变量添加到BP_Aircraft蓝图类中:

[*]AircraftStartOffset:类型float;可见性为public;用于确定航班在时间轴上的何处开始。由于时间线的Alpha值在0和1之间,Slider Range和Value Range应该设置在0和1之间。可以在蓝图编辑器的细节面板中给这些变量重新赋值。
[*]PlaneTrack:类型为PlaneTrack的引用; 可见性为public;用于保存PlaneTrack对象的引用,从样条线上获取位置。


最终的组件列表如下所示:


9. 按下图完成Move Aircraft自定义事件:


要使用变量,可以将它拖放到Event Graph中,或右键单击图中的任何位置,然后搜索变量的名称。要调用变量的函数,可以从变量节点拉出新连接,然后搜索该函数的名称。
编译蓝图。
10. 接下来,添加逻辑以将PlaneTrack样条线连接到Move Aircraft方法,并使用线性插值的方法从该样条线获取飞机的位置。还是在当前Event Graph中,在Move Aircraft下创建如下节点拓扑图:


11. 最后,用Set Actor Transform将两个蓝图拓扑连接在一起:


编译蓝图。
12. 回到主编辑器。要么从Content Browser中拖拽BP_Aircraft蓝图到场景中,要么在Place Actor面板中搜索BP_Aircraft并将BP_Aircraft蓝图添加到场景中。
13. 有几种触发Move Aircraft事件的方法。在本教程中,我们使用键盘事件来触发它。返回到Level Blueprint.。在Event Graph中,添加Keyboard节点(此处使用M键)。将此键盘节点连接到Move Aircraft事件,如下图所示:


编译蓝图。
14. 选中BP_Aircraft Actor,在Details面板中初始化公共变量Duration,PlaneTrack和AirplaneOffset。请注意,Duration的值越大,飞机沿轨迹飞行的时间就越长。最好是将Duration值设置为100,000。可以修改AirplaneOffset变量,让飞机在轨迹的某个中间位置开始飞行。比如,0.0是航班的起始位置,1.0是航班的终点,因此将AirplaneOffset设置为0.9的话,可以让飞机从靠近终点的位置开始飞行。


将当前视口对准飞机,点击Play按钮,然后按M键(或者任意您选择的按键)开始飞行。


译者注:其实此时运行工程的话,镜头不会跟着飞机一起移动,必须手动操作FloatingPawn,要不然飞机一会儿就没影了。挑战一下自己,看看能不能跟上飞机的节奏。在不同的镜头视角之间切换(可选)

您将实现镜头切换功能,以便从不同的角度观察飞行情况。这一步是可选的,但是可以为您的项目添加很棒的效果。

[*]使用Place Actor面板,将两个新的Camera Actor添加到场景中。在右侧的面板中,将Camera Actor拖放到BP_Aircraft Actor上,让Camera成为BP_Aircraft的孩子节点:


当飞机移动时,镜头也将随之移动。调整镜头,使其中一个对准飞机顶部,另外一个从侧面看:




2. 定位好镜头后,打开关卡蓝图。为侧面和顶部镜头添加自定义键盘事件:


编译蓝图,然后返回主编辑器,点击Play,您可以用在关卡蓝图中指定的键盘按键来测试新的镜头切换功能。
完整源码

这是本教程的完整源代码:
PlaneTrack.h:
// Copyright 2020-2021 CesiumGS, Inc. and Contributors

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/SplineComponent.h"
#include "CesiumGeoreference.h"
#include "Engine/DataTable.h"
#include <glm/vec3.hpp>
#include "CesiumGeospatial/Ellipsoid.h"
#include "CesiumGeospatial/Cartographic.h"
#include "PlaneTrack.generated.h"

USTRUCT(BlueprintType)
struct FAircraftRawData : public FTableRowBase
{
GENERATED_USTRUCT_BODY()

public:
FAircraftRawData()
    : Longitude(0.0)
    , Latitude(0.0)
    , Height(0.0)
{}

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FlightTracker")
    float Longitude;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FlightTracker")
    float Latitude;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "FlightTracker")
    float Height;
};

UCLASS()
class CESIUMUNREAL_API APlaneTrack : public AActor
{
GENERATED_BODY()

public:
// 设置默认值
APlaneTrack();

// 代表航班轨迹的样条线成员变量
UPROPERTY(BlueprintReadOnly, Category = "FlightTracker")
    USplineComponent* SplineTrack;

// Cesium工具类,包含很多有用的坐标转换相关的函数
UPROPERTY(EditAnywhere, Category = "FlightTracker")
    ACesiumGeoreference* CesiumGeoreference;

// 用于保存航班数据的虚幻引擎数据表
UPROPERTY(EditAnywhere, Category = "FlightTracker")
    UDataTable* AircraftsRawDataTable;

protected:
// 游戏开始时或者此Actor被创建时执行
virtual void BeginPlay() override;

public:
// 每帧执行
virtual void Tick(float DeltaTime) override;

// 解析数据表并创建样条线轨迹的方法
UFUNCTION(BlueprintCallable, Category = "FlightTracker")
    void LoadSplineTrackPoints();
};
PlaneTrack.cpp:
// Copyright 2020-2021 CesiumGS, Inc. and Contributors

#include "PlaneTrack.h"

// 设置默认值
APlaneTrack::APlaneTrack()
{
// 让该Actor每帧都执行Tick()方法,如果需要可以设置成false以提高性能
PrimaryActorTick.bCanEverTick = true;

// 初始化轨迹
SplineTrack = CreateDefaultSubobject<USplineComponent>(TEXT("SplineTrack"));
// 让样条线在运行模式下可见
SplineTrack->SetDrawDebug(true);
// 设置样条线的颜色
SplineTrack->SetUnselectedSplineSegmentColor(FLinearColor(1.f, 0.f, 0.f));
// 设置样条线的粗细或者宽度
SplineTrack->ScaleVisualizationWidth = 70.f;
}

// 游戏开始时或者此Actor被创建时执行
void APlaneTrack::BeginPlay()
{
Super::BeginPlay();
}

// 每帧执行
void APlaneTrack::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}

void APlaneTrack::LoadSplineTrackPoints()
{
if (this->AircraftsRawDataTable != nullptr && this->CesiumGeoreference != nullptr)
{
    int32 PointIndex = 0;
    for (auto& row : this->AircraftsRawDataTable->GetRowMap())
    {
      FAircraftRawData* Point = (FAircraftRawData*)row.Value;
      // 获取经度/维度/高度值,转换成UE4坐标
      float PointLatitude = Point->Latitude;
      float PointLongitude = Point->Longitude;
      float PointHeight = Point->Height;

      // 计算UE里的坐标
      glm::dvec3 UECoords = this->CesiumGeoreference->TransformLongitudeLatitudeHeightToUe(glm::dvec3(PointLongitude, PointLatitude, PointHeight));
      FVector SplinePointPosition = FVector(UECoords.x, UECoords.y, UECoords.z);
      this->SplineTrack->AddSplinePointAtIndex(SplinePointPosition, PointIndex, ESplineCoordinateSpace::World, false);

      // 获取飞机的向上方向
      const CesiumGeospatial::Ellipsoid& Ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
      glm::dvec3 upVector = Ellipsoid.geodeticSurfaceNormal(CesiumGeospatial::Cartographic(FMath::DegreesToRadians(PointLongitude), FMath::DegreesToRadians(PointLatitude), FMath::DegreesToRadians(PointHeight)));
      // 计算飞机的UE4向上方向
      glm::dvec4 ecefUp(
      upVector,
      0.0
      );
      const glm::dmat4& ecefToUnreal = this->CesiumGeoreference->GetEllipsoidCenteredToUnrealWorldTransform();
      glm::dvec4 unrealUp = ecefToUnreal * ecefUp;
      this->SplineTrack->SetUpVectorAtSplinePoint(PointIndex, FVector(unrealUp.x, unrealUp.y, unrealUp.z), ESplineCoordinateSpace::World, false);

      PointIndex++;
    }
    this->SplineTrack->UpdateSpline();
}
}
下一步

当前,飞机的速度在整个轨迹上都是固定的。如果您的数据集包含速度或加速度,则可以使用它来根据飞机在样条线轨迹上的位置调整飞机的速度。同时,您也可以使用实际数据调整飞机的航向、偏航角和俯仰角。
现在,您已经完成了Cesium for Unreal系列教程,请访问社区论坛,以分享您对Cesium for Unreal和教程的反馈,并向全世界展示您的作品。
都看到这里了,加个技术交流群一起组队研究呗^^

johnsoncodehk 发表于 2022-5-11 09:53

大哥,这个代码编译找不到 CesiumGeoreference.h这个文件,要怎么弄呢?

rustum 发表于 2022-5-11 10:02

你代码没有拉全?进Q群,有拉全的代码

量子计算9 发表于 2022-5-11 10:07

qq群是多少呀

量子计算9 发表于 2022-5-11 10:08

517825387

七彩极 发表于 2022-5-11 10:10

安装Sketchfab插件后,出现 VS不能编译,而且UE关闭后再打开模型丢失的情况

DomDomm 发表于 2022-5-11 10:13

编译显示无法打开源文件?[好奇]
页: [1]
查看完整版本: (七)Cesium for Unreal创建航班追踪器(译)