[UnrealC++]LandscapeのHeightmapをC++から変更する
今回は、図1のような、Landscapeの高さをC++コードによって変更する方法について紹介します。
個人的には、Landscapeをランタイムで生成するようなものを期待していたのですが、これは上手くいきませんでした。Editorモジュールも使っており、現状、ゲーム制作で使えるレベルではありません。それでも、Landscape関連のAPIを扱う際の取っ掛かりになるかもしれないので、方法をまとめておきます。
BlueprintFunctionLibraryで作成します。まず、.hを次のようにしました。
次に、.cppを次のようにしました。
順にコードについて説明します。
まず図3のように、Landscapeをデフォルト設定のまま作成します。
次に、エディタ上で呼び出すための適当なActorを作り、図4のような実装にします。先程作ったGenerateLandscapeを呼び出すイベントを作り、CallInEditorにチェックを入れます。
最後に、図5のように、作成したActorをレベルに配置し、ActorからCallInEditor化したイベントを呼び出します。これを実行すると、図1のような、C++コードによって高さを変更されたLandscapeを得ることができます。
この記事は次のバージョンで作成されました。
Unreal Editor(4.16.2-3514769+++UE4+Release-4.16)
Microsoft Visual Studio Community 2017 Version 15.1 (26403.7)
![]() |
図1 C++によって変更されたLandscape |
はじめに
本記事はUnreal Engine 4 Scripting with C++に記載されていた、LandscapeのHeighmapを変更する方法を参考にしています。個人的には、Landscapeをランタイムで生成するようなものを期待していたのですが、これは上手くいきませんでした。Editorモジュールも使っており、現状、ゲーム制作で使えるレベルではありません。それでも、Landscape関連のAPIを扱う際の取っ掛かりになるかもしれないので、方法をまとめておきます。
実装する
Build.csにモジュールを追加する
Landscape、LandscapeEditorモジュールをPrivateDependencyModuleNamesに追加しました。LandscapeEditorモジュールはEditorモジュールなので、Gameモジュールに追加するのは不適切なのですが、今回は簡略化のためにGameモジュールに追加しました。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Fill out your copyright notice in the Description page of Project Settings. | |
using UnrealBuildTool; | |
public class BlogTest416 : ModuleRules | |
{ | |
public BlogTest416(ReadOnlyTargetRules Target) : base(Target) | |
{ | |
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; | |
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); | |
PrivateDependencyModuleNames.AddRange(new string[] { "Landscape", "LandscapeEditor" }); | |
// Uncomment if you are using Slate UI | |
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); | |
// Uncomment if you are using online features | |
// PrivateDependencyModuleNames.Add("OnlineSubsystem"); | |
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true | |
} | |
} |
GenerateLandscape関数を作成する
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "CoreMinimal.h" | |
#include "Kismet/BlueprintFunctionLibrary.h" | |
#include "BPFL_GenerateLandscape.generated.h" | |
UCLASS() | |
class BLOGTEST416_API UBPFL_GenerateLandscape : public UBlueprintFunctionLibrary | |
{ | |
GENERATED_BODY() | |
public: | |
UFUNCTION(BlueprintCallable, Category = LandscapeTest, meta = (HidePin = "worldContextObject_", DefaultToSelf = "worldContextObject_")) | |
static bool GenerateLandscape(const UObject* worldContextObject_); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "BPFL_GenerateLandscape.h" | |
#include "EngineUtils.h" | |
#include "Landscape.h" | |
#include "LandscapeInfo.h" | |
#include "LandscapeEditorUtils.h" | |
bool UBPFL_GenerateLandscape::GenerateLandscape(const UObject* worldContextObject_) | |
{ | |
// 1: WorldからLandscapeActorを取得する | |
UWorld* world = worldContextObject_->GetWorld(); | |
for (TActorIterator<ALandscape> actorItr(world); actorItr; ++actorItr) | |
{ | |
ALandscape* landscape = *actorItr; | |
if (landscape != nullptr) | |
{ | |
// 2: ULandscapeInfoの初期化 | |
ULandscapeInfo::RecreateLandscapeInfo(world, false); | |
// 3: Landscapeの大きさを取得 | |
FIntRect rect = landscape->GetBoundingRect(); | |
int32 w = rect.Width() + 1; | |
int32 h = rect.Height() + 1; | |
// 4: Heighmapの作成 | |
TArray<uint16> Data; | |
Data.Init(0, w * h); | |
for (int32 x = 0; x < w; x++) | |
{ | |
for (int32 y = 0; y < h; y++) | |
{ | |
float offseted_x = (x - w / 2.0f) / 8.0f; | |
float offseted_y = (y - h / 2.0f) / 8.0f; | |
Data[x * h + y] = (uint16)FMath::FloorToInt(2048.0f * (FMath::Sin(FMath::Sqrt(offseted_x * offseted_x + offseted_y * offseted_y)) + 1.0f) * 0.5f); | |
} | |
} | |
// 5: Heightmapの適用 | |
LandscapeEditorUtils::SetHeightmapData(landscape, Data); | |
return true; | |
} | |
} | |
return false; | |
} |
- (11~15行目)レベルにあるLandscapeActorを取得しています。TActorIteratorによって列挙していますが、有効なものが1つ見つかれば、それだけをHeightmapの変更対象とします。特定のLandscapeを変更対象としたいときは、ActorTag等を使うべきかもしれません。
- (18行目)レベル内全てのLandscapeInfo(コンポーネントサイズやサブセクションサイズなどの情報を管理するオブジェクト)をリセットし、再生成する関数のようです。Unreal Engine 4 Scripting with C++に必要と書いてあるのですが、必要性がイマイチ分かりませんし、今回に限っては無くても動作しました。一応記述しておきました。
- (21~23行目)対象のLandscapeの大きさを取得しています。これはLandscapeのOverall Resolutionと一致します。これを用いてHeighmapのデータ長を決定します。
- (26~36行目)Heightmapを作成しています。ここでやっていることのイメージとしては、Heightmapをテクスチャと見て、各座標のピクセルにおける「高さ」の値を設定している、と考えると理解しやすいと思います。式が定数だらけで見にくいですが、やりたいことは図2のグラフのようなHeighmapを作ることです。
- (39行目)最後に、対象のLandscapeに作成したHeightmapを適用します。
![]() |
図2 作りたいグラフ:sin(sqrt(x*x+y*y)) |
CallInEditorを使って呼び出す
作ったBlueprintFunctionLibraryを実際に使ってみます。まず図3のように、Landscapeをデフォルト設定のまま作成します。
![]() |
図3 デフォルト設定でLandscapeを作成する |
次に、エディタ上で呼び出すための適当なActorを作り、図4のような実装にします。先程作ったGenerateLandscapeを呼び出すイベントを作り、CallInEditorにチェックを入れます。
![]() |
図4 GenerateLandscapeを呼び出すイベント |
最後に、図5のように、作成したActorをレベルに配置し、ActorからCallInEditor化したイベントを呼び出します。これを実行すると、図1のような、C++コードによって高さを変更されたLandscapeを得ることができます。
![]() |
図5 エディタ上でイベントを呼び出す |
おわりに
今回はLandscape関連のAPIを扱うための導入として、これを紹介しました。本来はランタイムで出来ることが望ましいのですが、実現するのはそれなりに難しそうに見えます。とはいえ、以前似たようなことを実現している有料サンプルを見かけたことがあるので、出来ない訳ではないのかなと考えています。現状はHeightmapを別の方法で用意した方が良いレベルなので、実用的ではないというのが個人的な感想です。この記事は次のバージョンで作成されました。
Unreal Editor(4.16.2-3514769+++UE4+Release-4.16)
Microsoft Visual Studio Community 2017 Version 15.1 (26403.7)
コメント
コメントを投稿