美文网首页
2022-03-11【UE】塔防制作规划

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

作者: 持刀的要迟到了 | 来源:发表于2022-03-11 01:25 被阅读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);
            //  }
            // }
        }
    }
    
    
    

    相关文章

      网友评论

          本文标题:2022-03-11【UE】塔防制作规划

          本文链接:https://www.haomeiwen.com/subject/xsqcdrtx.html