翼のカジュアル実装②動翼
(コントロールサーフェス)
前の項では、翼を実装しました。しかし翼を作っても、コントロールできないとまともに飛べません。
ここでは、前の項で作成した翼の揚力をコントロールできるようにしていきます。
動翼(コントロールサーフェス)とは
動翼(Control-Surface)は、↑こんな感じで、翼にくっついててパタパタと動いたり伸びたりする部分のことです。
これが動くことでその部分の翼の揚力や抗力を減らしたり増やしたりして飛行機を制御してバランスを保ちます。
Wikipediaを見ると、動翼には
主操縦翼面
舵面。ローリング / ピッチング / ヨーイング という三軸の基本的な機体制御をするための動翼
エルロン(水色の部分)
エレベータ(赤い部分)
ラダー(黄緑の部分)
二次操縦翼面
他のいろんな動翼
フラップ(橙色の部分)
トリムタブ
スポイラー
スラット
などがあるようです。
翼の前縁についてるスラットや上面についているスポイラーなどはちょっと違うのですが、主操縦翼面&フラップあたりは、ついてる翼と位置が違うだけで機能はほとんど同じです。
動翼の実装
『翼に揚力や抗力を追加する機構』と考えると、概念的には結構単純です。
翼では揚力係数や抗力係数を速度や翼面積と掛け合わせて揚力や抗力を適用していたのですが、基本的には翼の揚力係数や抗力係数にそれぞれ一定の量を追加するだけです。
※モーメント/トルク(回転しようとする力)も変化しますが、カジュアル実装ではモーメントは無視する方針なのでここでは触れません※今回のカジュアル実装では翼全体を一塊で扱っているため、例えるなら翼全体にエルロンがついているのと同じ扱いになっています。
しかし実際の飛行機は、エルロンは翼の翼端の方だけについているし、フラップは翼根の側だけについているのが普通です。
こうした、翼根~翼端などの位置の違いや、エルロンが翼の横幅や縦幅の何%を占めるかといった点を今回は意図的に無視していますが、もっとリアル指向なものを作る場合はここらへんも考慮する必要があるため、ここで説明するよりすこし複雑なアプローチになっていきます(=翼を短冊状に分割するなど)。
今回は、動翼の効き目を適当に加減することで「まぁちょうどいい感じ」の効き目になれば良いや、というかんじです。
揚力係数や抗力係数が翼に与える影響を実装するうえで、二つの微妙に異なるアプローチが取れます。
迎角(AoA)を変化させる
揚力・抗力係数を直接変化させる
どちらも揚力・抗力を変化させるという点で結果的には同じですが、途中のプロセスが少しだけ違います。
① 迎角(AoA)を変化させる手法
前の項で、迎角の説明にこんな画像を載せました。
翼弦線と気流との間の角度が迎角(AoA)で、これをもとに翼の揚力/抗力係数が求まるという内容でした。
そして、翼弦線の定義は、『翼の最前縁の点と最後縁の点を結んだ線』でした。
この定義に従うと、例えばフラップやエルロンを下げた翼の翼弦線は次のようなことになるはず。
つまり動翼を動かすだけで翼弦線が角度を持ってしまうので、結果的に翼自体の迎角(Angle of Attack)が変わり、迎角が変わるので揚力や抗力も変わる・・・というわけです。
このように、動翼を「翼の迎角を変化させるもの」として捉えるのがこちらの手法。
掘り下げると欠点がないわけではないのですが、ちゃんと実装すると結構説得力のある程よい効き目になります。
ただ、翼弦線をちゃんと計算しようとすると少し実装が複雑になり(*)、シンプル実装としてはこれまたオーバーなので、今回はコントローラーやキーボードからの入力に応じてテキトーな角度を迎角に追加する形で実装してしまいます。
(*) 動翼の弦長と回転軸の位置を元に動翼の後縁点の位置を計算し、翼前縁からの角度を求める必要がある。複雑になると言っても数行増えるだけですが・・・ソースコード
using UnityEngine;
public class SimpleControlSurface_Aoa : MonoBehaviour
{
// 適用したい翼
public SimpleWing wing;
// 追加する迎角(この値を変えると効き目が変わる)
public float maxAdditionalAngle = 2.5f;
// 入力軸
public string axisName = "Horizontal";
// 入力を反転するか否か
public bool invert;
void Update()
{
float sign = invert ? -1 : 1;
// 入力を得る
float input = Input.GetAxis(axisName) * sign;
// 迎角を翼に加える
wing.additionalAoa = maxAdditionalAngle * input;
}
}
翼の方のスクリプトにも少し変更が必要です。
(薄い部分は前の項で実装済みの部分、濃い部分が追加する部分)
public class SimpleWing : MonoBehaviour
{
// ~ 省略
// 追加の迎角
public float additionalAoa { get; set; }
void FixedUpdate()
{
// ~ 省略
// Get Angle-of-attack(pitch) from local vector ローカルなベクトルから迎角(ピッチ角)を計算
float aoa = -Mathf.Atan2(localVelocity.y, localVelocity.z) * Mathf.Rad2Deg;
// Add addtional AoA by control-surface 動翼の影響を迎角に加える
aoa += additionalAoa;
// ~ 省略
}
}
② 揚力・抗力係数を直接増減させる手法
もう一つの手法は、動翼の影響を翼のCl,Cdに直接上乗せする手法です。
こちらの手法を取る場合、リアルに考えると「どれだけ増減させるか?」という量の問題があるのですが、こちらもカジュアル実装なのでテキトーな量を適用すれば良いです。
Inputから得た入力の値にそのまま係数をかけて翼に渡すのが一番手っ取り早いですが、今回は一工夫挟んで、入力で動翼自体の角度値を変え、その動翼の角度に係数をかけて揚力・抗力係数を増加させるようにしました。
(今回のコード上では余計なひと手間ですが、動翼のメッシュを見た目上回転させたい場合などにこの角度値が役立ちます)
ソースコード
using UnityEngine;
public class SimpleControlSurface_Direct : MonoBehaviour
{
// 適用したい翼
public SimpleWing wing;
// 動翼の最大偏向角
public float maxDeflectionAngle = 15;
// 偏向角1度あたりの揚力係数増加量
public float clPerDegree = 0.017f;
// 偏向角1度あたりの抗力係数増加量
public float cdPerDegree = 0.0015f;
// 動翼の現在の角度
float currentAngle;
// 入力軸
public string axisName = "Horizontal";
// 入力を反転するか否か
public bool invert;
void Update()
{
float sign = invert ? -1 : 1;
// 入力を得る
float input = Input.GetAxis(axisName);
// 動翼の角度を変える
currentAngle = -input * sign * maxDeflectionAngle;
// 揚力係数を翼に追加する
wing.additionalCl = currentAngle * clPerDegree * sign;
// 抗力係数を翼に追加する
wing.additionalCd = Mathf.Abs(currentAngle * cdPerDegree * sign);
}
}
翼の方のスクリプトの方はこんな風に変更します。
(薄い部分は前の項で実装済みの部分、濃い部分が追加する部分)
public class SimpleWing : MonoBehaviour
{
// ~ 省略
// 追加の揚力係数
public float additionalCl { get; set; }
// 追加の抗力係数
public float additionalCd { get; set; }
void FixedUpdate()
{
// ~ 省略
// Lift coefficient 揚力係数(簡易版)
float cl = aoa * 0.1f;
// Add addtional cl by control-surface 動翼の影響を揚力係数に加える
cl += additionalCl;
// Drag coefficient 抗力係数(簡易版)
float cd = Mathf.Clamp01(0.005f + Mathf.Pow(Mathf.Abs(aoa)*0.0315f,5));
// Add addtional cd by control-surface 動翼の影響を抗力係数に加える
cd += additionalCd;
// ~ 省略
}
}
もう一歩踏み込んだやつ:翼を短冊状に分けて考える