上一节我们完成了数据的基础获取,现在我们需要在UE4中新建一个空白插件类,然后把上一节python文件和txt文件放在该插件类里的Content文件夹里面(作用是让UE4只需要读取相对位置就行,便于打包和其他项目使用)。
创建好C++插件工程后,我们需要创建一个函数用于读取txt里面的数据,我们可以看到txt生成的数据是double型,UE4的FVector2D位置坐标结构体是float型,所以我们需要创建一个结构体储存数据里面的double型数据,而且我们在每个拐弯处都分了段,所以创建了一个简单的结构体,用于储存每段结构的数据:
struct FVector2D_Double { double X; double Y; FVector2D_Double operator-(const FVector2D_Double& Other) const { return FVector2D_Double(X - Other.X, Y - Other.Y); } FVector2D_Double operator+(const FVector2D_Double& Other) const { return FVector2D_Double(X + Other.X, Y + Other.Y); } FVector2D_Double operator/(const FVector2D_Double& Other) const { return FVector2D_Double(X * Other.X, Y * Other.Y); } FVector2D_Double operator*(const float& Other) const { return FVector2D_Double(X * Other, Y * Other); } FVector2D_Double operator/(const float& Other) const { return FVector2D_Double(X / Other, Y / Other); } FVector2D_Double operator/(const FVector2D& Other) const { return FVector2D_Double(X / Other.X, Y / Other.Y); } FVector2D_Double operator*(const FVector2D& Other) const { return FVector2D_Double(X * Other.X, Y * Other.Y); } FVector2D_Double() :X(0.f), Y(0.f) {} FVector2D_Double(double X,double Y) :X(X),Y(Y) {} double Size() const { return sqrt(X * X + Y * Y); } };
struct LevelArray { unsigned int ID = 0; TArray<FVector2D_Double> Array; };
其中我重载了一些运算符,便于后面需要的计算,现在我们已经有了储存数据的结构,现在就需要读取txt里面的数据,我使用的是C++原生库里面的读取方法:
#include <iostream> #include <fstream> #include <cassert> #include <string>
TArray<LevelArray> LevelBaiData; TArray<int> BaiduDataTimeAndDistance; std::string file = TCHAR_TO_UTF8(*FPaths::ProjectPluginsDir()) + std::string("XXXXPlugins/Content/walking.txt"); std::ifstream infile; infile.open(file.data()); //将文件流对象与文件连接起来 unsigned int AID = 0; if (infile.is_open()) { std::string s; while (getline(infile, s)) { std::string s1, s2, s3; std::size_t pos = s.find(","); if (s._Equal("------")) { LevelArray A; AID += 1; A.ID = AID; LevelBaiData.Add(A); } if (pos != std::string::npos) { s1 = s.substr(0, pos); s2 = s.substr(pos + 1, s.size()); FVector2D_Double v2lf = FVector2D_Double(atof(s1.c_str()), atof(s2.c_str())); FVector2D_Double A = IBToXY(v2lf); // 把经纬度转化为平面坐标 LevelBaiData[LevelBaiData.Num() - 1].Array.Add(A); } std::size_t pos_1 = s.find(":"); if (pos_1 != std::string::npos) { s3 = s.substr(pos_1 + 1, s.size()); BaiduDataTimeAndDistance.Add(atoi(s3.c_str())); } } infile.close(); }
这样我们就可以把数据存储在结构体LevelBaiData和BaiduDataTimeAndDistance里面。
我还创建了一个函数,用于把经纬度坐标转化为平面坐标,我使用的是米勒投影转换:
FVector2D_Double MyClass::IBToXY(FVector2D_Double IB) { double L = 6378000 * PI * 2;//地球周长 double W = L;// 平面展开后,x轴等于周长 double H = L / 2;// y轴约等于周长一半 double Mill = 2.3;// 米勒投影中的一个常数,范围大约在正负2.3之间 double x = IB.X * PI / 180;// 将经度从度数转换为弧度 double y = IB.Y * PI / 180;// 将纬度从度数转换为弧度 y = 1.25 * log(tan(0.25 * PI + 0.4 * y));// 米勒投影的转换 // 弧度转为实际距离 x = (W / 2) + (W / (2 * PI)) * x; y = (H / 2) - (H / (2 * Mill)) * y; return FVector2D_Double(x,y); }
现在我们已经取得地球上的坐标数据,然后把该数据转化为UE4里的的float型数据,我们只需要知道UE4中起点位置,和缩放比例就可以实现该效果,起点位置好找,直接可以用Actor世界位置,可是缩放比例就有点难,我们可以知道UE4终点终点位置求得,也可以知道UE4地图比例就得,我使用的是获取另一个点的终点位置求得:
FVector Delate = EndActor - StartActor; BaiduDataXY = UpDataLevelArrayData(LevelBaiData, Number, IsIgnoreSamllLen, Number <= 0); FVector2D_Double StartEndVector2D = (BaiduDataXY[BaiduDataXY.Num() - 1] - BaiduDataXY[0]); //米勒投影坐标向量 double Scale = StartEndVector2D.Size() / FVector2D(Delate.X, Delate.Y).Size(); for (auto &Item : BaiduDataXY) { FVector2D_Double p = (Item - BaiduDataXY[0]) / Scale; /* p.X = DistanceLngLat(BaiduData[0].X, Item.X, BaiduData[0].Y, BaiduData[0].Y) / Scale; p.Y = DistanceLngLat(BaiduData[0].X, BaiduData[0].X, BaiduData[0].Y, Item.Y) / Scale; */ UE4Data.Add(FVector2D(p.X, p.Y)); }
UpDataLevelArrayData函数用于数据均匀分段,不会出现过多或者过少的拐点,这个算法不是当堂重点,当然你们需要参考可以在后面我贴出的源码里查看。
数据我们可以读取并且可在UE4中使用,但是如果我们需要在UE4中输入经纬度,然后C++调用python函数,获取的函数在UE4中显示出来,那就在下一节我讲解一下。