Gaming Life

一日24時間、ゲームは10時間

Dungeon Template LibraryをUE4で利用してLandscapeを自在に操る

@wanotaitei さんが開発しているDungeon Template Library(以後DTL) がUE4で使えそうだったので使ってみた。

f:id:ai_gaminglife:20190518201300g:plain

github.com

画像

UE4でDTLを使えるようにする

上記リンクからDTLをcloneしておく。

C++プロジェクトとしてUE4プロジェクトの Source/ 以下に library/DTL ディレクトリを作成。そこに、DTLライブラリの include 以下をコピーする。

UE4プロジェクトを開き直し、 (プロジェクト名)Build.cs に以下を記述。

        PublicIncludePaths.Add(ModuleDirectory + "/library/DTL/");

        PrivateDependencyModuleNames.AddRange(new string[] { "Landscape", "LandscapeEditor" });

これで、DTLと、後に使うLandscape関連の関数が利用できるようになる。

Landscapeを操作する関数を実装する

BlueprintFunctionLibraryを継承したクラスを作成し、以下をコピペ。

  • DTLLandscapeControlInEditor.h
#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "DTLLandscapeControlInEditor.generated.h"

UCLASS()
class DTLSAMPLEUE4_TPS_API UDTLLandscapeControlInEditor : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()

    UFUNCTION(BlueprintCallable, Category = DTL, meta = (HidePin = "worldContextObject_", DefaultToSelf = "worldContextObject_"))
    static bool perlinNoise(const UObject* worldContextObject_, float scale);
};
  • DTLLandscapeControlInEditor.cpp
#include "DTLLandscapeControlInEditor.h"

#include "EngineUtils.h"
#include "Landscape.h"
#include "LandscapeInfo.h"
#include "LandscapeEditor/Public/LandscapeEditorUtils.h"

#include <DTL.hpp>

bool UDTLLandscapeControlInEditor::perlinNoise(const UObject* worldContextObject_, float scale)
{
    UWorld* world = worldContextObject_->GetWorld();
    for (TActorIterator<ALandscape> actorItr(world); actorItr; ++actorItr)
    {
        ALandscape* landscape = *actorItr;
        if (landscape != nullptr)
        {
            // 2: ULandscapeInfoの初期化
            ULandscapeInfo::RecreateLandscapeInfo(world, false);

            FIntRect rect = landscape->GetBoundingRect();
            int32 w = rect.Width() + 1;
            int32 h = rect.Height() + 1;

            using shape_t = uint16;
            std::vector<std::vector<shape_t>> matrix(h, std::vector<shape_t>(w, 0));
            constexpr double frequency = 6.0;
            constexpr uint8_t octaves{ 16 };
            const uint32_t seed{ static_cast<uint32_t>( rand() ) };

            const dtl::utility::PerlinNoise perlin(seed);
            double frequency_x{ w / frequency };
            double frequency_y{ h / frequency };

            for (std::size_t row{}; row < h; ++row)
                for (std::size_t col{}; col < w; ++col)
                    matrix[row][col] = static_cast<shape_t>(50.0 * perlin.octaveNoise(octaves, col / frequency_x, row / frequency_y));

            TArray<uint16> Data;
            Data.Init(0, w * h);
            for (auto x = 0; x < w; x++)
            {
                for (auto y = 0; y < h; y++)
                {
                    Data[x * h + y] = static_cast<uint16>(FMath::FloorToInt(scale * matrix[y][x]));
                }
            }

            LandscapeEditorUtils::SetHeightmapData(landscape, Data);
            return true;
        }
    }
    return false;
}

エディタ側からコンパイルし、通れば成功。

実装した関数を呼び出すActorを作成

Actorを継承したブループリントクラスを作成。画像のようなBPを組む。この際、EventPerlinNoiseCall in Editor フラグにチェックを、Scale float変数は、Instance EditableExpose on Spawn にチェックを入れる、

f:id:ai_gaminglife:20190518201112p:plain

作成したActorを 適当にレベルに配置、続いて、Landscapeを作成し、機能別サンプルから取ってきたLandscape Material を適当に割り当てて上げれば完成。

まとめ

現在エディタ側からしかLandscapeを操作出来ないが、ランタイムでLandscapeを操作できると嬉しい。また、まだ試してないが、他のダンジョン生成関数との連携は簡単にできそう。

参考サイト

unwitherer.blogspot.com