Unity の汎用ジョイント「ConfigurableJoint 」について、設定と動作を解説します。
物理ベースの能動的なキャラ(アクティブラグドール)を作るのに、関節のサーボモータを駆動できる汎用のジョイント「ConfiguarableJoint 」は欠かせませんーーとはいえUnity のジョイントのなかでも、機能も設定項目も多いため、分かりにくい面もあるかもしれませんね。[※1]
ここでは、ConfigurableJoint の基本的な設定と動作について、かんたんなサンプルをもとに解説します。
- ※1
- ただこのジョイントをキャラ(=人型のオブジェクト)に適用する場合、既存のライブラリを使うかぎりは、必要な知識はほぼ接続のやり方だけです(ドライブの値を指定して、直接動かすことはありません)ーーアクティブラグドールのポージングやアニメーションは、キネマティクスのキャラから、ボーン群の姿勢をたんにコピーすることで行います。また多関節のための機械学習(強化学習)のライブラリ(ML-Agents )には、ジョイントを隠蔽して操作するスクリプト群が用意されているので。
準備
まずシーンに、次のモノを配置〜設定しますーーこれらは、ヒトの体(ボディ)と腕(アーム)を模しています:
- ◯
- 生成
- ・
- キューブ×1コ(ボディ用)、キューブ×1コ(アーム用)を生成
- ◯
- 配置(ボディの向かって右側に、アームを配置)
- ・
- ボディ:値群をセット:Transform (Position (X: 0, Y: 4, Z: 0) , Scale (X: 1, Y: 2, Z: 0.5))
- ・
- アーム:値群をセット:Transform (Position (X: -2, Y: 4, Z: 0) , Scale (X: 2, Y: 1, Z: 0.5))
- ◯
- 追加
- ・
- ボディ:コンポーネント群を追加:Regidbody (is Kenematic: YES)
- ・
- アーム:コンポーネント群を追加:Regidbody (is Kenematic: NIL) , Configuarable Joint
- ◯
- 設定:アーム:Configuarable Joint
- ・
- 値をセット:Connected Body: ボディ (Regidbody)
- ・
- 値をセット:Anchor (X: 0.5 | Y: 0)
- ・
- 値をセット:((X - Z) Motion | Angular (X - Z) Motion) : Locked
確認:動かしてみる
準備ができたら、とりあえず実行してみましょうーーいまは、アームの回転・直進をすべてロックしていますーーなのでボディの姿勢を変えても(回転・直進させても)、アームは動かないはずです:
- ◯
- 対象:ボディ:Transform
- ・
- 回転させる: Rotation (X: ... | Y: ... | Z: ...)
確認:動かす方向に制約を解く
アームの制約をひとつだけ解いてみますーーZ軸の回転のロックを解除し実行すると、アームが下がることを確認できると思います:
- ◯
- 対象:アーム:Configuarable Joint
- ・
- 値をセット:Angular Z Motion: Free
ヒトの腕は、上下だけでなく前後にも動きます。なのでZ軸のほかに、Y軸の回転も解放してみますーーボディを回転させると、アームが前後や上下に回転するのを確認できるはずです:
- ◯
- 対象:アーム:Configuarable Joint
- ・
- 値をセット:Angular Y Motion: Free
確認:能動的に動かす(モータを駆動する)
アームを能動的に動かす(サーボモータを駆動する)には、ジョイントの回転ドライブを使います。
ジョイントに目標の角度と駆動の強さを設定し、実行しますーーアームが自然に上げることを確認できるはずです(実行中に、目標の角度や駆動を強さを、項目のスライダモードで変えてみてくださいーー角度や強弱に応じて、アームの角度や速さが変わります):
- ◯
- 対象:アーム:Configuarable Joint
- ・
- 値をセット:Target Rotation: Z: 90
- ・
- 値をセット:Angular YZ Drive: 100
確認:動きを制約する
アームが上がりすぎるなら、回転に制約を設定できますーー次のように設定し、実行すると、アームの上がる角度が小さくなったことを確認できるはずです:
- ◯
- 対象:アーム:Configuarable Joint
- ・
- 値をセット:Angular A Motion: Limited
- ・
- 値をセット:Angular Z limit: Limit: 45
次いで同じように、Y軸(前後)にも回転の制約を設定します……が……そもそもヒトの腕は、前後の稼働範囲が違いますよねーー前方にはほぼ自由に曲げられますが、後方にはほとんど曲げられません……しかしモデルのこの状態だと、Y軸の回転の制約は両方向に対称になるので、そのような制約は設定できません(シーンに角度制約のエディタを表示できるので、バーをスライドさせることでも確認できます):[※2]
- ◯
- 対象:アーム:Configuarable Joint
- ・
- シーン上で編集する:Edit Angular Limits: YES
- ・
- 値をセット:Angular Y Motion: Limited
- ・
- 値をセット:Angular Y Limit: Limit: ?
確認:接続の軸を変える
ConfigurableJoint では、回転の制約を非対称に設定できる軸は、1軸のみです。[※1][※2]
なのでX軸以外に非対称な制約を設定する場合、ジョイントの接続の軸を変えることで対応しますーーたとえば次のように設定することで、Y軸方向への制約を、非対称にできます(ここではいちおう順を追って、プライマリの軸のみ確定させますーーセカンダリの軸など、他の要素はいったんリセットします):[※2]
- ◯
- 対象:アーム:Configuarable Joint
- ・
- 値をセット:Axis: (X: 0 | Y: -1)
- ・
- 値をセット:Angular X motion: Limited
- ・
- 値をセット:Low Angular X limit: 0
- ・
- 値をセット:High Angular X Limit: 90
- …
- ……
- ※
- 値をリセット:Secondary Axis: Y: 0
- ※
- 値をリセット:Angular Y Motion: Locked
- ※
- 値をリセット:Angular Z Motion: Locked
- ※
- 値をリセット:Target Rotation: Z: 0
- ※
- 値をリセット:Angular YZ Drive: 0
この場合、アームの上下の制約はZ軸に移るので、セカンダリの接続軸をZ軸にしますーー上下の制約は対称にしても、人型のキャラではそれほど違和感はなさそうなので:[※4]
- ◯
- 対象:アーム:ConfiguarableJoint
- ・
- 値をセット:Secondary Sxis: Z: 1
- ・
- 値をセット:Angular Y Motion: Limited
- ・
- 値をセット:Angular Y Limit: 90
- ※1
- ConfigurableJoint の非対称な制約が1軸のみなのは、すくなくとも人型のキャラまでなら、これだけでもとりあえず破綻なく設定できる、という判断があるのかもしれません。
- ※2
- Rigidbody ではなくArticulationBodyなら、これ自体が全軸で非対称に角度を制約できるジョイントをもっていますーーただしRigidbody と違い、直接キネマティクスは使えません(ただRigidbody のジョイントからArticulationBodyにつなげることはできるので、関節的にキネマティクスを使うことはできます)ーーまたML-Agents の多関節のライブラリは、いまのところArticulationBodyには対応していません。
- ※3
- 項目の名称が、軸の接続も回転の操作もともにX・Y・Zを使っているので、混乱しがちですが……CharacterJointでは、これらは1・2などの言葉を使っているので、その点では軸を変えたときの混乱は、CharacterJointの用語の方が小さいかもしれません。
- ※4
- セカンダリの接続軸も設定することで、ジョイントの接続軸は確定しますーーこのとき先に設定したプライマリの接続軸(Y軸)も方向が変わるので、この軸の制約も適切に変えておく必要があります(このサンプルではその必要がないようにしていますが)ーーなお、ほんらいなら2軸の確定は同時に行うので、こういった操作にはなりません。
確認:能動的に動かす(接続軸を変えて)
接続の軸を変えたので、回転ドライブの目標とする方向も変えます(Z軸→Y軸)ーーこれで同じように、アームを上げることができます:
- ◯
- 対象:アーム:ConfiguarableJoint
- ・
- 値をセット:Target Rotation: Y: 90
- ・
- 値をセット:Angular YZ Drive: 100
補足
キャラの体にConfigurableJoint で腕や脚をつなぐときは、ボーンのローカル軸の方向も考慮する必要がありますーーこういった場合も、接続する軸の変更で対応します。
仕様
以下、ConfigurableJoint の仕様を、用途を中心にまとめます:
- ◯
- 接続するオブジェクト
接続するオブジェクトは、物理特性によってRidigbody かArticulation Body を選べます。空にすると、ワールド空間に接続します:
- ・
- Connected Body
- ・
- Connected Articulation Body
- ◯
- ジョイントの接続点
2コの接続点は、姿勢を変える中心を定義するために使われます(2コのポイントの中間が、中心になります):
- ・
- Anchor (X | Y | Z)
- ・
- Connected Anchor (X | Y | Z)
- ◯
- ジョイントの接続軸
プライマリのみだと回転、プライマリとセカンダリの2軸を決めることで、ジョイントの接続軸が固定します:
- ・
- Axis (X | Y | Z)
- ・
- Secondary Axis (X | Y | Z)
- ◯
- 直進の制約
3方向(X・Y・Z方向)への直進の制約を設定します:
- ・
- (X | Y | Z) Motion
- ・
- Linear Limit (Limit | Bounciness | Contact Distance)
- ・
- Linear Limit Spring (Spring | Damper)
- ◯
- 直進のドライブ
3方向(X・Y・Z方向)への駆動を指定します:
- ・
- (XDrive | YDrive | ZDrive) (Position Spring | Position Damper | Maximum Force)
- ・
- (Target Position | Target Velocity) (X | Y | Z)
- ◯
- 回転の制約
3方向(X・Y・Z軸)の回転の制約を設定します:
- ・
- Angular (X | Y | Z) Motion
- ・
- (High Angular XLimit | Low Angular X Limit | Angular Y Limit | Angular Z Limit) (Limit | Bounciness | Contact Distance)
- ・
- (Angular X Limit Spring | Angular YZ Limit Spring) (Spring | Damper)
- ◯
- 回転のドライブ(2方式)
回転のドライブには、X軸・YZ軸を独立に指定して駆動させるモードと、一括指定して駆動させるモードがあります。一括指定のモードは、回転の最短距離をたどります(クォータニオンの球面線形補完)ーーこの場合、駆動は楽ですが、想定外の動きをすることもあります(ML-Agents の多関節のライブラリは、一括モードを使っています):
- ・
- Rotation Drive Mode: X and YZ
- ・
- (Angular X Drive | Angular YZDrive) (Position Spring | Position Damper | Maximum Force)
- ・
- (Target Rotation | Target Angular Velocity ) (X | Y | Z)
- ・
- Rotation Drive Mode: Slerp
- ・
- Slerp Drive (Position Spring | Position Damper | Maximum Force)
- ・
- (Target Rotation | Target Angular velocity ) (X | Y | Z)
- ◯
- ローカル座標/ワールド座標
各種設定を、ワールド座標で行うこともできます:
- ・
- Configured in World Space
- ◯
- ソルバの調整
動きを安定させるためのしくみもありますが、ある程度ムリな動きをさせるときは、オフにしておいた方がキャラが破綻する可能性が小さくはなります:
- ・
- Projection Mode: (None | Position and Rotation) , Projection distance, Projection angle
- ・
- Enable Preprocessing
- ◯
- 接続するオブジェクトとの関係
- ・
- Swap Bodies
- ・
- Enable Collision
- ・
- Break Force, Break Torque
- ※1
- 球面線形補完については、Unity 側で専用の関数を用意しているので、かんたんにふるまいを確かめることができます:
- ・
- Vector3.Slerp()