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