はじめに
2014年くらいにGitHubに上げたソースコードです。わりと評判が良いのでここにも転載します。
目標物に追従するだけですから簡単そうに思えるかもしれませんが、自然に見せるためには、センサーとアクチュエータをちゃんと設計してフィードバックゲインを調整する必要があります。
(1) やや遅れて、一定速度で追従する
敵(Enemy, 赤)が自機(Player, 白)を追いかけてきます。
自機はカーソルキーで操作できます。
敵はプレファブからインスタンス化して作成しています。
(2) 目標物と同じ速度で追従する
(1)は遅れて追従するので、あまり賢い敵には見えませんね。
では、速度を上げたらどうなるでしょうか? 目標物と同じ速度にしてみましょう。
(1)の白と赤に加えて、白のfollowers(青)があります。
followerは自機から一定の距離を保ちつつ、自機を追跡します。
followerの速度は自機と同じ値を使います。
自機の速度はPlayer.cs において private static でグローバル変数にしています。
(3) フィードバック制御
(2)のときは、followersの速度がゼロか自機と同じという2つの状態しかありませんので、動作がロボット的になり、自然に見えません。
特に、白が静止した瞬間に着目してください。その瞬間に青もピッタリと静止しています。
こういう動作は現実世界ではありえませんから、不自然に見えてしまうというわけです。
そこで、速度を指定して強制運動させるのではなく、AddForce() を使ったフィードバック制御により現実世界と同様の動作をさせます。
白: 目標物
赤: (1)遅れて一定速度で追従
青: (2)目標物と同じ速度で追従
緑: (3)フィードバック制御で追従
緑のfollowerは自機から一定の距離を保ちつつ、自機を追跡するのは(2)と同じです。
しかし今度は rigidbody2d.AddForce() によりフィードバック制御(PD制御)します。
制御フォース = 比例制御フォース + 速度制御フォース
ただし、
比例制御フォース = (目標物の位置 - 制御対象物の位置) x 比例ゲイン
速度制御フォース = (目標物の速度 - 制御対象物の速度) x 速度ゲイン
制御性能の調整は比例ゲインと速度ゲインで行います。Unityのインスペクターウィンドウで次の二つのパラメータを調整できるようにしてあります。
- controlGainP: 位置に対するゲイン。比例(P)ゲイン。
- controlGainD: 速度に対するゲイン。微分(D)ゲイン。
ちなみに、積分(I)ゲインがないため、残留偏差が残る制御系になっています。
残留偏差が問題になるのであれば積分ゲインを追加し、PID制御にすると良いでしょう。
(参考)
Wikipedia: PID制御
偏差の計算
目標物と制御対象物の位置や速度の差を「偏差」と読んでいます。
偏差の計算で物体の方向や演算の符号を間違ったり、ゲインの符号を間違ってしまうとフォースが逆向きに作用します。その結果、制御物の動きがおかしくなってしまいます。
AddForce() で制御フォースを与える前に、偏差の計算が正しいかどうかを確認しておいたほうが良いでしょう。
ゲインの調整手順
制御理論によれば、モデルの数式がわかれば最適なゲインを求めることができるらしいのですが、数式のことを考えたくない私は次のように調整しています。
[1] 比例ゲインに適当な数値、例えば「3」を入れてみる(最初は完全にカンです)。速度ゲインはゼロにします。
[1.1] ゲーム開始後も制御対象物が動かない
→ 比例ゲインが小さすぎます。比例ゲインを10倍にしてもう一度 [1] へ
[1.2] ゲーム開始直後に、目標物から離れる方向に飛んでいってしまった
→ 制御フォースの符号が違います。偏差の計算が正しいなら、比例ゲインの符号を反転させてもう一度 [1] へ
[2] 今度は比例ゲインを固定にして、速度ゲインに適当な数値、例えば「3」を入れてみる(これも、最初は完全にカンです)。
[2.1] 制御対象物がバネのように行ったり来たりする
→ 速度ゲインが小さすぎます。速度ゲインを10倍にしてもう一度 [2] へ
[2.2] 制御対象物の速度がどんどん増えていってしまう
→ 制御フォースの符号が違います。偏差の計算が正しいなら、速度ゲインの符号を反転させてもう一度 [2] へ
[3] 祈る
Licence
GPLv3
Author
溝江 智徳
https://github.com/mizoe