首页 > 编程语言 >C++重写

C++重写

时间:2024-05-01 12:33:38浏览次数:18  
标签:Index CurrentNeighbor CostFromStart C++ DiscoveredTileIndexed 数组 重写 DiscoveredTi

数组

DiscoveredTileIndexed 和 DiscoveredTileSortingCosts

这两个数组是用来存储遍历的方格的,DiscoveredTileSortingCosts存储的是每个方格的消耗,DiscoveredTileIndexed存储的是每个方格的位置即(x,y)。
DiscoveredTileSortingCosts中的消耗和DiscoveredTileIndexed位置是一一对应的,也即是DiscoveredTileIndexed中方格位置是按照消耗排序的

AnalysedTileIndex

每次从DiscoveredTileIndexed中取出第一个元素,即消耗最小的元素压入到该数组中,压入后表示该点已经走过,后面的点如果周围点出现在这个数组中那么就会跳过

PathFindingData

其为一个Map结构,存储的就是最短路径,由当前方格坐标和方格参数组成,

CurrentNeighbors

用来存储每次遍历一个节点其的下一步可以到达的点

方格的结构体

和蓝图中一样存放几个必须的参数

USTRUCT(BlueprintType)
struct FPathFindingData : public FTableRowBase
{
	GENERATED_USTRUCT_BODY()
	FPathFindingData() {}
	FPathFindingData(FIntPoint index, int costtoentertile, int costfromstart, int minimumcosttotarget, FIntPoint previousindex) :
		Index(index), CostToEnterTile(costtoentertile), CostFromStart(costfromstart),MinimumCostToTarget(minimumcosttotarget),PreviousIndex(previousindex)
	{}
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Data")
		FIntPoint Index = { -999, -999 };
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Data")
		int CostToEnterTile = 1;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Data")
		int CostFromStart = 999999;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Data")
		int MinimumCostToTarget = 999999;
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Data")
		FIntPoint PreviousIndex = { -999, -999 };
};

寻路函数

TArray<FIntPoint> AXGridPathFinding::FindPath(const FIntPoint& start, const FIntPoint& target, bool Diagonals)
返回PathFindingData中Key值组成的数组。
首先向函数中传入起点和终点以及是否考虑沿着对角线移动,然后清除上次寻路的结果,检验输入坐标是否合理

	StartIndex = start;
	TargetIndex = target;
	bIncludeDiagonals = Diagonals;

	//清除之前的路径
	ClearGenerateData();
	//判断输入的合理性
	if (!IsInputDataValid())
	{
		UE_LOG(LogTemp, Warning, TEXT("Input No Valid"));
		return TArray<FIntPoint>();
	}

计算起点的方格参数

	int MinCostToEnd = GetMinimumCostBetweenTwoTiles(StartIndex, TargetIndex, bIncludeDiagonals);
	FPathFindingData StartData;
	StartData.Index = StartIndex;
	StartData.CostToEnterTile = 1;
	StartData.CostFromStart = 0;
	StartData.MinimumCostToTarget = MinCostToEnd;

对起点进行计算得到初始的DiscoveredTileIndexed 和 DiscoveredTileSortingCosts并将起点压入到PathFindingData中

	DiscoverTile(StartData);
	if(DiscoveredTileIndexed.Num() == 0) UE_LOG(LogTemp, Warning, TEXT("No DiscoveredTileIndexed"));

当起点的可移动点求出,遍历DiscoveredTileIndexed数组,对每个点进行分析,直到DiscoveredTileIndexed数组长度为0,如果在分析中返回了true表示可以到终点,返回对应数组

	while (DiscoveredTileIndexed.Num() > 0)
	{
		//寻路
		if (AnalyseNextDiscoveredTile())
		{
			UE_LOG(LogTemp, Warning, TEXT("Find Way"));
			return GerneratePath();
		}
	}
	return TArray<FIntPoint>();

计算当前点的可到达方格

void AXGridPathFinding::DiscoverTile(const FPathFindingData& TilePathData)
通过传入当前节点的属性,将当前节点压入到PathFindingData中,然后从当前点计算更新DiscoveredTileIndexed 和 DiscoveredTileSortingCosts

void AXGridPathFinding::DiscoverTile(const FPathFindingData& TilePathData)
{
	PathFindingData.Add(TilePathData.Index, TilePathData);
	InsertTileInDiscoveredArray(TilePathData);
}

更新DiscoveredTileIndexed 和 DiscoveredTileSortingCosts

void AXGridPathFinding::InsertTileInDiscoveredArray(const FPathFindingData& TileData)
首先计算从起点经过该点TileData到终点所需要的路径消耗SortingData,利用TileData结构体中的数据进行计算。
当DiscoveredTileIndexed数组为空时,可以直接压入节点坐标,如果不为空,要比较DiscoveredTileSortingCosts中的最后一个元素和SortingData的大小,如果SortingData大,那么也可以直接压入,如果SortingData小,那么需要从DiscoveredTileSortingCosts寻找到合理的位置进行插入,而DiscoveredTileSortingCosts与DiscoveredTileIndexed是一一对应的所以可以使用同一个下标进行插入

void AXGridPathFinding::InsertTileInDiscoveredArray(const FPathFindingData& TileData)
{
	//计算当前点的消耗
	int SortingData = TileData.CostFromStart + TileData.MinimumCostToTarget;
	//如果排序数组长度为0,或者当前点的SortingData比排序数组中都大,那么直接压入
	if (DiscoveredTileSortingCosts.Num() == 0)
	{
		//都是在对应的队尾加入元素,保证顺序
		DiscoveredTileSortingCosts.Add(SortingData);
		DiscoveredTileIndexed.Add(TileData.Index);
	}
	else
	{
		if (SortingData >= DiscoveredTileSortingCosts.Last())
		{
			DiscoveredTileSortingCosts.Add(SortingData);
			DiscoveredTileIndexed.Add(TileData.Index);
		}
		else
		{
			for (int i = 0;i<DiscoveredTileSortingCosts.Num();i++)
			{
				if (SortingData < DiscoveredTileSortingCosts[i])
				{
					DiscoveredTileSortingCosts.Insert(SortingData, i);
					DiscoveredTileIndexed.Insert(TileData.Index,i);
					break;
				}
			}
		}
	}
}

对每个遍历到的节点的处理

bool AXGridPathFinding::AnalyseNextDiscoveredTile()
该函数不仅涉及到对每个节点的处理,还需要对每个节点进行判断是否为终点
每次处理的节点一定是当前消耗最小的节点,即首先需要从DiscoveredTileIndexed取出第一个元素,加入到AnalysedTileIndex数组中,然后每次取出一个就需要将其从DiscoveredTileIndexed 和 DiscoveredTileSortingCosts移除。
然后根据下标从PathFindingData中获取当前节点的结构体,然后计算当前节点下一步会到达哪些点,存储在CurrentNeighbors数组中,然后对数组中每个元素进行遍历,寻找其在DiscoveredTileIndexed 和 DiscoveredTileSortingCosts的合适位置进行插入,然后判断是否为终点。

bool AXGridPathFinding::AnalyseNextDiscoveredTile()
{
	//首先将消耗最小的节点取出,从存储数组和排序数组中移除(一定是第一个元素),然后加入到分析数组中
	CurrentDiscoveredTile = PullCheapestTileOutOfDiscoveredList();
	//获取当前点的下一步可移动的点
	CurrentNeighbors = GetValidTileNeighbor(CurrentDiscoveredTile.Index, bIncludeDiagonals);
	if(CurrentNeighbors.Num() == 0) UE_LOG(LogTemp, Warning, TEXT("No CurrentNeighbors"));
	while (CurrentNeighbors.Num() > 0)
	{
		if (DiscoverNextNeighbor())
		{
			return true;
		}
	}
	return false;
}

获取消耗最小点作为接下来的起始点

FPathFindingData AXGridPathFinding::PullCheapestTileOutOfDiscoveredList()
{
	FIntPoint TileIndex = DiscoveredTileIndexed[0];
	DiscoveredTileIndexed.RemoveAt(0);
	DiscoveredTileSortingCosts.RemoveAt(0);
	AnalysedTileIndex.Add(TileIndex);
	//获取存在于结果Map中的数据,如果没有就返回空
	FPathFindingData res = PathFindingData[TileIndex];
	return res;
}

对当前点的下一步可到达点进行处理

bool AXGridPathFinding::DiscoverNextNeighbor()
每次获取CurrentNeighbors中的第一个元素,然后移除它
首先对其下标进行判断,判断它是否之前出现在了AnalysedTileIndex数组中,如果出现过,说明如果现在到该点会向回走,跳过对该点的处理
接下来计算从起点到下一步点的消耗CostFromStart,然后判断下一步点是否之前存在DiscoveredTileIndexed中,如果存在那说明下一步点之前作为过其他点的下一步可移动点,但是并不是消耗最少的点。
那么就需要从PathFindingData中取出下一步点的起点到该点的数据,来和CostFromStart比较。如果CostFromStart较大,说明下一步点不是最优点,可以直接返回了。
如果CostFromStart较小,那么下一步点就可以作为下一步可到达点,首先从DiscoveredTileIndexed 和 DiscoveredTileSortingCosts移除它。
接下来的处理逻辑是公用的(CostFromStart较小或者该点之前不存在DiscoveredTileIndexed中
首先就是计算下一步点的结构体数据,然后对下一步点进行压入PathFindingData和更新DiscoveredTileIndexed 和 DiscoveredTileSortingCosts的操作。
接着判断下一步点是否为终点,从而返回bool值

bool AXGridPathFinding::DiscoverNextNeighbor()
{
	CurrentNeighbor = CurrentNeighbors[0];
	CurrentNeighbors.RemoveAt(0);
	
	//如果在分析数组中已经存在说明之前遍历过,即往回走了,直接返回false
	if(AnalysedTileIndex.Contains(CurrentNeighbor.Index)) return false;
	//计算起点到当前点的消耗,就是起点到前一个点的消耗 + 前一个点到目前点的消耗
	int CostFromStart = CurrentDiscoveredTile.CostFromStart + CurrentNeighbor.CostToEnterTile;
	
	//判断该点是否存在于之前点的邻接点但还未遍历的数组中
	//如果在
	int IndexInDiscovered = DiscoveredTileIndexed.Find(CurrentNeighbor.Index);
	if (IndexInDiscovered != -1)
	{
		CurrentNeighbor = PathFindingData[CurrentNeighbor.Index];
		if (CostFromStart >= CurrentNeighbor.CostFromStart)
		{
			
			return false;
		}
		else
		{
			DiscoveredTileIndexed.RemoveAt(IndexInDiscovered);
			DiscoveredTileSortingCosts.RemoveAt(IndexInDiscovered);
		}
	}
	//如果不在,那么直接加入就行了
	int MinimumCostToTarget = GetMinimumCostBetweenTwoTiles(CurrentNeighbor.Index, TargetIndex, bIncludeDiagonals);
	UE_LOG(LogTemp, Warning, TEXT("CurrentNeighbor:%d,%d,CostFromStart:%d,MinimumCostToTarget:%d"), CurrentNeighbor.Index.X, CurrentNeighbor.Index.Y, CostFromStart, MinimumCostToTarget);
	FPathFindingData temp(CurrentNeighbor.Index, CurrentNeighbor.CostToEnterTile, CostFromStart, MinimumCostToTarget, CurrentDiscoveredTile.Index);
	DiscoverTile(temp);
	if (CurrentNeighbor.Index == TargetIndex)
	{
		return true;
	}
	else return false;
}

生成路径

对PathFindingData进行从后向前遍历,通过存放在结构体中的前一个点坐标这个成员进行迭代直到退回到起点,然后再翻转数组即可。

TArray<FIntPoint> AXGridPathFinding::GerneratePath()
{
	FIntPoint Current = TargetIndex;
	TArray<FIntPoint> InvertedPath, Res;
	while (Current != StartIndex)
	{
		InvertedPath.Add(Current);
		Current = PathFindingData[Current].PreviousIndex;
	}
	for (int i = InvertedPath.Num() - 1; i >= 0; i--)
	{
		//UE_LOG(LogTemp, Warning, TEXT("InvertedPath[i]:%d,%d"), InvertedPath[i].X, InvertedPath[i].Y);
		Res.Add(InvertedPath[i]);
	}
	return Res;
}

标签:Index,CurrentNeighbor,CostFromStart,C++,DiscoveredTileIndexed,数组,重写,DiscoveredTi
From: https://www.cnblogs.com/XTG111/p/18169117

相关文章

  • C++指针与引用(Pointers OR References)
    一、PointersPointer是指针,可以用来指向任何一个objects,包括一般变量:1inti=3;2int*pi=&i;3cout<<pi<<endl;//0x0064FDF04cout<<*pi<<endl;//3此时pi本身内含i的地址,要取出pi所指向的object,可以使用*运算符(dereferenceoperator).Pointer......
  • C/C++如何写调试宏
    1.调试宏以及测试在写代码时,不可避免需要打印提示、警告、错误等信息,且要灵活控制打印信息的级别。另外,还有可能需要使用宏来控制代码段(主要是调试代码段)是否执行。为此,本文提供一种调试宏定义方案,包括打印字符串信息LOG1宏和格式化打印LOG2宏,且能通过宏控制代码段执行。完整代......
  • 《Effective C++》第三版-3. 资源管理(Resource Management)
    目录条款13:以对象管理资源(Useobjectstomanageresources)关键想法智能指针条款14:在资源管理类中小心copying行为(Thinkcarefullyaboutcopyingbehaviorinresource-managingclasses)条款15:在资源管理类中替工对原始资源的访问(Provideaccesstorawresourcesinresource-ma......
  • 学习 C++,从搭建 Visual Studio Code 开始
    0.声明本文针对Windows和Linux系统配置VisualStudioCode,Mac贵族请勿入内。本文以Windows10系统演示。1.准备工作1.1.安装VisualStudioCodeWindows:官网下载链接选择Windows(Windows10,11)进行安装Linux:在应用商店搜索VisualStudioCode,安装即......
  • C/C++、Java 与 Python 中未初始化变量的处理比较
    在C/C++中,未初始化的变量的值是不确定的,可能是随机的。 在Python中,如果直接使用未初始化的变量,会引发NameError异常。Python要求变量在使用前必须进行赋值或初始化。 而在Java中,直接使用未初始化的局部变量会导致编译错误,必须先对变量进行初始化。 C++和Java在字......
  • 一文详解C++的vector
    vector是C++中使用频率最高的标准库,可以在程序运行时动态改变其大小(例如添加或删除元素),因此又被称为动态数组。使用时,用户无需在意底层内存管理的细节,因为它已经帮你做了这件事情。使用前需要导入<vector>头文件,以下是vector的常见用法:1.创建vectorvector用于保存一组同类型的......
  • 浅析OpenCV分水岭变换watershed函数的markers参数[C++]
    0.前言本文是笔者在学习C++OpenCV库时学习心得,在学习分水岭变换函数时,由于缺少相关学习资料,导致笔者理解吃力,故写此文章阐述一下对该函数的理解,希望对其他学习人士提供帮助。本文主要介绍了watershed函数参数以及参数实际表示。请您按文章次序阅读。您需要提前了解的相关知......
  • 在Linux系统下用命令行编译调试C++
    在Linux系统下用命令行编译调试C++目录在Linux系统下用命令行编译调试C++一、编译1.单文件编译2.多文件编译3.链接第三方动态库二、调试1.启动和退出2.查看源代码:list/l3.断点:breakpoint/br、watchpoint4.单步、步入、跳出5.计算表达式命令:expression/expr、p、po6.操作......
  • C++ 学习笔记
    ​1、基础概念C++是一种高性能的编程语言,由BjarneStroustrup在1980年代初设计,旨在为C语言添加面向对象的功能。自那时起,C++已发展成为一种支持过程性、面向对象和泛型编程的多范式语言,广泛应用于系统软件、游戏开发、驱动程序、嵌入式固件等领域。要开始使用C++,首先需要......
  • 深入理解 C++ 中的多态与文件操作
    C++多态多态(Polymorphism)是面向对象编程(OOP)的核心概念之一,它允许对象在相同操作下表现出不同的行为。在C++中,多态通常通过继承和虚函数来实现。理解多态想象一个场景,你有一个动物园,里面有各种动物,如猫、狗、鸟等。每个动物都有自己的叫声。使用面向对象编程,我们可以创建一个......