N煎ログブログ

n番煎じと言っても過言ではない今更な、でも個人的に躓いたUnityやUE4等での開発についての云々を書いていきます

【UE4】4.18以降での被破壊メッシュの作成方法

目次

4.18から仕様が少し変わったようです

「被破壊メッシュ作りたいなー」と思い、メッシュを右クリックして「被破壊メッシュを作成」を探していた所...

 

...ない

 

無いんです。どこにも。そこで検索かけてみました。しかし...

こことか

物理-Destructible Mesh - UE4 Document@com04

このサイトを見ても、右クリックから生成していました。

pafuhana1213.hatenablog.com

そこで、ご存知UE4AnswerHubにて質問してみた所...

4.18からプラグインになった

被破壊メッシュを作ることが出来ない - UE4 AnswerHub

回答者様の情報によると、どうやら4.18から被破壊メッシュは「ApexDestructablePlugin」なるプラグインとなり、デフォルトでOFFになっているそうです。

 

そこで、エディタの「編集 > Plugins」にて検索かけてみた所...

f:id:isemito:20171215195605p:plain

ありました。これです。

これにチェックを入れると従来通り非破壊メッシュを右クリックから生成することが出来るようになりました!

まとめ

非破壊メッシュは4.18から「ApexDestructablePluginプラグイン化。

エディタの「編集 > Plugins」にて「APex」と検索で出て来るのでチェックを入れる。

で使用可能。

【UE4】大量のフラグが存在する場合の管理方法

目次

大量のフラグ変数は死の予兆

『フラグ』:ゲーム制作には無くてはならない存在

ですよね?

しかしゲームの規模が大きくなるに従って使用したいフラグも増えていき、管理する量が多くなっていき...

こんなことに...(ヒィッ

f:id:isemito:20171215193113p:plain

まだ少ないので良い(良くない)ですが、これが20,30と仮に増えていった場合のことは考えたくありません...。

UE4AnswerHub神

UE4ならではの解決策がないか質問してみました。

大量のフラグが存在する場合の管理方法 - UE4 AnswerHub

『Set』!? なんぞ!!!

回答者様に教えていただいた以下サイトによると、

docs.unrealengine.com

というものがあり、

データの中に何があって何がないかというのを管理するために使う物

だそうです。

Addで追加。Unionで合成。Contains Itemで存在確認。 等々なかなか便利そうです。

要は、

ある型の複数の値を1つの変数として扱う物

ざっくりしたイメージでこんな感じだと思います。

使ってみました

実際に使用してみた所(enum値を用意して使用しています)、

f:id:isemito:20171215194001p:plain

 ア...アァ...(神を拝む姿勢

これ↓が、

f:id:isemito:20171215193113p:plain

こう↓

f:id:isemito:20171215194001p:plain

すっごいスッキリ!!

しかもアニメーション遷移の情報を今まで取ってた(依存関係強すぎ)のが不要になったおかげで依存関係を弱くすることが出来ました。

皆も使おう「Set」!

ちなみに

UE4にはBitmaskという仕組みがあり、Bit演算でフラグ管理することも出来ます。

今回私はまだ使用したことなかったSetを用いました。

Bit演算に関しては過去記事にて公開していますので参考までに。

isemito.hatenablog.com

まとめ

「Set」と呼ばれる変数で一括管理できる。

余談

UE4 Set」と検索しても出てこないので苦労する

【UE4】PlayerController削除の仕方と注意すべきこと

目次

削除したくなった理由

マルチプレイ対応ゲームを実際に制作してみて、「CreatePlayer」にて生成したプレイヤーをあの世に葬り去りたいという欲が生まれてきました。

調べても情報が出てこなかったので質問してみました。

PlayerControllerを削除したい - UE4 AnswerHub

上記回答によると「RemovePlayer」を使用することでプレイヤーをコントローラ毎削除することが出来るようです。

生成したPlayerControllerは確保しろ!

しかし、単にRemovePlayerを使用しても、何故かPlayerControllerが削除されないという事案が発生しました。

「取得に時間がかかっているのか?」「-1指定でCreatePlayerしているのが悪いのか?」等の方法を試しても解決に至らなかったので、再び質問してみた所...

RemovePlayerでPlayerControllerが削除されない - UE4 AnswerHub

回答によると、

PlayerControllerのNetRoleが"ROLE_Authority"が以外だった場合はPlayerControllerが削除されない可能性があります。

別レベルで作成されたPlayerControllerはNetRoleが違うものが設定されている可能性もあります。特にOpenLevelでパーシスタントレベルを変更した際にはご注意ください。

という事で、 どうやらPlayerControllerの持つ情報により取得が意図する状態で行えていないことが原因のようでした。

しかし!

回答者様の検証BPでも行っていますが、CreatePlayerで生成したPlayerControllerを配列内に格納し、RemovePlayer時に取り出しています。

私も同じように予め配列内に確保しておき、RemovePlayerをしてみた所、何の問題もなく正常に全てのPlayerControllerを削除することが出来ました!

つまり、

「GetPlayerControllerで取得できないのなら初めから確保しておけば無問題」

ということでした。

 よくよく考えてみれば、後で削除などの操作を行うものを逐一再取得とか非効率といいますか、無駄と言いますか。初めから手元に持っておけばよかったんですね。

まとめ

CreatePlayerで生成したPlayerControllerは確保しておき、RemovePlayerで削除しよう!

【UE4】スキル使用時等の複数のアニメーションを単一のステートで管理する方法

目次

はじめに

UE4にはアニメーションをステートマシンで遷移させる仕組みがありますが、この機能をそのまま使用して複数のアニメーションを管理しようとした場合、あっちこっちに遷移の線が伸びてしまって管理しにくくなります。

f:id:isemito:20171215175351p:plain

複数アニメーションを使用する場面は多々ありますが、これらをたった1つのステートで管理できる方法があります。

以下ではgyazoによる動画を貼っていますがデフォルトでループ再生にならないため、右クリックから「コントロールを表示」で手動で再び再生させる必要があります(ループにチェックを入れればなお良し)。

アニメーションシーケンスに渡すアニメーションを変数化

通常、ステート内にアニメーションステートを追加する場合、以下のようにアニメーションアセットをD&Dして追加すると思いますが(検索かけたり等とか)、

gyazo.com

この時、ステート内の「再生◯◯」というノードに渡すアニメーションは変数化することが出来ます。

その方法は、まずステート内に移動し、「再生◯◯」ノードの詳細にある「(As pin) Sequence」にチェックを入れるだけです。

gyazo.com

こうすることで再生させるアニメーションを直接指定させることが出来るようになりました。

あとはこのピンから線を伸ばして変数を作成します。

gyazo.com

後は、プレイヤー等の外部から「何のアニメーションを再生させるか」を引数として持つ関数を作成して呼び出せるようにしておけば、ステートを複数作成しなくても単一のステートアニメーションとして動作させることが出来ます(型はAnimSequenceBase)。

f:id:isemito:20171215182209p:plain

どう使うか

私の場合は、事前にデータベース化しておいた各Perk(スキル)情報にPerk使用時に再生させたいアニメーションを格納しておき、使用時にアニメーションBPに情報を渡す、という手法をとっています。

f:id:isemito:20171215183833p:plain

f:id:isemito:20171215183846p:plain

これを、解釈が間違っていなければ「アフォーダンス指向」と呼ぶそうです。

以下のサイトの情報を参考にしました。

www.4gamer.net

まとめ

以下のような複数のアニメーションを...

f:id:isemito:20171215175351p:plain

アフォーダンス指向でこんなにスッキリさせることが出来た!

f:id:isemito:20171215184223p:plain

Unreal Engine 4 Fukuoka 勉強会(第一回)で登壇してきました

7/1に開催されたこちら

atnd.org

の勉強会に、UE4ぷちコン用ゲームを制作する際に用いた技術の解説を行いました。

内容は「インターフェース」と「イベントディスパッチャー」です。

個人的にも少し分かりづらかった内容だったので復習出来て登壇する側としてもすごくためになりました。

 

発表資料はSlideShareにあげていますので、参考になれば幸いです。

www.slideshare.net

複数のフラグを1つの変数のみで管理する

目次

はじめに

先日、ある場でプログラミングにおける「フラグ」の扱いについて学ばせていただいたことがあったので、自分の中での復習として、またこの記事を見てくれた読者の方の学習の一助となればと思います。プログラミングに達者な方は「え?そんなの当たり前でしょ?今更?」な話になるかもしれません。ブログタイトルが如し。

フラグとは?

まず、フラグ(flag, 旗)とは何なのかという話ですが、これは単に「〇〇を満たしているか否か」であると考えて行きます。つまり「ONかOFFか」です。

フラグ情報は一つに

今までの私はフラグの管理を以下のように定義、使用していました。

bool flagA; //int の場合もあり

bool flagB;

bool flagC;

...

 しかしこれは必要になった分だけ変数が作られるため非常に非効率であり、管理しにくくなります。定義した変数だけで何十行も使用するなど、余程のことでもない限り避けるべきでしょう(私も人のことを言えた義理ではありませんが...)。

そこで、この対策としてどうするかと言いますと「全フラグを一つにまとめてしまう」事です。具体的には、

unsigned int flags;

の一行で大体のフラグ情報は事足りるようになります。何故かは順を追って説明していきます。

bool[] flags; //intの場合もあり

これでも可能ですが、使用するたびに配列要素の検索・アクセスというオーバーヘッド(ざっくり言えば余分な処理のこと)が発生するため、すっきりするかと言うと怪しくもあります。

[フラグ] = [ON or OFF] = [2通り] = [0 or 1]

 冒頭で、フラグ情報はONかOFFで考えると述べました。つまり、1つのフラグ情報の持つパターンはON,OFFの2パターンしかないのです。そこで、それらフラグを「0 or 1」で考えてみることにします。つまり、一つのフラグは

OFF:0 / ON:1

 となるわけです。

0と非0

ここで余談ですが、boolも内部的には「0 or 1」で処理されています。しかし正しくは「0 or 非0」で処理されています(boolに限らず)。ここで「非0」と言ったのは、「0でない時『正』とみなされる」からです。プログラム本等でwhileを用いたループ処理のコードで、

while(1){ 略 }

というのを見たことないでしょうか。これは「非0」が「正」であるという仕組みを利用しているのです。その為「1が正である」と勘違いされがち(私もそうでした)なのかもしれませんが、実際は「0以外が正である」というのが正しい認識と言えると思います。

その為、bool値をインクリメントした場合、その時点で0ではなくなるため、ifで処理したとき「正」とみなされます。しかし逆に、デクリメントした場合は「偽」とみなされる保証はありません。なぜならば、「非0」が「正」となるため、値が「0」でない可能性もあるからです。

参考:bool (C++)

フラグの2進数化

さて、フラグを「0 or 1」にすることでどのようなメリットがあるのでしょうか。それは「フラグ情報を2進数化することができる」ということに尽きるでしょう。

2進数とは、0と1の集まりです。

www.pc-master.jp

つまり、例えば「生きているか(aliveFlag)」「移動しているか(moveFlag)」「回転しているか(rotateFlag)」「ジャンプしているか(jumpFlag)」等といった情報を、

aliveFlag, moveFlag, rotateFlag, jumpFlag 

true, false, false, true

1, 0, 0, 1

1001

と置き換えることができるのです。

 「1001(2進数)」は整数になおすと「9」です。4つのフラグ情報がたったこれだけになります。整数にできるということはint変数に格納することができます。これが、フラグの2進数化の魅力と言えるでしょう(実際にはマイナス情報はいらないので、符号情報を消すunsingedを付けます)。

フラグ情報へのアクセス

フラグ情報を整数にする仕組みはわかりましたが、ではフラグ情報の追加や、整数からフラグ情報へのアクセスはどのように行うのでしょうか。

ここで「ビット演算」を用います。

ビット演算についてはこの(複雑な演算子)サイトがわかりやすいかと。

フラグの作成(フラグを立てる)

フラグをまとめるには、もちろん1つ1つのフラグ情報が必要です。では、そのフラグ情報を作成するにはどうするかというと「ビットシフト」という仕組みを用います。具体的には、

1 << n

で作成します。これは「1」、つまり「0001」という基本のビット情報を「指定整数値分左にずらした」ビット情報を作成するということになります。つまり、前項での「生きているか(aliveFlag)」「移動しているか(moveFlag)」「回転しているか(rotateFlag)」「ジャンプしているか(jumpFlag)」といった情報を例にとると、

1 << 0

であれば、「0001」つまり「jumpFlag」をtrueにし、

1 << 1

であれば、「0010」つまり「rotateFlag」をtrueにすると言った感じで情報を作成できます。

フラグの格納

フラグ情報の作成はできるようになりましたが、では、肝心の「情報を1つにまとめる」にはどうすればよいでしょうか。

ここでビットの「OR演算」というものを使用します。

例えば、以下のような情報があるとします、

1 << 0 => jumpFlagをtrue

1 << 3 => aliveFlagをtrue

 これら情報をunsigned int 型変数「flags」に格納するには、

flags = 0;

flags |= 1 << 0;

flags |= 1 << 3;

 と記述することで各情報を合成し、以下のような流れで格納させることができます。

1. 0000    //flagsを0で初期化(本来はunsigned int なので32個の0がある)

2. 0001    //1 << 0情報(jumpFlagをtrue)を合成

3. 1001    //1 << 3情報(aliveFlagをtrue)を合成

フラグの比較

では、実際に作成したフラグ情報の中に、あるフラグが立っているかどうかを確認するにはどうすればよいでしょうか。

ここでビットの「AND演算」というものを使用します。

 例えば、先ほど作成した変数flagsに格納されている値があるとすると、

flags & 1 << 0    //0001となる

flags & 1 << 1    //0000となる

flags & 1 << 2    //0000となる

flags & 1 << 3    //1000となる

となります。

あとはこれらをif文内で用いればよいわけです。

しかし注意すべき点として、if文の条件式では0か非0かで判断するとは言っても「0以外の値を無条件に受け入れる」というわけではないので、

 if(flags & 1 << 3)

ではなく、

if((flags & 1 << 3) != 0)

等と言うようにきちんと書いてやる必要があります。

(補足)

C++では「 if(flags & 1 << 3)」でも通るようです。

とはいえ関数の返り値をintではなく「bool」にしてると言った場合にはやはり必要となります。

フラグの削除(フラグを折る)

既に存在するフラグ情報から、任意のフラグを折りたい場合はどのようにすればよいでしょうか。

フラグを折る場合は、複数の演算子を組み合わせる必要があります。具体的には、

flags &= ~(1 << 3)

とすれば、以下のような流れで指定したフラグの情報を0(false)にすることが可能となります。

・flags                        //元フラグ                        1001

・1 << 3                     //折りたいフラグ      1000

1. ~(1 << 3)                //折りたいフラグの反転 0111

2. flags &= ~(1 << 3)  //AND演算(1001 & 0111)  0001

まとめ

ビット演算によるフラグの管理の方法をまとめました。

自分自身、最近になるまで演算の方法をよく知りませんでしたので良い勉強になりました。過去の自分のソースコードをぶん殴る機会も出てきそうですね。

また、今回の記事では通しでunsigned int型を使用していましたが、フラグ情報が32個もいらないという場合はもっとビット数の少ない変数で管理するとメモリの節約にもなるかと思われます。

どの型がどのくらいのサイズなのかはここで見れます(1バイト = 8ビット)。

データ型の範囲

【Unity5】エディタ拡張を複数のクラスに渡って記述する方法

目次

はじめに

皆さんエディタ拡張はしていますでしょうか。

エディタ拡張を使えばある程度自由にInspectorの見た目を変えたりEditorWindowを自作したりできますよね。

でも機能拡張の処理が複雑化したり、処理そのものが長くなってくると、どうしても全体のコードの長さが膨大なものになってしまいます。

そこで拡張処理を機能ごとに複数に分けようと考えるわけですが、そこでいくつかの問題がありました。

処理を分けたい時の問題点

問題1.処理クラス:拡張クラス=1:1 の仕様?

初めは私も、「一つのファイルのソースコードが長くなるのなら複数クラスに分ければいいじゃない」と考え、各処理ごとに拡張クラスを作成(変数定義は実際にオブジェクトにアタッチするクラスに)。各クラスに「CustomEditorアトリビュート」を付与させてみました。

docs.unity3d.com

しかしなんということでしょう。

古い拡張クラスの処理は新しい(後に処理された)拡張クラスに上書きされ、最後に読み込まれた拡張処理しか適用されなかったのです。

gist.github.com

このコードを作成して見るとどうなるでしょう。

おそらく、Inspectorには「intValue1」「intValue2」の両方が表示されているということはないはずです。

これおそらく、一方が拡張処理を割り当てた後で別の拡張処理が実行されるために、前の処理が上書きされているためと考えられます。

つまり、「1クラスに1つしか拡張クラスが割り当てられない」ということです。

問題2.「serializedObject」の罠

次に、serializedObjectについての問題点もあります。

serializedObjectとはEditorクラスを継承したクラス内で用いることのできる変数ですが、これは拡張される側のクラスのシリアライズされた情報が格納されています。

docs.unity3d.com

つまり、「問題1.」関連で「CustomEditorアトリビュート」が付与できるクラスが一つのみという縛りではかなり致命的な問題になります。

どういうことかというと、上記リファレンスリンク内でも記述されているように、拡張される側のクラスのシリアライズされた情報を基にserializedObjectに情報を格納しているため、「CustomEditorアトリビュート」が付与されていない拡張クラスではserializedObjectの中身は拡張されるクラスがどれか分からないためnullとなってしまいます。もちろんUnity側でエラー吐かれます。

解決策

 問題1解決策.一括定義とC#のPropertyの活用

問題1について、要は「1クラスに1つ拡張クラス」が満たせていれば良いので、マネージャクラスなりを作成し、そのクラスにだけ「CustomEditorアトリビュート」を付与させます。

ここで重要なのは、「拡張する変数はマネージャクラスで一括定義する」ということです。

つまり、Hoge1, Hoge2,...等のクラス処理で使用する変数を全て定義するということです。

そして、定義した変数は全てprivate+SerializeField化し、実際に処理を行うクラスからの変数アクセスはC#の機能であるProperty機能を用いてアクセス権限を「取得(get)」のみに留めることでカプセル化を図ります。

どういうことかというと、従来は

f:id:isemito:20170613025615p:plain

のように各クラス(Component)に対してエディタ拡張のクラスを割り当てていたのを、

f:id:isemito:20170613030655p:plain

というように、エディタ拡張をManagerクラス側に任せるようにします。

 これにより、各処理とそれら処理のエディタ拡張処理を別々に記述することが可能になりました。

 問題2解決策.引数で渡す

問題2については簡単です。

マネージャクラスのシリアライズ情報を引数として各クラスに渡してやればよいのです。

これにより「CustomEditorアトリビュート」を付与していないクラスからでもマネージャクラスのシリアライズ情報を取得することが出来ます。

メリット

さて、ここまで複数に分けて拡張処理を書く方法を紹介してきましたが、結局これの何が良いかというと、「機能単位で処理を記述・管理することが出来る」事に尽きます。

どういうことかというと、例えば私が過去に開発した以下アセットでは、1クラス1拡張制約の為、全ての処理を一つのクラスに記述していました。

例えば以下のように。

gyazo.com

見てわかる通り、行数がとんでもないことになっています(これよりさらに下があります)。これでは管理するのも処理を変更・改変するのも苦労すること請け合いです。

では機能単位で記述したものはどうでしょう。

gyazo.com

このように機能ごとに記述するクラスを分けることが出来、かつそれぞれの行数も処理内容によりますが大体多くて100行かそこらで抑えられています。これにより、各機能の改変や拡張を行いやすくなりました。以前のように「あの機能のあの処理どこに書いてたっけ...」というようなことにはならなくなります。

デメリット

逆に問題となるのは、複数のクラスに分けて記述し、かつこの方法の仕様上、どうしてもプログラムを記述する量やファイル数が多くなります。マネージャクラスを経由する為、1つの機能を追加しようとすると「新規機能クラス」「新規機能クラスのエディタ拡張」「マネージャクラスへの変数やプロパティ追加」と3つの工程が必要になります。

また、マネージャクラスへの外部アクセスがプロパティを経由して頻繁に起こる為、アクセスのオーバーヘッドが心配になります。

と、思ったのですがどうやらプロパティはインライン展開されるようでオーバーヘッドは生じないそうです。

プロパティの使用 (C# プログラミング ガイド) | Microsoft Docs

が、インライン展開されるという事はその分プロパティ使用箇所に埋め込まれるため1つ1つのファイルサイズが大きくなりがちになりそうです。

まとめ

各問題点に対する解決策を適用したコード例が以下になります。

gist.github.com

一つのコード内に書いてますが実際に実装する際はもちろん別々のファイルに記述してください。

またLoadProperty等の中で共通処理がある場合Baseクラスなり作って継承させると良いでしょう。

例えば、私はBaseクラスのOnEnable関数に

this.hideFlags = HideFlags.HideInInspector;

と 記述することでInspectorにマネージャクラス以外を表示させないようにしています。

関連記事

isemito.hatenablog.com