見出し画像

【制作中】多脚ビークル開発記録【cluster】

◆注意:本記事はclusterのRidable Item(いわゆる"乗り物")機能で多脚型の乗り物を制作する際の備忘録です。進捗に合わせて順次更新されます。また、その性質上内容に不正確、あるいは不足不必要な要素の存在が予想されます。

どうも、カティアです。今回は2年ほど前に制作したワールド「UNBIPED MUSEUM」の更新に向けて、「乗れる」多脚型マシンの開発を目指します。もちろん乗れるだけではなく、ちゃんと歩き回れる(あるいはそれ以上)モデルです。

◆試製多脚車両「アラクネ」

画像1

■全長:3.6m
■全幅:3.6m
■車高:1.9m
■定員:1名
■動力:足漕ぎ式でないことは確か

画像2

折角魅力的な歩行ギミックを用意しても、乗ってる間足元が見えなかったら残念です。そこでまずは、テンプレートワールドのゴーカートを参考に車体サイズを設定しました。テンプレートの椅子や乗り物は座席やコクピットの寸法設計にも便利なので、研究大事です。

足回りは現実に研究されている多脚重機を参考にしたものを放射状に6本配置。3脚ずつ2ペアで動く、いわゆるトライポッド歩容型を目指します。

◆コンポーネントを追加する

画像3

モデルにボーンを設定したものをUnityにインポートして、乗り物として使えるようコンポーネントを追加していきます。モデルの差し替えや使いまわしを想定して、プレハブを作ってからモデルをその子として配置します。

画像4

まず椅子や乗り物を定義するRidable Item。モデリングしたらボーンを入れる前に、アバターがちゃんと座れるか、ハンドルや操縦桿の位置が正しいかテストするといいかもしれません。今回は歩くたびに座席が揺れて欲しいので、座席はアーマチュアの下に置きました。

Character ItemはUnityのキャラクター制御「Character Controller」を動かすためのもので、これで多少の傾斜や階段を上ったり出来るようになります。Unityにおけるキャラクター制御は他にRigid bodyを使ったものがあります(テンプレートでは車とヘリがこの方式)が、今回はCharacter Controllerを試してみます。

画像5

必要に応じて乗り降りのトリガーを設定、エンジン音などの設定や降りた時の操作状態をリセットします。これを設定しないと、移動中に降りた場合そのまま進んでいってしまいます。

画像6

乗り物に動いてもらうために、Steer Item Triggerを追加します。操作量は二次元ベクトル量のVector2で取得し、y成分とx成分をそれぞれ前進後退と旋回用のパラメータとしてアニメーションコントローラに渡します。

ジャンプや砲撃など追加のアクションは、Additional Axis Input Triggersを利用することになりますが、今回はひとまず割愛。

◆アニメーション設定

画像7

歩行アニメーションを設定して、先ほどのSteer Item Triggerで動かせるようにします。まずは、前進後退旋回を目指します。

画像9

アニメーションは前進後退右折左折、それに停止状態の5種類を用意します。今回アニメーションによってオブジェクトが動くので、AnimatorコンポーネントのApply Root Motionを設定した上で動きに合った移動量を指定します。また、歩く際に座席が上下してほしいのでアーマチュアも上下します。

画像8

Animation Controllerのベースレイヤーは、EntryからBlend Tree(右クリックで作れる)に直接繋ぎます。Blend Treeのパラメータは前進後退用と旋回用の2つを指定し、それぞれの組み合わせに対してどのアニメーションを再生させるか決定します。

◆歩行パターン

画像10

さて、3Dモデルを最低限歩行させるシステムは整いました。残るはある意味最大の問題である、歩行アニメーションの用意です。ここでは昆虫や多足ロボットの歩行アルゴリズムなどが参考になります。

画像11

六足歩行の基本、トライポッドは上の図でいう1,3,5と2,4,6をそれぞれセットで動かす歩行法です。例えば前進の場合、単純化するとモーションは以下の4ポーズの繰り返しとなります。

#前進モーション
pose1 初期状態 
pose2 1,3,5を持ち上げ、前に出す 2,4,6を後ろに下げる 全体を進める
pose3 1,3,5を下ろし、後ろに戻す 2,4,6を前に戻す 全体を進める
pose4 1,3,5を後ろに下げる 2,4,6を持ち上げ、前に出す 全体を進める

接地したままの足は、胴体が進むと相対的に後ろを向くので上から見ると足を前後に動かしているように見えます。同様に後退,右折,左折は、

#後退モーション
pose1 初期状態
pose2 1,3,5を持ち上げ、後ろに下げる 2,4,6を前に出す 全体を退く
pose3 1,3,5を下ろし、前に戻す 2,4,6を後ろに戻す 全体を退く
pose4 1,3,5を前に出す 2,4,6を持ち上げ、後ろに下げる 全体を退く
#右折モーション
pose1 初期状態
pose2 1,3,5を持ち上げ、1,3,4,6を後ろに下げ、2,5を前に出す 全体右旋回
pose3 1,3,5を下ろし、1,3,4,6を前に戻す 2,5を後ろに戻す 全体右旋回
pose4 1,3,4,6を前に出す 2,4,6を持ち上げ、2,5を後ろに下げる 全体右旋回
#左折モーション
pose1 初期状態
pose2 2,4,6を持ち上げ、1,3,4,6を後ろに下げ、2,5を前に出す 全体左旋回
pose3 2,4,6を下ろし、1,3,4,6を前に戻す 2,5を後ろに戻す 全体左旋回
pose4 1,3,4,6を前に出す 1,3,5を持ち上げ、2,5を後ろに下げる 全体左旋回

この4つのモーションと、停止している時のアイドルモーションを先ほどのBlend Treeに設定することで、平面状を自由に移動することが出来ます。あるいはモーションを追加して、超信地旋回ではなく前進しながらターンするのもありです。

◆改造1:ジャンプがしたい

画像12

さて、見た目がモックアップだったので飾りを少々。とりあえず完成かな…というところで気づく。遅い。

毎秒60フレームのモーションで1mしか進んでいない、つまり時速3.6㎞相当。フレームを縮めて速くしてもいいけど…ここは大ジャンプしてみよう。

画像13

Character ItemはJump Character Item Gimmickで簡単にジャンプさせられます。しかしSteer Item Triggerの追加入力で直接これを呼び出すと、不都合が生じることに気づくはず。乗ったとたん車体が跳ねる。簡易パワーローダーの時も気になっていましたが、どうやら直接呼び出してはいけないようです。

画像14

テンプレートのポニーは、もっとずーっと複雑な仕組みをしています。

・追加入力時、Float形式の「additionalInput」とSignal形式の「OnAdditionalInputChanged」を送る
・OnAdditionalInputChangedでItem Logicを起動
・additionalInputが0より大きければBool形式のisPositiveをtrue
・isPositiveとcanJump(共にBool)が両方trueの場合のみ、jump(Bool形式)をtrue
・jumpがtrueの場合、Jump(Signal形式)を送る
・jumpがtrueの場合、isInAir(Bool形式)をtrue
・jumpがtrueの場合、canJumpをfalse

……つまり、
・追加入力時、値とその変更通知の二種類の信号を送る
・値変更通知でロジックを起動
・入力値が0より大きく、ジャンプ可能状態であればジャンプにチェック
・ジャンプにチェックが入っている場合馬をジャンプさせ、滞空状態に設定して、ジャンプ可能状態から外す。
…成程。直接Signalで送ると、「0」が入力されて起動されてしまうので、信号と値をセットで送って値が正か判定。そして既にジャンプ中でないかの判定も行う。そのためには、接地判定が必要になる。沼の予感がする。

画像15

Is Grounded Character Item Triggerは接地状態とその変化を通知します。もう何となくわかってきましたが、接地状態の変化をトリガーにロジックを起動します。

・IsGroundedChangedをトリガーにロジックを起動
・isInAirとisGrounded(共にBool)が両方trueの場合isLandingをtrue
・isLandingがtrueの場合Land(Signal形式)を送信
・isGroundedがtrueの場合isInAirをflase
・CheckCanJumpを送信
・isGroundedがfalseの場合CheckGroundedDelay(Signal形式)を送る
……???

ええと、つまり……。
・接地状態の変化でロジックを起動
・ジャンプしてから接地している場合着地状態にチェック
・接地状態にチェックが入っている場合、滞空状態のチェックを外す
・CheckCanJump信号を送る
・接地状態でない場合CheckGroundedDelay信号を送る
……途中で入るLand信号はSEを鳴らすための様なので割愛します。問題は最後の二つで、さらにそれぞれ別のロジックを起動します。

・CheckCanJumpでロジックを起動
・canJumpのtrue/falseをisInAirの逆にする

・CheckGroundedDelayでタイマーを起動、0.15秒後にCheckGroundedを送信
・CheckGroundedでロジックを起動
・isInAirのtrue/falseをisGroundedの逆にする
・CheckCanJumpを呼び出す

……🤯
分かったような分からないような。多分図にすれば理解できる予感がする。
ここまで来て気づいたんですが、このポニーの中身を差し替えればよかったのでは。