この記事は、Unreal Engine 4 (UE4) Advent Calendar 2016 23日目の記事です。
UnrealC++のTipsということで、情報メモをお届けします。
お役に立てれば嬉しいです。
確信のないものは疑問形になってます。
基本
使うことが多いUE4内のクラス
配列
大体以下の2つを知っていればOKです。
- TArray
- TMap
基本型
- int32
- float
後ろの数値がバイト数になる。
文字列
- FString
- FName
- FText
クラス
- UObject
- AActor
- APawn
- UActorComponent
- UWorld
- UGameInstance
- UGameState
- UGameMode
- APlayerController
公式記事
公式にも記事がありますので読みましょう。
https://www.unrealengine.com/ja/blog/ue4-libraries-you-should-know-about
UCLASS Tips
UObject継承したとき
- クラス宣言前に
UCLASS
必須。 - クラス名先頭に
U
必須。
さらにクラス内宣言でGENERATED_BODY()
が必須。(GENERATED_UCLASS_BODY()
は古い宣言でレガシーらしい) -
const FObjectInitializer& ObjectInitializer
を引数としたコンストラクタは自分で書ける。
引数を増やすことは無理っぽい?カスタムコンストラクタという機能があってそれで出来る?
例:
MyGameModule内にクラスを作るとする。
UCLASS()
class MYGAMEMODULE_API UMyClass : public UObject
{
GENERATED_BODY()
public:
UMyClass(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {}
};
ヘッダーに~.generated.hを書くのが必要なとき
UCLASSやUSTRUCTが存在するヘッダーに追加する必要があり。
includeは最初ではなく、他のincludeするファイルがあったらの最後にしないといけない。
(多分 GENERATED_BODY()
が必要なタイプがあるときに必要。 UENUM
のときは必要なかった。Blueprintに露出する場合は必要かも?)
存在しないヘッダーに追加した場合は逆にエラーになる。
CPPファイルに必ずIncludeしなくてはいけないファイルが存在する
Moduleの名前になっているヘッダファイル。
HogeHoge.Build.cs
だったら、 #include "HogeHoge.h"
を一番最初のインクルードすることが必須。
PrefixHeaderはどれ?
もしModule名がHogeHogeだったら(ビルドソースコードが HogeHoge.Build.cs
だったら)、 HogeHoge.h
にIncludeしておくとそのHogeHogeModule内でPrefixHeader代わりになる?
Delegate
ExecuteIfBound関数が使えるのは返り値がvoidのDelegateのみ。
以下はDelegateの書き方の例なので参考にしてください。(よく忘れる)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FFoobarDelegate, int32, MyId, const FString&, message);
DECLARE_MULTICAST_DELEGATE_OneParam(FHogeHogeResultDelegate, const FMyResult&);
FFoobarDelegate FoobarDelegate;
FoobarDelegate->AddDynamic(this, &HogeHogeClass::OnFoobar);
FoobarDelegate->Broadcast(id, msg);
FHogeHogeResultDelegate HogeHogeResultDelegate;
HogeHogeResultDelegate->AddUObject(this, &HogeHogeClass::OnHogeHogeResult);
HogeHogeResultDelegate->Broadcast(result);
DECLARE_DELEGATE_OneParam(FMyToolSpinBoxClicked, float)
FMyToolSpinBoxClicked OnClicked;
FReply SMyToolMainMenu::OnResetClicked()
{
return FReply::Handled();
}
グローバル変数として持たせられるクラス
ゲームを作っているときに、レベルを跨ぐような変数が欲しい時がある。
その時には UGameInstance
のメンバ変数として持たせておくことで、グローバル変数のように使うことが出来る。
(これ以外レベルを跨ぐ変数を保持する方法は、多分ファイルに書き出すとかDBに持たせるとかしないと無理っぽい)
C++に定義されている関数をBlueprintでオーバーライドして実装する方法
細かい方法は、 3. Extend and Override C++ with Blueprints
(https://docs.unrealengine.com/latest/INT/Programming/Tutorials/VariablesTimersEvents/3/index.html
)の項目参照。
以下ざっくり説明。
C++側では、
UFUNCTION(BlueprintNativeEvent)
void CountdownHasFinished();
virtual void CountdownHasFinished_Implementation();
を宣言して、C++側は void CountdownHasFinished()
を実装せずに、 virtual void CountdownHasFinished_Implementation()
の _Implementation
が付いている方だけ実装。
void ACountdown::CountdownHasFinished_Implementation()
{
//Change to a special readout
CountdownText->SetText(TEXT("GO!"));
}
Blueprint側は CountdownHasFinished
イベントを作成する。
Blueprint側からC++の関数を呼び出したい場合は、 right-clicking the Countdown Has Finished node and selecting Add call to parent function from the context menu.
で実行線をつなげる。
また、C++側でオーバーライドしたBlueprintイベントを呼び出したい場合は、 CountdownHasFinished_Implementation
ではなく、
CountdownHasFinished
を呼ぶ必要があることに注意。
void ACountdown::Test()
{
// CountdownHasFinished_Implementation(); // こっちだと基底のC++の方しか呼び出されない
CountdownHasFinished(); // こっちだとオーバーライドされたBlueprintイベントが呼び出される
}
C++に定義されている関数をBlueprintでオーバーライドし、その関数をC++から呼び出す場合
UFUNCTION
宣言時に BlueprintImplementableEvent
を使う。
UFUNCTION(Category = "Player Health", BlueprintImplementableEvent, BlueprintCallable)
void PlayerIsHealthyTick(float CurrentHealth);
したら、UE4Editorで function override
から PlayerIsHealthyTick
を選択してイベントを作成する。
これで PlayerIsHealthyTick
関数が呼ばれると、オーバーライドしたBlueprintの PlayerIsHealthyTick
イベントが呼ばれるようになる。
あとC++から呼ぶときには this->
必須らしい?
this->PlayerIsHealthyTick(1.0f);
参考:https://wiki.unrealengine.com/Blueprints,_Empower_Your_Entire_Team_With_BlueprintImplementableEvent
オブジェクトの作成
NewObject<T>
通常のnewと同じ感じ。
クラス名からしか指定できないので、Blueprintクラスは生成不可?
Actor生成で使ってはいけない。(はず)
UBillboardComponent* BillboardComponent = NewObject<UBillboardComponent>(this);
CreateDefaultSubobject<T>
コンストラクタでしか使えない。
Defaultのコンストラクタが呼ばれる。
クラス名からしか指定できないので、Blueprintクラスは生成不可?
BlueprintでComponentを追加するときの処理とかはコンストラクタでこれを呼べば可能。
SpriteComponent = CreateDefaultSubobject<UBillboardComponent>(TEXT("Sprite"));
ConstructorHelpers::FObjectFinder<T>
assetファイルから取得する。
.Objectでそのオブジェクトのポインタを取得可能。
static ConstructorHelpers::FObjectFinder<UStaticMesh> CraneArmMesh(TEXT("/Engine/EditorMeshes/Camera/SM_CraneRig_Arm.SM_CraneRig_Arm"));
PreviewMesh_CraneArm = CreateOptionalDefaultSubobject<UStaticMeshComponent>(TEXT("PreviewMesh_CraneArm"));
if (PreviewMesh_CraneArm)
{
PreviewMesh_CraneArm->SetStaticMesh(CraneArmMesh.Object);
}
StaticLoadObject
assetファイルから読み込んで取得する。
UWorld::SpawnActor
UActorを継承したクラス専用。
Worldに生成する。(レベルに置くと同義?)
Actorを新しく作るときには NewObject
ではなくこっち。
通常のComponentとかに使ってはいけない。(というかこっちはコンパイルエラー出るはず)
Blueprintクラスを生成するには、 SpawnActor
の引数に .ClassObject
を渡す。
FString Tips
文字列変換
convert Fstring to normal c++ char[]
// FString -> TCHAR
TCHAR* chars = *MyString;
// FString -> TCHAR -> ANSI
ANSICHAR* ansiChars = TCHAR_TO_ANSI(*MyString);
参考:https://forums.unrealengine.com/showthread.php?3207-convert-Fstring-to-normal-c-char
char array to an FString
FString Fs = FString(ANSI_TO_TCHAR(arr)); // ANSI -> TCHAR -> FString
FString Fs = FString(UTF8_TO_TCHAR(arr)); // UTF-8 -> TCHAR -> FString
FName -> FString
FNameValue.ToString()
https://answers.unrealengine.com/questions/2616/manipulating-fname-and-fstring.html
FName、FText、FStringの相互変換について
危険な変換があるので、ドキュメントの【文字列の取り扱い】参照。
https://docs.unrealengine.com/latest/JPN/Programming/UnrealArchitecture/StringHandling/index.html
C言語でよくある通常の文字列関数使いたい
\Engine\Source\Runtime\Core\Public\Misc\CString.h
に定義されている、 FCString
、 FCStringAnsi
、 FCStringWide
の関数でいろいろできるようになっている。
例:
auto length = FCStringAnsi::Strlen(charPtrArray);
FTextでFormat
FString FileName;
(FText::Format(LOCTEXT("Test", "Save {FileName}.data."), FText::FromString(FileName)));
FStringの文字列操作
FString str = FString(TEXT("abcdefg"))のとき。
str.Left(5); // "abcde"
str.Right(5); // "cdefg"
str.LeftChop(5); // "ab"
str.RightChop(5); // "fg"
str.LeftPad(10); // " abcdefg"
str.RightPad(10); // "abcdefg "
拡張子を外したファイル名が欲しいなら、以下の関数で良い。
FPaths::GetBaseFilename(TEXT("hogehoge.txt"));
拡張子だけ欲しければ、
FPaths::GetExtension(TEXT("hogehoge.txt"));
StringからIntegerへの変換
FString str(TEXT("123"));
FCString::Atoi(*str);
IntegerからStringへの変換
FString::FromInt(123);
特定の文字で分割したい
FString str(TEXT("hogehoge_txt_temp"));
TArray<FString> Values;
str.ParseIntoArray(Values, TEXT("_")); // hogehoge, txt, tempに分割される
etc Tips
GCを強制的に呼ぶ
GetWorld()->ForceGarbageCollection();
Logカテゴリーの追加方法
ヘッダーでDECLARE_LOG_CATEGORY_EXTERN
して、
CPPにDEFINE_LOG_CATEGORY
する。
外部モジュールでも使いたい場合は、ヘッダー側の宣言にAPI宣言を前方に置いておく。
例:HogeHogeモジュール専用のログを追加したい場合
HOGEHOGE_API DECLARE_LOG_CATEGORY_EXTERN(LogHogeHoge, Display, All);
DEFINE_LOG_CATEGORY(LogHogeHoge);
そのCPPファイルだけで使いたい場合はDEFINE_LOG_CATEGORY_STATIC
?
WindowsのDWORDとか使いたい場合
#include "AllowWindowsPlatformTypes.h"
と
#include "HideWindowsPlatformTypes.h"
で囲んで、その囲んでいる中にDWORDなどの処理を書く。
ActorじゃないけどTickで動かしたい
ヒストリアさんのブログ参照。
[UE4] C++コードにゲームの初期化,終了,更新のタイミングで処理を追加する方法 | historia Inc - 株式会社ヒストリア
\Engine\Source\Runtime\Engine\Public\Tickable.h
のFTickableGameObject
を継承して使う。
レベルを跨いで実行したいTickにも効果あり。
CriticalSection
FCriticalSection
をメンバ変数などで変数を作る。
スコープ内だけ有効にしたい場合には、下記のような感じで使う。
FScopeLock Lock(&Critical);
スレッドセーフなインクリメントをしたい
以下の関数を使用すると安全。
int32 CurrentID; // インクリメントしたい整数(メンバ変数として持っていると仮定する)
const int32 NextId = FPlatformAtomics::InterlockedIncrement(&CurrentID);
もしくは素直にFThreadSafeCounter
型を使う。
FThreadSafeCounter Counter;
const int32 nextId = Counter.Increment();
UnrealC++でComponent登録
コンストラクタ
CreateDefaultSubobject
を使う。
コンストラクタ以外
NewObject
した後、AttachToComponent
関数を呼ぶだけではダメで、
RegisterComponent
関数を呼ぶ必要がある。
Level内にいるActorの検索
ActorInLevel
TArray<AActor*> Characters;
UGameplayStatics::GetAllActorsOfClass(this, AMyCharacter::StaticClass(), Characters);
FilePathの検索
LoadFilePath
const FName AnimMontageAssetPath(TEXT("/Game/Animation/AnimMontageSample"));
auto AnimMontage = Cast<UAnimMontage>(StaticLoadObject(UObject::StaticClass(), nullptr, *AnimMontageAssetPath.ToString()));
glob
TArray<FString> FileNames;
IFileManager::Get().FindFiles(FileNames, *FString::Printf(TEXT("%s/Datas/*.*"), *FPaths::GameContentDir()), true, false);
for (auto FileName : FileNames)
{
auto BaseFileName = FPaths::GetBaseFilename(FileName);
// 読み込み
...
}
外部ライブラリ追加方法
VisualStudioの設定ではなく、ライブラリを使うモジュールの~.Build.cs内で追加する。
lib追加方法
PublicAdditionalLibraries
のメンバ関数Addで.libを追加する。
IncludePath追加方法
PublicIncludePaths
、もしくは PrivateIncludePaths
のメンバ関数 Add
でIncludePathを追加する。
ビルドの構成は TargetInfo
の変数から取得できる。
例:
private string ModulePath
{
get { return Path.GetDirectoryName(RulesCompiler.GetModuleFilename(this.GetType().Name)); }
}
// ライブラリを置いた基底パス取得
private string ThirdPartyPath
{
get { return Path.GetFullPath(Path.Combine(ModulePath, "../../../ThirdParty/")); }
}
if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Win32))
{
string LibrariesPath = Path.Combine(ThirdPartyPath, "hogehogelib", "Windows"); // libファイルが置いてあるパスを設定
// ビルドターゲットによってリンクするライブラリを変更する
switch (Target.Configuration)
{
case UnrealTargetConfiguration.Shipping:
case UnrealTargetConfiguration.Test:
// Release版
PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "HogeHoge_x64.lib"));
break;
case UnrealTargetConfiguration.Debug:
default:
// Debug版
PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "HogeHoge_x64.lib"));
break;
}
}
レベルを開く
UGameplayStatics::OpenLevel(this, TEXT("/Game/Stage/Level001"));
URLを開く
FPlatformProcess::LaunchURL(*FString::Printf(TEXT("http://www.google.com")), NULL, NULL);
各ディレクトリ取得
FPaths::GameDir() // Gameフォルダのパス(Contentフォルダ?)
あとは大体名前の通りのフォルダのパスを取得できる。
FPaths::GetConfigDir()
FPath::GetSavedDir()
など。
C++側だけconstにしてBlueprintに出す
UFUNCTION(BlueprintCallable, Category = "Exprement")
int32 Exprement()
#if CPP
const
#endif
{
//test = 111; // コメント外すとエラー
return 201;
}
// このとき
UCLASS()
class UTest : public UClass
{
int32 test;
};
今日はUnrealC++の日
裏 Unreal Engine 4 (UE4) Advent Calendar 2016 - Qiita でも @Kaaaai さんがよく使われる処理をUnrealC++で書いてみようの巻。 - Kai's Tech というのが公開されています!是非読みましょう!
UnrealEngine4の技術書がセール中
現在、Packtのセールで電子書籍・ビデオが全部$5なので買いましょう。
Bundleとしてもまとまってます。
Unreal Engine 5 for $25 Bundles | PACKT Books
自分はMastering Unreal Engine Bundleをポチりました。
日本でもUnrealC++の技術書が出ることを願っています。(誰か書いてください)
実装が合っているはずなのに思った動作にならない時のチェックリスト
-
本当に変数に値が入っているか
-
デフォルト動作になっていないか
-
条件分岐、計算に使っている数値の単位が正しいか
-
初期化・終了処理し忘れていないか
-
デバッガで動作を追ってみる
-
使用している変数をPrintデバッグして確認
-
コードを読む、考えこむだけではなく、編集してみる
-
API仕様が間違っている可能性(辛いやつ)
-
リビルド
-
リビルドでも駄目な場合は、キャッシュを削除する。
-
ビルドキャッシュが入っているIntermediate のディレクトリは空にする
-
各ゲーム固有処理のキャッシュは Engineじゃない方のSaved フォルダ内のデータを消しましょう
-
気分転換する
-
散歩
-
風呂に入る
-
仮眠する
-
一旦寝て明日やる
明日は
明日はtarotarokunさんの、某MashupAwardに出す作品で得た、UE4で作ったVRゲームの動画配信ノウハウを大公開です。