地图生成
在游戏中,不同场景可能需要的美术资源不同,但是生成流程相同。
因此需要给每个场景配置不同的美术资源(或共用同一套)
生成流程:
1.在场景配置中,配置本局游戏地图的最小单位;目前只有两种类型,可行走区域(Passway)和不可行走区域(Rock)
2.与配置无关的固定情况3x3编组数据结构生成,共16种情况
3.生成mxn大小的舞台。
要求:需要填充mxn大小,并且周围一圈不能再向外延申。
为了能完整的填充,就要求,首先需要在四条边,各自选择一个【极点】(有可能两个【极点】重合)。然后完成全连通。最后在连通好的地图上随机选择一个3x3地图块作为起点。
连通算法:回溯法。也可以理解为悔棋算法。
先随机选择一个【极点】作为起始点,给它选择一种【可能性】。然后它向周围的通路流动,每次都给新的坐标块一种【可能性】。如果到最后,四个【极点】没有全部连通,那么回退上一个坐标块的【可能性】,给它一个新的【可能性】。当它尝试了所有【可能性】依然无法全部连通,那么回退到上上个坐标块进行尝试。一旦完成连通,地图完成。
6.18 地图摆放
之前做的随机地图能生成了,我会写一些ue的c++代码了。但是随机出来的东西不好控制,还需要有拖拽吸附功能。
现在的想法:
1.摆放吸附,生成地基
2.运行时摆放吸附,生成炮塔
3.生成ai,沿途行走
代码
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CubeBase.generated.h"
class UStaticMeshComponent;
class UStaticMesh;
USTRUCT(BlueprintType)
struct FTile
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, Category = "模型")
FName name;
UPROPERTY(EditAnywhere, Category = "模型")
int i;
UPROPERTY(EditAnywhere, Category = "模型")
int j;
UPROPERTY(EditAnywhere, Category = "模型")
bool isOpen; // 默认是地基,无法走动
UPROPERTY(EditAnywhere, Category = "模型")
bool isSetted; // 是否放置了
UPROPERTY(EditAnywhere, Category = "模型")
UStaticMeshComponent* floor;
UPROPERTY(EditAnywhere, Category = "模型")
UStaticMeshComponent* block;
void ApplyFloorMesh(UStaticMesh* mesh);
void ApplyBlockMesh(UStaticMesh* mesh);
};
// UENUM(BlueprintType)
// enum class ECubeBaseDirection : uint8
// {
// None = 0,
// n = 1,
// s = 2,
// w = 4,
// e = 8,
//
// ne = n | e,
// ns = n | s,
// nw = n | w,
// es = e | s,
// ew = e | w,
// sw = s | w,
//
// nes = n | e | s,
// nwe = n | e | w,
// nsw = n | s | w,
// esw = e | s | w,
//
// nswe = n | s | w | e,
// };
// inline ECubeBaseDirection operator |(ECubeBaseDirection a, ECubeBaseDirection b)
// {
// return static_cast<ECubeBaseDirection>(static_cast<uint8>(a) | static_cast<uint8>(b));
// }
// inline ECubeBaseDirection operator &(ECubeBaseDirection a, ECubeBaseDirection b)
// {
// return static_cast<ECubeBaseDirection>(static_cast<uint8>(a) & static_cast<uint8>(b));
// }
// inline ECubeBaseDirection& operator |=(ECubeBaseDirection& a, ECubeBaseDirection b)
// {
// return a = a | b;
// }
UCLASS()
class TDXJ_API ACubeBase : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ACubeBase();
// UPROPERTY(EditAnywhere, Category = "设置")
// ECubeBaseDirection openDirection = ECubeBaseDirection::nswe;
UPROPERTY(EditAnywhere, Category = "门")
bool upOpen = false;
UPROPERTY(EditAnywhere, Category = "门")
bool downOpen = false;
UPROPERTY(EditAnywhere, Category = "门")
bool leftOpen = false;
UPROPERTY(EditAnywhere, Category = "门")
bool rightOpen = false;
UPROPERTY(EditAnywhere, Category = "设置")
int cubeCount = 7;
UPROPERTY(EditAnywhere, Category = "设置")
int cubeScale = 100;
UPROPERTY(EditAnywhere, Category = "设置")
int rngID;
UPROPERTY(EditAnywhere, Category = "设置")
bool useRnd = false;
UPROPERTY(EditAnywhere, Category = "设置")
bool flowClockSequence = true;
// The mesh to use to show the direction of the checkpoint in the Editor.
UPROPERTY(EditAnywhere, Category = "模型")
UStaticMesh* Floor = nullptr;
// The mesh to use to show the direction of the checkpoint in the Editor.
UPROPERTY(EditAnywhere, Category = "模型")
UStaticMesh* Block = nullptr;
// UPROPERTY(EditAnywhere, Category = "模型")
// UStaticMesh* Center = nullptr;
// UPROPERTY(EditAnywhere, Category = "模型")
// TArray<UStaticMeshComponent*> meshes;
UPROPERTY(EditAnywhere, Category = "模型")
TArray<FTile> tiles;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
const FTile& FindTile(int i, int j);
// 顺时针获得边缘FTile
TArray<FTile> GetSurroundTiles(int _xmin, int _xmax, int _ymin, int _ymax);
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
public:
// Load a map after showing the loading screen.
UFUNCTION(BlueprintCallable, Category = "重置模型")
// UFUNCTION(CallInEditor, Category = "重置模型")
virtual void ResetModels();
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "CubeBase.h"
#include "TDXJ.h"
void FTile::ApplyFloorMesh(UStaticMesh* mesh)
{
floor->SetStaticMesh(mesh);
// if (mesh == nullptr)
// {
// floor->SetStaticMesh(nullptr);
// }
// else
// {
// floor->SetStaticMesh(mesh);
// }
}
void FTile::ApplyBlockMesh(UStaticMesh* mesh)
{
block->SetStaticMesh(mesh);
if (mesh == nullptr)
{
isOpen = true;
// if(block == NULL)
// {
// UE_LOG(TDLog, Error, TEXT("错误1::%d,%d"), i, j);
// }
// else
// {
// block->SetStaticMesh(nullptr);
// }
}
else
{
isOpen = false;
// if(block == NULL)
// {
// UE_LOG(TDLog, Error, TEXT("错误2::%d,%d"), i, j);
// }
// else
// {
// block->SetStaticMesh(blockMesh);
// }
}
}
// Sets default values
ACubeBase::ACubeBase()
{
// cubeCount必须是:5,7,9,11...
// //查找随机地基资源
// ConstructorHelpers::FObjectFinder<UStaticMesh> floorAsset(TEXT("/Game/Mesh/tile.tile"));
// //查找随机地板资源
// ConstructorHelpers::FObjectFinder<UStaticMesh> blockAsset(TEXT("/Game/Mesh/tile.tile"));
//
// if (floorAsset.Succeeded())
// {
// Floor = floorAsset.Object;
// }
// if (blockAsset.Succeeded())
// {
// Block = blockAsset.Object;
// }
// Floor = nullptr;
// Block = nullptr;
int middle = cubeCount / 2;
FVector center(middle * cubeScale, middle * cubeScale, 0);
// 创建Root:MiddleCube
auto root = CreateDefaultSubobject<USceneComponent>("Root");
RootComponent = root;
// if (floorAsset.Succeeded() && blockAsset.Succeeded())
{
for (int i = 0; i < cubeCount; ++i)
{
for (int j = 0; j < cubeCount; ++j)
{
FTile* tile = new FTile();
tile->i = i;
tile->j = j;
// 地基设置
auto Output = FName(FString::Printf(TEXT("Floor%d_%d"), i, j));
auto floorComp = CreateDefaultSubobject<UStaticMeshComponent>(Output);
floorComp->SetupAttachment(RootComponent);
floorComp->SetRelativeLocation(FVector(i * cubeScale, j * cubeScale, 0) - center);
tile->floor = floorComp;
tile->ApplyFloorMesh(nullptr);
// 地板设置
auto OutputBlock = FName(FString::Printf(TEXT("Block%d_%d"), i, j));
auto blockComp = CreateDefaultSubobject<UStaticMeshComponent>(OutputBlock);
blockComp->SetupAttachment(RootComponent);
blockComp->SetRelativeLocation(FVector(i * cubeScale, j * cubeScale, 20) - center);
tile->block = blockComp;
tile->ApplyBlockMesh(nullptr);
tile->name = OutputBlock;
tiles.Emplace(*tile);
}
}
}
// FMath::RandInit(rngID);
// float rng = FMath::FRand();
// Wall = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CubeMesh"));
// Wall->SetRelativeScale3D(FVector(1.0f, 1.0f, 1.0f));
// Wall->SetHiddenInGame(false);
// SetRootComponent(Wall);
}
// Called when the game starts or when spawned
void ACubeBase::BeginPlay()
{
Super::BeginPlay();
}
const FTile& ACubeBase::FindTile(int i, int j)
{
FTile* FoundEntry = tiles.FindByPredicate([i,j](const FTile& InItem)
{
return InItem.i == i && InItem.j == j;
});
return *FoundEntry;
}
// Called every frame
void ACubeBase::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
/**
* 顺时针,顺序获取边缘一圈的所有FTile
*/
TArray<FTile> ACubeBase::GetSurroundTiles(int _xmin, int _xmax, int _ymin, int _ymax)
{
TArray<FTile> rtn;
// 上边一行
for (int i = _xmin; i < _xmax; ++i)
{
auto FoundEntry = FindTile(i, _ymax);
rtn.Emplace(FoundEntry);
}
// 右边一列
for (int j = _ymax; j > _ymin; --j)
{
auto FoundEntry = FindTile(_xmax, j);
rtn.Emplace(FoundEntry);
}
// 下边一行
for (int i = _xmax; i > _xmin; --i)
{
auto FoundEntry = FindTile(i, _ymin);
rtn.Emplace(FoundEntry);
}
// 左边一列
for (int j = _ymin; j < _ymax; ++j)
{
auto FoundEntry = FindTile(_xmin, j);
rtn.Emplace(FoundEntry);
}
return rtn;
}
void ACubeBase::ResetModels()
{
if (cubeCount < 3) return;
// UE_LOG(TDLog, Error, TEXT("当前门:%d"), static_cast<uint8>(openDirection));
// UE_LOG(TDLog, Error, TEXT("找到上门:%s"), *((FoundEntry->name).ToString()));
if (useRnd)
{
rngID = FDateTime::Now().GetMillisecond();
UE_LOG(TDLog, Error, TEXT("当前Rnd:%d"), rngID);
}
FMath::RandInit(rngID);
// 挖城池
// 中心必须有一个方块,基于这个中心进行周边扩展
int centerIJ = cubeCount / 2;
int explodeLen = centerIJ - 2;
int explodeUp = FMath::RandRange(0, explodeLen);
int explodeDown = FMath::RandRange(0, explodeLen);
int explodeLeft = FMath::RandRange(0, explodeLen);
int explodeRight = FMath::RandRange(0, explodeLen);
int xmin = centerIJ - explodeLeft - 1;
int xmax = centerIJ + explodeRight + 1;
int ymin = centerIJ - explodeDown - 1;
int ymax = centerIJ + explodeUp + 1;
// 关闭所有格子
for (auto& tile : tiles)
{
tile.ApplyFloorMesh(Floor);
tile.ApplyBlockMesh(Block);
}
int updoorI = cubeCount / 2;
int updoorJ = cubeCount - 1;
int updoorEndJ = ymax;
int downdoorI = cubeCount / 2;
int downdoorJ = 0;
int downdoorEndJ = ymin;
int leftdoorI = 0;
int leftdoorEndI = xmin;
int leftdoorJ = cubeCount / 2;
int rightdoorI = cubeCount - 1;
int rightdoorEndI = ymax;
int rightdoorJ = cubeCount / 2;
TArray<FTile> riverPoint; // 护城河连接点
// 开上门
if (upOpen)
{
for (int j = updoorEndJ; j <= updoorJ; ++j)
{
auto FoundEntry = FindTile(updoorI, j);
FoundEntry.ApplyBlockMesh(nullptr);
if (j == updoorEndJ)
{
riverPoint.Emplace(FoundEntry);
}
}
}
// 开右门
if (rightOpen)
{
// 从右往左,流淌到护城河旁边
for (int i = rightdoorI; i >= rightdoorEndI; --i)
{
auto FoundEntry = FindTile(i, rightdoorJ);
FoundEntry.ApplyBlockMesh(nullptr);
if (i == rightdoorEndI)
{
riverPoint.Emplace(FoundEntry);
}
}
}
// 开下门
if (downOpen)
{
// 从下往上,流淌到护城河旁边
for (int j = downdoorJ; j <= downdoorEndJ; ++j)
{
auto FoundEntry = FindTile(downdoorI, j);
FoundEntry.ApplyBlockMesh(nullptr);
if (j == downdoorEndJ)
{
riverPoint.Emplace(FoundEntry);
}
}
}
// 开左门
if (leftOpen)
{
// 从左往右,流淌到护城河旁边
for (int i = leftdoorI; i <= leftdoorEndI; ++i)
{
auto FoundEntry = FindTile(i, leftdoorJ);
FoundEntry.ApplyBlockMesh(nullptr);
if (i == leftdoorEndI)
{
riverPoint.Emplace(FoundEntry);
}
}
}
if(riverPoint.Num() == 0) return; // 无门
int startID = FMath::RandRange(0, riverPoint.Num() - 1);
auto startTile = riverPoint[startID];
auto surroundTiles = GetSurroundTiles(xmin, xmax, ymin, ymax);
int startIndex = -1;
for (int32 Index = 0; Index != surroundTiles.Num(); ++Index)
{
auto item = surroundTiles[Index];
if(item.i == startTile.i && item.j == startTile.j)
{
startIndex = Index;
break;
}
}
// 顺时针流淌
// if (flowClockSequence)
{
for (int32 Index = startIndex; Index != surroundTiles.Num(); ++Index)
{
if(riverPoint.Num() == 0)
{
break;
}
auto _tile = surroundTiles[Index];
for (int32 rId = 0; rId != riverPoint.Num(); ++rId)
{
auto item = riverPoint[rId];
if(item.i == _tile.i && item.j == _tile.j)
{
riverPoint.RemoveAt(rId);
break;
}
}
if(riverPoint.Num() == 0)
{
break;
}
else
{
_tile.ApplyBlockMesh(nullptr);
}
}
for (int32 Index = 0; Index != startIndex; ++Index)
{
if(riverPoint.Num() == 0)
{
break;
}
auto _tile = surroundTiles[Index];
for (int32 rId = 0; rId != riverPoint.Num(); ++rId)
{
auto item = riverPoint[rId];
if(item.i == _tile.i && item.j == _tile.j)
{
riverPoint.RemoveAt(rId);
break;
}
}
if(riverPoint.Num() == 0)
{
break;
}
else
{
_tile.ApplyBlockMesh(nullptr);
}
}
}
// 逆时针流淌
// else
{
// bool flowedAll = false;
// for (int32 Index = startIndex; Index != surroundTiles.Num(); ++Index)
// {
// if(flowedAll) break;
// auto _tile = surroundTiles[Index];
// if(riverPoint.Contains(_tile))
// {
// riverPoint.Remove(_tile);
// if(riverPoint.Num() == 0)
// {
// flowedAll = true;
// break;
// }
// }
// else
// {
// _tile.ApplyBlockMesh(nullptr);
// }
// }
// for (int32 Index = 0; Index != startIndex; ++Index)
// {
// if(flowedAll) break;
// auto _tile = surroundTiles[Index];
// if(riverPoint.Contains(_tile))
// {
// riverPoint.Remove(_tile);
// if(riverPoint.Num() == 0)
// {
// flowedAll = true;
// break;
// }
// }
// else
// {
// _tile.ApplyBlockMesh(nullptr);
// }
// }
}
}
网友评论