接入Spine
This commit is contained in:
@ -11,7 +11,7 @@ public class BusyRabbit : ModuleRules
|
||||
PublicDependencyModuleNames.AddRange(new string[] {
|
||||
"Core", "CoreUObject", "Engine", "InputCore",
|
||||
"EnhancedInput", "Paper2D", "UMG", "Slate",
|
||||
"GameplayAbilities", "GameplayTags","GameplayTasks"
|
||||
"GameplayAbilities", "GameplayTags","GameplayTasks", "SpinePlugin"
|
||||
});
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[] { });
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
void ABusyPlayerController::BeginPlay(){
|
||||
Super::BeginPlay();
|
||||
bShowMouseCursor = true; // <20><>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD>
|
||||
bShowMouseCursor = true;
|
||||
FInputModeGameAndUI InputMode;
|
||||
InputMode.SetHideCursorDuringCapture(false);
|
||||
InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
|
||||
|
||||
42
Source/BusyRabbit/Private/Level/Actor/BusyPawnBase.cpp
Normal file
42
Source/BusyRabbit/Private/Level/Actor/BusyPawnBase.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include "Level/Actor/BusyPawnBase.h"
|
||||
#include "Components/SphereComponent.h"
|
||||
#include "SpineSkeletonRendererComponent.h"
|
||||
#include "SpineSkeletonAnimationComponent.h"
|
||||
#include "Level/Actor/Components/BusyPawnMovement.h"
|
||||
#include "SpineBoneFollowerComponent.h"
|
||||
|
||||
ABusyPawnBase::ABusyPawnBase()
|
||||
{
|
||||
RootScene = CreateDefaultSubobject<USceneComponent>(TEXT("RootScene"));
|
||||
SpineRoot = CreateDefaultSubobject<USceneComponent>(TEXT("SpineRoot"));
|
||||
SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
|
||||
SpineRenderComponent = CreateDefaultSubobject<USpineSkeletonRendererComponent>(TEXT("SpineRenderComponent"));
|
||||
SpineAnimationComponent = CreateDefaultSubobject<USpineSkeletonAnimationComponent>(TEXT("SpineAnimationComponent"));
|
||||
MovementComponent = CreateDefaultSubobject<UBusyPawnMovement>(TEXT("MovementComponent"));
|
||||
|
||||
RootComponent = RootScene;
|
||||
SpineRoot->SetupAttachment(RootScene);
|
||||
SphereComponent->SetupAttachment(SpineRoot);
|
||||
SpineRenderComponent->SetupAttachment(SpineRoot);
|
||||
|
||||
|
||||
SpineRoot->SetRelativeRotation(FRotator(0, 0, -90));
|
||||
|
||||
}
|
||||
|
||||
void ABusyPawnBase::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
SpineAnimationComponent->SetSkin(DefaultSkinName);
|
||||
SpineAnimationComponent->SetAnimation(0, DefaultAnimationName, true);
|
||||
}
|
||||
|
||||
void ABusyPawnBase::UpdateMoveDirection_Implementation(const FVector2D& InDirection)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
float ABusyPawnBase::GetSpeed_Implementation()const
|
||||
{
|
||||
return 280;
|
||||
}
|
||||
18
Source/BusyRabbit/Private/Level/Actor/BusyPlayerRole.cpp
Normal file
18
Source/BusyRabbit/Private/Level/Actor/BusyPlayerRole.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include "Level/Actor/BusyPlayerRole.h"
|
||||
|
||||
#include "Camera/CameraComponent.h"
|
||||
#include "GameFramework/SpringArmComponent.h"
|
||||
|
||||
ABusyPlayerRole::ABusyPlayerRole()
|
||||
{
|
||||
CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
|
||||
SpringArmComponent = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComponent"));
|
||||
|
||||
SpringArmComponent->SetupAttachment(SpineRoot);
|
||||
CameraComponent->SetupAttachment(SpringArmComponent);
|
||||
|
||||
CameraComponent->SetOrthoWidth(3840);
|
||||
CameraComponent->SetProjectionMode(ECameraProjectionMode::Orthographic);
|
||||
|
||||
SpringArmComponent->SetRelativeRotation(FRotator(0, -90.0, 0.0));
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
#include "Level/Actor/Components/BusyPawnMovement.h"
|
||||
|
||||
|
||||
|
||||
UBusyPawnMovement::UBusyPawnMovement()
|
||||
{
|
||||
this->PrimaryComponentTick.bCanEverTick = true;
|
||||
}
|
||||
|
||||
void UBusyPawnMovement::MoveTo(const FVector2D& Target)
|
||||
{
|
||||
MoveTargetLocation = Target;
|
||||
}
|
||||
|
||||
FVector2D UBusyPawnMovement::GetMoveDirection()const
|
||||
{
|
||||
if (AActor *Owner = GetOwner())
|
||||
{
|
||||
const FVector CurrentLocation = Owner->GetActorLocation();
|
||||
FVector2D Direction = MoveTargetLocation - FVector2D(CurrentLocation);
|
||||
if (Direction.Normalize())
|
||||
{
|
||||
return Direction;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FVector2D();
|
||||
}
|
||||
}
|
||||
return FVector2D();
|
||||
}
|
||||
|
||||
void UBusyPawnMovement::TickComponent(float DeltaTime, ELevelTick TickType,
|
||||
FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
AActor* Owner = GetOwner();
|
||||
const IBusyMovable* Movable = Cast<IBusyMovable>(Owner);
|
||||
if (!Owner || !Movable) return;
|
||||
|
||||
FVector NewLocation;
|
||||
const float MoveDistance = DeltaTime * Movable->Execute_GetSpeed(Owner);
|
||||
const FVector CurrentLocation = Owner->GetActorLocation();
|
||||
|
||||
FVector2D MoveDirection = MoveTargetLocation - FVector2D(CurrentLocation);
|
||||
|
||||
if (MoveDistance * MoveDistance > MoveDirection.SizeSquared()) // 已经到达目的地
|
||||
{
|
||||
NewLocation = FVector(MoveTargetLocation, CurrentLocation.Z);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MoveDirection.Normalize())
|
||||
{
|
||||
NewLocation = CurrentLocation + FVector(MoveDirection * MoveDistance, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
NewLocation = CurrentLocation;
|
||||
}
|
||||
}
|
||||
if (!NewLocation.Equals(CurrentLocation))
|
||||
{
|
||||
Owner->SetActorLocation(NewLocation, true);
|
||||
}
|
||||
Movable->Execute_UpdateMoveDirection(Owner, GetMoveDirection());
|
||||
}
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
#include "Level/Generator/MitchellBestCandidate.h"
|
||||
#include "Math/UnrealMathUtility.h"
|
||||
|
||||
UMitchellBestCandidate::UMitchellBestCandidate()
|
||||
{
|
||||
// 初始化随机数生成器
|
||||
RandomStream = FRandomStream(FMath::Rand());
|
||||
}
|
||||
|
||||
TArray<FVector2D> UMitchellBestCandidate::GeneratePoints(int32 NumPoints, float Width, float Height, int32 NumCandidates)const
|
||||
{
|
||||
TArray<FVector2D> Points;
|
||||
|
||||
// 参数验证
|
||||
if (NumPoints <= 0 || Width <= 0 || Height <= 0 || NumCandidates <= 0)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Invalid parameters for GeneratePoints"));
|
||||
return Points;
|
||||
}
|
||||
|
||||
// 步骤1: 添加第一个随机点作为起始点
|
||||
FVector2D FirstPoint(
|
||||
RandomStream.FRandRange(0.0f, Width),
|
||||
RandomStream.FRandRange(0.0f, Height)
|
||||
);
|
||||
Points.Add(FirstPoint);
|
||||
|
||||
// 步骤2: 为剩余的每个点生成候选点并选择最佳的一个
|
||||
for (int32 i = 1; i < NumPoints; i++)
|
||||
{
|
||||
FVector2D BestCandidate;
|
||||
float BestDistance = -1.0f;
|
||||
|
||||
// 为当前点生成NumCandidates个候选点
|
||||
for (int32 j = 0; j < NumCandidates; j++)
|
||||
{
|
||||
// 生成随机候选点
|
||||
FVector2D Candidate(
|
||||
RandomStream.FRandRange(0.0f, Width),
|
||||
RandomStream.FRandRange(0.0f, Height)
|
||||
);
|
||||
|
||||
// 计算候选点到现有点集的最小距离
|
||||
float MinDist = MinDistanceToSet(Candidate, Points);
|
||||
|
||||
// 选择距离现有点集最远的候选点
|
||||
if (MinDist > BestDistance)
|
||||
{
|
||||
BestDistance = MinDist;
|
||||
BestCandidate = Candidate;
|
||||
}
|
||||
}
|
||||
|
||||
// 将最佳候选点添加到点集中
|
||||
Points.Add(BestCandidate);
|
||||
}
|
||||
|
||||
return Points;
|
||||
}
|
||||
|
||||
float UMitchellBestCandidate::Distance(const FVector2D& A, const FVector2D& B)
|
||||
{
|
||||
// 使用UE内置的向量距离计算函数
|
||||
return FVector2D::Distance(A, B);
|
||||
}
|
||||
|
||||
float UMitchellBestCandidate::MinDistanceToSet(const FVector2D& Point, const TArray<FVector2D>& PointSet)
|
||||
{
|
||||
// 处理空点集的情况
|
||||
if (PointSet.Num() == 0)
|
||||
{
|
||||
return FLT_MAX;
|
||||
}
|
||||
|
||||
float MinDist = FLT_MAX;
|
||||
|
||||
// 遍历点集中的所有点,找到最小距离
|
||||
for (const FVector2D& ExistingPoint : PointSet)
|
||||
{
|
||||
float Dist = Distance(Point, ExistingPoint);
|
||||
if (Dist < MinDist)
|
||||
{
|
||||
MinDist = Dist;
|
||||
}
|
||||
}
|
||||
|
||||
return MinDist;
|
||||
}
|
||||
@ -0,0 +1,158 @@
|
||||
#include "Level/Generator/TerrainGeneratorBase.h"
|
||||
|
||||
UTerrainGeneratorBase::UTerrainGeneratorBase()
|
||||
{
|
||||
}
|
||||
|
||||
int32 UTerrainGeneratorBase::AddTerrain(const FGameplayTag& TerrainTag)
|
||||
{
|
||||
FTerrainNodeInfo NewNode;
|
||||
NewNode.TerrainTag = TerrainTag;
|
||||
NewNode.Probability = 1.0f;
|
||||
NewNode.Weight = 1.0f;
|
||||
NewNode.ParentHandle = -1;
|
||||
NewNode.bIsLeafNode = true;
|
||||
|
||||
int32 Handle = NextHandle++;
|
||||
TerrainNodes.Add(Handle, NewNode);
|
||||
|
||||
return Handle;
|
||||
}
|
||||
|
||||
void UTerrainGeneratorBase::SetWeight(int32 Handle, float Weight)
|
||||
{
|
||||
if (FTerrainNodeInfo* Node = TerrainNodes.Find(Handle))
|
||||
{
|
||||
Node->Weight = FMath::Max(0.0f, Weight);
|
||||
}
|
||||
}
|
||||
|
||||
void UTerrainGeneratorBase::SetProbability(int32 Handle, float Probability)
|
||||
{
|
||||
if (FTerrainNodeInfo* Node = TerrainNodes.Find(Handle))
|
||||
{
|
||||
Node->Probability = FMath::Clamp(Probability, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void UTerrainGeneratorBase::SetExclusive(const TArray<int32>& Handles){
|
||||
if (Handles.Num() <= 1) return;
|
||||
ExclusiveGroups.Add(TSet<int32>(Handles));
|
||||
}
|
||||
|
||||
void UTerrainGeneratorBase::BindChildTerrain(int32 ParentHandle, const TArray<int32>& ChildHandles)
|
||||
{
|
||||
if (FTerrainNodeInfo* ParentNode = TerrainNodes.Find(ParentHandle))
|
||||
{
|
||||
// 设置父节点为非叶子节点
|
||||
ParentNode->bIsLeafNode = false;
|
||||
|
||||
// 添加子节点
|
||||
ParentNode->ChildHandles = ChildHandles;
|
||||
|
||||
// 设置子节点的父节点
|
||||
for (int32 ChildHandle : ChildHandles)
|
||||
{
|
||||
if (FTerrainNodeInfo* ChildNode = TerrainNodes.Find(ChildHandle))
|
||||
{
|
||||
ChildNode->ParentHandle = ParentHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FGameplayTag UTerrainGeneratorBase::GetFinalTerrainTag(const int32 Handle) const{
|
||||
if (const FTerrainNodeInfo* Node = TerrainNodes.Find(Handle)){
|
||||
return Node->TerrainTag;
|
||||
}
|
||||
return FGameplayTag();
|
||||
}
|
||||
|
||||
TArray<int32> UTerrainGeneratorBase::GetValidLeafNodes() const
|
||||
{
|
||||
TSet<int32> ValidNodes;
|
||||
GetPreCheckVisibleHandles(ValidNodes);
|
||||
|
||||
// 检查互斥关系, 待实现
|
||||
TSet<int32> FinalNodes;
|
||||
for (int32 Handle : ValidNodes){
|
||||
if (FinalNodes.Find(Handle)) continue;
|
||||
FinalNodes.Add(Handle);
|
||||
}
|
||||
|
||||
// 只保留叶子节点
|
||||
TArray<int32> LeafNodes;
|
||||
for (int32 Handle : FinalNodes)
|
||||
{
|
||||
const FTerrainNodeInfo* Node = TerrainNodes.Find(Handle);
|
||||
if (Node && Node->bIsLeafNode)
|
||||
{
|
||||
LeafNodes.Add(Handle);
|
||||
}
|
||||
}
|
||||
|
||||
return LeafNodes;
|
||||
}
|
||||
|
||||
|
||||
void UTerrainGeneratorBase::GetPreCheckVisibleHandles(TSet<int32>& VisibleHandles)const{
|
||||
bool bShouldAppear;
|
||||
TArray<bool> AppearanceCheckResult;
|
||||
const int TerrainNodeCount = TerrainNodes.Num();
|
||||
AppearanceCheckResult.Init(true, TerrainNodeCount);
|
||||
|
||||
for (const auto& Pair : TerrainNodes) {
|
||||
const int Handle = Pair.Key;
|
||||
const FTerrainNodeInfo* Node = &Pair.Value;
|
||||
|
||||
if (AppearanceCheckResult[Handle]) {
|
||||
bShouldAppear = FMath::FRand() < Node->Probability;
|
||||
AppearanceCheckResult[Handle] = bShouldAppear;
|
||||
}
|
||||
else {
|
||||
bShouldAppear = false;
|
||||
}
|
||||
|
||||
if (!bShouldAppear) {
|
||||
for (const int32 ChildHandle : Pair.Value.ChildHandles) {
|
||||
AppearanceCheckResult[ChildHandle] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < TerrainNodeCount; ++i) {
|
||||
if (AppearanceCheckResult[i]) {
|
||||
VisibleHandles.Add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TArray<FGameplayTag> UTerrainGeneratorBase::GenerateMap(int32 Width, int32 Height)
|
||||
{
|
||||
TArray<FGameplayTag> Map;
|
||||
Map.SetNum(Width * Height);
|
||||
|
||||
// 获取有效的叶子节点
|
||||
TArray<int32> ValidLeafNodes = GetValidLeafNodes();
|
||||
|
||||
if (ValidLeafNodes.Num() == 0)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("No valid terrain nodes to generate map!"));
|
||||
return Map;
|
||||
}
|
||||
|
||||
if (GenerateWithNodes(Map, Width, Height, ValidLeafNodes))
|
||||
{
|
||||
return Map;
|
||||
}
|
||||
else
|
||||
{
|
||||
return TArray<FGameplayTag>();
|
||||
}
|
||||
}
|
||||
|
||||
bool UTerrainGeneratorBase::GenerateWithNodes(TArray<FGameplayTag>& Map, int32 Width, int32 Height, const TArray<int32>& ValidLeafNodes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
178
Source/BusyRabbit/Private/Level/Generator/VoronoiDiagram.cpp
Normal file
178
Source/BusyRabbit/Private/Level/Generator/VoronoiDiagram.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
#include "Level/Generator/VoronoiDiagram.h"
|
||||
#include "Math/UnrealMathUtility.h"
|
||||
#include "Math/Vector2D.h"
|
||||
|
||||
UVoronoiDiagram::UVoronoiDiagram()
|
||||
{
|
||||
// 默认构造函数
|
||||
}
|
||||
|
||||
TArray<FVoronoiCell> UVoronoiDiagram::GenerateDiagram(const TArray<FVector2D>& Sites, float Width, float Height)
|
||||
{
|
||||
TArray<FVoronoiCell> Cells;
|
||||
|
||||
// 参数验证
|
||||
if (Sites.Num() == 0 || Width <= 0 || Height <= 0)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Invalid parameters for GenerateDiagram"));
|
||||
return Cells;
|
||||
}
|
||||
|
||||
// 为每个生成点创建泰森多边形单元
|
||||
for (int32 i = 0; i < Sites.Num(); i++)
|
||||
{
|
||||
FVoronoiCell Cell;
|
||||
Cell.Site = Sites[i];
|
||||
|
||||
// 计算边界框的四个角点(作为初始多边形)
|
||||
TArray<FVector2D> Bounds = {
|
||||
FVector2D(0, 0),
|
||||
FVector2D(Width, 0),
|
||||
FVector2D(Width, Height),
|
||||
FVector2D(0, Height)
|
||||
};
|
||||
|
||||
// 简化的泰森多边形生成算法
|
||||
// 注意:这是一个简化实现,完整的泰森多边形算法更复杂
|
||||
TArray<FVector2D> Polygon = Bounds;
|
||||
|
||||
// 对每个其他生成点进行裁剪(使用垂直平分线)
|
||||
for (int32 j = 0; j < Sites.Num(); j++)
|
||||
{
|
||||
if (i == j) continue;
|
||||
|
||||
FVector2D Midpoint, Direction;
|
||||
if (CalculatePerpendicularBisector(Sites[i], Sites[j], Midpoint, Direction))
|
||||
{
|
||||
// 这里简化处理,实际泰森多边形算法需要更复杂的几何计算
|
||||
// 使用中点作为参考点来调整多边形边界
|
||||
}
|
||||
}
|
||||
|
||||
// 裁剪多边形到边界范围内
|
||||
Cell.Vertices = ClipPolygonToBounds(Polygon, Width, Height);
|
||||
Cells.Add(Cell);
|
||||
}
|
||||
|
||||
return Cells;
|
||||
}
|
||||
|
||||
int32 UVoronoiDiagram::FindCellIndex(const FVector2D& Point, const TArray<FVector2D>& Sites)
|
||||
{
|
||||
// 处理空点集的情况
|
||||
if (Sites.Num() == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32 ClosestIndex = 0;
|
||||
float MinDistance = FVector2D::Distance(Point, Sites[0]);
|
||||
|
||||
// 遍历所有生成点,找到距离最近的点
|
||||
for (int32 i = 1; i < Sites.Num(); i++)
|
||||
{
|
||||
float Distance = FVector2D::Distance(Point, Sites[i]);
|
||||
if (Distance < MinDistance)
|
||||
{
|
||||
MinDistance = Distance;
|
||||
ClosestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return ClosestIndex;
|
||||
}
|
||||
|
||||
TArray<int32> UVoronoiDiagram::ConvertToGrid(const TArray<FVector2D>& Sites, int32 Width, int32 Height, float RegionWidth, float RegionHeight)
|
||||
{
|
||||
TArray<int32> Grid;
|
||||
Grid.SetNum(Width * Height);
|
||||
|
||||
// 参数验证
|
||||
if (Sites.Num() == 0 || Width <= 0 || Height <= 0 || RegionWidth <= 0 || RegionHeight <= 0)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Invalid parameters for ConvertToGrid"));
|
||||
return Grid;
|
||||
}
|
||||
|
||||
// 计算每个网格单元格的大小
|
||||
float CellWidth = RegionWidth / Width;
|
||||
float CellHeight = RegionHeight / Height;
|
||||
|
||||
// 遍历网格中的每个单元格
|
||||
for (int32 Y = 0; Y < Height; Y++)
|
||||
{
|
||||
for (int32 X = 0; X < Width; X++)
|
||||
{
|
||||
// 计算单元格中心点坐标
|
||||
FVector2D GridPoint(
|
||||
(X + 0.5f) * CellWidth,
|
||||
(Y + 0.5f) * CellHeight
|
||||
);
|
||||
|
||||
// 查找该点所在的泰森多边形单元
|
||||
int32 CellIndex = FindCellIndex(GridPoint, Sites);
|
||||
Grid[Y * Width + X] = CellIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return Grid;
|
||||
}
|
||||
|
||||
bool UVoronoiDiagram::CalculatePerpendicularBisector(const FVector2D& A, const FVector2D& B, FVector2D& OutMidpoint, FVector2D& OutDirection)
|
||||
{
|
||||
// 计算两点中点
|
||||
OutMidpoint = (A + B) * 0.5f;
|
||||
FVector2D AB = B - A;
|
||||
|
||||
// 检查两点是否重合
|
||||
if (AB.SizeSquared() < KINDA_SMALL_NUMBER)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 计算垂直方向向量(AB向量的垂直向量)
|
||||
OutDirection = FVector2D(-AB.Y, AB.X);
|
||||
OutDirection.Normalize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UVoronoiDiagram::CalculateLineIntersection(const FVector2D& P1, const FVector2D& D1, const FVector2D& P2, const FVector2D& D2, FVector2D& OutIntersection)
|
||||
{
|
||||
// 检查两条直线是否平行(叉积接近0)
|
||||
float Cross = D1.X * D2.Y - D1.Y * D2.X;
|
||||
if (FMath::Abs(Cross) < KINDA_SMALL_NUMBER)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 计算交点参数
|
||||
FVector2D P2MinusP1 = P2 - P1;
|
||||
float T = (P2MinusP1.X * D2.Y - P2MinusP1.Y * D2.X) / Cross;
|
||||
|
||||
// 计算交点坐标
|
||||
OutIntersection = P1 + D1 * T;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UVoronoiDiagram::IsPointInBounds(const FVector2D& Point, float Width, float Height)
|
||||
{
|
||||
// 检查点是否在矩形边界内
|
||||
return Point.X >= 0 && Point.X <= Width && Point.Y >= 0 && Point.Y <= Height;
|
||||
}
|
||||
|
||||
TArray<FVector2D> UVoronoiDiagram::ClipPolygonToBounds(const TArray<FVector2D>& Polygon, float Width, float Height)
|
||||
{
|
||||
TArray<FVector2D> ClippedPolygon;
|
||||
|
||||
// 遍历多边形所有顶点,只保留在边界内的顶点
|
||||
for (const FVector2D& Vertex : Polygon)
|
||||
{
|
||||
if (IsPointInBounds(Vertex, Width, Height))
|
||||
{
|
||||
ClippedPolygon.Add(Vertex);
|
||||
}
|
||||
}
|
||||
|
||||
return ClippedPolygon;
|
||||
}
|
||||
@ -0,0 +1,175 @@
|
||||
#include "Level/Generator/VoronoiTerrainGenerator.h"
|
||||
#include "Math/UnrealMathUtility.h"
|
||||
#include "Math/RandomStream.h"
|
||||
|
||||
UVoronoiTerrainGenerator::UVoronoiTerrainGenerator()
|
||||
{
|
||||
// 初始化默认参数
|
||||
NextHandle = 0;
|
||||
VoronoiRegionCount = 50; // 默认50个泰森多边形区域
|
||||
MitchellCandidateCount = 10; // 默认每个点10个候选点
|
||||
|
||||
// 创建算法实例
|
||||
MitchellGenerator = NewObject<UMitchellBestCandidate>();
|
||||
VoronoiGenerator = NewObject<UVoronoiDiagram>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool UVoronoiTerrainGenerator::GenerateWithNodes(
|
||||
TArray<FGameplayTag>& Map, int32 Width, int32 Height,
|
||||
const TArray<int32>& ValidLeafNodes
|
||||
){
|
||||
// 使用泰森多边形算法生成地图
|
||||
GenerateWithVoronoi(Map, Width, Height, ValidLeafNodes);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void UVoronoiTerrainGenerator::SetVoronoiRegionCount(int32 Count)
|
||||
{
|
||||
// 设置泰森多边形区域数量,确保至少为1
|
||||
VoronoiRegionCount = FMath::Max(1, Count);
|
||||
}
|
||||
|
||||
void UVoronoiTerrainGenerator::SetMitchellCandidateCount(int32 Count)
|
||||
{
|
||||
// 设置米切尔候选点数,确保至少为1
|
||||
MitchellCandidateCount = FMath::Max(1, Count);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void UVoronoiTerrainGenerator::GenerateWithVoronoi(TArray<FGameplayTag>& Map, int32 Width, int32 Height,
|
||||
const TArray<int32>& ValidLeafNodes) const
|
||||
{
|
||||
// 检查是否有有效的叶子节点
|
||||
if (ValidLeafNodes.Num() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FRandomStream RandomStream(FMath::Rand());
|
||||
|
||||
// 步骤1: 计算所有有效叶子节点的总权重
|
||||
float TotalWeight = 0.0f;
|
||||
for (int32 Handle : ValidLeafNodes)
|
||||
{
|
||||
if (const FTerrainNodeInfo* Node = TerrainNodes.Find(Handle))
|
||||
{
|
||||
TotalWeight += Node->Weight;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查总权重是否有效
|
||||
if (TotalWeight <= 0.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 步骤2: 使用米切尔最佳候选算法生成均匀分布的点集
|
||||
TArray<FVector2D> Sites = MitchellGenerator->GeneratePoints(
|
||||
VoronoiRegionCount, Width, Height, MitchellCandidateCount
|
||||
);
|
||||
|
||||
// 步骤3: 将泰森多边形转换为网格表示
|
||||
TArray<int32> VoronoiGrid = UVoronoiDiagram::ConvertToGrid(Sites, Width, Height, Width, Height);
|
||||
|
||||
// 步骤4: 根据权重分配地形类型到各个泰森多边形区域
|
||||
TArray<int32> RegionToTerrain;
|
||||
RegionToTerrain.SetNum(Sites.Num());
|
||||
|
||||
// 为每个泰森多边形区域分配地形类型
|
||||
for (int32 RegionIndex = 0; RegionIndex < Sites.Num(); RegionIndex++)
|
||||
{
|
||||
float RandValue = RandomStream.FRand();
|
||||
float CurrentWeight = 0.0f;
|
||||
|
||||
// 根据权重比例分配地形
|
||||
for (int32 i = 0; i < ValidLeafNodes.Num(); i++)
|
||||
{
|
||||
if (const FTerrainNodeInfo* Node = TerrainNodes.Find(ValidLeafNodes[i]))
|
||||
{
|
||||
CurrentWeight += Node->Weight / TotalWeight;
|
||||
if (RandValue <= CurrentWeight)
|
||||
{
|
||||
RegionToTerrain[RegionIndex] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤5: 填充地图数据
|
||||
for (int32 Y = 0; Y < Height; Y++)
|
||||
{
|
||||
for (int32 X = 0; X < Width; X++)
|
||||
{
|
||||
int32 Index = Y * Width + X;
|
||||
int32 RegionIndex = VoronoiGrid[Index];
|
||||
|
||||
// 确保区域索引有效
|
||||
if (RegionIndex >= 0 && RegionIndex < RegionToTerrain.Num())
|
||||
{
|
||||
int32 TerrainIndex = RegionToTerrain[RegionIndex];
|
||||
// 确保地形索引有效
|
||||
if (TerrainIndex >= 0 && TerrainIndex < ValidLeafNodes.Num())
|
||||
{
|
||||
FGameplayTag TerrainTag = GetFinalTerrainTag(ValidLeafNodes[TerrainIndex]);
|
||||
Map[Index] = TerrainTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤6: 应用平滑处理,改善地形过渡
|
||||
for (int32 Y = 1; Y < Height - 1; Y++)
|
||||
{
|
||||
for (int32 X = 1; X < Width - 1; X++)
|
||||
{
|
||||
int32 Index = Y * Width + X;
|
||||
|
||||
// 统计周围8个邻居的地形类型
|
||||
TMap<FGameplayTag, int32> NeighborCount;
|
||||
for (int32 dy = -1; dy <= 1; dy++)
|
||||
{
|
||||
for (int32 dx = -1; dx <= 1; dx++)
|
||||
{
|
||||
if (dx == 0 && dy == 0) continue; // 跳过自身
|
||||
|
||||
int32 NX = X + dx;
|
||||
int32 NY = Y + dy;
|
||||
|
||||
// 检查邻居是否在边界内
|
||||
if (NX >= 0 && NX < Width && NY >= 0 && NY < Height)
|
||||
{
|
||||
FGameplayTag NeighborTag = Map[NY * Width + NX];
|
||||
NeighborCount.FindOrAdd(NeighborTag)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 找到出现次数最多的邻居地形
|
||||
FGameplayTag MostCommonTag;
|
||||
int32 MaxCount = 0;
|
||||
|
||||
for (const auto& Pair : NeighborCount)
|
||||
{
|
||||
if (Pair.Value > MaxCount)
|
||||
{
|
||||
MaxCount = Pair.Value;
|
||||
MostCommonTag = Pair.Key;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果周围大多数是另一种地形,则改变当前地形(平滑处理)
|
||||
if (MaxCount >= 5 && MostCommonTag != Map[Index]) // 至少5个相同邻居
|
||||
{
|
||||
Map[Index] = MostCommonTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Source/BusyRabbit/Private/Level/LevelPlayerController.cpp
Normal file
52
Source/BusyRabbit/Private/Level/LevelPlayerController.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "Level/LevelPlayerController.h"
|
||||
#include "Level/Actor/BusyPlayerRole.h"
|
||||
#include "EnhancedInput/Public/EnhancedInputSubsystems.h"
|
||||
|
||||
ALevelPlayerController::ALevelPlayerController()
|
||||
{
|
||||
}
|
||||
|
||||
void ALevelPlayerController::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
bShowMouseCursor = true;
|
||||
FInputModeGameAndUI InputMode;
|
||||
InputMode.SetHideCursorDuringCapture(false);
|
||||
InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
|
||||
SetInputMode(InputMode);
|
||||
|
||||
// 注册输入
|
||||
const auto Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer());
|
||||
if (InputMapping && Subsystem)
|
||||
{
|
||||
Subsystem->AddMappingContext(InputMapping, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool ALevelPlayerController::GetCursorPosition(FVector2D& Position) const
|
||||
{
|
||||
float CursorX = 0.f, CursorY = 0.f;
|
||||
if (GetMousePosition(CursorX, CursorY))
|
||||
{
|
||||
FVector WorldLocation, WorldDirection;
|
||||
if (DeprojectMousePositionToWorld(WorldLocation, WorldDirection))
|
||||
{
|
||||
Position.Set(WorldLocation.X, WorldLocation.Y);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ALevelPlayerController::GetCursorHitResult(TArray<AActor*>& Results) const
|
||||
{
|
||||
}
|
||||
|
||||
void ALevelPlayerController::GetControlledRole() const
|
||||
{
|
||||
}
|
||||
|
||||
void ALevelPlayerController::SwitchControlledRole(ABusyPlayerRole* Target)
|
||||
{
|
||||
this->SetViewTarget(Target);
|
||||
}
|
||||
55
Source/BusyRabbit/Private/Level/LevelPlayerState.cpp
Normal file
55
Source/BusyRabbit/Private/Level/LevelPlayerState.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include "Level/LevelPlayerState.h"
|
||||
|
||||
#include "Level/LevelPlayerController.h"
|
||||
#include "Level/Actor/BusyPlayerRole.h"
|
||||
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogLevelPlayerState);
|
||||
|
||||
|
||||
void ALevelPlayerState::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
UWorld* World = GetWorld();
|
||||
const auto PC = Cast<ALevelPlayerController>(GetPlayerController());
|
||||
|
||||
if (!World || !PC)
|
||||
{
|
||||
UE_LOG(LogLevelPlayerState, Error, TEXT("ALevelPlayerController::BeginPlay() failed!"));
|
||||
return;
|
||||
}
|
||||
|
||||
FVector2D SpawnLocation = GetSpawnLocation();
|
||||
for (auto PawnClass : RoleClasses)
|
||||
{
|
||||
FTransform SpawnTransform;
|
||||
FActorSpawnParameters Params;
|
||||
SpawnTransform.SetLocation(FVector(SpawnLocation, 50));
|
||||
if (auto *NewRole = Cast<ABusyPlayerRole>(World->SpawnActor(PawnClass, nullptr, Params)))
|
||||
{
|
||||
Roles.Add(NewRole);
|
||||
}
|
||||
}
|
||||
|
||||
if (Roles.IsValidIndex(0))
|
||||
{
|
||||
PC->SwitchControlledRole(Roles[0]);
|
||||
}
|
||||
}
|
||||
|
||||
ABusyPlayerRole* ALevelPlayerState::GetControlledRole() const
|
||||
{
|
||||
if (Roles.IsValidIndex(ControlledRoleIndex))
|
||||
{
|
||||
return Roles[ControlledRoleIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
FVector2D ALevelPlayerState::GetSpawnLocation()const
|
||||
{
|
||||
return FVector2D::ZeroVector;
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
#include "Level/Map/ClimateLayerComponent.h"
|
||||
@ -0,0 +1 @@
|
||||
#include "Level/Map/CreatureLayerComponent.h"
|
||||
@ -0,0 +1 @@
|
||||
#include "Level/Map/DecorationLayerComponent.h"
|
||||
22
Source/BusyRabbit/Private/Level/Map/GameMapActor.cpp
Normal file
22
Source/BusyRabbit/Private/Level/Map/GameMapActor.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "Level/Map/GameMapActor.h"
|
||||
|
||||
AGameMapActor::AGameMapActor()
|
||||
{
|
||||
SceneComp = CreateDefaultSubobject<USceneComponent>(TEXT("SceneComp"));
|
||||
|
||||
|
||||
TerrainLayer = CreateDefaultSubobject<UTerrainLayerComponent>(TEXT("TerrainLayer"));
|
||||
|
||||
|
||||
this->RootComponent = SceneComp;
|
||||
}
|
||||
|
||||
void AGameMapActor::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
}
|
||||
|
||||
void AGameMapActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
#include "Level/Map/LightingLayerComponent.h"
|
||||
@ -0,0 +1 @@
|
||||
#include "Level/Map/PlacementLayerComponent.h"
|
||||
264
Source/BusyRabbit/Private/Level/Map/TerrainLayerComponent.cpp
Normal file
264
Source/BusyRabbit/Private/Level/Map/TerrainLayerComponent.cpp
Normal file
@ -0,0 +1,264 @@
|
||||
#include "Level/Map/TerrainLayerComponent.h"
|
||||
#include "Level/Generator/VoronoiTerrainGenerator.h"
|
||||
#include "Paper2D/Classes/PaperTileLayer.h"
|
||||
#include "Paper2D/Classes/PaperTileMapComponent.h"
|
||||
#include "Paper2D/Classes/PaperTileSet.h"
|
||||
#include "PaperTileMap.h"
|
||||
|
||||
|
||||
/* 相邻四格地形信息映射到TileSet的索引
|
||||
* 以左上、右上、左下、右下地形块存在为1,不存在为0, 从左到右构建一个4位二进制整数
|
||||
* 假设原始TileSet中,0号索引左上、右上、右下为空, 对应二进制整数为0x0010
|
||||
* 那么TileSet的这个索引与相邻数据对应关系就是[0] -> 2
|
||||
* 也就是DefaultNeighborDataToIdxMappings[2] = 0
|
||||
*/
|
||||
const static int DefaultNeighborDataToIdxMappings[16] = {
|
||||
12, 13, 0, 3, 8, 1, 14, 5, 15, 4, 11, 2, 9, 10, 7, 6
|
||||
};
|
||||
|
||||
const static FGameplayTag SortedTerrainsWithPriority [] = {
|
||||
FGameplayTag::RequestGameplayTag("Terrain.Desert"),
|
||||
FGameplayTag::RequestGameplayTag("Terrain.Forest"),
|
||||
FGameplayTag::RequestGameplayTag("Terrain.Grassland"),
|
||||
FGameplayTag::RequestGameplayTag("Terrain.Swamp.Land"),
|
||||
FGameplayTag::RequestGameplayTag("Terrain.Swamp.Water"),
|
||||
FGameplayTag::RequestGameplayTag("Terrain.Land"),
|
||||
FGameplayTag::RequestGameplayTag("Terrain.Water.Deep"),
|
||||
FGameplayTag::RequestGameplayTag("Terrain.Water.Shallow"),
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static bool GenerateTerrain(const TArray<FGameplayTag>& InTerrains, const TArray<int32> InPriority, int32 InMapSize, TArray<FGameplayTag>& OutTerrains)
|
||||
{
|
||||
// 验证输入参数
|
||||
if (InTerrains.Num() == 0 || InTerrains.Num() != InPriority.Num() || InMapSize <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 计算总权重
|
||||
int32 TotalPriority = 0;
|
||||
for (int32 Priority : InPriority)
|
||||
{
|
||||
if (Priority < 0) return false; // 权重不能为负数
|
||||
TotalPriority += Priority;
|
||||
}
|
||||
|
||||
if (TotalPriority == 0) return false; // 总权重不能为零
|
||||
|
||||
// 准备输出数组
|
||||
OutTerrains.Empty();
|
||||
OutTerrains.SetNum(InMapSize * InMapSize);
|
||||
|
||||
// 计算每种地形的阈值范围
|
||||
TArray<float> Thresholds;
|
||||
Thresholds.SetNum(InTerrains.Num());
|
||||
|
||||
float Accumulated = 0.0f;
|
||||
for (int32 i = 0; i < InPriority.Num(); i++)
|
||||
{
|
||||
Accumulated += static_cast<float>(InPriority[i]) / TotalPriority;
|
||||
Thresholds[i] = Accumulated;
|
||||
}
|
||||
|
||||
// 设置柏林噪声参数
|
||||
float Frequency = 0.05f; // 可以调整这个值来改变地形的细节程度
|
||||
float OffsetX = FMath::RandRange(0.0f, 1000.0f); // 随机偏移以确保每次生成不同的地形
|
||||
float OffsetY = FMath::RandRange(0.0f, 1000.0f);
|
||||
|
||||
// 生成地形
|
||||
for (int32 Y = 0; Y < InMapSize; Y++)
|
||||
{
|
||||
for (int32 X = 0; X < InMapSize; X++)
|
||||
{
|
||||
// 计算柏林噪声值 (范围在-1到1之间)
|
||||
float NoiseValue = FMath::PerlinNoise2D(FVector2D((X + OffsetX) * Frequency, (Y + OffsetY) * Frequency));
|
||||
|
||||
// 将噪声值映射到0到1范围
|
||||
float NormalizedNoise = (NoiseValue + 1.0f) / 2.0f;
|
||||
|
||||
// 根据噪声值选择地形
|
||||
for (int32 i = 0; i < Thresholds.Num(); i++)
|
||||
{
|
||||
if (NormalizedNoise <= Thresholds[i] || i == Thresholds.Num() - 1)
|
||||
{
|
||||
OutTerrains[Y * InMapSize + X] = InTerrains[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static UPaperTileLayer* GetTileMapLayer(
|
||||
const FGameplayTag& InTerrainType,
|
||||
const TMap<FGameplayTag, TObjectPtr<UPaperTileMapComponent>>& TileMapMeshes
|
||||
){
|
||||
// 将给定的数据绘制到TileMapLayer上
|
||||
const TObjectPtr<UPaperTileMapComponent> *TileMapMesh = TileMapMeshes.Find(InTerrainType);
|
||||
if (!TileMapMesh)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const TObjectPtr<UPaperTileMap> TileMap = TileMapMesh->Get()->TileMap;
|
||||
if (!TileMap)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (TileMap->TileLayers.Num() == 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return TileMap->TileLayers[0];
|
||||
}
|
||||
|
||||
static UPaperTileSet* GetMapTileSet(
|
||||
const FGameplayTag& InTerrainType,
|
||||
TMap<FGameplayTag, TObjectPtr<UPaperTileSet>>& TileSetConfigs
|
||||
){
|
||||
if (const TObjectPtr<UPaperTileSet> *TileSet = TileSetConfigs.Find(InTerrainType))
|
||||
{
|
||||
return TileSet->Get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline int32 GetTileSetIndex(const bool LeftUp, const bool RightUp, const bool LeftDown, const bool RightDown)
|
||||
{
|
||||
int32 GetTileSetIndex = 0;
|
||||
GetTileSetIndex += (LeftUp ? 1 : 0) << 3;
|
||||
GetTileSetIndex += (RightUp ? 1 : 0) << 2;
|
||||
GetTileSetIndex += (LeftDown ? 1 : 0) << 1;
|
||||
GetTileSetIndex += (RightDown ? 1 : 0);
|
||||
return GetTileSetIndex;
|
||||
}
|
||||
|
||||
|
||||
|
||||
UTerrainLayerComponent::UTerrainLayerComponent()
|
||||
{
|
||||
}
|
||||
|
||||
void UTerrainLayerComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
SetupTerrainMeshes();
|
||||
|
||||
TArray<int32> Priority;
|
||||
TArray<FGameplayTag> TerrainData;
|
||||
TArray<FGameplayTag> TerrainTypes;
|
||||
TArray<bool> FilteredTerrainData;
|
||||
for (auto TileSetConfig : TileSetConfigs)
|
||||
{
|
||||
TerrainTypes.Add(TileSetConfig.Key);
|
||||
Priority.Add(1);
|
||||
}
|
||||
|
||||
GenerateTerrain(TerrainTypes, Priority, MapWidth, TerrainData);
|
||||
|
||||
for (auto TerrainType : TerrainTypes)
|
||||
{
|
||||
FilteredTerrainData.Init(false, TerrainData.Num());
|
||||
for (int32 i = 0; i < TerrainData.Num(); i++)
|
||||
{
|
||||
FilteredTerrainData[i] = (TerrainData[i] == TerrainType);
|
||||
}
|
||||
SetTerrainData(TerrainType, FilteredTerrainData);
|
||||
}
|
||||
}
|
||||
|
||||
void UTerrainLayerComponent::SetTerrainData(const FGameplayTag& InTerrainType, const TArray<bool>& TerrainData)
|
||||
{
|
||||
// 将给定的数据绘制到TileMapLayer上
|
||||
UPaperTileSet* TileSet = GetMapTileSet(InTerrainType, TileSetConfigs);
|
||||
UPaperTileLayer *TileMapLayer = GetTileMapLayer(InTerrainType, TerrainMeshes);
|
||||
if (!TileSet || !TileMapLayer)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("UTerrainLayerComponent::SetTerrainData"));
|
||||
return;
|
||||
}
|
||||
if (TerrainData.Num() != MapWidth * MapHeight)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("UTerrainLayerComponent::SetTerrainData"));
|
||||
return;
|
||||
}
|
||||
for (int32 i = 0; i < MapHeight - 1; i++)
|
||||
{
|
||||
for (int32 j = 0; j < MapWidth - 1; j++)
|
||||
{
|
||||
FPaperTileInfo TileInfo;
|
||||
const int32 CurRow = i * MapWidth;
|
||||
const int32 NextRow = (i + 1) * MapWidth;
|
||||
const int32 NeighborIndex = GetTileSetIndex(
|
||||
TerrainData[CurRow + j],
|
||||
TerrainData[CurRow + j + 1],
|
||||
TerrainData[NextRow + j],
|
||||
TerrainData[NextRow + j + 1]
|
||||
);
|
||||
TileInfo.TileSet = TileSet;
|
||||
TileInfo.PackedTileIndex = DefaultNeighborDataToIdxMappings[NeighborIndex];
|
||||
TileMapLayer->SetCell(j, i, TileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UTerrainLayerComponent::SetupTerrainMeshes()
|
||||
{
|
||||
if (TileSetConfigs.Num() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TerrainMeshes.Empty();
|
||||
USceneComponent *RootScene = GetOwner()->GetRootComponent();
|
||||
|
||||
int32 Z = 0;
|
||||
for (const FGameplayTag &TerrainType : SortedTerrainsWithPriority)
|
||||
{
|
||||
if (TileSetConfigs.Find(TerrainType) == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 创建一个新的TileMap组件,新的TileMap,新的TileLayer
|
||||
auto* NewTileMapMesh = NewObject<UPaperTileMapComponent>(this);
|
||||
NewTileMapMesh->RegisterComponent();
|
||||
|
||||
|
||||
UPaperTileMap* NewTileMap = NewObject<UPaperTileMap>(NewTileMapMesh);
|
||||
|
||||
NewTileMap->MapWidth = MapWidth;
|
||||
NewTileMap->MapHeight = MapHeight;
|
||||
NewTileMap->TileWidth = 128;
|
||||
NewTileMap->TileHeight = 128;
|
||||
|
||||
|
||||
UPaperTileLayer* NewLayer = NewObject<UPaperTileLayer>(NewTileMap);
|
||||
NewLayer->LayerName = FText::FromString("TerrainLayer");
|
||||
NewLayer->ResizeMap(NewTileMap->MapWidth, NewTileMap->MapHeight);
|
||||
NewTileMap->TileLayers.Add(NewLayer);
|
||||
|
||||
NewTileMapMesh->SetTileMap(NewTileMap);
|
||||
NewTileMapMesh->SetRelativeLocation(FVector(-(NewTileMap->TileWidth / 2), -(NewTileMap->TileHeight / 2), Z++));
|
||||
NewTileMapMesh->SetRelativeRotation(FRotator(0, 0, -90));
|
||||
NewTileMapMesh->AttachToComponent(RootScene, FAttachmentTransformRules::KeepWorldTransform);
|
||||
TerrainMeshes.Add(TerrainType, NewTileMapMesh);
|
||||
}
|
||||
}
|
||||
@ -1,34 +1,33 @@
|
||||
#include "Level/PaperTerrainMapActor.h"
|
||||
|
||||
#include "Level/TerrainGenerator.h"
|
||||
#include "Level/TerrainGeneratorBlueprintLibrary.h"
|
||||
#include "Paper2D/Classes/PaperTileMapComponent.h"
|
||||
#include "Paper2D/Classes/PaperTileSet.h"
|
||||
#include "Paper2D/Classes/PaperTileMap.h"
|
||||
#include "Paper2D/Classes/PaperTileLayer.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "UObject/ConstructorHelpers.h"
|
||||
|
||||
/* 相邻四格地形信息映射到TileSet的索引
|
||||
* 以左上、右上、左下、右下地形块存在为1,不存在为0, 从左到右构建一个4位二进制整数
|
||||
* 假设原始TileSet中,0号索引左上、右上、右下为空, 对应二进制整数为0x0010
|
||||
* 那么TileSet的这个索引与相邻数据对应关系就是[0] -> 2
|
||||
* 也就是DefaultNeighborDataToIdxMappings[2] = 0
|
||||
*/
|
||||
const static int DefaultNeighborDataToIdxMappings[16] = {
|
||||
12, 13, 0, 3, 8, 1, 14, 5, 15, 4, 11, 2, 9, 10, 7, 6
|
||||
};
|
||||
|
||||
APaperTerrainMapActor::APaperTerrainMapActor()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = false;
|
||||
|
||||
// 创建根组??
|
||||
// 创建根组件
|
||||
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
|
||||
|
||||
// 创建TileMap组件
|
||||
TileMapComponent = CreateDefaultSubobject<UPaperTileMapComponent>(TEXT("TileMapComponent"));
|
||||
TileMapComponent->SetupAttachment(RootComponent);
|
||||
|
||||
// 创建地形生成??
|
||||
TerrainGenerator = CreateDefaultSubobject<UTerrainGenerator>(TEXT("TerrainGenerator"));
|
||||
|
||||
// 默认参数设置
|
||||
bAutoGenerateOnConstruction = true;
|
||||
bAutoGenerateOnBeginPlay = false;
|
||||
DefaultTileIndex = 0;
|
||||
|
||||
// 设置默认地形映射
|
||||
SetupDefaultTerrainConfig();
|
||||
}
|
||||
|
||||
void APaperTerrainMapActor::BeginPlay()
|
||||
@ -44,7 +43,8 @@ void APaperTerrainMapActor::BeginPlay()
|
||||
void APaperTerrainMapActor::OnConstruction(const FTransform& Transform)
|
||||
{
|
||||
Super::OnConstruction(Transform);
|
||||
|
||||
// 初始化地形生成器
|
||||
InitializeGenerator();
|
||||
// 初始化TileMap
|
||||
InitializeTileMap();
|
||||
|
||||
@ -61,6 +61,11 @@ void APaperTerrainMapActor::GenerateTerrainMap()
|
||||
UE_LOG(LogTemp, Error, TEXT("TerrainGenerator is null!"));
|
||||
return;
|
||||
}
|
||||
if (!TerrainTileSetConfigs)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("TerrainTileSetConfigs is null!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成地形数据
|
||||
GeneratedTerrainData = TerrainGenerator->GenerateMap(MapWidth, MapHeight);
|
||||
@ -99,16 +104,6 @@ void APaperTerrainMapActor::ClearMap()
|
||||
UE_LOG(LogTemp, Log, TEXT("Map cleared!"));
|
||||
}
|
||||
|
||||
UTerrainGenerator* APaperTerrainMapActor::GetTerrainGenerator() const
|
||||
{
|
||||
return TerrainGenerator;
|
||||
}
|
||||
|
||||
TArray<FGameplayTag> APaperTerrainMapActor::GetGeneratedTerrainData() const
|
||||
{
|
||||
return GeneratedTerrainData;
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void APaperTerrainMapActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
@ -121,33 +116,31 @@ void APaperTerrainMapActor::PostEditChangeProperty(FPropertyChangedEvent& Proper
|
||||
// 当地图尺寸或Tile大小改变时,重新初始化TileMap
|
||||
if (PropertyName == GET_MEMBER_NAME_CHECKED(APaperTerrainMapActor, MapWidth) ||
|
||||
PropertyName == GET_MEMBER_NAME_CHECKED(APaperTerrainMapActor, MapHeight) ||
|
||||
PropertyName == GET_MEMBER_NAME_CHECKED(APaperTerrainMapActor, TileSize))
|
||||
PropertyName == GET_MEMBER_NAME_CHECKED(APaperTerrainMapActor, TileSize) ||
|
||||
PropertyName == GET_MEMBER_NAME_CHECKED(APaperTerrainMapActor, TileSetDataAsset))
|
||||
|
||||
{
|
||||
InitializeTileMap();
|
||||
|
||||
// 如果已经有生成的数据,重新应??
|
||||
if (GeneratedTerrainData.Num() > 0)
|
||||
{
|
||||
ApplyTerrainToTileMap(GeneratedTerrainData);
|
||||
}
|
||||
GenerateTerrainMap();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void APaperTerrainMapActor::InitializeTileMap()
|
||||
{
|
||||
if (!TileMapComponent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!TileMapComponent) return;
|
||||
|
||||
// 创建或获取TileMap资产
|
||||
UPaperTileMap* TileMap = TileMapComponent->TileMap;
|
||||
if (!TileMap)
|
||||
if (TileSetDataAsset)
|
||||
{
|
||||
TileMap = NewObject<UPaperTileMap>(this);
|
||||
TileMapComponent->SetTileMap(TileMap);
|
||||
TerrainTileSetConfigs = &TileSetDataAsset.Get()->TerrainTileSetConfigs;
|
||||
}
|
||||
else
|
||||
{
|
||||
TerrainTileSetConfigs = nullptr;
|
||||
}
|
||||
|
||||
// 创建TileMap
|
||||
UPaperTileMap *TileMap = NewObject<UPaperTileMap>(this);
|
||||
|
||||
// 设置TileMap参数
|
||||
TileMap->MapWidth = MapWidth;
|
||||
@ -155,134 +148,128 @@ void APaperTerrainMapActor::InitializeTileMap()
|
||||
TileMap->TileWidth = TileSize;
|
||||
TileMap->TileHeight = TileSize;
|
||||
|
||||
// 确保有足够的图层
|
||||
if (TileMap->TileLayers.Num() == 0)
|
||||
// 创建四层layer
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
UPaperTileLayer* NewLayer = NewObject<UPaperTileLayer>(TileMap);
|
||||
NewLayer->LayerName = FText::FromString("TerrainLayer");
|
||||
NewLayer->LayerName = FText::FromString(
|
||||
FString::Format(TEXT("TerrainLayer{0}"), {FString::FromInt(i)})
|
||||
);
|
||||
NewLayer->ResizeMap(MapWidth, MapHeight);
|
||||
TileMap->TileLayers.Add(NewLayer);
|
||||
}
|
||||
|
||||
// 设置组件大小
|
||||
FVector NewScale(TileSize * MapWidth / 100.0f, 1.0f, TileSize * MapHeight / 100.0f);
|
||||
TileMapComponent->SetRelativeScale3D(NewScale);
|
||||
|
||||
|
||||
TileMapComponent->SetTileMap(TileMap);
|
||||
UE_LOG(LogTemp, Log, TEXT("TileMap initialized: %dx%d, TileSize: %d"), MapWidth, MapHeight, TileSize);
|
||||
}
|
||||
|
||||
void APaperTerrainMapActor::ApplyTerrainToTileMap(const TArray<FGameplayTag>& TerrainData)
|
||||
void APaperTerrainMapActor::InitializeGenerator()
|
||||
{
|
||||
// 创建地形生成器
|
||||
if (GeneratorClass.Get())
|
||||
{
|
||||
TerrainGenerator = NewObject<UTerrainGeneratorBase>(this, GeneratorClass.Get());
|
||||
UTerrainGeneratorBlueprintLibrary::SetupExampleTerrainConfig(TerrainGenerator);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("APaperTerrainMapActor::APaperTerrainMapActor, GeneratorClass is nullptr"));
|
||||
}
|
||||
}
|
||||
|
||||
void APaperTerrainMapActor::ApplyTerrainToTileMap(const TArray<FGameplayTag>& TerrainData) const
|
||||
{
|
||||
if (!TileMapComponent || !TileMapComponent->TileMap || TerrainData.Num() != MapWidth * MapHeight)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("Cannot apply terrain data to TileMap!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (TileMapComponent->TileMap->TileLayers.Num() == 0)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("No tile layers in TileMap!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 应用地形数据到TileMap - 创建新的TileMap并设置所有单元格
|
||||
UPaperTileMap* NewTileMap = NewObject<UPaperTileMap>(this);
|
||||
NewTileMap->MapWidth = MapWidth;
|
||||
NewTileMap->MapHeight = MapHeight;
|
||||
NewTileMap->TileWidth = TileSize;
|
||||
NewTileMap->TileHeight = TileSize;
|
||||
|
||||
// 创建地形图层
|
||||
UPaperTileLayer* TerrainLayer = NewObject<UPaperTileLayer>(NewTileMap);
|
||||
TerrainLayer->LayerName = FText::FromString("TerrainLayer");
|
||||
NewTileMap->TileLayers.Add(TerrainLayer);
|
||||
|
||||
// 设置图层单元??- 使用SetCell方法
|
||||
// 首先调整图层尺寸
|
||||
TerrainLayer->ResizeMap(MapWidth, MapHeight);
|
||||
FGameplayTag TerrainTags[4];
|
||||
const int32 LimitedWidth = MapWidth - 1;
|
||||
const int32 LimitedHeight = MapHeight - 1;
|
||||
|
||||
for (int32 Y = 0; Y < MapHeight; Y++)
|
||||
for (int32 Y = 0; Y < LimitedHeight; Y++)
|
||||
{
|
||||
for (int32 X = 0; X < MapWidth; X++)
|
||||
for (int32 X = 0; X < LimitedWidth; X++)
|
||||
{
|
||||
int32 Index = Y * MapWidth + X;
|
||||
if (TerrainData.IsValidIndex(Index))
|
||||
{
|
||||
FGameplayTag TerrainTag = TerrainData[Index];
|
||||
int32 TileIndex = GetTileIndexForTerrain(TerrainTag);
|
||||
|
||||
// 创建Tile信息并使用SetCell方法
|
||||
FPaperTileInfo TileInfo;
|
||||
TileInfo.TileSet = TileSet;
|
||||
TileInfo.PackedTileIndex = TileIndex;
|
||||
TerrainLayer->SetCell(X, Y, TileInfo);
|
||||
}
|
||||
TerrainTags[0] = TerrainData[Y * MapWidth + X];
|
||||
TerrainTags[1] = TerrainData[Y * MapWidth + X + 1];
|
||||
TerrainTags[2] = TerrainData[(Y + 1) * MapWidth + X];
|
||||
TerrainTags[3] = TerrainData[(Y + 1) * MapWidth + X + 1];
|
||||
DrawTile(X, Y, TerrainTags);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置新的TileMap
|
||||
TileMapComponent->SetTileMap(NewTileMap);
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("Terrain data applied to TileMap!"));
|
||||
}
|
||||
|
||||
int32 APaperTerrainMapActor::GetTileIndexForTerrain(const FGameplayTag& TerrainTag) const
|
||||
static inline int32 GetNeighborInfo(const FGameplayTag& TargetTag, FGameplayTag TerrainTags[4])
|
||||
{
|
||||
if (!TerrainTag.IsValid())
|
||||
{
|
||||
return DefaultTileIndex;
|
||||
}
|
||||
int32 NeighborInfo = 0;
|
||||
NeighborInfo += (TargetTag == TerrainTags[0] ? 1 : 0) << 3;
|
||||
NeighborInfo += (TargetTag == TerrainTags[1] ? 1 : 0) << 2;
|
||||
NeighborInfo += (TargetTag == TerrainTags[2] ? 1 : 0) << 1;
|
||||
NeighborInfo += (TargetTag == TerrainTags[3] ? 1 : 0);
|
||||
return NeighborInfo;
|
||||
}
|
||||
|
||||
// 查找地形映射
|
||||
for (const FTerrainTileMapping& Mapping : TerrainTileMappings)
|
||||
static inline void GetSortedLayerDrawingInfo(FGameplayTag **LayerTerrainTags, int32 *LayerNeighborInfo, FGameplayTag TerrainTags[4])
|
||||
{
|
||||
// 返回0-3层每层应该画哪种地形格子,以及这个格子的哪种形状,可能需要排序
|
||||
LayerTerrainTags[0] = &TerrainTags[0];
|
||||
LayerTerrainTags[1] = &TerrainTags[1];
|
||||
LayerTerrainTags[2] = &TerrainTags[2];
|
||||
LayerTerrainTags[3] = &TerrainTags[3];
|
||||
LayerNeighborInfo[0] = GetNeighborInfo(TerrainTags[0], TerrainTags);
|
||||
LayerNeighborInfo[1] = GetNeighborInfo(TerrainTags[1], TerrainTags);
|
||||
LayerNeighborInfo[2] = GetNeighborInfo(TerrainTags[2], TerrainTags);
|
||||
LayerNeighborInfo[3] = GetNeighborInfo(TerrainTags[3], TerrainTags);
|
||||
}
|
||||
|
||||
void APaperTerrainMapActor::DrawTile(const int32 X, const int32 Y, FGameplayTag TerrainTags[4])const
|
||||
{
|
||||
int32 LayerNeighborInfo[4];
|
||||
FGameplayTag *LayerTerrainTags[4];
|
||||
UPaperTileMap* TileMap = TileMapComponent->TileMap;
|
||||
GetSortedLayerDrawingInfo(LayerTerrainTags, LayerNeighborInfo, TerrainTags);
|
||||
|
||||
for (int32 Index = 0; Index < 4; ++Index)
|
||||
{
|
||||
if (Mapping.TerrainTag == TerrainTag)
|
||||
int32 TileIndex = 0;
|
||||
FPaperTileInfo TileInfo;
|
||||
|
||||
const FTerrainTileSetConfig *TileSetConfig = TerrainTileSetConfigs->Find(*LayerTerrainTags[Index]);
|
||||
if (!TileSetConfig)
|
||||
{
|
||||
return Mapping.TileIndex;
|
||||
UE_LOG(LogTemp, Warning, TEXT("APaperTerrainMapActor::DrawTile TileSetConfig not found: %s"), *LayerTerrainTags[Index]->GetTagName().ToString());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到映射,返回默认Tile
|
||||
return DefaultTileIndex;
|
||||
}
|
||||
|
||||
void APaperTerrainMapActor::SetupDefaultTerrainConfig()
|
||||
{
|
||||
// 设置默认地形映射
|
||||
TerrainTileMappings.Empty();
|
||||
|
||||
// 森林 -> Tile 1
|
||||
FTerrainTileMapping ForestMapping;
|
||||
ForestMapping.TerrainTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Forest"));
|
||||
ForestMapping.TileIndex = 1;
|
||||
TerrainTileMappings.Add(ForestMapping);
|
||||
|
||||
// 草地 -> Tile 2
|
||||
FTerrainTileMapping GrasslandMapping;
|
||||
GrasslandMapping.TerrainTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Grassland"));
|
||||
GrasslandMapping.TileIndex = 2;
|
||||
TerrainTileMappings.Add(GrasslandMapping);
|
||||
|
||||
// 水体 -> Tile 3
|
||||
FTerrainTileMapping WaterMapping;
|
||||
WaterMapping.TerrainTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Water"));
|
||||
WaterMapping.TileIndex = 3;
|
||||
TerrainTileMappings.Add(WaterMapping);
|
||||
|
||||
// 沼泽 -> Tile 4
|
||||
FTerrainTileMapping SwampMapping;
|
||||
SwampMapping.TerrainTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Swamp"));
|
||||
SwampMapping.TileIndex = 4;
|
||||
TerrainTileMappings.Add(SwampMapping);
|
||||
|
||||
// 土地 -> Tile 5
|
||||
FTerrainTileMapping LandMapping;
|
||||
LandMapping.TerrainTag = FGameplayTag::RequestGameplayTag(TEXT("Terrain.Land"));
|
||||
LandMapping.TileIndex = 5;
|
||||
TerrainTileMappings.Add(LandMapping);
|
||||
|
||||
// 设置地形生成器的默认配置
|
||||
if (TerrainGenerator)
|
||||
{
|
||||
UTerrainGeneratorBlueprintLibrary::SetupExampleTerrainConfig(TerrainGenerator);
|
||||
const int32 NeighborInfo = LayerNeighborInfo[Index];
|
||||
if (TileSetConfig->bNeedOverrideMappings)
|
||||
{
|
||||
if (NeighborInfo < TileSetConfig->NeighborDataToIdxMappings.Num())
|
||||
{
|
||||
TileIndex = TileSetConfig->NeighborDataToIdxMappings[NeighborInfo];
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("APaperTerrainMapActor::DrawTile Neighbor Data not find: %s, %d"),
|
||||
*LayerTerrainTags[Index]->GetTagName().ToString(), NeighborInfo
|
||||
)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TileIndex = DefaultNeighborDataToIdxMappings[NeighborInfo];
|
||||
}
|
||||
UPaperTileLayer* TerrainLayer = TileMap->TileLayers[Index];
|
||||
TileInfo.TileSet = TileSetConfig->TileSet;
|
||||
TileInfo.PackedTileIndex = TileIndex;
|
||||
TerrainLayer->SetCell(X, Y, TileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,196 +7,20 @@ UTerrainGenerator::UTerrainGenerator()
|
||||
NextHandle = 0;
|
||||
}
|
||||
|
||||
int32 UTerrainGenerator::AddTerrain(const FGameplayTag& TerrainTag)
|
||||
|
||||
bool UTerrainGenerator::GenerateWithNodes(TArray<FGameplayTag>& Map, int32 Width, int32 Height, const TArray<int32>& ValidLeafNodes)
|
||||
{
|
||||
FTerrainNodeInfo NewNode;
|
||||
NewNode.TerrainTag = TerrainTag;
|
||||
NewNode.Probability = 1.0f;
|
||||
NewNode.Weight = 1.0f;
|
||||
NewNode.ParentHandle = -1;
|
||||
NewNode.bIsLeafNode = true;
|
||||
|
||||
int32 Handle = NextHandle++;
|
||||
TerrainNodes.Add(Handle, NewNode);
|
||||
|
||||
return Handle;
|
||||
}
|
||||
|
||||
void UTerrainGenerator::SetWeight(int32 Handle, float Weight)
|
||||
{
|
||||
if (FTerrainNodeInfo* Node = TerrainNodes.Find(Handle))
|
||||
{
|
||||
Node->Weight = FMath::Max(0.0f, Weight);
|
||||
}
|
||||
}
|
||||
|
||||
void UTerrainGenerator::SetProbability(int32 Handle, float Probability)
|
||||
{
|
||||
if (FTerrainNodeInfo* Node = TerrainNodes.Find(Handle))
|
||||
{
|
||||
Node->Probability = FMath::Clamp(Probability, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void UTerrainGenerator::SetExclusive(const TArray<int32>& Handles){
|
||||
if (Handles.Num() <= 1) return;
|
||||
ExclusiveGroups.Add(TSet<int32>(Handles));
|
||||
}
|
||||
|
||||
void UTerrainGenerator::BindChildTerrain(int32 ParentHandle, const TArray<int32>& ChildHandles)
|
||||
{
|
||||
if (FTerrainNodeInfo* ParentNode = TerrainNodes.Find(ParentHandle))
|
||||
{
|
||||
// 设置父节点为非叶子节点
|
||||
ParentNode->bIsLeafNode = false;
|
||||
|
||||
// 添加子节点
|
||||
ParentNode->ChildHandles = ChildHandles;
|
||||
|
||||
// 设置子节点的父节点
|
||||
for (int32 ChildHandle : ChildHandles)
|
||||
{
|
||||
if (FTerrainNodeInfo* ChildNode = TerrainNodes.Find(ChildHandle))
|
||||
{
|
||||
ChildNode->ParentHandle = ParentHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TArray<FGameplayTag> UTerrainGenerator::GenerateMap(int32 Width, int32 Height)
|
||||
{
|
||||
TArray<FGameplayTag> Map;
|
||||
Map.SetNum(Width * Height);
|
||||
// 获取有效的叶子节点
|
||||
TArray<int32> ValidLeafNodes = CalcValidLeafNodes();
|
||||
|
||||
if (ValidLeafNodes.Num() == 0)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("No valid terrain nodes to generate map!"));
|
||||
return Map;
|
||||
}
|
||||
|
||||
// 使用区域生长算法生成基础地图
|
||||
RegionGrowing(Map, Width, Height, ValidLeafNodes);
|
||||
|
||||
// 应用柏林噪声进行平滑处理
|
||||
ApplyPerlinNoiseSmoothing(Map, Width, Height);
|
||||
|
||||
return Map;
|
||||
}
|
||||
|
||||
TMap<int32, FTerrainNodeInfo> UTerrainGenerator::GetAllTerrainNodes() const
|
||||
{
|
||||
return TerrainNodes;
|
||||
}
|
||||
|
||||
void UTerrainGenerator::GetPreCheckVisibleHandles(TSet<int32>& VisibleHandles)const{
|
||||
bool bShouldAppear;
|
||||
const FTerrainNodeInfo* Node;
|
||||
int32 Handle, TerrainNodeCount;
|
||||
TArray<bool> AppearanceCheckResult;
|
||||
|
||||
TerrainNodeCount = TerrainNodes.Num();
|
||||
AppearanceCheckResult.Init(true, TerrainNodeCount);
|
||||
|
||||
for (const auto& Pair : TerrainNodes) {
|
||||
Handle = Pair.Key;
|
||||
Node = &Pair.Value;
|
||||
|
||||
if (AppearanceCheckResult[Handle]) {
|
||||
bShouldAppear = FMath::FRand() < Node->Probability;
|
||||
AppearanceCheckResult[Handle] = bShouldAppear;
|
||||
}
|
||||
else {
|
||||
bShouldAppear = false;
|
||||
}
|
||||
|
||||
if (!bShouldAppear) {
|
||||
for (auto ChildHandle : Pair.Value.ChildHandles) {
|
||||
AppearanceCheckResult[ChildHandle] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < TerrainNodeCount; ++i) {
|
||||
if (AppearanceCheckResult[i]) {
|
||||
VisibleHandles.Add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UTerrainGenerator::ShouldNodeAppear(int32 Handle) const
|
||||
{
|
||||
const FTerrainNodeInfo* Node = TerrainNodes.Find(Handle);
|
||||
if (!Node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查概率
|
||||
if (FMath::FRand() > Node->Probability)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 递归检查父节点
|
||||
if (Node->ParentHandle != -1)
|
||||
{
|
||||
return ShouldNodeAppear(Node->ParentHandle);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UTerrainGenerator::CheckExclusive(int32 Handle, const TSet<int32>& AppearingNodes) const {
|
||||
//for (const TArray<int32>& Group : ExclusiveGroups)
|
||||
//{
|
||||
// if (Group.Contains(Handle))
|
||||
// {
|
||||
// // 检查互斥组中是否有其他节点已经出现
|
||||
// for (int32 OtherHandle : Group)
|
||||
// {
|
||||
// if (OtherHandle != Handle && AppearingNodes.Contains(OtherHandle))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
return true;
|
||||
}
|
||||
|
||||
TArray<int32> UTerrainGenerator::CalcValidLeafNodes()const{
|
||||
TSet<int32> ValidNodes;
|
||||
GetPreCheckVisibleHandles(ValidNodes);
|
||||
|
||||
// 检查互斥关系
|
||||
TSet<int32> FinalNodes;
|
||||
for (int32 Handle : ValidNodes){
|
||||
if (FinalNodes.Find(Handle)) continue;
|
||||
if (CheckExclusive(Handle, ValidNodes))
|
||||
{
|
||||
FinalNodes.Add(Handle);
|
||||
}
|
||||
}
|
||||
|
||||
// 只保留叶子节点
|
||||
TArray<int32> LeafNodes;
|
||||
for (int32 Handle : FinalNodes)
|
||||
{
|
||||
const FTerrainNodeInfo* Node = TerrainNodes.Find(Handle);
|
||||
if (Node && Node->bIsLeafNode)
|
||||
{
|
||||
LeafNodes.Add(Handle);
|
||||
}
|
||||
}
|
||||
|
||||
return LeafNodes;
|
||||
}
|
||||
|
||||
void UTerrainGenerator::RegionGrowing(TArray<FGameplayTag>& Map, int32 Width, int32 Height,
|
||||
const TArray<int32>& ValidLeafNodes) const
|
||||
const TArray<int32>& ValidLeafNodes) const
|
||||
{
|
||||
if (ValidLeafNodes.Num() == 0)
|
||||
{
|
||||
@ -393,10 +217,4 @@ void UTerrainGenerator::ApplyPerlinNoiseSmoothing(TArray<FGameplayTag>& Map, int
|
||||
}
|
||||
}
|
||||
|
||||
FGameplayTag UTerrainGenerator::GetFinalTerrainTag(int32 Handle) const{
|
||||
const FTerrainNodeInfo* Node = TerrainNodes.Find(Handle);
|
||||
if (Node){
|
||||
return Node->TerrainTag;
|
||||
}
|
||||
return FGameplayTag();
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ UTerrainGenerator* UTerrainGeneratorBlueprintLibrary::CreateTerrainGenerator()
|
||||
return NewObject<UTerrainGenerator>();
|
||||
}
|
||||
|
||||
void UTerrainGeneratorBlueprintLibrary::SetupExampleTerrainConfig(UTerrainGenerator* Generator)
|
||||
void UTerrainGeneratorBlueprintLibrary::SetupExampleTerrainConfig(UTerrainGeneratorBase* Generator)
|
||||
{
|
||||
if (!Generator)
|
||||
{
|
||||
|
||||
@ -15,8 +15,11 @@ void UBusyRoleMovement::BeginPlay(){
|
||||
}
|
||||
|
||||
|
||||
void UBusyRoleMovement::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction){
|
||||
void UBusyRoleMovement::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
const AActor* Owner = GetOwner();
|
||||
if (!Owner) return;
|
||||
this->ReceiveComponentTick(DeltaTime);
|
||||
}
|
||||
|
||||
|
||||
61
Source/BusyRabbit/Public/Level/Actor/BusyPawnBase.h
Normal file
61
Source/BusyRabbit/Public/Level/Actor/BusyPawnBase.h
Normal file
@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include "LuaPawn.h"
|
||||
#include "Level/Actor/Components/BusyPawnMovement.h"
|
||||
#include "BusyPawnBase.generated.h"
|
||||
|
||||
|
||||
class USphereComponent;
|
||||
class USpineBoneFollowerComponent;
|
||||
class USpineSkeletonRendererComponent;
|
||||
class USpineSkeletonAnimationComponent;
|
||||
|
||||
|
||||
UCLASS()
|
||||
class ABusyPawnBase : public ALuaPawn, public IBusyMovable
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
ABusyPawnBase();
|
||||
|
||||
virtual void BeginPlay()override;
|
||||
|
||||
virtual void UpdateMoveDirection_Implementation(const FVector2D& InDirection) override;
|
||||
|
||||
virtual float GetSpeed_Implementation()const override;
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TObjectPtr<USceneComponent> RootScene; //场景根组件
|
||||
|
||||
/*-----------------------------碰撞相关组件-----------------------------*/
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TObjectPtr<USphereComponent> SphereComponent;
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/*----------------------------spine相关组件----------------------------*/
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TObjectPtr<USceneComponent> SpineRoot;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TObjectPtr<USpineSkeletonRendererComponent> SpineRenderComponent;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TObjectPtr<USpineSkeletonAnimationComponent> SpineAnimationComponent;
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/*-------------------------------移动组件------------------------------*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TObjectPtr<UBusyPawnMovement> MovementComponent;
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere)
|
||||
FString DefaultSkinName;
|
||||
|
||||
UPROPERTY(EditAnywhere)
|
||||
FString DefaultAnimationName;
|
||||
|
||||
};
|
||||
32
Source/BusyRabbit/Public/Level/Actor/BusyPlayerRole.h
Normal file
32
Source/BusyRabbit/Public/Level/Actor/BusyPlayerRole.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "BusyPawnBase.h"
|
||||
#include "BusyPlayerRole.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class ABusyPlayerRole : public ABusyPawnBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
ABusyPlayerRole();
|
||||
|
||||
protected:
|
||||
/*--------------------相机相关--------------------------*/
|
||||
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
TObjectPtr<class USpringArmComponent> SpringArmComponent;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
TObjectPtr<class UCameraComponent> CameraComponent;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
TObjectPtr<class UBusyRoleMovement> Movement; // 移动组件
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
TObjectPtr<class UPW_AbilitySystemComponent> RoleAbility; // 技能组件
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
TObjectPtr<class URoleAnimation> RoleAnimation;
|
||||
|
||||
};
|
||||
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "LuaActorComponent.h"
|
||||
#include "BusyPawnMovement.generated.h"
|
||||
|
||||
UINTERFACE(MinimalAPI, Blueprintable)
|
||||
class UBusyMovable : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
class IBusyMovable
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent)
|
||||
float GetSpeed() const;
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "Movement")
|
||||
void UpdateMoveDirection(const FVector2D &InDirection);
|
||||
|
||||
};
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UBusyPawnMovement : public ULuaActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UBusyPawnMovement();
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void MoveTo(const FVector2D& Target);
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
FVector2D GetMoveDirection()const;
|
||||
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)override;
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
float MoveSpeed = 400;
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
FVector2D MoveTargetLocation;
|
||||
};
|
||||
@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "MitchellBestCandidate.generated.h"
|
||||
|
||||
/**
|
||||
* @brief 米切尔最佳候选算法实现类
|
||||
*
|
||||
* 该算法用于生成高质量、均匀分布的随机点集,常用于程序化内容生成。
|
||||
* 通过为每个点生成多个候选点并选择距离现有点集最远的点,确保点集分布均匀。
|
||||
*/
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class BUSYRABBIT_API UMitchellBestCandidate : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UMitchellBestCandidate();
|
||||
|
||||
/**
|
||||
* @brief 生成米切尔最佳候选点集
|
||||
*
|
||||
* 使用米切尔最佳候选算法生成均匀分布的点集。算法为每个点生成多个候选点,
|
||||
* 然后选择距离现有点集最远的候选点,确保点集分布均匀。
|
||||
*
|
||||
* @param NumPoints 要生成的点数
|
||||
* @param Width 生成区域的宽度
|
||||
* @param Height 生成区域的高度
|
||||
* @param NumCandidates 每个点生成的候选点数(默认为10,值越大点集越均匀但性能开销越大)
|
||||
* @return 生成的均匀分布点集
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Mitchell Best Candidate")
|
||||
TArray<FVector2D> GeneratePoints(int32 NumPoints, float Width, float Height, int32 NumCandidates = 10)const;
|
||||
|
||||
/**
|
||||
* @brief 计算两点之间的欧几里得距离
|
||||
*
|
||||
* @param A 第一个点
|
||||
* @param B 第二个点
|
||||
* @return 两点之间的欧几里得距离
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Mitchell Best Candidate")
|
||||
static float Distance(const FVector2D& A, const FVector2D& B);
|
||||
|
||||
/**
|
||||
* @brief 计算点到点集的最小距离
|
||||
*
|
||||
* 计算给定点到点集中所有点的最小距离,用于评估候选点的质量。
|
||||
*
|
||||
* @param Point 要评估的点
|
||||
* @param PointSet 点集
|
||||
* @return 点到点集的最小距离
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Mitchell Best Candidate")
|
||||
static float MinDistanceToSet(const FVector2D& Point, const TArray<FVector2D>& PointSet);
|
||||
|
||||
private:
|
||||
/** 随机数生成器,用于生成随机点 */
|
||||
FRandomStream RandomStream;
|
||||
};
|
||||
@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "TerrainGeneratorBase.generated.h"
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FTerrainNodeInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FGameplayTag TerrainTag;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
float Probability;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
float Weight;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 ParentHandle;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TArray<int32> ChildHandles;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
bool bIsLeafNode;
|
||||
|
||||
FTerrainNodeInfo()
|
||||
: Probability(1.0f)
|
||||
, Weight(1.0f)
|
||||
, ParentHandle(-1)
|
||||
, bIsLeafNode(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class BUSYRABBIT_API UTerrainGeneratorBase : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UTerrainGeneratorBase();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
int32 AddTerrain(const FGameplayTag& TerrainTag);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
void SetWeight(int32 Handle, float Weight);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
void SetProbability(int32 Handle, float Probability);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
void SetExclusive(const TArray<int32>& Handles);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
void BindChildTerrain(int32 ParentHandle, const TArray<int32>& ChildHandles);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
TArray<FGameplayTag> GenerateMap(int32 Width, int32 Height);
|
||||
|
||||
public: // get
|
||||
FGameplayTag GetFinalTerrainTag(int32 Handle) const;
|
||||
|
||||
TArray<int32> GetValidLeafNodes()const;
|
||||
|
||||
void GetPreCheckVisibleHandles(TSet<int32>& VisibleHandles)const;
|
||||
|
||||
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
TMap<int32, FTerrainNodeInfo> GetAllTerrainNodes() const {return TerrainNodes;};
|
||||
|
||||
public: // 需要子类重写的函数
|
||||
|
||||
|
||||
virtual bool GenerateWithNodes(TArray<FGameplayTag>& Map, int32 Width, int32 Height, const TArray<int32>& ValidLeafNodes);
|
||||
|
||||
protected:
|
||||
int32 NextHandle;
|
||||
TArray<TSet<int32>> ExclusiveGroups;
|
||||
TMap<int32, FTerrainNodeInfo> TerrainNodes;
|
||||
|
||||
};
|
||||
139
Source/BusyRabbit/Public/Level/Generator/VoronoiDiagram.h
Normal file
139
Source/BusyRabbit/Public/Level/Generator/VoronoiDiagram.h
Normal file
@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "VoronoiDiagram.generated.h"
|
||||
|
||||
/**
|
||||
* @brief 泰森多边形单元结构
|
||||
*
|
||||
* 表示泰森多边形图中的一个单元,包含生成点、顶点和相邻单元信息。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FVoronoiCell
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** 泰森多边形单元的生成点(站点) */
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
FVector2D Site;
|
||||
|
||||
/** 泰森多边形单元的顶点列表,按顺时针或逆时针顺序排列 */
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
TArray<FVector2D> Vertices;
|
||||
|
||||
/** 相邻泰森多边形单元的索引列表 */
|
||||
UPROPERTY(BlueprintReadOnly)
|
||||
TArray<int32> NeighborCells;
|
||||
|
||||
/** 默认构造函数 */
|
||||
FVoronoiCell()
|
||||
: Site(FVector2D::ZeroVector)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 泰森多边形图生成器
|
||||
*
|
||||
* 用于生成和管理泰森多边形图(Voronoi Diagram),将平面划分为多个区域,
|
||||
* 每个区域包含距离其生成点最近的所有点。
|
||||
*/
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class BUSYRABBIT_API UVoronoiDiagram : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UVoronoiDiagram();
|
||||
|
||||
/**
|
||||
* @brief 生成完整的泰森多边形图
|
||||
*
|
||||
* 根据给定的生成点集创建泰森多边形图,将平面划分为多个区域。
|
||||
* 每个区域包含距离对应生成点最近的所有点。
|
||||
*
|
||||
* @param Sites 生成点集(站点)
|
||||
* @param Width 生成区域的宽度
|
||||
* @param Height 生成区域的高度
|
||||
* @return 生成的泰森多边形单元数组
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Voronoi Diagram")
|
||||
TArray<FVoronoiCell> GenerateDiagram(const TArray<FVector2D>& Sites, float Width, float Height);
|
||||
|
||||
/**
|
||||
* @brief 查找点所在的泰森多边形单元索引
|
||||
*
|
||||
* 通过计算点到所有生成点的距离,找到最近的生成点对应的单元索引。
|
||||
*
|
||||
* @param Point 要查询的点坐标
|
||||
* @param Sites 生成点集
|
||||
* @return 单元索引(从0开始),如果未找到返回-1
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Voronoi Diagram")
|
||||
static int32 FindCellIndex(const FVector2D& Point, const TArray<FVector2D>& Sites);
|
||||
|
||||
/**
|
||||
* @brief 将泰森多边形图转换为二维网格
|
||||
*
|
||||
* 将连续的泰森多边形图离散化为网格,每个网格单元格包含对应的生成点索引。
|
||||
*
|
||||
* @param Sites 生成点集
|
||||
* @param Width 网格的宽度(单元格数量)
|
||||
* @param Height 网格的高度(单元格数量)
|
||||
* @param RegionWidth 实际区域的宽度
|
||||
* @param RegionHeight 实际区域的高度
|
||||
* @return 网格数据数组,每个元素是对应的生成点索引
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Voronoi Diagram")
|
||||
static TArray<int32> ConvertToGrid(const TArray<FVector2D>& Sites, int32 Width, int32 Height, float RegionWidth, float RegionHeight);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 计算两个点的垂直平分线
|
||||
*
|
||||
* 计算两点连线的垂直平分线,用于泰森多边形边界的生成。
|
||||
*
|
||||
* @param A 第一个点
|
||||
* @param B 第二个点
|
||||
* @param OutMidpoint 输出的中点坐标
|
||||
* @param OutDirection 输出的垂直方向向量(单位向量)
|
||||
* @return 是否成功计算(两点不能重合)
|
||||
*/
|
||||
static bool CalculatePerpendicularBisector(const FVector2D& A, const FVector2D& B, FVector2D& OutMidpoint, FVector2D& OutDirection);
|
||||
|
||||
/**
|
||||
* @brief 计算两条直线的交点
|
||||
*
|
||||
* 计算两条直线的交点,用于确定泰森多边形顶点。
|
||||
*
|
||||
* @param P1 第一条直线的起点
|
||||
* @param D1 第一条直线的方向向量
|
||||
* @param P2 第二条直线的起点
|
||||
* @param D2 第二条直线的方向向量
|
||||
* @param OutIntersection 输出的交点坐标
|
||||
* @return 是否成功计算(直线不能平行)
|
||||
*/
|
||||
static bool CalculateLineIntersection(const FVector2D& P1, const FVector2D& D1, const FVector2D& P2, const FVector2D& D2, FVector2D& OutIntersection);
|
||||
|
||||
/**
|
||||
* @brief 检查点是否在边界框内
|
||||
*
|
||||
* @param Point 要检查的点
|
||||
* @param Width 边界框宽度
|
||||
* @param Height 边界框高度
|
||||
* @return 点是否在边界框内
|
||||
*/
|
||||
static bool IsPointInBounds(const FVector2D& Point, float Width, float Height);
|
||||
|
||||
/**
|
||||
* @brief 裁剪多边形到边界
|
||||
*
|
||||
* 移除多边形中超出边界的顶点,确保多边形在指定边界内。
|
||||
*
|
||||
* @param Polygon 要裁剪的多边形顶点列表
|
||||
* @param Width 边界宽度
|
||||
* @param Height 边界高度
|
||||
* @return 裁剪后的多边形顶点列表
|
||||
*/
|
||||
static TArray<FVector2D> ClipPolygonToBounds(const TArray<FVector2D>& Polygon, float Width, float Height);
|
||||
};
|
||||
@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Level/Generator/MitchellBestCandidate.h"
|
||||
#include "Level/Generator/VoronoiDiagram.h"
|
||||
#include "Level/Generator/TerrainGeneratorBase.h"
|
||||
#include "VoronoiTerrainGenerator.generated.h"
|
||||
|
||||
|
||||
/**
|
||||
* @brief 泰森多边形地形生成器
|
||||
*
|
||||
* 结合米切尔最佳候选算法和泰森多边形算法的高级地形生成器。
|
||||
* 使用米切尔算法生成均匀分布的点集,然后使用泰森多边形划分区域,
|
||||
* 最后根据权重分配地形类型到各个区域。
|
||||
*/
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class BUSYRABBIT_API UVoronoiTerrainGenerator : public UTerrainGeneratorBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UVoronoiTerrainGenerator();
|
||||
|
||||
|
||||
/**
|
||||
* @brief 设置泰森多边形区域数量
|
||||
*
|
||||
* 控制地图被划分的泰森多边形区域数量,影响地形的粒度。
|
||||
*
|
||||
* @param Count 区域数量(至少为1)
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Voronoi Terrain Generator")
|
||||
void SetVoronoiRegionCount(int32 Count);
|
||||
|
||||
/**
|
||||
* @brief 设置米切尔候选点数
|
||||
*
|
||||
* 控制米切尔算法中每个点生成的候选点数,影响点集的质量。
|
||||
*
|
||||
* @param Count 候选点数(至少为1)
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Voronoi Terrain Generator")
|
||||
void SetMitchellCandidateCount(int32 Count);
|
||||
|
||||
virtual bool GenerateWithNodes(TArray<FGameplayTag>& Map, int32 Width, int32 Height, const TArray<int32>& ValidLeafNodes)override;
|
||||
|
||||
private:
|
||||
/** 泰森多边形区域数量参数 */
|
||||
int32 VoronoiRegionCount;
|
||||
|
||||
/** 米切尔候选点数参数 */
|
||||
int32 MitchellCandidateCount;
|
||||
|
||||
/** 米切尔最佳候选算法实例 */
|
||||
UPROPERTY()
|
||||
UMitchellBestCandidate* MitchellGenerator;
|
||||
|
||||
/** 泰森多边形算法实例 */
|
||||
UPROPERTY()
|
||||
UVoronoiDiagram* VoronoiGenerator;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 使用泰森多边形算法生成地图
|
||||
*
|
||||
* 核心生成算法,结合米切尔最佳候选和泰森多边形技术。
|
||||
*
|
||||
* @param Map 输出的地图数组
|
||||
* @param Width 地图宽度
|
||||
* @param Height 地图高度
|
||||
* @param ValidLeafNodes 有效的叶子节点列表
|
||||
*/
|
||||
void GenerateWithVoronoi(TArray<FGameplayTag>& Map, int32 Width, int32 Height,
|
||||
const TArray<int32>& ValidLeafNodes) const;
|
||||
};
|
||||
41
Source/BusyRabbit/Public/Level/LevelPlayerController.h
Normal file
41
Source/BusyRabbit/Public/Level/LevelPlayerController.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "LuaPlayerController.h"
|
||||
#include "LevelPlayerController.generated.h"
|
||||
|
||||
class ABusyPlayerRole;
|
||||
|
||||
UCLASS()
|
||||
class ALevelPlayerController : public ALuaPlayerController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
ALevelPlayerController();
|
||||
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Controller")
|
||||
bool GetCursorPosition(FVector2D& Position) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Controller")
|
||||
void GetCursorHitResult(TArray<AActor*>& Results) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Controller")
|
||||
void GetControlledRole() const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Controller")
|
||||
void SwitchControlledRole(ABusyPlayerRole* Target);
|
||||
|
||||
|
||||
|
||||
public: // 输入相关
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Input")
|
||||
TObjectPtr<class UInputMappingContext> InputMapping;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Input")
|
||||
TObjectPtr<class UInputAction> TouchAction;
|
||||
|
||||
};
|
||||
40
Source/BusyRabbit/Public/Level/LevelPlayerState.h
Normal file
40
Source/BusyRabbit/Public/Level/LevelPlayerState.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "LuaPlayerState.h"
|
||||
#include "LevelPlayerState.generated.h"
|
||||
|
||||
class ABusyPlayerRole;
|
||||
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogLevelPlayerState, Log, All);
|
||||
|
||||
UCLASS()
|
||||
class ALevelPlayerState : public ALuaPlayerState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable)
|
||||
ABusyPlayerRole* GetControlledRole() const;
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
virtual FVector2D GetSpawnLocation()const;
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere)
|
||||
TArray<TSubclassOf<ABusyPlayerRole>> RoleClasses;
|
||||
|
||||
protected:
|
||||
UPROPERTY()
|
||||
int ControlledRoleIndex = -1;
|
||||
|
||||
|
||||
UPROPERTY()
|
||||
TArray<ABusyPlayerRole*> Roles;
|
||||
};
|
||||
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
class ClimateLayerComponent
|
||||
{
|
||||
public:
|
||||
|
||||
};
|
||||
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
class CreatureLayerComponent
|
||||
{
|
||||
public:
|
||||
|
||||
};
|
||||
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
class DecorationLayerComponent
|
||||
{
|
||||
public:
|
||||
|
||||
};
|
||||
25
Source/BusyRabbit/Public/Level/Map/GameMapActor.h
Normal file
25
Source/BusyRabbit/Public/Level/Map/GameMapActor.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "Level/Map/TerrainLayerComponent.h"
|
||||
#include "GameMapActor.generated.h"
|
||||
|
||||
UCLASS(Blueprintable, Blueprintable)
|
||||
class AGameMapActor : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
AGameMapActor();
|
||||
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditDefaultsOnly)
|
||||
TObjectPtr<UTerrainLayerComponent> TerrainLayer;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
|
||||
TObjectPtr<class USceneComponent> SceneComp;
|
||||
|
||||
};
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
class LightingLayerComponent
|
||||
{
|
||||
public:
|
||||
|
||||
};
|
||||
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
class PlacementLayerComponent
|
||||
{
|
||||
public:
|
||||
|
||||
};
|
||||
66
Source/BusyRabbit/Public/Level/Map/TerrainLayerComponent.h
Normal file
66
Source/BusyRabbit/Public/Level/Map/TerrainLayerComponent.h
Normal file
@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "Paper2D/Classes/PaperTileMapComponent.h"
|
||||
#include "TerrainLayerComponent.generated.h"
|
||||
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FTerrainTileSetConfig{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, DisplayName="该地形对应的TileSet")
|
||||
TObjectPtr<class UPaperTileSet> TileSet;
|
||||
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, DisplayName="是否需要重载周围数据信息到TileSet的索引映射")
|
||||
bool bNeedOverrideMappings = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, DisplayName="周围数据信息到TileSet索引的映射", meta=(EditCondition="bNeedOverrideMappings", EditConditionHides))
|
||||
TArray<int32> NeighborDataToIdxMappings;
|
||||
};
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UTerrainDoubleGridDataAsset: public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, DisplayName="瓦片设置")
|
||||
TMap<FGameplayTag, FTerrainTileSetConfig> TerrainTileSetConfigs;
|
||||
};
|
||||
|
||||
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class UTerrainLayerComponent:public UActorComponent
|
||||
{
|
||||
GENERATED_BODY(TerrainLayerComponent)
|
||||
|
||||
public:
|
||||
UTerrainLayerComponent();
|
||||
|
||||
public:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
void SetTerrainData(const FGameplayTag& InTerrainType, const TArray<bool> &TerrainData);
|
||||
|
||||
|
||||
protected:
|
||||
void SetupTerrainMeshes();
|
||||
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
int32 MapWidth = 32;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
int32 MapHeight = 32;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TMap<FGameplayTag, TObjectPtr<UPaperTileSet>> TileSetConfigs;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly)
|
||||
TMap<FGameplayTag, TObjectPtr<UPaperTileMapComponent>> TerrainMeshes;
|
||||
};
|
||||
@ -3,27 +3,13 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Generator/TerrainGeneratorBase.h"
|
||||
#include "Map/TerrainLayerComponent.h"
|
||||
#include "PaperTerrainMapActor.generated.h"
|
||||
|
||||
// 地形到Tile的映射结<E5B084><E7BB93>?
|
||||
USTRUCT(BlueprintType)
|
||||
struct FTerrainTileMapping
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// 地形标签
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FGameplayTag TerrainTag;
|
||||
|
||||
// 对应的TileSet中的Tile索引
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 TileIndex;
|
||||
|
||||
FTerrainTileMapping()
|
||||
: TileIndex(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class BUSYRABBIT_API APaperTerrainMapActor : public AActor
|
||||
@ -38,7 +24,7 @@ protected:
|
||||
virtual void OnConstruction(const FTransform& Transform) override;
|
||||
|
||||
public:
|
||||
// 生成地图(可在蓝图中调用<EFBFBD><EFBFBD>?
|
||||
// 生成地图(可在蓝图中调用)
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Map")
|
||||
void GenerateTerrainMap();
|
||||
|
||||
@ -46,36 +32,31 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Map")
|
||||
void ClearMap();
|
||||
|
||||
// 获取地形生成器实<E599A8><E5AE9E>?
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Map")
|
||||
class UTerrainGenerator* GetTerrainGenerator() const;
|
||||
|
||||
// 获取生成的地形数<E5BDA2><E695B0>?
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Map")
|
||||
TArray<FGameplayTag> GetGeneratedTerrainData() const;
|
||||
|
||||
#if WITH_EDITOR
|
||||
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Paper2D瓦片地图组件
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
|
||||
class UPaperTileMapComponent* TileMapComponent;
|
||||
|
||||
// 地形生成器实<EFBFBD><EFBFBD>?
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Terrain")
|
||||
class UTerrainGenerator* TerrainGenerator;
|
||||
protected: // 生成器相关
|
||||
// 地形生成器类
|
||||
|
||||
// 生成的地形数<E5BDA2><E695B0>?
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Terrain")
|
||||
UPROPERTY(EditAnywhere, Category = "Terrain Map")
|
||||
TSubclassOf<class UTerrainGeneratorBase> GeneratorClass;
|
||||
|
||||
// 地形生成器实例
|
||||
UPROPERTY()
|
||||
TObjectPtr<class UTerrainGeneratorBase> TerrainGenerator;
|
||||
|
||||
// 生成的地形数据
|
||||
TArray<FGameplayTag> GeneratedTerrainData;
|
||||
|
||||
// 地图宽度(格子数<E5AD90><E695B0>?
|
||||
|
||||
protected: // 地图配置相关
|
||||
// 地图宽度
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings", meta = (ClampMin = "1", ClampMax = "1024"))
|
||||
int32 MapWidth = 16;
|
||||
|
||||
// 地图高度(格子数<EFBFBD><EFBFBD>?
|
||||
// 地图高度
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings", meta = (ClampMin = "1", ClampMax = "1024"))
|
||||
int32 MapHeight = 16;
|
||||
|
||||
@ -83,35 +64,34 @@ protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings", meta = (ClampMin = "1"))
|
||||
int32 TileSize = 16;
|
||||
|
||||
protected: // 瓦片相关
|
||||
TMap<FGameplayTag, FTerrainTileSetConfig> *TerrainTileSetConfigs;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings", DisplayName="瓦片设置")
|
||||
TObjectPtr<UTerrainDoubleGridDataAsset> TileSetDataAsset;
|
||||
|
||||
|
||||
protected:
|
||||
// Paper2D瓦片地图组件
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TObjectPtr<class UPaperTileMapComponent> TileMapComponent;
|
||||
|
||||
// 是否在构造时自动生成地图
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings")
|
||||
bool bAutoGenerateOnConstruction;
|
||||
bool bAutoGenerateOnConstruction = true;
|
||||
|
||||
// 是否在开始时自动生成地图
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings")
|
||||
bool bAutoGenerateOnBeginPlay;
|
||||
|
||||
// 地形到Tile的映射表
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Terrain Mapping")
|
||||
TArray<FTerrainTileMapping> TerrainTileMappings;
|
||||
|
||||
// 默认Tile索引(用于未映射的地形)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Terrain Mapping", meta = (ClampMin = "0"))
|
||||
int32 DefaultTileIndex;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = Sprite)
|
||||
TObjectPtr<class UPaperTileSet> TileSet;
|
||||
|
||||
bool bAutoGenerateOnBeginPlay = true;
|
||||
private:
|
||||
// 初始化TileMap组件
|
||||
void InitializeTileMap();
|
||||
|
||||
void InitializeGenerator();
|
||||
|
||||
// 应用地形数据到TileMap
|
||||
void ApplyTerrainToTileMap(const TArray<FGameplayTag>& TerrainData);
|
||||
void ApplyTerrainToTileMap(const TArray<FGameplayTag>& TerrainData)const;
|
||||
|
||||
// 根据地形标签获取Tile索引
|
||||
int32 GetTileIndexForTerrain(const FGameplayTag& TerrainTag) const;
|
||||
void DrawTile(const int32 X, const int32 Y, FGameplayTag TerrainTags[4])const;
|
||||
|
||||
// 创建默认地形配置
|
||||
void SetupDefaultTerrainConfig();
|
||||
};
|
||||
|
||||
@ -1,91 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Generator/TerrainGeneratorBase.h"
|
||||
#include "TerrainGenerator.generated.h"
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FTerrainNodeInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FGameplayTag TerrainTag;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
float Probability;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
float Weight;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
int32 ParentHandle;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
TArray<int32> ChildHandles;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
bool bIsLeafNode;
|
||||
|
||||
FTerrainNodeInfo()
|
||||
: Probability(1.0f)
|
||||
, Weight(1.0f)
|
||||
, ParentHandle(-1)
|
||||
, bIsLeafNode(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
class BUSYRABBIT_API UTerrainGenerator : public UObject
|
||||
class BUSYRABBIT_API UTerrainGenerator : public UTerrainGeneratorBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UTerrainGenerator();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
int32 AddTerrain(const FGameplayTag& TerrainTag);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
void SetWeight(int32 Handle, float Weight);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
void SetProbability(int32 Handle, float Probability);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
void SetExclusive(const TArray<int32>& Handles);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
void BindChildTerrain(int32 ParentHandle, const TArray<int32>& ChildHandles);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
TArray<FGameplayTag> GenerateMap(int32 Width = 256, int32 Height = 256);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
TMap<int32, FTerrainNodeInfo> GetAllTerrainNodes() const;
|
||||
virtual bool GenerateWithNodes(
|
||||
TArray<FGameplayTag>& Map, int32 Width, int32 Height,
|
||||
const TArray<int32>& ValidLeafNodes
|
||||
)override;
|
||||
|
||||
private:
|
||||
TMap<int32, FTerrainNodeInfo> TerrainNodes;
|
||||
|
||||
TArray<TSet<int32>> ExclusiveGroups;
|
||||
|
||||
int32 NextHandle;
|
||||
|
||||
void GetPreCheckVisibleHandles(TSet<int32>& VisibleHandles)const;
|
||||
|
||||
bool ShouldNodeAppear(int32 Handle) const;
|
||||
|
||||
bool CheckExclusive(int32 Handle, const TSet<int32>& AppearingNodes) const;
|
||||
|
||||
TArray<int32> CalcValidLeafNodes()const;
|
||||
|
||||
void RegionGrowing(TArray<FGameplayTag>& Map, int32 Width, int32 Height,
|
||||
const TArray<int32>& ValidLeafNodes) const;
|
||||
|
||||
void ApplyPerlinNoiseSmoothing(TArray<FGameplayTag>& Map, int32 Width, int32 Height) const;
|
||||
|
||||
FGameplayTag GetFinalTerrainTag(int32 Handle) const;
|
||||
};
|
||||
|
||||
@ -5,22 +5,22 @@
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "TerrainGeneratorBlueprintLibrary.generated.h"
|
||||
|
||||
// 蓝图函数库,方便在蓝图中使用地形生成<E7949F><EFBFBD>?
|
||||
// 蓝图函数库,方便在蓝图中使用地形生成<E7949F>?
|
||||
UCLASS()
|
||||
class BUSYRABBIT_API UTerrainGeneratorBlueprintLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// 创建地形生成器实<E599A8><EFBFBD>?
|
||||
// 创建地形生成器实<E599A8>?
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
static class UTerrainGenerator* CreateTerrainGenerator();
|
||||
|
||||
// 快速设置示例地形配<E5BDA2><EFBFBD>?
|
||||
// 快速设置示例地形配<E5BDA2>?
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
static void SetupExampleTerrainConfig(class UTerrainGenerator* Generator);
|
||||
static void SetupExampleTerrainConfig(class UTerrainGeneratorBase* UTerrainGenerator);
|
||||
|
||||
// 生成地图并返回一维数组(蓝图不支持嵌套TArray<61><EFBFBD>?
|
||||
// 生成地图并返回一维数组(蓝图不支持嵌套TArray<61>?
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
static TArray<FGameplayTag> GenerateMap(
|
||||
class UTerrainGenerator* Generator,
|
||||
@ -36,11 +36,11 @@ public:
|
||||
int32& Height
|
||||
);
|
||||
|
||||
// 获取地形标签的显示名<E7A4BA><EFBFBD>?
|
||||
// 获取地形标签的显示名<E7A4BA>?
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
static FString GetTerrainDisplayName(const FGameplayTag& TerrainTag);
|
||||
|
||||
// 检查地形标签是否有<E590A6><EFBFBD>?
|
||||
// 检查地形标签是否有<E590A6>?
|
||||
UFUNCTION(BlueprintCallable, Category = "Terrain Generator")
|
||||
static bool IsValidTerrainTag(const FGameplayTag& TerrainTag);
|
||||
};
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
@ -11,9 +9,9 @@
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class ERoleMoveDirection: uint8 {
|
||||
Move_Right, // <20><><EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD>
|
||||
Move_Left, // <20><><EFBFBD><EFBFBD><EFBFBD>ƶ<EFBFBD>
|
||||
Move_All_Cnt // ö<><C3B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ
|
||||
Move_Right,
|
||||
Move_Left,
|
||||
Move_All_Cnt
|
||||
};
|
||||
|
||||
/**
|
||||
@ -21,12 +19,14 @@ enum class ERoleMoveDirection: uint8 {
|
||||
*/
|
||||
UCLASS()
|
||||
class BUSYRABBIT_API UBusyRoleMovement : public ULuaActorComponent
|
||||
//class BUSYRABBIT_API UBusyRoleMovement : public UObject, public ILuaOverriderInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UBusyRoleMovement();
|
||||
|
||||
public:
|
||||
|
||||
public:
|
||||
virtual void BeginPlay()override;
|
||||
|
||||
@ -37,4 +37,7 @@ public:
|
||||
void ReceiveComponentBeginPlay();
|
||||
|
||||
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)override;
|
||||
|
||||
protected:
|
||||
FVector MovementDirection;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user