PavilionDV7の雑多なやつ

Qiitaから移行しました。UE4に関する記事から興味のあることまで色々書きます。

【UE5】MannyやQuinnでシーケンサーを使ってアニメーションを作るとメッシュがおかしくなる問題

はじめに

第18回ぷちコン中に遭遇した不具合の症状と回避策を備忘録的にメモしておく。

環境

UE5.0.3

症状

SKM_Manny・SKM_Quinnのコントロールリグをシーケンサーで動かし、そのアニメーションをベイクするとメッシュが破綻する。

この破綻した状態はPIE時には出現しない。(自分の環境では)

シーケンサーで作成したアニメーションをベイクする方法は以下のドキュメントの「シーケンス」という項目に詳細が書かれている。

Unreal Engine のアニメーション シーケンス | Unreal Engine ドキュメント

解決法

SKM_Manny・SKM_Quinnに同梱されているSKM_Manny_Simple・SKM_Quinn_Simpleを使う。

次の画像はSKM_Manny_Simpleを使って全く同じアニメーションを作成しプレビューさせたもの。プレビューを停止したフレームは「症状」に貼った画像と同一だが、メッシュの破綻は見られない。

Twitter

【UE5】第18回ぷちコン反省会

はじめに

第18回ぷちコンに作品を提出した。この記事では制作中に自分が感じた反省点をベッと吐き出していく。

youtu.be

ゲームの実行ファイルは以下のリンクから。

Microsoft OneDrive - Access files anywhere. Create docs with free Office Online.

反省

ゲーム開始~終了の流れは早めに完成させておく

・目的は「最悪でもこれで提出できる」状態を維持するため

・「提出できる」状態が維持できると精神的にとても楽。メンタル脆弱人間なので締切日が近づくと軽く体調を崩すが、この意識によって落ち着いて制作に取り掛かることができた。

・今回提出したゲームだと「3,2,1,START(操作開始) -> 規定数のターゲットへ到達 -> スタート地点へ戻る -> 制限時間がゼロになった -> TIME UP(終了のメッセージ)」となる

・例えば「アイテムを集めるゲーム」なら「操作開始 -> 規定数のアイテムを集める -> 集め終わった or 目的地へ向かう -> 終了の旨のメッセージ表示」という流れ

・グラフィックやサウンドマーケットプレイスや他サンプルを活用すれば素早く見栄えのいいものができる。積極的に活用しよう。

タイトルシーン~エンディングシーンの流れは早めに完成させておく

・目的は上と同様に「最悪でもこれで提出できる」状態を維持するため

・各シーンは独立して動作するようにしておくとテストプレイが捗る

・シーン遷移時に前シーンの情報が必要な仕組みを作ってしまうとテストプレイ時に時間を食ってしまう

・プレイヤーの動作を確認するためにタイトルシーンからプレイしなければならない状況はテンポが悪い

・ハイスコアとかランキングとか作る場合は一度テキストファイル等で吐き出して必要なタイミングでファイル読み込みとかで対応する。シーン間で直接、数値をやり取りするのではなく、一旦ファイルを介してやり取りする。

パラメータの調整はチョロッと触ってサッと離れる

・プレイヤーの移動速度とかカメラの慣性とか数値系は無限に調整できる

・調整中は他の要素は手つかずになるので満足に調整出来てもゲームとしては全体の進捗は全く進んでないという自体に陥ってしまう

・手早く調整して、さっさと離れることが肝心。それでも調整が止められなければタイマーをセットして鳴ったら無理矢理別のタスクに取り掛かる。

・今回、プレイヤーの移動速度やカメラの動きの数値を4日ほどいじり続けたが、色々と要素を追加・削除していく過程で再び数値を調整する必要が出てきてしまった。この時点でいじり続けた4日ほどの時間は無駄になった。

パッケージは細かくチェック

・パッケージにして初めて出現する不具合は結構ある。なるだけ細かくパッケージ化して動作確認をすることがオススメ

・今回提出したバージョンでは省いたが、コントロールリグを使ってポーズアニメーションを作成するとスケルタルメッシュが破綻する不具合が出た

・ぷちコンに注力できる時間が残り僅かだったので、原因を追求することは早々に諦めて使用箇所はゲームから削除した

・削除箇所はゲームの見栄えの要素だったので問題なかったが早めにパッケージ化していれば修正して乗っけることができたかもしれない

・スケルタルメッシュが破綻する不具合は一応回避策がある

【UE5】アンリアルクエスト3 振り返り 5日目 ~ おまけ

はじめに

最後のパート。1日目 ~ 2日目の記事でマケプレアセットを除いたプロジェクトデータをダウンロード出来るぞ。

1日目 ~ 2日目
3日目 ~ 4日目

5日目 ブラッシュアップ

初級 1ステージに複数のBGMを入れよう

初めてSound Cueの「Crossfade by Param」というノードを使ってBGMの切り替えを実装してみた。Crossfade by Paramノードではブループリントからパラメータを与えることで予めセットしてある2つのサウンドを交互にフェードさせることが出来る。

サウンド キュー リファレンス | Unreal Engine ドキュメント

プロジェクトではSound Cueで次のような実装になっている。

Crossfade by Paramノードの使い方がバチッと分かるようなページが見つけられなかったので、Fade In/Out Start/EndについてはForumに載っていた使用例の値をそのまま拝借した。何も理解せずパラメータを設定したことでブループリント側では妙な実装になっている。

Begin Playでは使用するSound CueをAudio Componentにセットし、再生をする。この時、Set Float Parameterノードを介して「Music」というパラメータに「0.3」という奇妙な値をセットしている。これはSound Cue側のCrossfade by Paramノードで1曲目のサウンドのフェードインが完了するパラメータを「0.3」に設定しているから。1曲目はフェードイン無しで再生してほしいため、パラメータMusicの初期値として0.3を渡しておく必要があった。


改めて見ると、設定するパラメータの値は次のようにしたほうが綺麗。

1曲目のFade In StartとFade In Endの値を0.0にすることで初期値を設定せずとも曲が正しい音量で再生される。パラメータに0.4以降の数値が与えられると1曲目は徐々に音量が下がっていく。0.6が渡された時、1曲目は完全に無音になる。それと同時に2曲目も0.4から音量が徐々に上がっていき、0.6が渡されると正しい音量で再生される。2曲目のFade Out Start/Endが2.0という値になっているが、与えられるパラメータは最大で1.0を想定しているので、このような大きな値にすることで2曲目が無音になってしまう事を防ぐ。

与えられたパラメータとそれぞれの曲の音量は次のようなグラフになる。横軸がパラメータ、縦軸が音量を表す。

中級 ライティングとポストプロセスを調整しよう

レベルを構成する各種床は青、赤、オレンジと色が付いているが、これはポストプロセスマテリアルによって色付けをしている。ポストプロセスの有り無しでこのように変化する。

実装には次のチュートリアル動画を参考にした。「UnrealQuest3/Materials/PPM_MultiColordOutlines」が該当するマテリアル。

youtu.be

動画からの変更点として面に対する不透明度を設定出来るようにしている。「トロン」のようなエッジにエミッシブがかかったデジタルチックな見た目にしたかったので、狙い通りのマテリアルを作成出来るチュートリアルを見つけられたことは非常に幸運だった。

動画での実装方法はカスタムデプスステンシルを使っているので、各オブジェクトに設定されたステンシル値毎に異なる色を適用出来る。この仕様のお陰で、床の属性(静的な床・壁、ダメージを食らう床、動く床等)を可視化することが出来たので、ゲーム内容をより素早く把握出来るようになった。

ポストプロセス マテリアル | Unreal Engine ドキュメント

おまけ

定数の取り扱い

プロジェクトでは以下の画像のような「特定のオブジェクト」を判別するための手段としてActor Has Tagノードを利用している。

このとき指定するタグ名はBlueprint Macro Libraryである「BPML_Constatns」で定義している。例えば「壁蹴り可能な壁」を指すタグ名は以下のように定義されている。

この「定数をマクロで定義する」方法はSEGA TECH Blogにて紹介されている。

20年オヤジのUnreal Engine 4 TIPS - SEGA TECH Blog


定数が定義できると次のような事態を大きく減らしたり、起こってしまった場合の修正作業の手間が軽減される。

どのような規模のゲームであっても画像のようなミスは誰でもどこでも発生し得る(自分はよくやる )。少し手間だがやっておく価値はある。

レベル遷移エフェクト

レベル遷移時のエフェクトはマテリアルで実装されている。FF8のバトル遷移エフェクトを目指して、Youtubeにあるチュートリアルをベースに実装したのだが、チュートリアルへのURLを失念してしまった。

少しごちゃごちゃしているが次のグループを2つ組み合わせているだけ。

プロジェクトをダウンロードしている方はLerpノードでPreviewing Nodを有効にし、Progressの値をいじると動作を理解しやすい。

ifノードの動作については公式ドキュメントやQiitaが参考になる。

Math 表現式 | Unreal Engine ドキュメント 【UE4】マテリアルノードのifについて - Qiita

今思えば、結局ベタ塗りの矩形を動かすだけなので、マテリアルじゃなくてウィジェットで作成できるアニメーションで実装したほうが簡単だった。

理想としていたFF8のバトル遷移のエフェクトはこんな感じ。 youtu.be

おわりに

エスト1つ1つの実装難易度はそれほど高くないにしても、エフェクトを追加したり、今後を見据えて丁寧な実装をしたりしているとあっという間に時間が足りなくなる。その結果、遠回りな実装、複数箇所で同じような実装をしてしまった。

もう少しゆとりを持って取り組むとイカす実装が出来と思うのだが、実際とてもむずかしい。色々チャレンジしたくなるし。

アンリアルクエストはUE歴にかかわらず、新しい機能に挑戦したり、自分のカラーを出してみたり、色々チャレンジ出来る良いイベントなので今後も続けて欲しいなと思う。

【UE5】アンリアルクエスト3 振り返り 3日目 ~ 4日目

はじめに

3日目 ~ 4日目までの内容を書いていく。1日目 ~ 2日目の記事でマケプレアセットを除いたプロジェクトデータをダウンロード出来るぞ。

1日目 ~ 2日目
5日目 ~ おまけ

3日目 ギミック

初級 当たると死ぬ炎

炎パーティクルはレベルに直置きし、それに被せるように「Pain Causing Volume」を配置している。

Pain Causing Volumeはダメージを与えるためだけのアクター。ダメージ量だけではなく、毎秒単位で与えられるダメージ量、オーバーラップ時に追加でダメージを与えるかといったパラメータがある。即死エリアや、マグマ、毒沼といった継続ダメージを受けるような箇所では活躍する。

Pain-Causing ボリュームのリファレンス | Unreal Engine ドキュメント

初めは3回食らうと死んでしまうダメージ量だった。4日目の上級「チェックポイント・リスタート」が発表されたので問答無用で即死するようなダメージ量にした。難易度は上がってしまうが、即死ギミックがあるエリアの前には必ずチェックポイントを配置し、すぐさまリトライ出来るようにしたので、それほどストレスは感じないと考えた。

この判断で良かった点は、即死にしたことでダメージのフィードバックや残体力の表現等を考える必要が無くなったので、他の要素のブラッシュアップやビジュアル面で色々実験出来る時間と余裕が出来たことである。


同様の機能を持つアクターを作るのが面倒に感じたため、ダメージを与えるボリュームとパーティクルをレベルに直置きすることにした。1箇所出来てしまえば後はコピペするだけで完了すると考えていたのだが、炎エリアは場所ごとに長さが異なるのでコピペ後にパーティクルを削除したり、でも削除するとスカスカになってしまうので、更に間隔を調整したり...という微調整が発生してしまった。

土台作りをサボったために後の工程で余計に時間を取られてしまう典型的な失敗。炎エリアはゲーム全体でも4箇所しかなかったのでそれほどダメージは無いが、あと数箇所追加するとなると大きくやる気が削がれていたと思う。

ダメージを与えるボリュームを持つアクターを作成し、Construction ScriptでNiagara ActorをAdd Child Actor Componentで子アクターとして生成する感じにすると、もっと素早く、楽にレベルデザインが出来たかもしれない。

中級 動く床

動く床は「BP_Floating_Floor」に実装されている。中身を開くとコメントにある通りBP_SineMove_Componentと実装はほとんど同じ。

「どうしてこんなことになってしまったのか」というと理由はとても単純で「BP_SineMove_Componentの存在をすっかり忘れていた」から。「BP_SineMove_Componentが使い回せるような実装になっていなかった」とかいうレベルではないおっちょこちょいっぷりである。

上級 玉が飛び出てくる砲台

一定間隔で砲弾を発射するためにTick Intervalを利用することにした。

TickイベントはTick Intervalを設定することで任意の間隔でTickイベントを呼び出すことが出来る。毎フレーム処理する必要が無いポーリング処理などは、これを設定すると余計な処理負荷を削減出来る。

「一定間隔で処理する」ためのもう一つの方法として「Set Timer by Event」を使う方法もある。(パラメータのLoopingにチェックを入れること)

こちらはTickと異なり任意の場所でTimeに指定した間隔でイベントを繰り返し実行することが出来る。

タイマーを使用する | Unreal Engine ドキュメント

UE4 一定時間ごとのタイマー処理を行う(Set Timer by Event、Clear and Invalidate Timer by Handle) 凛(kagring)のUE5/UE4とゲーム制作と雑記ブログ

4日目 ステージ

初級 ステージ変更

スーパーマリオブラザーズのワールド1-1がクリア出来る腕前があれば容易にクリア出来るように設計した。特にステージ1は死亡ポイントを配置せず、一通りの移動用ギミックを登場させて、それぞれがどのような結果になるかが体験出来るように配置した。ステージ2以降からは死亡ポイントが配置されるが、死亡ポイントの直前には必ずチェックポイントを配置してリトライし易い環境にしたつもり。

全体を通して背景には「ここは2段ジャンプするのだ」「壁蹴りするのだ」的なメッセージを込めて矢印のオブジェクトを配置してみた。本当は2段ジャンプを表す何らかのアイコンや「Space x 2」みたいな文字を出したほうが分かりやすいが、アイコンを用意するのがどれほど時間が掛かるかわからないし、文字を出すのはゲームの雰囲気に合わないと感じたのでこのようになった。

遠景の空はレベルに配置されたSkySphereBlueprintのZenith Color、Horizon Color、Overall Colorの各色を調整して設定している。これらの色を変更するには「Directional Light Actor」を「None」にし、「Colors Determined By Sun Position」のチェックを外す必要がある。

3つの項目の色をいじることで最も自由に空の色を設定出来るが、簡単に朝、昼、夕、夜としたい場合は「Colors Determined By Sun Position」にチェックを入れて「Sun Height」の数値を-1.0~1.0の範囲で変更すればOK。

UE5で新しくレベルを作成する際に選択できる「Basic」ではBP_SkySphereが無いが、Directional Lightを直接Y軸を回転させれば同様の効果が得られる。

中級 ステージを3つ以上作ろう

たまたま目に入ったカラーピッカーから「あ、RGBで3ステージ作れるじゃん」という発想に至った。ステージ1はRで赤色といった具合でステージごとのカラーに合わせて、背景の賑やかしオブジェクトの色や地面の格子マテリアルのカラーを統一させた。ステージを構成するオブジェクトがほぼキューブだけだったとこともあり、「サイバー感」「デジタル感」が強く出た統一感のあるテイストにすることが出来た。結構お気に入り。


レベル遷移はステージ最奥にあるBP_Breakable_Actorを破壊することでBlueprint Interfaceを通じてGame Modeに通知される。

レベル遷移の実装は以下の通り(「BP_UnrealQuest3_Mode」にある)。

今回のようなステージ1 -> ステージ2 -> ステージ3のような順番に1方向にステージが遷移するゲームの場合、レベルの命名規則を決めておくとレベル遷移処理は簡単な文字列操作で実装することが出来る。プロジェクトでは各レベルは「SideScrollerExampleMap_0」をタイトルレベルとして「SideScrollerExampleMap_1」「SideScrollerExampleMap_2」...「SideScrollerExampleMap_4」とレベル名を命名している。次のレベルに遷移するには末尾の数字をインクリメントすればOK。


個人的にゲームのレベル遷移やリスポーンなど「ゲーム進行に関わる要素」はGame Modeに集約するようにしている、レベルに配置されているアクターは直接キャストをして通知したり、インターフェースを通じて通知する。これによってGame Mode側で必要な処理がトリガーされるイメージ。

このへんの「どのクラスがどの役割か」については以下のドキュメントが非常に参考になる。

ゲームプレイ フレームワーク | Unreal Engine ドキュメント Game Mode と Game State | Unreal Engine ドキュメント

上級 チェックポイント・リスタートを作ろう

チェックポイントの機能は「BP_RespawnPoint」に実装している。

内容はとても単純。コリジョンにプレイヤーが衝突したら、Blueprint Interfaceを通してGame Modeに次回以降のリスポーン位置を通知している。

リスタート機能はGame Modeに実装している。実はリスタート機能そのものは下の画像の「Restart Player at Transform」のみで完結している。

注意点として、Restart Playerは生成されたプレイヤーポーンにアタッチされるPlayer Controller Objectを必要とする。Restart Playerに渡す前にPlayer Controller Objectを変数に保存せず、プレイヤーをDestroy Actorしてしまうと、参照エラーを吐いてしまうので画像のようにDestroy Actorの前に変数にPlayer Controller Objectを保存し、変数からRestart Playerに渡す必要がある。

このプレイヤーリスポーンの流れはUE4のドキュメントで解説されている。

プレイヤー キャラクターをリスポーンする | Unreal Engine ドキュメント

おわりに

次回は5日目(最終日)とおまけの内容を書く予定。 書きました。 【UE5】アンリアルクエスト3 振り返り 5日目 ~ おまけ - PavilionDV7の雑多なやつ

【UE5】アンリアルクエスト3 振り返り 1日目 ~ 2日目

【UE5】アンリアルクエスト3 振り返り 1日目 ~ 3日目

はじめに

2022-05-23 ~ 2022-5-27 の5日間に渡って開催されたアンリアルクエスト3に参加した。

この記事では主に各クエストに対する自分なりの実装を紹介・解説する。時々反省もする。「これが正解だ!」というものではなく、スマートではない実装もあるので「ははぁ、こういう実装をしたのか..」みたいな感じで読んでくれると嬉しい。

3日目 ~ 4日目
5日目 ~ おまけ

プロジェクトのダウンロード

マケプレアセット(音楽やアニメーション)を除いたプロジェクトデータは以下のリンクからダウンロード可能。

UEバージョン:UE 5.0.2

https://1drv.ms/u/s!Au-8FqgREBKZjwVYFuCn0tHMAUB7?e=mZKaxf

1日目 玉

発表当初は「なるだけ簡単にセットアップ出来るようにして、いろいろなオブジェクトを壊せるようにしよう。」と考え「基底クラスに必要な処理を組み、継承してパラメータを変更する」という方法ではなく「壊されるオブジェクトに要求される機能を個々でコンポーネントとして処理を組んで、壊したくなったオブジェクトに必要なコンポーネントをAdd Componentしていく方式」とした。Health(体力)を管理・計算する「BP_Health_Component」、簡単な上下運動をする「BP_SineMove_Component」を作った。

初級 玉が当たったら物が壊れる

BP_Health_Componentは「Health」という体力を表す変数を持つ。「Take Damageイベント」はHealth変数の値を減少させるイベント。

「体力が0以下になったかどうか」を判断する「Is Dead関数」もこのコンポーネントに実装している。関数で実装しているのは「返り値」があるから。カスタムイベントは「返り値」を返すことができない。

このコンポーネントを持つアクターは「Apply Damage」の処理が実行されると、BP_Health_Componentの「Take Damageイベント」を呼び出し体力を減らす。続いて「Is Dead関数」を呼び出し自分の体力が0以下になったかを確認する。TRUEと判断されればDestroy Actorで自身を消去する。という手順になる。

ただDestroy Actorで消滅させるだけでは寂しかったので、早々に破壊エフェクトを作った。破壊エフェクトは「キラー7」というゲームの敵死亡エフェクトを参考に一度メッシュの形状に合わせてにエフェクトを生成し、個々の粒子を散らしている。トレイラー内でエフェクトが発生している映像がある。

store.steampowered.com

中級 動く的に玉を当てる

上下運動はBP_SineMove_Componentによって実行されている。実装はこんな感じ。

sin関数を使っているのは滑らかな曲線が欲しかったから。

Frequencyはサインカーブの周期。1.0で1往復がおおよそ6秒程。2.0ならおおよそ1往復3秒ほど。0.5ならおおよそ1往復12秒ほどになる。MoveAmountは移動量で片道どれくらい移動するかを表す。この2つの変数はインスタンス編集可能にしているので、レベルに配置した後でも動きを調整しやすくしている。

これらのパラメータを調整する時は「2秒くらいで往復して欲しいから、1.0なら大体1往復6秒くらいだから...うんぬんかんぬん」とは全く考えてない。「あれ?思ったより遅い...数値増やすか」という感じでなんとなく調整している。

SineMove_Componentという名前からいろんな方向に自由な距離と時間で動かせそうだが、実装の通りZ軸に沿って動くだけ。このコンポーネントを実装したときは「今のところ動かすオブジェクトは1つだけだし、上下に動かせれば十分だから必要になったらもう少し自由に動かせるように修正しよう」と考えていた。結局は修正なんてしなかったし、何ならこのコンポーネントは1つのアクターでのみ使われるだけで、全く再利用されずじまいだった。

もう少し汎用的な往復運動を実行するコンポーネントを作るとしたら次のような実装にしたほうがいいかもしれない。

MoveDirにX=0,Y=0,Z=1と指定すればワールド座標系のZ軸に往復運動するし、XとYに1を指定すれば斜めに往復運動する。

ここではsinを使って滑らかに移動させたが、最終日に放送された解説編では「InteroToMovementコンポーネント」なるものを使って移動させていた。それを知っていれば、このような面倒な計算とかしなくてよかったのではとも思う。

上級 玉を投げる軌跡を表示しよう

軌跡は「VRテンプレート」に同梱されている「NS_TeleportTrace」というNiagaraアセットをそのまま移行し、パラメータの調整とマテリアルの変更をしただけ。つまりパーティクルで描画している。

軌跡の曲線は発射物の軌道を求める「Predict Projectile Path by Object Type」によって求められた頂点配列をNiagaraシステムに定義したユーザー変数に渡すことで実現している。(この仕組みもVRテンプレートをそのままコピペした)


軌跡パーティクルは投げる方向に模様が動くアニメーションをしている。これはリボンレンダラーに適用するマテリアル上でアニメーションさせている。ベースは次のチュートリアル動画を参考にした。

youtu.be

ここから「投げる方向」というのをわかりやすくするために少しノードを加えた。

2日目 アクション

初級 2段ジャンプで輪をくぐる

2段ジャンプはCharacterクラスの「Jump Max Count」を「2」に指定すればOK。

UE4 Characterの多段ジャンプについて - PaperSloth’s diary

UE4 多段ジャンプを実装しよう - UE4初心者が頑張ってるブログ


自分のゲームでは輪をブースターとして利用することにした。ブースターとしての機能は次のように実装するだけでOK。

輪の中央部分に配置したBox Collisionがプレイヤーを検知すると、Launch CharacterでBoost Dir方向に勢いよく飛ばす(瞬間的に押し出す)実装になっている。LaunchCharacterノードは対象がCharacterクラスまたはCharacterクラスを継承しているもののみが対象となるので、ActorやPawnでは機能しないことに注意。

どの方向にブーストされるかが分かるようなパーティクルと連続使用できないようにクールダウンを実装し、最終的には次のような実装になった。


ブースターのエフェクトはシンプルな構成になっている。

重要なものは「Shape Location」「Add Velocity」「Gravity Force」の3つ。

Shape Loationでは粒子の初期生成位置を決めていて、円の外周に沿って生成されるようにしている。Ring Radiusパラメータはブースターアクターにアタッチし、都度再生しながら数値を調整した。

Add Velocityは粒子のスポーン時に「物理的な力」を瞬間的に加える。「From Point」モードはパーティクルの中心から物理的な力を加える事を意味している。通常Velocity Speedに正の値を設定すると「中心から外側」に向かって粒子が飛ぶ。しかし負の値を入れることで「外側から中心へ向かう力」に変化する。

Gravity Forceは名前の通り「重力」を適用するが、力の方向や大きさは自由に設定出来る。初期値はX=0,Y=0,Z=-980で-Z方向(つまり地面)に向かって常に力が加わる。この値を正の値にするとZ方向(つまり空)方向に向かって力が加わることになる。Add Velocityで中心へと向かわせ、Gravity Forceで上方向に打ち上げることで「中心へと向かうながら上に移動する」ちょっとオシャレなエフェクトができた。

中級 空中ブランコ

実はゲーム中の空中ブランコは物理由来のものは一切使っていない。初めはPhysics ConstraintとRadial Forceを使い、物理の力で作っていたが、理想とする動きができなさそうと感じたので1日目の中級と同じように数学を利用してブランコっぽく動かすことにした。

ブランコの動きには「シグモイド関数」と呼ばれる関数を利用している。シグモイド関数が出力する値は0-1の範囲でS字カーブを描く。この特徴的なグラフがブランコのような振り子運動をそれっぽく再現するのに最適だと思った。

[活性化関数]シグモイド関数(Sigmoid function)とは?:AI・機械学習の用語辞典 - @IT

シグモイド関数への入力値にsin値を3.5~-3.5の範囲に調整している。シグモイド関数は入力値が-6の時おおよそ0を出力し、入力値が6の時おおよそ1を出力する。この通りに6~-6の範囲に調整すると、振り始めと振り終わりで「あれ?止まった?」というくらい値の変化が小さくなってしまうため、いろいろな値を試した結果3.5~-3.5の範囲に調整したほうが心地よかった。入力値によってシグモイド関数のグラフがどのように変化するかについては次のような計算サイトを使ってみると理解しやすいと思う。

シグモイド関数 - 高精度計算サイト

シグモイド関数の結果はLerpに繋がっており、Lerpは-15~-165の値を出力する。これはsinとcosによる円の動きを横から見た時、正の値を指定すると右回り、負の値を指定すると左回りになる。今回は下半分で振り子運動して欲しいので負の値を指定し左回りにおおよそ半回転するようにした。

「なんで0と-180にしなかったのか?」についてはシグモイド関数のパラメータの理由と同様、この値が実行して一番心地よい動きをしたから。Lerpの値は度数を取るsin(degree)とcos(degree)で使われる。ラジアン値を取るsin、cosを使わなかったのは「度数のほうがわかりやすいよなぁ」と思ったから。

上級 壁蹴り

プレイヤーキャラクターには壁を検知するためのBox Collisionコンポーネントを追加しており、コンポーネントが「壁蹴り可能な壁」に衝突するとフラグが立って壁蹴りのための処理が実行される。

壁蹴りフラグが立った時、Tickでは壁ズリ処理が実行される。画像の処理によって、壁に爪を立てて落下速度をゆっくりにしているように見せる事ができる。お手軽処理でプレイヤーに要求するアクションの難易度を下げることができたはず。

壁蹴りはプレイヤーに複雑な操作を必要とせず、ボタンを押すだけでポンポン実行されるようにしてみた。具体的には壁蹴り時は真上に飛ぶのではなく、斜め後ろに飛ぶようにし、それに合わせてプレイヤーの向きを反転(つまり後ろを向く)ようにした。

この実装はアクションの難易度を大きく下げることが出来るが、同時にレベルデザインについても制限がかかってしまう。斜め後ろにジャンプするため、1つの壁だけで高く登る事ができないので、ゲーム内では壁蹴りが必要な箇所は必ず「壁に挟まれた地形」となっている。

1つの壁だけで壁蹴りが出来るようにするためには、Character MovementコンポーネントAir Controlといった数値を大きくしたり、ジャンプの方向を真上に近い方向にすることで実現できるかもしれないが、必要以上に操作難易度を上げる必要は無いと判断しこのような実装になった。

おわりに

今回は2日目までの実装内容を紹介した。次回は3日目以降の処理内容を紹介する。

【UE5】アンリアルクエスト3 振り返り 3日目 ~ 4日目 - PavilionDV7の雑多なやつ

【UE5】Mass AIでTrafficシステムを動かしてみる - Part2 車両を出す

はじめに

【UE5】Mass AIでTrafficシステムを動かしてみる - Part1 表示するまで からの続き。

Wheeled VehicleとStatic Meshをセットアップして、3段階(High、Mid、Low)のLODを正しく機能させる。

Part 1から追記する形を取る
使用しているUE5バージョン : UE 5.0.1

1. Wheeled Vehicleのセットアップ

Content Browserにて[Content -> Vehicle Template -> Blueprints -> Sports Car]にある[SportsCar_Pawn]を複製する。名前を[BP_High_Res_Vehicle]に変更する。


BP_High_Res_Vehicleブループリントを開き、Event Graphにあるノードをすべて削除する。

ComponentsにあるMesh(VehicleMesh)以外のコンポーネントも削除する。

[Vehicle Movement Component]を選択し、詳細パネルにある[Vehicle Input -> Requires Controller for Inputs]のチェックを外す。


上部にあるバーの[Class Settings]をクリック。詳細パネルの[Interfaces][Mass Traffic Vehicle Control Interface]を追加する。

My Blueprintパネルの[Interfaces -> Mass Traffic -> Traffic Vehicle -> Set Vehicle Inputs]をダブルクリックする。Event GraphにSet Vehicle Inputsノードが追加される。

Set Vehicle Inputsで次のようにノードを組む。

2. Part 1の6番で作成したData Assetを編集する

該当のData Assetを開き[Traffic Vehicle Visualization]を次のように変更する。

  • High Res Template Actor : [BP_High_Res_Vehicle]
  • LOD Representation -> High : [High Res Spawned Actor]

続いて[Traffic Vehicle Simulation]を次のように変更する。

  • Half Length : [260]
  • Half Width: [120]
  • Front Axle X : [130]
  • RearAxle X : [-130]
  • Physics Vehicle Template Actor : [BP_High_Res_Vehicle]

3. 動作確認

道幅が狭くカーブで頻繁に事故るが、Wheeled Vehicleは問題なくスポーンされ、カメラから離れるとPart 1で作ったBP_Low_Res_Vehicleに切り替わる様子が確認できる。

4. Instanced Static Meshを表示する前準備

4-1. MassUpdateISMProcessorの設定を変更する

ツールバーから[Edit -> Project Settings]でProject Settingsを開く。検索バーに[MassUpdateISM]と打ち[Module Settings -> Mass Entity -> Processor CDOs -> Index[84] -> Auto Register with Processing Phases]のチェックを外す。

4-1. Instanced Static Mesh用のマテリアルを作成する

Content Browserを右クリックし[Material]をクリック。名前を[M_Mass_StaticMesh]とする。


M_Mass_StaticMeshを開き詳細パネルの検索バーに[Instanced]と入力し[Used with Instanced Static Meshes]にチェックを入れる。

次のようなノードを組む。

PerInstanceCustomDataノードは特に何かを設定する必要はない。

この箇所は非常におまじないめいていて奇妙な感じがする。やっていることは恐らくGPGPU(GPUを使った汎用的な演算処理)を利用し車両のカラーを変更したりするため。City SampleではPerInstancedCustomDataから車両のカラーを決定したり、ウィンカーの明滅といった処理で使っていた。(詳細は追っていないので興味のある方は是非覗いてみる事をおすすめ)

少し脱線するがInstanced Static MeshとマテリアルのCustom Dataを利用したGPGPUについていくつかの記事を貼っておく。

InstancedStaticMeshとCustomDataとTextureで大量オブジェクト (UE4 Advent Calendar 2020) - ながむしメモ

【UE4】Custom Data Valueを利用してブループリントからマテリアルにランダム値を渡したい – 建築グラビア

UE4でゆうなまを作りたい 第2回 - Qiita

5. Data Assetを編集する

[Traffic Vehicle Visualization][Static Mesh Instance Desc]を展開。+ボタンを押し、次のように設定する。


  • Mesh : [SM_Ramp](ここは何でも良い。とりあえず頂点数が少ないやつを選んでおく)
  • Material Overrides -> Index[0] : [M_Mass_StaticMesh]

続いて、Params -> LOD Representationを展開しLowに[Static Mesh Instance]を設定する。

6. 動作確認

新しくWheeled Vehicle、Static Meshがスポーンされ、カメラから遠くなるとStatic Meshに切り替わる様子がわかる。

プロジェクトのダウンロード

https://1drv.ms/u/s!Au-8FqgREBKZjwRmpj5KS14aYXmA?e=mgVFUk

このパートの公開に伴いPart 1で公開していたプロジェクトデータは非公開にする。

おまけ1. Half LengthやHalf Widthの測り方

2番で設定したHalf LengthやHalf Widthに設定した数値は以下の長さを表す。画像は車両メッシュを真下から写したもの。(このプロパティの詳細な説明はHalf LengthやHalf Widthをマウスオーバーしたときに表示されるパネルに書かれている)

Half LengthやHalf Widthは車両の中心から最も遠い距離を入力する。

具体的な計測方法は車両メッシュコンポーネントの子としてScene Componentを追加し座標を[X:0 ,Y:0, Z:0]に設定する。この地点を車両の中心位置とする。Half Lengthを測りたいときは、追加したScene ComponentをそのままX軸に沿って動かし、車両の端に位置したときのX座標がHalf Lengthになる。

この記事ではHalf Lengthを260と少し大きめに取った。 同じ要領でHalf WidthやFront Axle Xを測定する。

おまけ2. Mass Trafficの設定

[Project Settings -> Mass -> Mass Traffic]ではTrafficシステムの設定をいじることが出来る。Speed Limitsではレーンに設定したTagsに応じて速度制限を設ける事が出来る。次の画像は直線では早く、カーブや交差点では遅いスピードになるようにしてみた。(あまり効いてる感じはしない...)

他にも車両の速度(これはVehicle Movement Componentとは別)を設定したりも出来る。色々触ってみると面白いかも。

おまけ3. アサーションによるエディタクラッシュ

4番で行った各設定やマテリアルのおまじないの内、どれか1つでも欠けると次のようにCustom Dataに関するアサーションによってエディタがクラッシュする。(画像赤枠に注目)

これはエディタのプレイ直後に現れるので、同様のアサーションで引っかかっていれば再度設定忘れやマテリアルの設定忘れ(特にUsed with Instanced Static Meshのチェック忘れ)を確認する。

【UE5】Mass AIでTrafficシステムを動かしてみる - Part1 表示するまで

はじめに

おおまかな流れはMass AIを動かしてみる - Part1と同じ

載せている画像は撮影時期が前後しており、おそらく手元のエディタと見た目が一致しない箇所がある。その場合でも文章通りに正しく設定できていればOK。気にしないこと

このページの工程が完了すると、次のような結果が得られる。

続きのPart 2はこちら。

【UE5】Mass AIでTrafficシステムを動かしてみる - Part2 車両を出す - PavilionDV7の雑多なやつ

プロジェクトはこちら

Part 2の最後にプロジェクトのダウンロードURLを貼ったのでそちらからダウンロードする。

1. Vehicleテンプレートで新しいプロジェクトを作成する

作成後は後の手順のために一度プロジェクトを閉じておく

2. City Sampleから必要なプラグインを移行する

  • .uprojectがあるフォルダに直接[Pluginsフォルダ]を作成
    • 「直接」=エクスプローラーからフォルダを新規作成すること
    • エディタからフォルダを作成してもPluginとして認識されないので注意
  • City Sampleの同じくPluginsフォルダから3つのプラグインを自身で作成したPluginsフォルダにコピペする
    • AnimToTexture
    • RuleProcessor
    • Traffic

City Sampleが手元に無い、もしくはダウンロード出来るほど空き容量が無い場合、上の「プロジェクトはこちら」で配布しているプロジェクトの中に必要なプラグインが同梱されているので、そちらからコピペしても良い。

3. プロジェクトを開き必要なプラグインを有効化

  • Mass AI
  • Mass Crowd
  • Mass Entity
  • Mass Gameplay
  • Zone Graph
  • Zone Graph Annotations
  • Traffic

一覧の中にTrafficが無ければプラグイン移行に失敗しているので再度手順を確認する。 有効後プロジェクトを再起動。

4. Project Settings -> Engine -> Zone Graph でセットアップ

Lane Profile の作成。名前は[↑↓400]とし、2つのレーンを作成する。それぞれDirectionを[Forward]と[Backward]に設定しWidthは[400]とする。

上矢印は全角で「zk」、下矢印は全角で「zj」と打てば変換出来る。自分はGoogle日本語入力を使用しているが他のIMEではできないかもしれない。

Tags で2つのタグ名を定義する。それぞれ[Vehicle][Intersection]とする。 セットアップ終了時点でこのようになる。

5. Actorクラスを継承したブループリントを作成

名前を[BP_Low_Res_Vehicle]とする

Default Scene Rootの子として[Cone]を追加。ConeのLocationを[X:0, Y:0, Z:60]にする。

6. Mass Entity Config Asset構造体を継承した Data Asset を作成

Content Browser で右クリック -> Miscellaneous -> Data Asset

一覧から[Mass Entity Config Asset]を選択。

作成したData Assetの名前を[VehicleEntityConfig]とする。

7. 6番で作成したData Assetをセットアップ

Traitsを3つ追加しそれぞれ

  • LOD Collector
  • Traffic Vehicle Visualization
  • Traffic Vehicle Simulation

を追加する。


[Traffic Vehicle Visualization]を展開し、[Low Res Template Actor][BP_Low_Res_Vehicle]を指定。 [Params -> LOD Representation]を展開しHigh、Midium、Lowの全てに[Low Res Spawned Actor]を指定する。

この時点でStatic Mesh Instance DescやTraffic Vehicle Simulationトレイトについては触れる必要はない。

8. レベルにZone Shapeアクターを追加する

T字路、カーブ、直線の3つの道路パーツを作成する。

最初にT字路を作る。上部のツールバーからZone Shapeアクターを追加する。

名前を[Intersection]に変更する。

座標を[X:0.0 Y:0.0 Z:0.0]に移動。詳細パネルの[Zone -> Shape Type][Polygon]に変更。続いて[Tags][Vehicle、Intersection]を指定する。2つのTagを設定すると枠内に伸びる矢印の色が変わるはず。

Zone Shapeを構成するポリゴンのポイント(頂点)の内、いずれかのポイントを選択し[Selected Points -> Type][Lane Profile]に変更する。

もう片方のポイントも同様に設定する。 設定が終わると画像のように「ねじれ」が発生する。

「ねじれ」を修正するためにはポイント付近にある[黒矢印][枠線内に向かう]ように各ポイントを回転させてあげれば解決する。 画像内の青枠はわかりやすくするために画像編集ソフトで後付したもの。実際のエディタでは枠が見づらいので頑張るしかない。(頑張れ)

この修正方法はポイントが3点、4点になっても同じ。枠の外を向いているポイントがあれば枠内に向くように回転させる。

Zone Shapeの枠の内、1つを選択し右クリックから[Add Point Here]を選択する。(自分はエディタの言語を「英語」にしている。「日本語」の場合はどのような表記になっているか不明)

新しく追加されたポイントを選択し、Positionを[X:0, Y:400, Z:0]に変更。続いてTypeを[Lane Profile]に変更する。「ねじれ」が発生しているので、先程の要領で修正する。(黒矢印を枠内に向ける)

各ポイントの位置を次の画像のように修正する。

  • X軸方向 : [X:800, Y:0, Z:0]
  • -X軸方向: [X:-800, Y:0, Z:0]
  • Y軸方向: [X:0, Y:800, Z:0]

同じ要領で空いた枠にもポイントを追加すると十字路を作ることが出来る。(今回は必要ないので割愛)


「カーブ」を作成する。 先程作成した[Intersection]を複製し、名前を[Curve]に変更する。座標を[X:0.0 Y:0.0 Z:0.0]に移動する。

「Curve」を選択し[-X軸側]のポイントを削除する。

これでカーブが得られる。


「直線」を作成する。 上部のツールバーからZone Shapeアクターを追加する。名前を[Road]にする。座標を[X:0.0 Y:0.0 Z:0.0]に移動。

詳細パネルの[Tags][Vehicle]を指定する。

Roadの長さは配置時に調整する。

9. Zone Shapeを配置する

8番で作成した道路パーツを以下の数だけ揃える。

  • Intersection x 2
  • Curve x 4
  • Road x 7

各パーツを以下に示すLocationとRotationに設定する。

  • Intersection
    • Location X:0, Y:-3000, Z:0
    • Rotation X:0, Y:0, Z:0
  • Intersection2
    • Location X:0, Y:3000, Z:0
    • Rotation X:0, Y:0, Z:180


  • Curve
    • Location X:-4000, Y:-3000, Z:0
    • Rotation X:0, Y:0, Z:0
  • Curve2
    • Location X:4000, Y:-3000, Z:0
    • Rotation X:0, Y:0, Z:90
  • Curve3
    • Location X:4000, Y:3000, Z:0
    • Rotation X:0, Y:0, Z:180
  • Curve4
    • Location X:-4000, Y:3000, Z:0
    • Rotation X:0, Y:0, Z:270


  • Road
    • Location X:-2000.0, Y:-3000, Z:0
    • Rotation X:0, Y:0, Z:0
  • Road2
    • Location X:2000.0, Y:-3000, Z:0
    • Rotation X:0, Y:0, Z:0
  • Road3
    • Location X:-2000.0, Y:3000, Z:0
    • Rotation X:0, Y:0, Z:0
  • Road4
    • Location X:-2000.0, Y:3000, Z:0
    • Rotation X:0, Y:0, Z:0
  • Road5
    • Location X:0, Y:0, Z:0
    • Rotation X:0, Y:0, Z:90
  • Road6
    • Location X:0, Y:0, Z:0
    • Rotation X:0, Y:0, Z:90
  • Road7
    • Location X:0, Y:0, Z:0
    • Rotation X:0, Y:0, Z:90

10. ツールバー -> Build -> Build Zone Graph をクリックする

Zone Graphをビルドすると次のような結果が得られる。

Roadのスプラインのポイント位置を調整し他のIntersectionやCurveと接続する。 再び[Build Zone Graph]をクリックすると次のような結果が得られる。

他のZone Shapeと接続するとき各枠はできるだけぴったりと重なるように配置する。オーバーでも寸足らずでもレーン同士は接続していないと判断され、車両がスタックしてしまう。

[Project Settings -> Zone Graph -> Build Settings]にはレーン同士を接続する最大角度やスナップ距離、スナップ角度等が設定出来るので、多少大雑把に配置したい場合はこの辺の数値をいじってみると良いかもしれない。

11. レベルにMass Spawnerアクターを追加する

Mass Spawnerの座標はどこでも良い。

追加したMass Spawnerアクターを選択し、Mass プロパティをセットアップする。 Count に生成したいエージェントの数を[10]と指定。 Entity Typesの+ボタンを押し、追加された要素を展開。Entity Configに6番で作成したData Assetを指定する。

Spawn Data Generatorsの+ボタンを押す。Generator Instance に[Mass Traffic Vehicle Spawn Data Generator]を指定。 Generator Instanceを展開し[Vehicle Type Spacing]の+ボタンを押す。更に展開し次の画像のように各パラメータを設定する。

それ以外のDefault SpaceやRandom Seed等は触らなくてよし。

12. 動作確認

プレイしエージェントがスポーンされているか確認する。 画像は「シミュレーション」で実行し、Zone Shape内にきちんとスポーンされていることを確認している。