BuildaFlightTrackerwithCesiumforUnreal_译
这个教程会使用真实世界飞机数据控制一个飞机飞过从圣弗朗西斯科到哥本哈根。
你会学到怎样:
1)输入真实数据到UnrealEngine
2)使用数据和USplineComponent
3)输入一个飞机模型和有让飞机跟随飞行航线
4)在飞机上转换不同的相机视角
预备:
1)一个安装好的UneralEngine版本(至少4.26或者以上)。怎样安装UnrealEngine的提示,看Unreal Engine download page,详细的提示参考Installing Unreal Engine guide。
2)用C++的VisualStudio2019桌面开发
3)知道怎么设置以恶基础的CesiumforUnreal应用程序。看CesiumforUnrealQuickstartguide提示怎么开始CesiumforUnreal插件。
步骤1:创建一个Unreal关卡
你可能或者用UnrealEngine市场从 Cesium for Unreal Samples01_CesiumWorld关卡开始或者创建一个新的关卡。如果开始一个新的关卡,确保至少用CesiumWorldTerrain和一些光照,或者用CesiumSunSky或用你自己的光照构成关卡。看 Quickstart guide学习怎么用CesiumforUnreal设置一个新的关卡。
对于这个教程,CesiumGeoreference设置飞机起点在圣弗朗西斯科国际机场。
Origin Latitude = 37.61779
Origin Longitude = -122.390533
Origin Height = 0.0
步骤2:添加PlaneTrack类
PlaneTrack类会包含拥有飞行数据和为代表航线的样条线生成位置点的逻辑。
1)在UnrealEditor左上角File -> New C++Class添加一个新的C++类。选择Actor作为父类。点击Next按钮。在接下来的页面设置新类名字为“PlaneTrack”。点击绿色的CreateClass按钮。
VisualStudio自动打开文件。如果没有,在File -> OpenVisualStudio用VisualStudio打开工程。
新的C++文件在VisualStudio项目的源文件下。
2)在项目的Source文件夹下的.Build.cs文件中添加以下代码段
// Add Cesium for Unreal plugin dependency path
PrivateDependencyModuleNames.AddRange(new string[] { "CesiumRuntime" });
// Tell Unreal Engine to use C++17
CppStandard = CppStandardVersion.Cpp17;
3)我们开始给PlaneTrack添加一些成员变量存储飞行数据,样条线,和转换数据到合适的坐标系统。输入必要的库,并给PlaneTrack添加一下公开变量:
...
// Add import paths. Make sure they go above the PlaneTrack.generated.h line
#include "Components/SplineComponent.h"
#include "CesiumGeoreference.h"
#include "Engine/DataTable.h"
...
public:
// Spline variable to represent the plane track
UPROPERTY(BlueprintReadOnly, Category = "FlightTracker")
USplineComponent* SplineTrack;
// Cesium class that contain many useful coordinate conversion functions
UPROPERTY(EditAnywhere, Category = "FlightTracker")
ACesiumGeoreference* CesiumGeoreference;
// An Unreal Engine data table to store the raw flight data
UPROPERTY(EditAnywhere, Category = "FlightTracker")
UDataTable* AircraftsRawDataTable;
4)导航到PlaneTrack.cpp并在PlaneTrack构造中初始化SplineTrack变量,如下:
APlaneTrack::APlaneTrack()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// Initialize the track
SplineTrack = CreateDefaultSubobject<USplineComponent>(TEXT("SplineTrack"));
// This lets us visualize the spline in Play mode
SplineTrack->SetDrawDebug(true);
// Set the color of the spline
SplineTrack->SetUnselectedSplineSegmentColor(FLinearColor(1.f, 0.f, 0.f));
}
ACesiumGeoference和UDataTable变量在UnrealEngine编辑器中设置。
下一步之前,保存并编译代码。在UnrealEngineEditor顶端工具面板点击Compile:
如果代码格式好且正确,你会在UnrealEngine主编辑器底端右角看到“CompileComplete”消息。这个教程,编译代码参考这步。
步骤3:引入真实世界飞行数据
接下来,你会使用真实的从圣弗朗西斯科到哥本哈根的飞行数据。这个数据由FlightRadar24收集。CesiumforUnreal中的高度参考WHS84椭球以米记。这个数据预处理从相对平均海平面的英尺转换为相对椭球的米。
你可以下载转换数据here。
为PlaneTrack可以完成坐标转换,你要在项目中使用UnrealEngine的DataTable存储数据。这一步,你要创建一个代表航线数据的数据结构。
1)在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;
};
这个结构包含三个成员变量:Longitute,Latitute,Height。这些变量在原数据表中和行名一致。注意结构从FTableBase中继承。
编译代码。
2)拖拉.csv数据文件UnrealEngine的ContentBrowser。
在ChooseDataTableRowType下拉中选择AircraftRawData:
点击Apply并且在ContentBrowser中新的UDataTable对象双击打开数据表:
!)问题解决:如果你收到一个错误说UnrealEngine不能输入,检查看是否数据保存在项目文件夹。如果数据存在其他位置错误经常发生。
步骤4:给飞行追踪添加位置
这一步,你会给PlaneTrack类添加更多的代码去完成创建样条路径的剩下的功能需要。
1)给PlaneTrack.h在 PlaneTrack.Generated.h行上添加以下输入:
// Imports should be placed above the PlaneTrack.Generated.h line.
...
#include
#include "CesiumGeospatial/Ellipsoid.h"
#include "CesiumGeospatial/Cartographic.h"
仍在PlaneTrack.h在AplaneTrack类定义后添加以下功能:
public:
// Function to parse the data table and create the spline track
UFUNCTION(BlueprintCallable, Category = "FlightTracker")
void LoadSplineTrackPoints();
2)转到PlaneTrack.cpp添加以下代码段去创建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;
// Get row data point in lat/long/alt and transform it into UE4 points
double PointLatitude = Point->Latitude;
double PointLongitude = Point->Longitude;
double PointHeight = Point->Height;
// Compute the position in UE coordinates
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);
// Get the up vector at the position to orient the aircraft
const CesiumGeospatial::Ellipsoid& Ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
glm::dvec3 upVector = Ellipsoid.geodeticSurfaceNormal(CesiumGeospatial::Cartographic(FMath::DegreesToRadians(PointLongitude), FMath::DegreesToRadians(PointLatitude), FMath::DegreesToRadians(PointHeight)));
// Compute the up vector at each point to correctly orient the plane
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)导航到UnrealEngine添加飞行追踪到场景。在PlaceActors面板,搜索“PlaneTrack”并拖拉进视口。
4)选择PlaneTrack。在Detail面板找到FlightTracker目录。设置CesiumGeoference变量为你场景中的CesiumGeoference并色湖之AircraftsRawDataTable为步骤3添加的数据表。
5)导航到LevelBlueprint。你将添加蓝图节点去完成样条路径。
6)找到EventBeginPlay节点。这个节点子Play模式下最开始的时候调用。从WorldOutliner中拖拉PlaneTrack,从这个节点拉出一个连接,搜索添加ClearSplinePoints功能节点。
样条线第一次添加到场景时,默认有两个点。这两个点是任意的,在本教程中不需要,所以它们可以用Clear Spline points来清除。
7)从PlaneTrack对象节点拉出另一个连接并搜索“LoadSplineTrackPoints”。连接ClearSplinePoints和LoadSplineTrackPoints节点。并且连接EventBeginPlay节点到ClearSplinePoints。这最终蓝图网看起来像这样:
在BlueprintEditor左上角点击Compile。因为样条可见默认是关闭的,你可以选择视口,在你的键盘桑按下`键(通常在Esc键下方)并输入 ShowFlag.Splines 1 命令让它打开。检查是否所有都设置正确,在主编辑点击Play按钮。你应该可以看到数据点由在圣弗朗西斯科国际机场的终端建筑开始的样条曲线连接。
步骤5:添加飞行器
完成飞行器追踪的最后一步是添加一个跟随样条路径的飞行器。飞行器格网模型,你可以用你喜欢的任何模型。Here是一个来自TurboSquid的波音787的飞行器模型,你可以免费下载。
这个教程中的飞行器模型是来自 Sketchfab波音737飞行器模型。
1)在ContentBrowser,通过选择Add/Import -> ImporttoGame/[Path]输入飞行器模型到内容浏览器。你可以在StaticMeshEditor看模型。
2)在ContentBrowser空白区域右键点击并选择BlueprintClass。当推出选择父类时,选择Actor。命名为“BP_Aircraft”(BP代表蓝图)。这个类会包含沿着样条路径移动飞行器的逻辑。
3)在新的蓝图类上双击进入BlueprintEditor。在左上角点击绿色的AddComponent按钮并搜索“StaticMesh”。添加带组件列表并命名为“AircraftMesh”。
4)选择这个组件。定位Detail面板在BlueprintEditor的右边。找到StaticMesh表变量并在下拉中找到并选择你之前输入的飞行器。
5)在BlueprintEditor顶部通过点击EventGraph转到EventGraph。在任何地方右击并搜索“CustomEvent”。命名这个事件为“MoveAircraft”。
6)再在EventGraph右击并搜索“AddTimeline”。这个时间线节点会作为整个飞行器移动时间的时间线。
7)双击时间线节点打开TimelineEditor。通过在编辑器左上角点击AddFloatTrack创建一个单精度的曲线并给出一个名字。这个教程中,FloatTrack叫做“Alpha”。在曲线上右击选择AddKeytoCurve给时间线添加关键帧。最后曲线看起来像这样:
第一个关键点在Time=0,Value=0,第二个关键点在Time=1,Value=1.为给关键点安排更精确的值,选择并在左上角键入值:
选中UseLastKeyframe选择框。如果你想飞行器结束之后循环,你可以选中Loop选择框。
返回EventGraph并通过右键选择Promotetovariable提升Timeline输出引脚的Alpha到变量。这样将添加一个新的变量到左边Components下的面板。
8)在 My Blueprint面板使用绿色的AddNew给BP_Aircraft添加一个叫Duration的变量。这个变量决定了飞行器沿着路径飞行花费多长时间。给它一个float类型并点击就眼睛图标使它在主UnrealEngine编辑器公开并可编辑。
相似地,给飞机蓝图添加其他变量:
AircraftStartOffset:float类型;公开可见;用于据欸的那个飞行器在时间线上开始的位置。SliderRange和ValueRange应该在0到1之间,因为时间线在0 到1之间。这些变量可以在BlueprintEditor的Detail面板编辑。
PlaneTrack:用ObjectReference的PlaneTrack类型;公开可见;用于存储一个PlaneTrack对象的引用以检索样条上的位置。
最后组件看起来像这样:
9)如下完成剩下的MoveAircraft事件:
使用变量,可以拖拉变量到EventGraph或者在图表任意位置右击并搜索变量名。调用和变量一致的功能可以从变量节点拉出一个新的连接并搜索函数名。
编译蓝图。
10)接下来,你会添加一个连接PlaneTrack样条线到MoveAircraft函数并且使用样条线插入飞行器位置。在同一个EventGraph中,在MoveAircraft下创建以下节点网:
11)最后,连接两个蓝图网,一起用setActorTransform节点:
编译蓝图。
12)导航到主编辑器。从ContentBrowser中拖拉BP_Aircraft蓝图到场景或者在PlaceActor中搜索Aitcraft把你的飞行器对象添加到场景。
13)这儿由好几种方式去触发MoveAircraft事件。在这个教程中,你会用一个键触发。返回LevelBlueprint。在EventGraph,添加一个键盘节点(这儿用M)。连接这个键盘节点到MoveAircraft事件如下:
编译蓝图。
14)选择BP_Aircraft,返回Detail面板并且初始化Duration,PlaneTrack,AirplaneOffset变量。需要注意的是,Duration值越高,飞机在这条路径上飞行的时间就越长。对于Duration,100000的值会很好的运行。修改AirplaneOffset变量以在路径上的另一个点开始飞行。例如,由于0.0是飞行的开始,1.0是飞行的结束,因此将AirplaneOffset设置为0.9以开始接近飞行的结束。
当视口聚焦在飞机上时,单击Play按钮并按下M(或你选择连接Move aircraft事件的键)来开始飞行。
切换不同的相机视图(可选)
你将实现一些摄像机切换,从不同的角度观察飞行。这一步是可选的,但它可以为您的项目添加一个很好的效果。
1)使用PlaneActor面板添加两个新的CaneraActors到场景。在右边的Detail面板拖拉Camera Actors给BP_Aircraft,使得他们是BP_Aircraft的孩子:
当飞行器移动时,相机也随着移动。调整相机让看起来一个在顶部往下看,另一个在侧边看:
2)一旦相机放好,导航到LevelBlueprint。为侧边和顶部相机视图添加键盘事件:
编译蓝图并返回主编辑器看结果。在Play模式下,你可以在LevelBlueprint按下指定键测试新的相机转换要素。
完整源码
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
#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:
// Sets default values for this actor's properties
APlaneTrack();
// Spline variable to represent the plane track
UPROPERTY(BlueprintReadOnly, Category = "FlightTracker")
USplineComponent* SplineTrack;
// A pawn from the Cesium for Unreal API that can convert between different coordinates
UPROPERTY(EditAnywhere, Category = "FlightTracker")
ACesiumGeoreference* CesiumGeoreference;
// An Unreal Engine data table to store our raw flight data
UPROPERTY(EditAnywhere, Category = "FlightTracker")
UDataTable* AircraftsRawDataTable;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Function to parse the data table and create the spline track
UFUNCTION(BlueprintCallable, Category = "FlightTracker")
void LoadSplineTrackPoints();
};
PlaneTrack.cpp
// Copyright 2020-2021 CesiumGS, Inc. and Contributors
#include "PlaneTrack.h"
// Sets default values
APlaneTrack::APlaneTrack()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// Initialize the track
SplineTrack = CreateDefaultSubobject<USplineComponent>(TEXT("SplineTrack"));
// This lets us visualize the spline in Play mode
SplineTrack->SetDrawDebug(true);
// Set the color of the spline
SplineTrack->SetUnselectedSplineSegmentColor(FLinearColor(1.f, 0.f, 0.f));
}
// Called when the game starts or when spawned
void APlaneTrack::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
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;
// Get row data point in lat/long/alt and transform it into UE4 points
float PointLatitude = Point->Latitude;
float PointLongitude = Point->Longitude;
float PointHeight = Point->Height;
// Compute the position in UE coordinates
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);
// Get the up vector at the position to orient the aircraft
const CesiumGeospatial::Ellipsoid& Ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
glm::dvec3 upVector = Ellipsoid.geodeticSurfaceNormal(CesiumGeospatial::Cartographic(FMath::DegreesToRadians(PointLongitude), FMath::DegreesToRadians(PointLatitude), FMath::DegreesToRadians(PointHeight)));
// Compute the up vector at each point to correctly orient the plane
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();
}
}
下步:
目前,飞机的速度在整个飞行路径中是恒定的。如果数据集具有速度或速度变量,则可以使用它根据飞机在样条轨迹上的位置来调整飞机的速度。类似地,你可以用真实数据调整飞机的航向、偏航、俯仰。
现在你已经完成了这个CesiumforUnreal教程,请到Community Forum去分享关于CesiumforUnreal和这个教程你的反馈,或者在Twitter上的标签 @CesiumJS,向世界展示你的成就。
ps:
原文地址:https://cesium.com/learn/unreal/unreal-flight-tracker/