2022-03-11【UE】塔防制作规划

2022-03-11  本文已影响0人  持刀的要迟到了

地图生成

在游戏中,不同场景可能需要的美术资源不同,但是生成流程相同。
因此需要给每个场景配置不同的美术资源(或共用同一套)

生成流程:
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);
        //  }
        // }
    }
}


上一篇下一篇

猜你喜欢

热点阅读