ブループリントの基本的な構成要素⑩ ループの利用: プロシージャルなレベル デザイン

ビデオの内容紹介 
ループを利用して 1 本の柱から手動で壁を作る方法を紹介しています。使用されるノード数はこれまでよりもかなり多く中身の濃い内容となっています。最後はおまけとして入り口がついた壁が複数連結したような壁をループを利用して作ります。なお、本ブログでは、ビデオで紹介されている技法の基礎となるものも合わせて紹介していきます。

原題  Blueprint Essentials – 10 – Using Loops : Procedural Level Design

柱から壁を作る

これから作るグラフは、ウィジェット (小さな座標軸。実体は EndPoint という変数) をドラッグすることによって、1 本の柱が拡張されて (柱が追加されて) 壁になるグラフです。ウィジェットが動くとそれに合わせて壁も回転します。ただし、ウィジェットが z 方向 (高さ) に動いても影響ないようにします。

スロー グラフを組むまでの前準備
コンテンツブラウザで Blueprints フォルダを選択 → 新規 → Blueprint → アクタ (Actor) → 名前を付ける (ビデオではProceduralWall) → そのフォルダをダブルクリック (ブループリント エディタが開く)

【4.7 以降は、デフォルトで設定されているため下は必要ありません】————
→ Components タブで「コンポーネントを追加」をクリック (展開) → Scene を選択 → 名前を付ける (ビデオではRoot)
————————————————————————————————————-

→ レベルエディタに戻りコンテンツブラウザで Architecture フォルダを選択 → Pillar_50×500 を Components タブにドラッグアンドドロップ → 名前を付ける (ビデオでは BaseWall)

→ 「グラフ」編集モードにして +V をクリック (変数の作成) → 名前を付ける (ビデオでは EndPoint) → 「詳細」パネルで「検索可能」と「3D ウィジェットを表示」にチェックを入れる → コンパイル

→ レベルエディタに戻りコンテンツブラウザからこのブループリント (ProceduralWall) をビューポートにドラッグアンドドロップ → EndPoint のウィジェットをつかんで動かしてみる → ブループリント エディタのグラフ モードの Construction Script タブを開きグラフを組む

1 つ 1 つのノードの配置方法はこれまでどおり検索などを利用して簡単に配置できると思いますので (配置時の注意点はいくつか後に挙げます)、まずグラフの完成画像からご覧ください。

wall完成

次のような柱を EndPoint のウィジェットをドラッグすると

wallE1

このように壁ができます。

wallE2

それでは、グラフの意味を考えていきます。(意味が分かれば応用も簡単です。)

大まかな流れは、これまでどおり、実行ピン (ノード内の白い右向き三角) をたどると簡単に分かります (実行ピンがあるノードにつながっているその他の一連のノードは、実行ピンのノードが発動された時に実行されます)。大まかな流れは次のようになります。

Construction Script からすべてが始まります。これによって「ブランチ」(条件による枝分かれ) ノードが発動します。「ブランチ」につながっているのが条件 (Condition) です。条件は、「EP (= EndPoint) が柱から離れている」です (詳しくは、後ほど)。この条件が True である (正しい) 場合、ForLoop が作動します。離れていないと (= ウィジェットをつかんでドラッグしなければ) 何事も起こりません。

ForLoop の Last Index に接続している「EPまでの距離を柱で換算」では、柱の幅 50 単位で EndPoint までの距離を割っていくつ分柱を追加すればよいか (何回追加を繰り返せばよいか) を計算しています。

ForLoop の Loop Body には「f StaticMesh Pillar_50×500 を追加する」ノード (関数) が接続されているため、First Index から Last Index まで繰り返し Pillar_50×500 が追加されます。

追加するものは親 (Parent) に付属 (Attach To) させることになります。親は BaseWall です。(付属させる対象 (Target) は「f StaticMesh Pillar_50×500 を追加する」で追加される柱なので、その Return Value (つまり追加される柱) と f Attach To 関数の Target ピンをつなげています。

さらに付属させるための位置を Set Relative Location (相対的位置をセットする) で定めています。ループ 1 回につき (Pillar_50×500 1 つを追加するたびに) 50 単位の幅が必要となるため、Index × 50 を計算しています。X 軸方向の座標をその分増やしていくことによって、柱がその位置に追加されていくことになります。なお、位置は親を原点としてそれからの位置として解釈されます (相対的位置)。

ForLoop が完了 (Completed) すると (柱の追加が完了すると)、最後に出来上がった壁の向きを変えています。

メモ 
①Construction Script は、すでに『ブループリントの基本的な構成要素⑥ 変数の Get と Set』で出てきています。「Construction Scriptを含むアクタがレベルに配置されたり改変(更新)された場合に、このノードにつながっている関数などが実行されます」とその時に書きましたが、もっとはっきり言うと、その Construction Script が含まれているブループリントがレベルに配置された瞬間に実行されます。また、そのブループリントが修正されて改めてコンパイルされた瞬間に実行されます。ただし、ゲームプレイに入ると、Construction Script の実行は停止します。

②「f StaticMesh Pillar_50×500 を追加する」ノードは、レベルエディタのコンテンツブラウザで Pillar_50×500 を選択しておかなければ、ブループリント エディタの右クリック メニューに表示されるようになりません。

③実行ピンからワイヤをドラッグアンドドロップして検索しても目当ての関数などが見つからない場合は、出力ピンからドラッグアンドドロップして検索してみるか、あるいは次の方法を使うとよいです。

右クリックメニューの検索にすべて表示させる方法
右クリックメニューの「文脈依存」のチェックを外す

以下、部分部分の基本的な事柄を確認していきます。

「EPが柱から離れているか否かを判定」の部分について
EndPoint の位置は BaseWall からの相対的な位置としてセットされます。BaseWall がレベルに配置された段階では、EndPoint の位置が、X=0.000 Y=0.000 Z=0.000 となっています。次の画像を実行すると、

wallA1

下のような結果となります。

wallA2

EndPoint の位置は X=0.000 Y=0.000 Z=0.000 となっています。

つまり、EndPoint の各座標に (1,1,0) を掛けて (0,0,0) になると、z 軸に関してはさておいて (z 軸に関しては 0 を掛けているので、動いていても 0 になる)、EndPoint が BaseWall から x 軸および y 軸方向に一切動いていないということになり、柱を追加する必要がないわけです。ですから、「!=」(Not Equal Vector 関数) で (0,0,0) と等しくないと判断された時 (True の時) に ForLoop が発動するようにしているのです。

ところで、「!=」は「イコールではない」という意味です。たとえば、「A != B」で、A と B が等しくなければ、この「A != B」という条件は真 (True) になります。

なお、 (1,1,0) を掛けたからといって EndPoint の座標の位置は変わりません。なぜなら、その結果はどこにも (特に EndPoint に) 保存されていないからです。

「EPまでの距離を柱で換算」の部分について
f Vector Length はベクタ (この場合 EndPoint) の長さを求める関数です。ベクタの長さ (BaseWall から EndPoint がどのくらい離れたか) が分かれば、それを 50 (柱の幅) で割ることによって、柱が何個分入るかが分かります。その個数分だけ ForLoop させるわけです。なお、Floor は前にも出てきましたが、小数点以下を切り捨てています。

ところで、ベクタの長さは、この場合 (BaseWall を原点としているため)、ピタゴラスの定理を利用して √(x^2+y^2+z^2) で求められます。しかし、次のようなことが起きてしまいます (上の式のように z がベクタの長さに影響を与えるため)。

wallB1

wallB2

つまり、z 軸上に (上下に) 動かしても壁が拡張されるのです。これはこれでよいのですが、「EPが柱から離れているか否かを判定」の部分では z 軸について無視していたのですから、矛盾するような感が無きにしもあらずです。f Vector Length の代わりに f Break Vector を使って、たとえば Y ピンを割り算の関数につなげると、Y 軸方向に動かした時だけ柱が拡張されるようになります。

「追加する柱の位置を計算およびセット」の部分について
先ほどの計算を逆転させるような計算をしています。ForLoop の Index は First Index から Last Index まで変化しますが、その数字それぞれについて、50 を掛けて位置を算出しています。その値を Make Vector の Y にではなく X に入れています。だから、ForLoop が完了した時に実行される「柱をEPに正対させる」処理を外すと (ワイヤをあえて切ると)、次のように Y 軸方向 (画像では黄色に変色している方向) に EndPoint をドラッグすると、X 軸方向に柱が拡張されるのが分かります。

wallC1

Make Vector でベクタを作っているのは、f Set Relative Location (相対的位置をセットする) が vector を必要とするためです (vector は X、Y、Z の値が入るので 3D 空間内の位置を指定できます)。f Set Relative Location は、BaseWall の位置を原点として追加の柱の位置をセットします (そもそも EndPoint が BaseWall を原点としていました)。セットする位置は、追加する柱の位置ですから、「f StaticMesh Pillar_50×500 を追加する」ノードの Return Value (追加分の柱) を f Set Relative Location の Target にもつなげています。

「柱をEPに正対させる」の部分について

BaseWall の位置と EndPoint の位置をそれぞれ求めて、それらから 2 者間の角度を求め、それを BaseWall にセットします。それによって、BaseWall が EndPoint に正対します (ワールドにおける角度が同じになります)。

まず、EndPoint の位置は BaseWall を原点とした相対的位置なので、ワールドでの位置を求める (BaseWall と同じ条件にする) ためには、BaseWall の位置+ EndPoint の位置を計算します。そこで、Vector + Vector 関数を使っています。

f Find Look at Rotation (ルックアットした時の回転を求める) では、BaseWall (Start = 始点) が、Target の EndPoint に向くようにした時 (Look at するようにした時、正対するようにした時) どのくらい回転させなければならないかを計算します (BaseWall の位置をベクタとして考えると、ベクタには方向があるので、その方向が EndPoint を向くようにするということです)。なお、角度は位置が分かれば計算できます。たとえば、(便宜上 2 次元で考えると) 原点から (1,0) のベクタと原点から (1,√3) のベクタがあれば、その角度は 60 度と出せます。

f Break Rot では、f Find Look at Rotation で得られた Rotator 構造体を Pitch、Yaw、Roll に分解しています (別々に扱えるようにしています)。Yaw はそのままで、Yaw 以外をすべて 0 度にして新たな Rotator を作っているのは、Yaw が Z 方向の回転 (つまり地面に対して平行な平面での回転) であり、必要な回転はこれだけだからです。

この Rotator が新たな回転 (New Rotation) として f Set World Rotation に渡され、この関数で実際の回転がセットされることになります。もちろん、その対象 (Target) は BaseWall です。

最後に入り口がついた壁を複数連結させるグラフの画像を掲載しておきます。これはビデオとまったく同じものです (配置は若干見やすく変えていますが)。

wall71

wall72

先のグラフと異なる点は、「f StaticMesh Wall_Door_400x400を追加する」を使っている点と、幅を 50 から 400 に変わるため、割り算と掛け算の関数で 50 から 400 に変更している点です。

おまけ

グラフにコメントを入れる方法
左クリックしながら範囲指定 → C キーを押す

どうもお疲れさまでした。今回はこれにて終了です。



シェアする

  • このエントリーをはてなブックマークに追加

フォローする

コメント

  1. platoro より:

    初めまして、勉強させていただいております。
    日本語にされていらっしゃるので、非常に助かっております。

    とても基本的な質問になってしまうのですがよろしいでしょうか。
    ブルーポイントを作成して、シーンを追加し、Pillar20_500を置いて(BaseWall)、EndPointというvectorを作成していらっしゃいます。
    私の勉強環境では、BaseWallおよびEndPointが一緒に動いてしまいます。(動画では離れて動いていますよね)
    これがどうしても解けなくてコメントさせていただきました。
    おそらくもっと基礎的なところで躓いているのだと思うのですが、ご教授お願いできませんでしょうか。
    使用しているバージョンは、win7 64bit / UE4.7.2 です。

    よろしくお願いいたします。

    • なつのや より:

      platoroさん
      お読みいただき、ありがとうございます。また、返信が滞りすみません。

      (おそらく、なのですが)、EndPoint の矢印をつかむ前に中心にある小さな白い○のようなものをクリックしてみると上手くいくのではないかと思われます。まだ駄目なようでしたら、またコメントしてください。遅くなってすみません。