執筆バージョン: Unreal Engine 5.6
|
こんにちは。最近は物理演算を遊びの肝としたゲームが増えてまいりました。筆者はそういったゲームが大好きです。そこで今回は、物理演算挙動下にあるアクターを、これまた物理的な「力」で制御する方法について、「プレイヤーを追従する浮遊ロボット」をサンプルに紹介していきます。
完成イメージ
プレイヤーがロボットに近づくと起動します。起動後はプレイヤーにアタッチした目標座標を目指して追従する力制御を行っています。また砲身の旋回についても力(トルク)を使った姿勢制御を行っています。
このサンプルの制御手法としては古典制御であるPD制御を活用していますが、この味付けを調整する事により、「キビキビ感」や「ふよふよ感」の調整にもつながります。
- また、ロボットが吹き飛んでも、元の場所に戻ろうとする様子が見られます。
準備
今回はThirdPersonテンプレートからプロジェクト作成後、以下に示す実装を行いました。
実装:浮遊ロボット(BP_HoveringRobot)
1.Actorの設定
プレイヤーが浮遊ロボットに接近したタイミングでロボットを起動したいので、ClassDefaultsのStart with Tick Enabledはfalseにしておきます。
2.ActorComponentの編集
各々で思い描くロボットをメッシュで作ってください。筆者はビジュアル面の実装が苦手なので、球と角柱を繋げたものを「ロボット」としています。
そのメッシュをルートコンポーネントへ設定(DefaultScceneRootへドラッグ)後、物理演算を適用するために詳細パネルのPhysicsから赤枠のパラメータを編集します。数値は任意ですが、今回はこの画像の通りとしました。

また、プレイヤーのカプセルコンポーネントとは衝突しないよう、Mesh(Sphere)コリジョンを画像のように設定しています。

コンポーネントの設定は以上です。この段階ではこのBPをレベルに配置しても、重力に従って落下するだけです。
3.BP関数の実装
ロボットを起動するActivate関数を追加後、以下のよう編集します。SceneComponent型の変数:HoveringDestinationとbool型の変数:bInCooldownを追加しています。

次にDeactivate関数を追加後、以下のよう編集します。StartCoolTimeCountイベント関数はEventGraphにて実装しました。クールダウン時間間隔を表すfloat型の変数:DefaultCoolTimeを追加しています。


また、このロボットがクールダウン時間中か?を判別するためのPure関数を以下のように用意しています。

4.制御:Update関数の実装
ロボットという重さのある物体を、目標とする位置と姿勢(Rotation)に動かすための力とトルク値を計算します。その方法として今回は古典制御のPD制御を利用する事とします。PD制御は「比例微分制御」の略で、目標値と現在値の差を使ってモノを滑らかに動かす制御手法の一つです。今回は以下の要件で使用します。
P項(比例項) – 位置の補正
・役割:目標位置と現在位置の差に応じて力を加える。目標まで遠いほど強い力で引っ張る。(スプリングばねで引っ張るようなイメージ)
D項(微分項) – 速度の補正
・役割:現在の速度に応じてブレーキをかける。速く動いているほど強くブレーキをかけてオーバーシュート(目標位置を通り過ぎてしまう)を抑える。(車やバイクのオイルダンパーで減衰するようなイメージ)
今回ロボットを浮かすための計算式を以下に示します。位置補正のためにロボットに与える制御力をF, 回転補正のためにロボットに与える制御トルクがTとなります。

各記号の意味は以下の通りです。

特に「ゲイン」というのは、P項、D項の影響力を調整するときに使うパラメーターです。これらの値を調整することで今回のロボットの「ふよふよ感」や「キビキビ感」をアレンジしています。
あらためて式の話に戻ります。今回のロボットは目標位置に到達したら「ピタっと」静止させたいので、目標速度と目標角速度は0とします。よって(1), (2)式は以下のように簡略化できます。

この(1)’ を実現する関数名をUpdatePositionControlという関数で、(2)’を実現する関数名をUpdateRotattionControlという関数で実装していきます。

4.1. UpdatePositionControl関数の実装
BPは以下の通りとなります。

基本的に(1)’式の通りに計算しており、変数:Kpp = (100.0位)、変数:Kpd =(25.0位)としています。ただし、制御力(推力)にリミッターをかけています。今回はロボットの加速度上限を、重力加速度の5倍程度にしたいと思ったので、980(cm/s^2) x 5 x ロボット質量の1 (kg) = 4900 (cN )の値を変数:ThrustLimitに設定しています。
最後に計算した値を加算後、SphereにAddForceしています。
4.2. UpdateRotationControl関数の実装
BPは以下の通りとなります。

比例項の計算では、① RotaionをQuaternionに変換後、② 目標と現在の回転誤差を計算(Δq) ③トルクは回転軸 (独楽の軸のイメージ。左手系)の向きと、その力の大きさ(軸が長いほどトルクが大きくなる)で表せるため、Δqの回転軸の向き情報はそのまま、あとは回転誤差の大きさにゲインを乗じてトルクベクトルを計算しています。その部分にフォーカスした図を以下に示します。

変数:Krp = (5.0位)、変数:Krd = (1.0位)としています。トルクに関しては特にリミットを設けませんでした。最後に計算した値を加算後、SphereにAdd Torque in Radiansしています。これによりSphere原点で計算したトルクを作用できます。
実装:プレイヤー(BP_ThirdPersonCharacter)
本稿ではBP_ThirdPersonCharacterを直接編集して実装しています。
1.コンポーネント設定
① プレイヤーに浮遊ロボットをまとわせる目標座標を設けるため、今回はSceneComponent(画像のHoveringDestination x)を任意座標で四つほど追加しました。
② プレイヤーの近くにあるロボットを自動起動する仕組みを用意するため、今回はSphere Collision (画像のPickItenSphere)を追加し、適当な大きさに設定しました。

2.BP関数の実装
ロボットにプレイヤーが近づいた時に有効化する処理を実装します。まずPickItemSphere->Events->OnComponentBeginOverlapを有効化後、以下のように処理を組み、AttachHoveringRobot関数を追加します。


(使用中のロボットを登録する変数:UsingHoveringRobotsをプレイヤーに追加しています。)
最後に操作者が適当なキー(今回はキーボード:P)を押したとき、ロボットを無効化し、地面に落とす処理を実装します。


ゲームプレイ
ロボットに近づくとHoveringDestination x の位置にロボットがふよふよ漂い、Pボタンを押すとロボットが再び地面に転げ落ちます。CoolDown時間を過ぎるとまたロボットを再取得できます。
まとめ
この実装によって得られるメリットとしては
- 力によってロボットを動かすという自然現象と同様の手順をとるため、その挙動が自然な見た目になりやすい
- ふよふよ、キビキビとした動きを「ゲイン」の値を変えるだけで調整可能
- ロボットが何らかの外力によって吹き飛んだ時でも、特殊な復帰処理がなくとも自然に目標の位置へ復帰しようとする
などが挙げられると思います。ただ、以下のようなデメリットも挙げられます。
- 実はPD制御のみでは、ロボットが目標座標へ完全に一致する事は不可能であり、一定の誤差が残り続ける(定常偏差)。ゲインの値が小さい時は顕著にその影響が表れる
- ゲインの設定次第では目標座標に一致するどころか、むしろ振動的になってどこか検討違いの座標に飛んでいくことがある(発散)
- ゲーム中のフレームレート低下に応じて物理演算&制御の演算回数低下が起きる場合、物体の挙動が変化してしまい、ゲーム体験が損なわれる場合がある※1
今回示した制御のような分野に興味が湧いてきた方は、是非この解決方法について調べてみてください。ただ幸いにも、ゲームの世界ではこのような事が起きたとしてもプログラム的処置での対応も可能ではあります。(リアリズムを求められるゲーム等ではそうはいかないかもですが)
以上、お目通し頂きありがとうございました。余裕がある方は是非、ロボットを敵キャラクターの死角に飛ばしたり、ビームを出したり、アレンジを加えて遊んでみてください!
※1 この対策としては固定周期で物理演算&制御を行う事が重要です。例えばUEの「物理サブステップ機能」を有効化する事で毎フレームあたりの演算回数を一定に保つことができます(本稿の実装においても有効性を確認)。考慮すべきは、本設定でゲーム全ての物理シミュレーションに対して演算が行われるため、パフォーマンスと相談しながら調整等詰める必要があります。物理サブステップ機能の詳細については以下UE公式情報を参照願います。
https://dev.epicgames.com/documentation/ja-jp/unreal-engine/physics-sub-stepping-in-unreal-engine