UnrealEngineのc++チュートリアルを自分なりに日本語翻訳して理解する
UnrealEngineの公式c++導入チュートリアルの4.9versionを自分なりに翻訳したものです。
c++とBluePrint
UnrealEngineでは、c++とBlueprintを使って実装ができます。
ゲームを作るためのapiやフレームワークはc++・Blueprints共にアクセスすることができます。
ここでは、c++で作成したUnrealEngineのAPIクラスを継承したものにBlueprintsからアクセス
する方法をチェックします。
c++ファイルを作る
コンテンツブラウザー(画面下部のアセット参照できる項目)で右クリックし、
新規c++クラスを選択し、c++ファイルを作成します。
または、上部のメニューからファイルをクリックし、新規c++ファイルの作成を選択します。
すると、以下の画面が表れるれるので、親クラスにActorを選択します。
ファイルネームは以下のようにします。
すると、MyActorクラスが作成されます。
プロジェクトをc++で実行できるように設定しておくと、macの場合
Xcodeが開かれ、自動で以下のようなMyActorのcppとheaderファイルが作成されます。
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
UCLASS()
class DOCAN_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
overlode関数BeginPlay()とTick(float DeltaTime)が自動で定義されます。
BeginPlay()はAMyActorクラスが実行可能になった際に実行される関数で
Tick()はフレーム毎に呼び出されます。
Tick()関数が不要な場合コードを削除し、constructorで呼び出されている
以下のコードを変更します。
// Sets default values
AMyActor::AMyActor()
{
// performanceを向上させたい場合は、falseに設定する
PrimaryActorTick.bCanEverTick = true;
}
C++で設定したPropertyをEditorでアクセスする
UnrealEditorでプロパティを設定したい場合は、変数名の上に以下のように
UPROPERTY(EditAnywhere)マクロを宣言します。
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
int32 TotalDamage;
...
};
設定したプロパティに表題(例:Damage)を与えたい場合は以下のように書きます。
UPROPERTY(EditAnywhere, Category="Damage")
int32 TotalDamage;
表題を与えることによって、値の管理がしやすくなります。
また、BluePrints上でプロパティを表示したい場合は以下のように書きます。
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Damage")
int32 TotalDamage;
今回は、BlueprintReadWriteを選択しましたが、読み込み専用のBlueprintReadOnly
などのプロパティもあります。
Blueprintのプロパティに関しては、こちらを参照してください。
ここで、ダメージに関する以下のプロパティを追加します。
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Damage")
int32 TotalDamage;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Damage")
float DamageTimeInSeconds;
UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Transient, Category="Damage")
float DamagePerSecond;
...
};
DamageTimeInSecondsはデザイナーが変更できるプロパティです。
DamagePerSecondは計算結果を格納をするプロパティです。
Transient flagは固定値でないことを示すためのプロパティです。
これらのプロパティはUnrealEditorで以下のように表示されます。
constructorでdefault値をセットする
コンストラクタでプロパティのDefault値をセットしてみます。
AMyActor::AMyActor()
{
TotalDamage = 200;
DamageTimeInSeconds = 1.f;
}
AMyActor::AMyActor() :
TotalDamage(200),
DamageTimeInSeconds(1.f)
{
}
上下のコンストラクタ共に以下のようにUnrealEditorに反映されます。
Transient flagをセットしたDamagePerSecondプロパティの値をセットするためには、
PostInitProperties()をよびます。
void AMyActor::PostInitProperties()
{
Super::PostInitProperties();
DamagePerSecond = TotalDamage / DamageTimeInSeconds;
}
UnrealEditorには、以下のように表示されます。
c++のクラスをBlueprintsで拡張する
ここまで、デザイナーが変更できるようなプロパティを追加したシンプルなAMyActorクラスを作りましたが、
このクラスから新しいBlueprintクラスを作成してみます。
Selectボタンをクリックし、名前にCustomActor1と入力します。
するとContent Browserに以下のように作成されます。
ここで、UnrealEditorからTotalDamageとDamageTimeinSecondsプロパティを
変更すると、DamagePerSecondには次の表示されます。
150と表示されるべきところが200と表示されてしまっています。
下記のようなコードを書くことで、UnrealEditorで変更した値を反映することができます。
void AMyActor::PostInitProperties()
{
Super::PostInitProperties();
CalculateValues();
}
void AMyActor::CalculateValues()
{
DamagePerSecond = TotalDamage / DamageTimeInSeconds;
}
#if WITH_EDITOR
void AMyActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
CalculateValues();
Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif
PostEditChangeProperty()を#if WITH_EDITORと#endif内に定義します。
こうすることで、余計なコードをコンパイルに加えないことで、容量を減らすことができるようです。
実行結果は以下のようになります。
void AMyActor::PostInitProperties()
{
Super::PostInitProperties();
CalculateValues();
}
void AMyActor::CalculateValues()
{
DamagePerSecond = TotalDamage / DamageTimeInSeconds;
}
#if WITH_EDITOR
void AMyActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
CalculateValues();
Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif
C++とBlueprint間で関数を呼ぶ
ここまで、Blueprintsで、プロパティにアクセスする方法を見てきましたが、
Blueprintとc++間でお互いの関数を呼ぶ方法を見ていきます。
まず、CalculateValues()をBlueprints側から呼び出す方法です。
c++で定義したCalculateValues()の上に以下のようにマクロを定義します。
UFUNCTION(BlueprintCallable, Category="Damage")
void CalculateValues();
UFUNCTIONマクロはにより、リフレクションシステムにc++の関数が公開されます。
また、BlueprintCallableオプションにより、Blueprint仮装マシンに公開されます。
このように定義することで、以下のようにBlueprint内に表示されます。
上部の図のようにDamegeカテゴリ内に表示されます。
Blueprint code内では、CalculateValues()で計算されたTotalDamageが表示されています。
続いて、c++で定義されBlueprints内で実装される関数の例を見ていきます。
UFUNCTION(BlueprintImplementableEvent, Category="Damage")
void CalledFromCpp();
もしBlueprint内で何も実装されなかった場合、空の中身の関数が実装されますが、
以下のようにBlueprintNativeEventオプションをつけることで、
Blueprint内で実装されていない場合、c++で書かれた内容を実行させる方法があります。
UFUNCTION(BlueprintNativeEvent, Category="Damage")
void CalledFromCpp();
実装する中身は以下のように
形式で書きます。
void AMyActor::CalledFromCpp_Implementation()
{
// Do something cool here
}