PaperSloth’s diary

主にゲーム開発関連についての記事を書きます。

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の種類

先ずは通常のC++での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して使うことが出来ます。
f:id:PaperSloth:20170707005116p:plain


モダンな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に設定するクラスを設定します。
f:id:PaperSloth:20170704230345p:plain


今回は図のようなStaticMeshだけが配置されたActorBPを使用します。
f:id:PaperSloth:20170704230241p:plain

先程のSpawnerのBPに設定します。
この時に"Instance Editable"にチェックを入れておきましょう。
Levelに配置後に任意のActor継承クラスを設定出来るようになります。
f:id:PaperSloth:20170704230608p:plain

ここまでで下記のように生成されることが確認できると思います。
f:id:PaperSloth:20170704230726p:plain


続いて、同一のActorクラスが複数生成されるように変更を加えます。
・ForLoopとSpawnNumを追加しました。
SpawnNumにはとりあえず10を入れました。
先程同様に"Instance Editable"にチェックを入れておきましょう。
f:id:PaperSloth:20170704231405p:plain
f:id:PaperSloth:20170704231519p:plain

また、Actorが複数生成されているか確認するために適当に250ずつ高さをずらしています。
とりあえず10個生成されていることが確認できました。
f:id:PaperSloth:20170704231713p:plain


試しにLevelに配置されたBPの値を変更してみてください。
・SpawnNumの数を変える
・ActorClassのStaticMeshを変えてみる。またはActorClassの指定を変えてみる。
Actorの派生クラスも使用可能なため、Characterクラスも指定可能です。
f:id:PaperSloth:20170704232015p:plain

指定範囲にSpawnさせる方法

今回は2種類のアプローチを書きます。

①距離、高さの上限を決めてその範囲内で生成する

まずはランダムで回転角を決めます。
最大角度のみ指定しておけば良いでしょう。
また、回転の方向もYawのみにしています。Roll, Pitchもランダムで指定したい場合は稀でしょう。
f:id:PaperSloth:20170704232602p:plain

次に生成させる位置を決める計算の元となるベクトルを生成します。
最大距離と最低、最高の高さを決めましょう。
f:id:PaperSloth:20170704233740p:plain


先程のノードと合わせてみましょう。
変更点は赤枠で囲っています。
SpawnNumという名前に対して生成される数が1つ多く感じてしまう紛らわしい名前だったため、LoopのStartIndexを1に変更しました。
あとは先程作成したノードを組み合わせているだけです。
f:id:PaperSloth:20170704234005p:plain


最大距離:1000
最低高さ: 0
最大高さ:1000
最大角度: 360
で設定した結果以下のようになりました。
f:id:PaperSloth:20170704234137p:plain

ConstructionScriptで実行しているため、移動したり実行したりするたびに違う結果となるでしょう。


②BoxCollisionを置いてその範囲内で生成する

まずはBoxCollisionを追加します。
サイズは適当にBoxExtentXYZを500にしています。
f:id:PaperSloth:20170704234451p:plain


次にBoxの範囲内でのランダムなベクトルの取得を行います。
内部の計算としては
Origin - BoxExtent を最小
Origin + BoxExtent を最大
とした範囲でXYZ成分の乱数を生成してベクトルとして返しているだけで非常にシンプルな実装です。
f:id:PaperSloth:20170704235711p:plain


あとは先程同様にノードをつなぎます。
f:id:PaperSloth:20170704235641p:plain


今回はBoxが地面に埋まる範囲にあったため、埋まってしまいました
f:id:PaperSloth:20170704235859p:plain

ですが先程の距離と高さでの実装に比べて生成される範囲が目に見えて分かりやすいため、汎用性が高いです。
また、flagをひとつかませてBoxでの生成と距離、高さ指定での生成方法を共存させてしまってもいいかもしれません。



ConstructionScriptを活用すれば色々なテストが楽に行なえますし、現場でのゲーム開発にも活かせる便利な機能を沢山作れるでしょう。
f:id:PaperSloth:20170705000735p:plain

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;

f:id:PaperSloth:20170704202246p:plain

図のように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を加えることで理解しやすく意味のあるコードになりました。
f:id:PaperSloth:20170704203618p:plain

さらにbitmaskは複数の状態を持たせる事ができます。
そのため
回復効果のある射撃
f:id:PaperSloth:20170704203947p:plain
HP吸収の斬撃兼打撃属性の攻撃
f:id:PaperSloth:20170704203956p:plain
等を作ることが出来ます。
(先も述べたように通常は分けます。このような使い方はしないでしょう。)


EditCondition

meta = (EditCondition = boolean)

プロパティの編集が許可されているかどうかを示すための条件を指定します。
ようはdetailタブから変更できるかどうかを切り替えるものです。

今度はゲーム中のfadeをイメージして使ってみます。

UPROPERTY(EditAnywhere, Category = Fade)
bool isFade;
UPROPERTY(EditAnywhere, Category = Fade, meta = (EditCondition = isFade))
float fadeTime;

遷移が禁止されている場合はFadeIn, Outの時間を変更できません。
f:id:PaperSloth:20170704204840p:plain

遷移が許可されている場合はFadeIn, Outの時間を任意で変更することが可能になりました。
f:id:PaperSloth:20170704204921p:plain


最後に余談ですが
UPROPERTY()のmetaの定義は大文字、小文字どちらでも可能です。
UPROPERTY(meta = (EditCondition = isFade))
UPROPERTY(Meta = (EditCondition = isFade))

UE4 ファイルパスの話

目次

・ContentBrowserの話
Unreal C++からアセットをロードする時の注意点

環境

Visual Studio Community 2015
・Unreal Engine4.16

ContentBrowserの話

Unreal C++でプロジェクトを作成すると下記のように

  • Content
  • C++ Clsasses

という構成になります。
f:id:PaperSloth:20170703204116p:plain


Content Browser上でアセットを選択すると
Copy Referenceでそのアセットへのパスがクリップボードに保存されます。

f:id:PaperSloth:20170703204941p:plain
図の例では
StaticMesh'/Engine/VREditor/BasicMeshes/SM_Cube_01.SM_Cube_01'
が保存されます。

Unreal C++からアセットをロードする時の注意点

さて、ここからが本題です。

Content以下の自作のAssetをコード上から参照したい場合に詰まったことがあります。
(当時はCopy Referenceの存在を知らなかったので)

今回は下記のスタティックメッシュを参照してみようと思います。
f:id:PaperSloth:20170703205830p:plain

先程と同様に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);
}


結果
f:id:PaperSloth:20170703224659p:plain
無事にビルのモデルがセットされたActorが出来ました。




余談ですが、Content Browserは4つまで表示出来ます。
f:id:PaperSloth:20170703204541p:plain

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に直接書いてしまえばいいのではないか、と。
ということで実際に書いてみました。
今回書いたのはこちら


で紹介されていた、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);

f:id:PaperSloth:20161214223014p:plain


Dither(ditherInput);

f:id:PaperSloth:20161214223211p:plain

returnがあってもなくても良いみたいです。



余談ですが、Ditherと半透明の比較。
手前が半透明で奥がDitherです。
f:id:PaperSloth:20161214223813p:plain
ライティングが暗いので、分かりにくいのもありますがDitherは半透明の代替として十分なクオリティですね!

f:id:PaperSloth:20161214223913p:plain
空とCubeの一部が真っ赤なのはさておき、半透明とDitherではシェーダーパフォーマンスが随分と違うのがお分かりかと思います。


以上です!


明日のAdvent Calendar

明日(19日目)は どんぶつさん(@donbutsu17) による
UE4のロード周りの設定と検証 - Qiita」です。



ではまた!

ブログ初デビューです!

前々から作ろうとは思ってたけど、ついにブログ始めた。 

 

このブログではどうでもいい日常のこととか、プログラムについて色々垂れ流していく予定です。

Twitterやってます@PaperSlothです。

 

プログラム歴は3年と少々。。。

今までやってきたプログラムとか触れたもの

 C/C++,C#,JavaScript,Java,Objective-C,Maya,UnrealEngine4,Unity...etc

 

このブログでは

Visual Studio2010でC/C++DirectXを使ったり

UnityでC#を使ったり

UnrealEngine4でBlueprintを使って

ゲーム開発をする上でのあれやこれやを書く予定です。

 

よろしくお願いします。