优化资源排布算法 #1
之前的实现有bug,现在营地基本一定能生成 现在还有一个问题是,森林非常小的情况下,在森林的资源点可能达不到必须生成的数量要求 需要进一步解决这个问题
This commit is contained in:
		| @ -3,46 +3,58 @@ | ||||
| #include "Level/Actor/BusyStaticResource.h" | ||||
| #include "Utils/MitchellBestCandidate.h" | ||||
|  | ||||
| static FName PeekOneOfAlwaysPresent(TMap<FName, int32>& AlwaysPresentResource) | ||||
|  | ||||
| static void CleanUpEmptyResources(TMap<FName, int32>& AlwaysPresentResource) | ||||
| { | ||||
| 	TArray<FName> NeedRemoveKeys; | ||||
| 	TArray<FName> NeedRemoveList; | ||||
| 	for (auto& Pair : AlwaysPresentResource) | ||||
| 	{ | ||||
| 		if (Pair.Value <= 0) | ||||
| 		{ | ||||
| 			NeedRemoveKeys.Add(Pair.Key); | ||||
| 			NeedRemoveList.Add(Pair.Key); | ||||
| 			continue; | ||||
| 		} | ||||
| 	} | ||||
| 	for (auto& RemovedKey : NeedRemoveKeys) | ||||
| 	for (FName& RemovedKey : NeedRemoveList) | ||||
| 	{ | ||||
| 		AlwaysPresentResource.Remove(RemovedKey); | ||||
| 	} | ||||
| 	 | ||||
| 	if (AlwaysPresentResource.Num() == 0) | ||||
| 	{ | ||||
| 		return FName(); | ||||
| 	} | ||||
| 	const auto &Pair = *AlwaysPresentResource.begin(); | ||||
| 	return Pair.Key; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void ConsumeOneOfAlwaysPresent(TMap<FName, int32>& AlwaysPresentResource, const FName &ResourceName) | ||||
| /** | ||||
|  * 给定地形,遍历所有的资源,找一个能生成在这个地形上的资源 | ||||
|  * @param CurrentTerrain 指定地形 | ||||
|  * @param AlwaysPresentResource 必须要生成的资源清单 | ||||
|  * @param GenerateConfig 表格配置 | ||||
|  * @return 生成的资源的ID | ||||
|  */ | ||||
| static FName TryGenerateResourceAtTerrain(const FGameplayTag &CurrentTerrain, TMap<FName, int32>& AlwaysPresentResource, const UDataTable* GenerateConfig) | ||||
| { | ||||
| 	if (const int32 *RemainCount = AlwaysPresentResource.Find(ResourceName)) | ||||
| 	FName Selected; | ||||
| 	for (auto& Pair : AlwaysPresentResource) | ||||
| 	{ | ||||
| 		if (*RemainCount > 0) | ||||
| 		if (Pair.Value <= 0) continue; | ||||
|  | ||||
| 		const auto Config = GenerateConfig->FindRow<FStaticResourceGenerateConfig>(Pair.Key, TEXT("")); | ||||
| 		if (!Config) | ||||
| 		{ | ||||
| 			AlwaysPresentResource[ResourceName] = *RemainCount - 1; | ||||
| 			return; | ||||
| 			// TODO LOG | ||||
| 			continue; | ||||
| 		} | ||||
| 		 | ||||
| 		if (Config->TerrainTypes.HasTag(CurrentTerrain)) | ||||
| 		{ | ||||
| 			Pair.Value -= 1; | ||||
| 			Selected = Pair.Key; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	UE_LOG(LogMapGenerate, Error, TEXT("ConsumeOneOfAlwaysPresent %s failed"), *ResourceName.ToString()); | ||||
| 	CleanUpEmptyResources(AlwaysPresentResource); | ||||
| 	return Selected; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| inline static IGameMapInterface * GetMapActor(const UActorComponent* Component) | ||||
| { | ||||
| 	AActor *Owner = Component->GetOwner(); | ||||
| @ -54,12 +66,16 @@ inline static IGameMapInterface * GetMapActor(const UActorComponent* Component) | ||||
| void UStaticResourceLayerComponent::GenerateResources() | ||||
| { | ||||
| 	TArray<FVector2D> ResourcePoints; | ||||
| 	TMap<FName, int32> AlwaysPresentResource; | ||||
|  | ||||
| 	// 生成资源点 | ||||
| 	GenerateResourcePoints(ResourcePoints); | ||||
|  | ||||
| 	// 获取所有要生成的资源 | ||||
| 	GetAlwaysPresentResourceList(AlwaysPresentResource); | ||||
| 	 | ||||
| 	// 生成必定生成的资源 | ||||
| 	GenerateAlwaysPresentInfo(ResourcePoints); | ||||
| 	GenerateAlwaysPresentInfo(ResourcePoints, AlwaysPresentResource); | ||||
|  | ||||
| 	// 利用剩下的资源点,根据权重生成资源 | ||||
| 	GenerateResourceByWeight(ResourcePoints); | ||||
| @ -104,6 +120,10 @@ void UStaticResourceLayerComponent::GenerateResourcePoints(TArray<FVector2D>& Ou | ||||
| 	OutResourcePoint = PointCreator->GeneratePoints(ResourcePointCount, MapWidth, MapHeight); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 获取所有要生成的资源列表 | ||||
|  * @param OutAlwaysPresentResource 资源名和资源数量的键值Map | ||||
|  */ | ||||
| void UStaticResourceLayerComponent::GetAlwaysPresentResourceList(TMap<FName, int32>& OutAlwaysPresentResource)const | ||||
| { | ||||
| 	OutAlwaysPresentResource.Empty(); | ||||
| @ -150,53 +170,38 @@ bool UStaticResourceLayerComponent::GetCanGenerateResourcesWeight(const FVector2 | ||||
| } | ||||
|  | ||||
|  | ||||
| bool UStaticResourceLayerComponent::GenerateAlwaysPresentInfo(TArray<FVector2D>& ResourcePoints) | ||||
| bool UStaticResourceLayerComponent::GenerateAlwaysPresentInfo(TArray<FVector2D>& ResourcePoints, TMap<FName, int32>& AlwaysPresent) | ||||
| { | ||||
| 	AActor *Owner = GetOwner(); | ||||
| 	if (!Owner) return false; | ||||
| 	const IGameMapInterface * MapInterface = Cast<IGameMapInterface>(Owner); | ||||
| 	if (!MapInterface) return false; | ||||
|  | ||||
| 	TMap<FName, int> AlwaysPresentResource; | ||||
| 	GetAlwaysPresentResourceList(AlwaysPresentResource); | ||||
|  | ||||
| 	for (int i = ResourcePoints.Num() - 1; i >= 0; i--)  // 遍历所有的资源点尝试生成资源 | ||||
| 	{ | ||||
| 		FName CurrentSelected = PeekOneOfAlwaysPresent(AlwaysPresentResource); | ||||
| 		if (CurrentSelected.IsNone())  // 必须生成的资源已经全部生成了,则结束 | ||||
| 		{ | ||||
| 			UE_LOG(LogMapGenerate, Log, TEXT("UStaticResourceLayerComponent::GenerateAlwaysPresentInfo All Always present created.")) | ||||
| 			break; | ||||
| 		} | ||||
| 		 | ||||
| 		// 取资源点 | ||||
| 		const FVector2D& CurrentPoint = ResourcePoints[i]; | ||||
| 		FGameplayTag CurrentTerrain = MapInterface->Execute_GetTerrainAt(Owner, CurrentPoint.X, CurrentPoint.Y);  // 获取这个资源点的地形类型 | ||||
| 		const auto Config = GenerateConfig->FindRow<FStaticResourceGenerateConfig>( | ||||
| 			CurrentSelected, TEXT("") | ||||
| 		); | ||||
| 		if (!Config) continue; | ||||
| 		 | ||||
| 		// 获取这个资源点的地形类型 | ||||
| 		FGameplayTag CurrentTerrain = MapInterface->Execute_GetTerrainAt(Owner, CurrentPoint.X, CurrentPoint.Y); | ||||
|  | ||||
| 		if (Config->TerrainTypes.HasTag(CurrentTerrain))  // 资源的地形配置包含当前的地形,则添加进生成的列表 | ||||
| 		// 获取要生成的资源名 | ||||
| 		FName&& ResourceName = TryGenerateResourceAtTerrain(CurrentTerrain, AlwaysPresent, GenerateConfig); | ||||
|  | ||||
| 		if (!ResourceName.IsNone()) | ||||
| 		{ | ||||
| 			TryPushGenerateResult(CurrentSelected, CurrentPoint); | ||||
| 			// GeneratedResourcePoints.Add(TTuple<FName, FVector2D>(CurrentSelected, FVector2D(ResourcePoints[i].X, ResourcePoints[i].Y))); | ||||
| 			ConsumeOneOfAlwaysPresent(AlwaysPresentResource, CurrentSelected); | ||||
| 			TryPushGenerateResult(ResourceName, CurrentPoint); | ||||
| 			ResourcePoints[i].X = ResourcePoints[i].Y = -1; | ||||
| 			UE_LOG(LogMapGenerate, Log, TEXT("UStaticResourceLayerComponent::GenerateAlwaysPresentInfo Create %s at (%d, %d)"), | ||||
| 				*CurrentSelected.ToString(), int32(ResourcePoints[i].X), int32(ResourcePoints[i].Y)) | ||||
| 				*ResourceName.ToString(), int32(ResourcePoints[i].X), int32(ResourcePoints[i].Y)) | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			UE_LOG(LogMapGenerate, Log, TEXT("UStaticResourceLayerComponent::GenerateAlwaysPresentInfo Failed create %s at (%d,%d) in %s"), | ||||
| 				*CurrentSelected.ToString(), int32(ResourcePoints[i].X), int32(ResourcePoints[i].Y), *CurrentTerrain.ToString()); | ||||
| 				*ResourceName.ToString(), int32(ResourcePoints[i].X), int32(ResourcePoints[i].Y), *CurrentTerrain.ToString()); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (!PeekOneOfAlwaysPresent(AlwaysPresentResource).IsNone())  // 如果还有必须要生成的没有生成,则生成失败 | ||||
| 	{ | ||||
| 		GeneratedResourcePoints.Empty(); | ||||
| 		return false; | ||||
| 	} | ||||
| 	// 清理已经生成的数据 | ||||
| 	ResourcePoints.RemoveAll([](const FVector2D Element){ return Element.X < 0 && Element.Y < 0; }); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| @ -60,7 +60,7 @@ protected: | ||||
| 	virtual void GenerateResourcePoints(TArray<FVector2D>& OutResourcePoint); | ||||
|  | ||||
|  | ||||
| 	bool GenerateAlwaysPresentInfo(TArray<FVector2D>& ResourcePoints); | ||||
| 	bool GenerateAlwaysPresentInfo(TArray<FVector2D>& ResourcePoints, TMap<FName, int32>& AlwaysPresent); | ||||
|  | ||||
| 	void GenerateResourceByWeight(TArray<FVector2D>& ResourcePoints); | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user