UE4 C++とUnreal C++の列挙型の扱い
目次
環境
・Visual Studio Community 2015
・Unreal Engine4.16
結果
まず最初に結果を述べます。
Unreal C++では下記のような書き方が良さそうです。
UENUM(BlueprintType) enum class EMusicType : uint8 { MT_Alternative UMETA(DisplayName="Alternative"), MT_Classic UMETA(DisplayName="Classic"), MT_Dance UMETA(DisplayName="Dance"), MT_Pop UMETA(DisplayName="Pop"), MT_Rock UMETA(DisplayName="Rock"), Num UMETA(Hidden) };
DisplayNameを別途分けるか下記のような記述にするかは好みの問題だと思いますので、どちらでも。
Hiddenを指定するとBlueprint側からは非表示になります。
UENUM(BlueprintType)
enum class EMusicType : uint8
{
Alternative,
Classic,
Dance,
Pop,
Rock,
};
では長々と説明していきます。
enumの種類
enum Color
{
Red,
Green,
Blue
};
最もよく見てきた宣言だと思います。
続いてUnreal C++でのenumについてです。
Unreal C++では"UENUM"を使用する必要があります
また、コーディング規約として接頭詞にEを付けます。
UENUM()
enum EColor
{
Red,
Green,
Blue
};
これがUnreal C++での標準的な列挙型になります。
しかし、この場合は下記のようにプロパティ宣言するとエラーになります。
詳細は後ほど。
UENUM(BlueprintType) enum EColor { Red, Green, Blue }; // ~省略~ UPROPERTY(BlueprintReadWrite, Category = Color) EColor ColorType;
続いてC++ではnamespaceを使用して宣言を行うようになりました。
理由としてはenumの名前をグローバルスコープにしないためです。
namespace Color
{
enum Type
{
Red,
Green,
Blue
};
}
上記の宣言はUnreal C++でも取り入れられました。
"KismetMathLibrary.h"で宣言されているものです。
UENUM(BlueprintType)
namespace EEasingFunc
{
enum Type
{
Linear,
Step,
SinusoidalIn,
SinusoidalOut,
SinusoidalInOut,
EaseIn,
EaseOut,
EaseInOut,
ExpoIn,
ExpoOut,
ExpoInOut,
CircularIn,
CircularOut,
CircularInOut,
};
}
しかし、この記述ではローカルで宣言されたenumは、名前空間を使用出来ません。
この場合、ローカル構造体の中にenumを宣言します。
class FontColor { struct Color { enum Type { Red, Green, Blue }; }; }
これに関してはUE4でも同様のようです。
フラグとして使用している部分ですので、参考としてはイマイチですが
"UnrealClient.h"内で宣言されています。
class FViewportClient { public: struct ESoundShowFlags { enum Type { Disabled = 0x00, Debug = 0x01, Sort_Distance = 0x02, Sort_Class = 0x04, Sort_Name = 0x08, Sort_WavesNum = 0x10, Sort_Disabled = 0x20, Long_Names = 0x40, }; }; // ~省略~ }
C++11からはenum classを使ってスコープを扱えるようになりました。
enum class Color
{
Red,
Green,
Blue
};
Unreal C++もベースはモダンなC++11, 14のため、早速取り入れられています。
"ReflectionCaptureComponent.h"内で定義されています。
UENUM()
enum class EReflectionSourceType : uint8
{
CapturedScene,
SpecifiedCubemap,
};
また、UPROPERTYでBPに公開するenumを持たせたい場合は原則としてモダンな書き方にする必要があります。
UENUM(BlueprintType) enum class EMusicType : uint8 { MT_Alternative UMETA(DisplayName = "Alternative"), MT_Classic UMETA(DisplayName = "Classic"), MT_Dance UMETA(DisplayName = "Dance"), MT_Pop UMETA(DisplayName = "Pop"), MT_Rock UMETA(DisplayName = "Rock"), }; // ~省略~ UPROPERTY(BlueprintReadWrite, Category = Music) EMusicType MusicType;
下記の場合は全てUPROPERTYでBPに公開する事ができません。
enum EMusicType enum EMusicType : uint8 namespace EColorType { enum Type : uint8 enum class EMusicType
無事にBPに公開できれば下記のようにGetして使うことが出来ます。
モダンなC++に対応していますが、エンジン内には一部古い定義も残っています。
それらも関数の引数やプロパティとして扱えるような書き方があります。
下記はSkinnedMeshComponent.hの例です。
UENUM() namespace EBoneSpaces { enum Type { WorldSpace UMETA(DisplayName = "World Space"), ComponentSpace UMETA(DisplayName = "Component Space"), }; }
上記を使用する例
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Bone)
TEnumAsByte<EBoneSpaces::Type> BoneSpace;
UFUNCTION(BlueprintCallable, Category = Bone)
void UpdateBoneSpace(TEnumAsByte<EBoneSpaces::Type> boneSpace);
まとめ
UE4でもエンジンコード内に旧形式のコードがあります。
徐々にモダンなC++に移行しています。
探せばnullptrでなくNULLも残っています。
理由としては変更に対する費用対効果が薄い。というもののようです。
エラーになる箇所やバグを生む危険性のある箇所はちゃんと修正してくれています。
おとなしく待ちましょう。
そして、見つけたら報告しましょう。
できればUDNで。
https://udn.unrealengine.com/users/login.html
無理ならAnswer Hubで。
Japanese - UE4 AnswerHub
日本語の誤植等はForumで。
日本語ドキュメント 誤植&誤訳 投稿所 - Unreal Engine Forums
そして、我々Engineを使う側の人間はモダンなC++を覚えましょう。
UE4 指定範囲内にActorを配置するBP
目次
・Spawnerの作成
・指定範囲にSpawnさせる方法
環境
・Unreal Engine4.16
Spawnerの作成
まずはSpawnerとなるBlueprintを作成します
親クラスはActorを選択しました。
ConstructionScriptにChildActorComponentの追加とChildActorComponentに設定するクラスを設定します。
今回は図のようなStaticMeshだけが配置されたActorBPを使用します。
先程のSpawnerのBPに設定します。
この時に"Instance Editable"にチェックを入れておきましょう。
Levelに配置後に任意のActor継承クラスを設定出来るようになります。
ここまでで下記のように生成されることが確認できると思います。
続いて、同一のActorクラスが複数生成されるように変更を加えます。
・ForLoopとSpawnNumを追加しました。
SpawnNumにはとりあえず10を入れました。
先程同様に"Instance Editable"にチェックを入れておきましょう。
また、Actorが複数生成されているか確認するために適当に250ずつ高さをずらしています。
とりあえず10個生成されていることが確認できました。
試しにLevelに配置されたBPの値を変更してみてください。
・SpawnNumの数を変える
・ActorClassのStaticMeshを変えてみる。またはActorClassの指定を変えてみる。
Actorの派生クラスも使用可能なため、Characterクラスも指定可能です。
指定範囲にSpawnさせる方法
今回は2種類のアプローチを書きます。
①距離、高さの上限を決めてその範囲内で生成する
まずはランダムで回転角を決めます。
最大角度のみ指定しておけば良いでしょう。
また、回転の方向もYawのみにしています。Roll, Pitchもランダムで指定したい場合は稀でしょう。
次に生成させる位置を決める計算の元となるベクトルを生成します。
最大距離と最低、最高の高さを決めましょう。
先程のノードと合わせてみましょう。
変更点は赤枠で囲っています。
SpawnNumという名前に対して生成される数が1つ多く感じてしまう紛らわしい名前だったため、LoopのStartIndexを1に変更しました。
あとは先程作成したノードを組み合わせているだけです。
最大距離:1000
最低高さ: 0
最大高さ:1000
最大角度: 360
で設定した結果以下のようになりました。
ConstructionScriptで実行しているため、移動したり実行したりするたびに違う結果となるでしょう。
②BoxCollisionを置いてその範囲内で生成する
まずはBoxCollisionを追加します。
サイズは適当にBoxExtentXYZを500にしています。
次にBoxの範囲内でのランダムなベクトルの取得を行います。
内部の計算としては
Origin - BoxExtent を最小
Origin + BoxExtent を最大
とした範囲でXYZ成分の乱数を生成してベクトルとして返しているだけで非常にシンプルな実装です。
あとは先程同様にノードをつなぎます。
今回はBoxが地面に埋まる範囲にあったため、埋まってしまいました
ですが先程の距離と高さでの実装に比べて生成される範囲が目に見えて分かりやすいため、汎用性が高いです。
また、flagをひとつかませてBoxでの生成と距離、高さ指定での生成方法を共存させてしまってもいいかもしれません。
ConstructionScriptを活用すれば色々なテストが楽に行なえますし、現場でのゲーム開発にも活かせる便利な機能を沢山作れるでしょう。
UE4 便利なUPROPERTY(Bitmasks, EditCondition)
目次
・Bitmasks
・EditCondition
環境
・Visual Studio Community 2015
・Unreal Engine4.16
Bitmasks
meta = (Bitmask)
オンオフの切り替えが可能なフラグをドロップダウン形式で編集可能になります。
追加方法はUPOPERTYのmetaに"bitmask"を追加します。
UPROPERTY(EditAnywhere, Category = BitMask, meta = (Bitmask))
int bitTestFlag;
図のようにFlagが連番で並びます。
BitMaskのみのUPROPERTYを使用したいということはまずないでしょう。
そこで、列挙型と組み合わせて使用することがあります。
今回はゲーム中の攻撃をイメージして使ってみます。
列挙型定義
通常は攻撃とダメージ種別は分けるでしょう。
ですが、今回は説明のために統合された列挙型があると仮定します。
UENUM(meta = (Bitflags)) enum class EAttackType { Blow, // 打撃 Slash, // 斬撃 Shot, // 射撃 Cannon, // 砲撃 Damage, // ダメージを与える Heal, // 回復する Drain, // 吸収する };
変数定義
Bitmaskと合わせてBitmaskEnumを加えました。
UPROPERTY(EditAnywhere, Category = Attack, meta = (Bitmask, BitmaskEnum = "EAttackType"))
int8 attackType;
BitmaskEnumを加えることで理解しやすく意味のあるコードになりました。
さらにbitmaskは複数の状態を持たせる事ができます。
そのため
回復効果のある射撃
HP吸収の斬撃兼打撃属性の攻撃
等を作ることが出来ます。
(先も述べたように通常は分けます。このような使い方はしないでしょう。)
EditCondition
meta = (EditCondition = boolean)
プロパティの編集が許可されているかどうかを示すための条件を指定します。
ようはdetailタブから変更できるかどうかを切り替えるものです。
今度はゲーム中のfadeをイメージして使ってみます。
UPROPERTY(EditAnywhere, Category = Fade) bool isFade; UPROPERTY(EditAnywhere, Category = Fade, meta = (EditCondition = isFade)) float fadeTime;
遷移が禁止されている場合はFadeIn, Outの時間を変更できません。
遷移が許可されている場合はFadeIn, Outの時間を任意で変更することが可能になりました。
最後に余談ですが
UPROPERTY()のmetaの定義は大文字、小文字どちらでも可能です。
UPROPERTY(meta = (EditCondition = isFade))
UPROPERTY(Meta = (EditCondition = isFade))
UE4 ファイルパスの話
環境
・Visual Studio Community 2015
・Unreal Engine4.16
ContentBrowserの話
- Content
- C++ Clsasses
という構成になります。
Content Browser上でアセットを選択すると
Copy Referenceでそのアセットへのパスがクリップボードに保存されます。
図の例では
StaticMesh'/Engine/VREditor/BasicMeshes/SM_Cube_01.SM_Cube_01'
が保存されます。
Unreal C++からアセットをロードする時の注意点
さて、ここからが本題です。
Content以下の自作のAssetをコード上から参照したい場合に詰まったことがあります。
(当時はCopy Referenceの存在を知らなかったので)
今回は下記のスタティックメッシュを参照してみようと思います。
先程と同様にCopy Referenceを行ってみると
StaticMesh'/Game/Sandbox/Meshes/SM_Building.SM_Building'
というようなパスが取得できます。
そこで、気になったのが/Game/Sandbox/という部分です。
Engineのアセットの場合は/Engine/なのですが
Content以下の場合は/Game/となっており/Content/ではありません。
ロードする必要のあるアセットが正確に分かっていて、そのプロパティを初期化時に設定する場合の例です。
header側
private: const FName MeshComponentName = TEXT("Mesh"); UPROPERTY(Category = Actor, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true")) class UStaticMeshComponent* Mesh; public: ABaseActor(const FObjectInitializer& ObjectInitializer);
cpp側
ABaseActor::ABaseActor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { static ConstructorHelpers::FObjectFinder<UStaticMesh> buildingMesh(TEXT("/Game/Sandbox/Meshes/SM_Building")); Mesh = CreateOptionalDefaultSubobject<UStaticMeshComponent>(MeshComponentName); if (Mesh == nullptr) return; Mesh->SetStaticMesh(buildingMesh.Object); }
結果
無事にビルのモデルがセットされたActorが出来ました。
余談ですが、Content Browserは4つまで表示出来ます。
UE4 Materialの小話01
こちらは「裏 Unreal Engine 4 (UE4) Advent Calendar 2016」18日目の記事です。
裏 Unreal Engine 4 (UE4) Advent Calendar 2016 - Qiita
今回はMaterialのCustomノードのお話です。
使用環境
・Unreal Engine4.13.2
目次
・参考文献
・Customノードとは
・別の方法でHLSLを書きたい
・参考文献
一昨年Advent Calenderのシモダさんのエンジン拡張の記事
UE4のShading Modelを拡張して独自のライティングや描画機能を追加する方法を公開するよ! - Qiita
昨年のAdvent CalenderのもんしょさんのCustomノードの記事
もんしょの巣穴blog [UE4] Customノード3分ハッキング
おかずさんのCustomノードの記事
UE4のCustomノード(カスタムHLSLシェーダ)を使ってみた - ぼっちプログラマのメモ
・Customノードとは
Customノードはノード内にHLSLを記述できる特殊なノードです。
公式ドキュメント
Unreal Engine | Custom 表現式
メリット
・ノードで書くと冗長な処理を楽に記述できる。
・マテリアルBPにはない、ローカル変数やループ処理が使える。
デメリット
・基本的には#includeが出来ない。
・ノードで作成したシェーダーよりも効率が悪い(詳しくは上記ドキュメント参照)
・コメントを書けない。(と思ったら書けるようになってました)
・別の方法でHLSLを書きたい
理由
・ノード内に書くのは書きにくい。IDEやテキストエディタで書きたい。
方法
①参考文献で紹介したもんしょさんのinclude方式
ただし
「ハック的な手法なのでいつ穴を塞がれるかわかりません」
とのことでしたので、別の方法で書いてみたいと思いました。
そこでシモダさんのEditor拡張の記事を眺めていてふと思いました。
②Engineに書いちゃおう
Engine/Shaders/Common.usfに直接書いてしまえばいいのではないか、と。
ということで実際に書いてみました。
今回書いたのはこちら
やり方簡単DitherTemporalAAとオパシティマスクの間にCustomノード挟んでfloat入力1つDitherInput等の名前にし#if SHADOW_DEPTH_SHADER
— シモダジュンヤ Jun Shimoda (@junyash) 2016年11月22日
return 1;#else
return DitherInput;#endif
と pic.twitter.com/g1eL2fVYZ1
で紹介されていた、Ditherのシャドウのちらつきを回避するコードです。
Engine/Shaders/Common.usf
float Dither(float DitherInput) { #if SHADOW_DEPTH_SHADER return 1; #else return DitherInput; #endif }
後はエンジンを起動後にCtrl + Shift + . でシェーダーをコンパイル
実際の呼び出し
Customノード内で関数呼び出しを行うだけ。
①
return Dither(ditherInput);
②
Dither(ditherInput);
returnがあってもなくても良いみたいです。
余談ですが、Ditherと半透明の比較。
手前が半透明で奥がDitherです。
ライティングが暗いので、分かりにくいのもありますがDitherは半透明の代替として十分なクオリティですね!
空とCubeの一部が真っ赤なのはさておき、半透明とDitherではシェーダーパフォーマンスが随分と違うのがお分かりかと思います。
以上です!
ブログ初デビューです!
前々から作ろうとは思ってたけど、ついにブログ始めた。
このブログではどうでもいい日常のこととか、プログラムについて色々垂れ流していく予定です。
Twitterやってます@PaperSlothです。
プログラム歴は3年と少々。。。
今までやってきたプログラムとか触れたもの
C/C++,C#,JavaScript,Java,Objective-C,Maya,UnrealEngine4,Unity...etc
このブログでは
Visual Studio2010でC/C++とDirectXを使ったり
UnityでC#を使ったり
UnrealEngine4でBlueprintを使って
ゲーム開発をする上でのあれやこれやを書く予定です。
よろしくお願いします。