网站直播间怎么做,购物网站开发django,抽奖网站开发,android 网站模板目录
一、进出口清单
二、路径计算 三、包裹
1.包裹的数据结构 2.包裹在场景中的运动
四、道路
1.道路的数据结构
2.道路的建造
3.道路的销毁
4.某个有道路连接的建筑被删除 作为一个工厂类模拟经营游戏#xff0c;各个工厂之间的运输必不可少#xff0c;本游戏采用的…目录
一、进出口清单
二、路径计算 三、包裹
1.包裹的数据结构 2.包裹在场景中的运动
四、道路
1.道路的数据结构
2.道路的建造
3.道路的销毁
4.某个有道路连接的建筑被删除 作为一个工厂类模拟经营游戏各个工厂之间的运输必不可少本游戏采用的是按需进口的模式工厂之间可以建立类似于传送带一样的直连道路每个工厂根据自身当前缺少的所需物品按照从近到远的顺序依次访问能够生产该物品的工厂然后收到出口订单的工厂会发出包裹沿着玩家建设的道路送达发出进口需求的工厂玩家可以手动配置进出口清单也就是工厂仓库中某类物品少于多少个就要进口以及某类物品多于多少个才可以出口效果如下 一、进出口清单 玩家可以编辑每一个建筑的进出口清单实现对进出口的调控即库存少于多少进口多于多少出口。清单是一个数组包括物品的种类和数量同时还有自动和手动计算的功能切换在自动模式下清单中的数值即为生产时实际需求的原料数量在改为手动模式后对应物品的数量等于上次手动设置过的数量清单数组中的数据结构如下
USTRUCT(BlueprintType)
struct FImportStardust
{FImportStardust(const FName StardustId, const int Quantity): StardustId(StardustId),Quantity(Quantity){}GENERATED_BODY()UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Import)FName StardustId{ Empty };UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Import)int Quantity{ 0 };//是否手动更新数量UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Import)bool IsAuto{true};//上一次手动设定的值UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Import)int LastManualSet{0};FImportStardust()default;
}; 设置清单中某类星尘的数量
bool ABP_Asters::SetElementInImportingStardust(const int Index, const int Amount)
{//检查索引是否合法if(Index0||IndexImportingStardust.Num()){UE_LOG(LogTemp,Error,TEXT(SetElementInImportingStardust failed,invalid index:%d),Index);return false;}ImportingStardust[Index].QuantityAmount;//维护上一次手动设置的值if(!ImportingStardust[Index].IsAuto){ImportingStardust[Index].LastManualSetAmount;}return true;
}
设置某类星尘的计算是否手动
void ABP_Asters::SetIsAutoInImportingStardust(const int Index, const bool IsAuto)
{//检查索引是否合法if(Index0||IndexImportingStardust.Num()){UE_LOG(LogTemp,Error,TEXT(SetIsAutoInImportingStardust failed,invalid index:%d),Index);return;}ImportingStardust[Index].IsAutoIsAuto;if(IsAuto){ImportingStardust[Index].LastManualSetImportingStardust[Index].Quantity;//计算某类星尘的需求量
ImportingStardust[Index].QuantityCalCulateReactionConsumption(ImportingStardust[Index].StardustId);}else{ImportingStardust[Index].QuantityImportingStardust[Index].LastManualSet;}
}
二、路径计算 我们的物流是由进口需求引导的所以寻路也是由某一个建筑出发依次遍历连通的最近的建筑来尝试从其进口需要的物品路径为从出口天体到该天体的路径
TArrayFStardustBasic ATradingSystemActor::TriggerImport(const int SourceAsterIndex, const TArrayFStardustBasic ImportingStardust)
{//输入进口源天体的索引和需求的星尘返回有哪些进口需求未被满足//检查输入索引是否合法if(!DebugActor-AllAster.Find(SourceAsterIndex)){UE_LOG(LogTemp,Error,TEXT(TriggerImport failed,invalid index:%d),SourceAsterIndex);return TArrayFStardustBasic();}std::unordered_mapstd::string,intStardustNeed;for(const auto it:ImportingStardust){StardustNeed[TCHAR_TO_UTF8(*it.StardustId.ToString())]it.Quantity;}//建立一个dijkstra算法使用的节点结构包含点的ID和到起点距离struct Node{Node(const int ID, const long long DIstance): ID(ID),DIstance(DIstance){}Node(const Node Other):ID(Other.ID),DIstance(Other.DIstance){}int ID;long long DIstance;};//重载优先队列排序规则auto cmp{[](const TSharedPtrNodea,const TSharedPtrNode b){return a-DIstanceb-DIstance;}};//储存当前待遍历的点的优先队列按到起点路径长度从小到大排序
std::priority_queueTSharedPtrNode,std::vectorTSharedPtrNode,decltype(cmp)Queue(cmp);//放入起点Queue.push(MakeSharedNode(SourceAsterIndex, 0));//起点到每一个点的最短距离std::mapint,long longMinimumDistance;//每个点是否被处理完毕std::mapint,boolDone;//储存最短路径中每个点的父节点std::mapint,intPath;for(auto it:DebugActor-AllAster){//初始化最短距离为极大值MinimumDistance[it.Key]1e18;Done[it.Key]false;}MinimumDistance[SourceAsterIndex]0;while(!Queue.empty()){auto Current{Queue.top()};Queue.pop();if(Done[Current-ID]){continue;}if(Current-ID!SourceAsterIndex){if(!DebugActor-AllAster.Find(Current-ID)){continue;}//当前遍历到的天体auto FoundedAster{DebugActor-AllAster[Current-ID]};TArrayFStardustBasicPackgingStardust;//遍历出口清单for(const autoit:FoundedAster-GetExportingStardust()){std::string IDString{TCHAR_TO_UTF8(*it.StardustId.ToString())};if(StardustNeed.find(IDString)StardustNeed.end()||!StardustNeed[IDString]){continue;}//找到的天体可出口的星尘数量int Available{FoundedAster-OutputInventory-CheckStardust(it.StardustId)-it.Quantity};//实际出口的数量if(int Transfered{std::max(0,std::min(StardustNeed[IDString],Available))}){//维护当前包裹中的星尘和天体仓库中的星尘PackgingStardust.Add(FStardustBasic(it.StardustId,Transfered));FoundedAster-OutputInventory-RemoveStardust(it.StardustId,Transfered);StardustNeed[IDString]-Transfered;if(!StardustNeed[IDString]){StardustNeed.erase(IDString);}}}//该天体进行了出口if(!PackgingStardust.IsEmpty()){TArrayintPassedAsters;int CurrentPosition{Current-ID};//记录该天体到进口需求发出天体的路径while (CurrentPosition!SourceAsterIndex){CurrentPositionPath[CurrentPosition];PassedAsters.Add(CurrentPosition);}TArrayintPassedAsters2;//使路径从后往前为包裹要走过的天体for(int iPassedAsters.Num()-1;i0;i--){PassedAsters2.Add(PassedAsters[i]);}//令目标天体发送包裹SendPackage(FPackageInformation(Current-ID,PassedAsters2,PackgingStardust));//所有进口需求都被满足提前终止if(StardustNeed.empty()){return TArrayFStardustBasic();}}}//该天体处理完毕防止被再次处理Done[Current-ID]true;//遍历该天体所有联通的天体for(const autoit:AsterGraph[Current-ID]){if(Done[it-TerminalIndex])continue;//这条路是最短路if(MinimumDistance[it-TerminalIndex]it-distanceCurrent-DIstance){Path[it-TerminalIndex]Current-ID;//更新最短路径MinimumDistance[it-TerminalIndex]it-distanceCurrent-DIstance;Queue.push(MakeSharedNode(it-TerminalIndex,MinimumDistance[it-TerminalIndex]));}}}//返回未满足的进口需求TArrayFStardustBasic Result;if(!StardustNeed.empty()){for(const autoit:StardustNeed){Result.Add(FStardustBasic(FName(UTF8_TO_TCHAR(it.first.c_str())),it.second));}}return Result;
}
重新寻路的逻辑与之类似区别在于只是搜索确定的两点之间的最短路不会发送包裹
TArrayint ATradingSystemActor::ReRoute(const int Start, const int end)
{TArrayintResult;struct Node{Node(const int ID, const int DIstance): ID(ID),DIstance(DIstance){}int ID;long long DIstance;};auto cmp{[](const TSharedPtrNodea,const TSharedPtrNode b){return a-DIstanceb-DIstance;}};std::priority_queueTSharedPtrNode,std::vectorTSharedPtrNode,decltype(cmp)Queue(cmp);Queue.push(MakeSharedNode(Start,0));std::unordered_mapint,long longMinimumDistance;std::unordered_mapint,boolDone;std::mapint,intPath;for(auto it:DebugActor-AllAster){MinimumDistance[it.Key]1e18;Done[it.Key]false;}MinimumDistance[0]0;while(!Queue.empty()){auto Current{Queue.top()};Queue.pop();if(Done[Current-ID]){continue;}Done[Current-ID]true;for(const autoit:AsterGraph[Current-ID]){//找到终点立刻终止运算if(it-TerminalIndexend){TArrayintPassedAsters;int CurrentPosition{Current-ID};while (CurrentPosition!Start){CurrentPositionPath[CurrentPosition];PassedAsters.Add(CurrentPosition);}TArrayintPassedAsters2;for(int iPassedAsters.Num()-1;i0;i--){PassedAsters2.Add(PassedAsters[i]);}return PassedAsters2;}if(Done[it-TerminalIndex])continue;if(MinimumDistance[it-TerminalIndex]it-distanceCurrent-DIstance){Path[it-TerminalIndex]Current-ID;MinimumDistance[it-TerminalIndex]it-distanceCurrent-DIstance;Queue.push(MakeSharedNode(it-TerminalIndex,MinimumDistance[it-TerminalIndex]));}}}//没找到路径返回的是空数组return Result;
} 三、包裹
1.包裹的数据结构 包裹的数据包裹发出该包裹的建筑的索引计划要经过的所有建筑的索引和携带的星尘
USTRUCT(BlueprintType)
struct FPackageInformation
{explicit FPackageInformation(const int SourceAsterIndex, const TArrayint ExpectedPath,const TArrayFStardustBasicExpectedStardusts): SourceAsterIndex(SourceAsterIndex),ExpectedPath(ExpectedPath),Stardusts(ExpectedStardusts){}FPackageInformation() default;GENERATED_BODY()//发出包裹的源天体UPROPERTY(VisibleAnywhere,BlueprintReadWrite,CategoryPackage)int SourceAsterIndex{0};//计划的路径从后到前依次为即将走过的天体索引UPROPERTY(VisibleAnywhere,BlueprintReadWrite,CategoryPackage)TArrayint ExpectedPath;//包裹携带的星尘UPROPERTY(VisibleAnywhere,BlueprintReadWrite,CategoryPackage)TArrayFStardustBasicStardusts;
}; 2.包裹在场景中的运动 每个包裹的路径是在其生成时就计算好的数组中从后到前依次是其计划经过的建筑的索引每到达一个建筑后将末尾的元素弹出直到全部弹出即到达终点
bool APackageActor::AsterReached(const int AsterIndex)
{//检查输入的天体索引是否真实存在if(!TradingSystem-DebugActor-AllAster.Find(AsterIndex)){UE_LOG(LogTemp,Error,TEXT(AsterReached failed,invalid index:%d),AsterIndex);return false;}//即将到达终点if(PackgeInfo.ExpectedPath.Num()1){//送达包裹中的星尘for(autoit:PackgeInfo.Stardusts){TradingSystem-DebugActor-AllAster[AsterIndex]-InputInventory-AddStardust(it.StardustId,it.Quantity);it.Quantity-std::min(it.Quantity,TradingSystem-DebugActor-AllAster[AsterIndex]-InputInventory-CheckAddable(it.StardustId));}//更新库存UITradingSystem-DebugActor-AllAster[AsterIndex]-MCUpdateEvent();TArrayFStardustBasicLostStardust;//统计因终点库存已满而丢包的星尘for(const autoit:PackgeInfo.Stardusts){if(it.Quantity){LostStardust.Add(FStardustBasic(it.StardustId,it.Quantity));UE_LOG(LogTemp,Error,TEXT(%d %s cant put in target aster),it.Quantity,*it.StardustId.ToString());}}return true;}//弹出路径中队尾的元素PackgeInfo.ExpectedPath.Pop();//更新包裹的路径UpdatePathEvent(PackgeInfo.ExpectedPath);return false;
}我们使用时间轴和设置actor变换的方式来使包裹在场景中移动也可以实现游戏暂停时停止移动和恢复移动 四、道路
1.道路的数据结构 在本游戏中玩家可以建造多种道路每种道路有不同的传输速度最大建造距离和消耗首先是数据表格的数据结构这里和DataTable的互动可以看开发日志2独立游戏《星尘异变》UE5 C程序开发日志2——实现一个存储物品数据的c类-CSDN博客
USTRUCT(BlueprintType)
struct FRoadDataTable:public FTableRowBase
{FRoadDataTable() default;FRoadDataTable(const FString RoadName, ERoadType RoadType, int TransferSpeed, double MaximumLength): RoadName(RoadName),RoadType(RoadType),TransferSpeed(TransferSpeed),MaximumLength(MaximumLength){}GENERATED_USTRUCT_BODY()//道路名称UPROPERTY(EditAnywhere,BlueprintReadWrite,CategoryRoadInfo)FString RoadName{Empty};//道路种类UPROPERTY(EditAnywhere,BlueprintReadWrite,CategoryRoadInfo)ERoadType RoadType{ERoadType::Empty};//传输速度单位距离/秒UPROPERTY(EditAnywhere,BlueprintReadWrite,CategoryRoadInfo)int TransferSpeed{1};//最大长度UPROPERTY(EditAnywhere,BlueprintReadWrite,CategoryRoadInfo)double MaximumLength{1};//道路建造消耗UPROPERTY(EditAnywhere,BlueprintReadWrite,CategoryRoadInfo)TMapFString,intRoadConsumption;}; 然后是每条建造出来的道路的数据结构包括道路的起点和终点用的是所连建筑物的全局索引以及这条路建成的长度和表格数据。我们有一个数组维护着所有场上的建筑物的指针通过这两个索引就可以访问到道路两端的建筑 USTRUCT(BlueprintType)
struct FRoadInformation
{friend bool operator(const FRoadInformation Lhs, const FRoadInformation RHS){return Lhs.distance RHS.distance;}friend bool operator(const FRoadInformation Lhs, const FRoadInformation RHS){return !(RHS Lhs);}friend bool operator(const FRoadInformation Lhs, const FRoadInformation RHS){return RHS Lhs;}friend bool operator(const FRoadInformation Lhs, const FRoadInformation RHS){return !(Lhs RHS);}friend bool operator(const FRoadInformation Lhs, const FRoadInformation RHS){return Lhs.TerminalIndex RHS.TerminalIndex Lhs.StartIndexRHS.StartIndex;}friend bool operator!(const FRoadInformation Lhs, const FRoadInformation RHS){return !(Lhs RHS);}FRoadInformation() default;explicit FRoadInformation(const int StartIndex,const int TerminalIndex,const FVectorStartLocation,const FVectorEndLocation,const FRoadDataTable Road):StartIndex(StartIndex), TerminalIndex(TerminalIndex),distance(StartLocation.Distance(StartLocation,EndLocation)),RoadInfo(Road){}GENERATED_USTRUCT_BODY()UPROPERTY(EditAnywhere,BlueprintReadWrite,CategoryRoad)int StartIndex{0};//起点天体的索引UPROPERTY(EditAnywhere,BlueprintReadWrite,CategoryRoad)int TerminalIndex{0};//终点天体的索引UPROPERTY(EditAnywhere,BlueprintReadWrite,CategoryRoad)int distance{0};//两个天体之间的距离取整//道路的数据UPROPERTY(EditAnywhere,BlueprintReadWrite,CategoryRoad)FRoadDataTable RoadInfo;};
2.道路的建造 我们用一个红黑树来储存每个建筑都分别链接了哪些建筑 std::mapint,TArrayTSharedPtrFRoadInformation AsterGraph;//所有天体构成的图 在建造道路时传入起点和终点索引以及道路类型的名称将建造的道路存入上面存图的容器中 bool ATradingSystemActor::RoadBuilt(const int Aster1, const int Aster2,const FString RoadName)
{if(!DebugActor-IsValidLowLevel()){UE_LOG(LogTemp,Error,TEXT(RoadBuild failed,invalid pointer:DebugActor));return false;}//这两个建筑之间已存在道路不可重复建造if(AsterGraph[Aster1].FindByPredicate([Aster2](const TSharedPtrFRoadInformation Road){return Road-TerminalIndexAster2;})){return false;}//对应索引的天体不存在if(!DebugActor-AllAster.Find(Aster1)||!DebugActor-AllAster.Find(Aster2)){UE_LOG(LogTemp,Error,TEXT(RoadBuilt failed,invalid index :%d %d),Aster1,Aster2);return false;}//数据表中存储的道路信息auto RoadInfo{*Instance-RoadDataMap[TCHAR_TO_UTF8(*RoadName)]};//存双向边AsterGraph[Aster1].Add(MakeSharedFRoadInformation(Aster1,Aster2,DebugActor-AllAster[Aster1]-AsterPosition,DebugActor-AllAster[Aster2]-AsterPosition,RoadInfo));AsterGraph[Aster2].Add(MakeSharedFRoadInformation(Aster2,Aster1,DebugActor-AllAster[Aster2]-AsterPosition,DebugActor-AllAster[Aster1]-AsterPosition,RoadInfo));return true;
}
3.道路的销毁 在销毁道路时我们需要将存的图中的该道路删除同时对于所有传输中的包裹如果其原本的路径中包含这条道路则重新计算路径如果计算路径失败则将包裹送到下一个到达的建筑物处
void ATradingSystemActor::RoadDestructed(const int Aster1, const int Aster2)
{if(!DebugActor-IsValidLowLevel()){UE_LOG(LogTemp,Error,TEXT(RoadDestructed failed,invalid pointer:DebugActor));return;}//两个方向都要删除AsterGraph[Aster1].RemoveAll([Aster2](const TSharedPtrFRoadInformation Road){return Road-TerminalIndexAster2;});AsterGraph[Aster2].RemoveAll([Aster1](const TSharedPtrFRoadInformation Road){return Road-TerminalIndexAster1;});//遍历所有在路上的包裹for(autoit:TransferingPackage){auto Temp{it-GetPackageInfo()};//遍历其计划经过的天体for(int iTemp.ExpectedPath.Num()-1;i1;i--){//是否经过该条道路if(Temp.ExpectedPath[i]Aster1Temp.ExpectedPath[i-1]Aster2||Temp.ExpectedPath[i]Aster2Temp.ExpectedPath[i-1]Aster1){//尝试重新计算路径auto TempArray{ReRoute(Temp.ExpectedPath[Temp.ExpectedPath.Num()-1],Temp.ExpectedPath[0])};//没有能到终点的道路了if(TempArray.IsEmpty()){UE_LOG(LogTemp,Error,TEXT(RerouteFailed));//将终点改为下一个天体TArrayintResult;Result.Add(Temp.ExpectedPath[Temp.ExpectedPath.Num()-1]);Temp.ExpectedPathResult;it-SetPackageInfo(Temp);it-UpdatePathEvent(Temp.ExpectedPath);break;}//应用新的路径Temp.ExpectedPathTempArray;it-SetPackageInfo(Temp);it-UpdatePathEvent(Temp.ExpectedPath);break;}}}
}
4.某个有道路连接的建筑被删除 在有道路连接的建筑被删除后所有路径中包含该建筑的包裹要重新寻路如果不能到达终点同样送到下一个建筑为止
void ABP_Asters::AsterDestructed()
{ //这里展示的仅是该函数中关于物流系统的部分//删除以该天体为起点的道路TradingSystem-AsterGraph.erase(AsterIndex);for(autoit:TradingSystem-AsterGraph){//删除以该天体为终点的道路auto temp{AsterIndex};it.second.RemoveAll([temp](const TSharedPtrFRoadInformation Road){return Road-TerminalIndextemp;});}for(int i0;iTradingSystem-TransferingPackage.Num();i){auto it{TradingSystem-TransferingPackage[i]};if(!IsValid(it)){TradingSystem-TransferingPackage.RemoveAt(i);i--;continue;}auto Temp{it-GetPackageInfo()};bool NeedReroute{false};//计划路径中有该天体就需要重新寻路for(auto it2:Temp.ExpectedPath){if(it2AsterIndex){NeedReroutetrue;}}if(NeedReroute){ //下一个目的地就是该天体直接删除if(Temp.ExpectedPath.Num()1){it-Destroy();continue;}//终点是该天体那肯定找不到路了if(Temp.ExpectedPath[0]AsterIndex){UE_LOG(LogTemp,Error,TEXT(Reroute failed));TArrayintResult;Result.Add(Temp.ExpectedPath[Temp.ExpectedPath.Num()-1]);Temp.ExpectedPathResult;it-SetPackageInfo(Temp);it-UpdatePathEvent(Temp.ExpectedPath);continue;}//尝试重新寻路auto TempArray{TradingSystem-ReRoute(Temp.ExpectedPath[Temp.ExpectedPath.Num()-1],Temp.ExpectedPath[0])};//没找到合适的道路if(TempArray.IsEmpty()){UE_LOG(LogTemp,Error,TEXT(Reroute failed));TArrayintResult;Result.Add(Temp.ExpectedPath[Temp.ExpectedPath.Num()-1]);Temp.ExpectedPathResult;it-SetPackageInfo(Temp);it-UpdatePathEvent(Temp.ExpectedPath);continue;}//应用新的路径Temp.ExpectedPathTempArray;it-SetPackageInfo(Temp);it-UpdatePathEvent(Temp.ExpectedPath);}}//蓝图实现的事件因为道路的指针存在蓝图里所以交给蓝图来删除对象AsterDestructedEvent(this);
}