読者です 読者をやめる 読者になる 読者になる

きかいや。

機械といいつつだいたいプログラミングのはなし

FinalIKを使って頭位置にキャラクターの姿勢を追従させる。[OculusRiftDK2]

というわけでその忘備録です。

アセット購入から実装まで実質期間:2日。

感想:ちょう楽。おススメアセット

 

[目的]

この記事の内容、要点は以下のとおり。

フライトシミュレータ、レースゲーム、ロボ操縦もの、コタツでミカン食べるなど、着座姿勢を基本とする場合に特に有用です。
コンポーネントの適用
①キャラクターは着座姿勢を保つ

②ペダルの回転に合わせて、キャラクターの足を動かす

③手は操縦桿に固定

④OculusDK2のポジトラに合わせて、キャラクターの頭位置を追従させる。

出来上がるとこんな感じ。 


FinalIK - YouTube

 

[背景]

人力飛行機シミュレータ(HPASim)は没入感を得られるよう、
キャラクターを画面に表示しています。DK1では首(頭)のボーンにOculusRiftの回転角を反映してやれば、自分とキャラクターの動きを合わせることができましたが、DK2のポジショントラッキングでは自由度が増えたせいで位置合わせが煩雑になりました。せっかくのポジショントラッキング、自分が動いたらキャラクターも追従してほしいものです。

で、最近Unity-OculusクラスタFinalIKというアセット(有料)を知りました。(だいたい1万円)。 (@izmさんのめかしむ 1 2)。やりたいことが近かったので、先人がいる安心感と、簡単にできたというコメントを聞いて購入。

 

[手法]

 今回動かしたいのはpilot(学ラン君)です。MMD4Mecanimでインポート。verは忘れましたが、最新版をお勧めします。(僕の使ったのは古いverなので、モデルの物理演算をさせると問題が出るらしい。キャラの物理演算しないので気にせずやりました。)
パイロット周りのオブジェクトをまとめて飛行機に乗せるため、
親 sag2(人力飛行機モデル)-pilotParent-pilot という構成です。

 

 ⓪おもむろにFullBodyBipedコンポーネントをpilotにぶち込む
するとあら不思議。人型モデルのボーン構造を認識してくれるため、ボーン周り設定をほとんど設定なしに使えます。謎の技術だ…

あ、bipedとは二足歩行のことです。

f:id:machinemaker:20141028203404p:plain

f:id:machinemaker:20141028204759p:plain

 

Final IKでは手や腰など、コレの部位をこの位置に固定したい/動かしたいんだ、というときに、その位置にターゲットのオブジェクトを配置します。ターゲットを動かさなければ手や腰はそこに固定され、ターゲットを動かすと人モデルの関節がイイ感じに動いてくれて、手や腰がターゲットにひっついて動きます。

①着座させる
まずは座らせます。
(1)ターゲット作成
空のオブジェクト IKTargetTorso を作り、飛行機の子にして座席あたりに置きます。f:id:machinemaker:20141028210606p:plain

(2)BodyEffectorのターゲット指定とIK設定
body effector タブのtarget に、先ほど作ったIKTargetTorsoを指定します。
[重要!]:この時Maintain Head Rotを1にします。コレをしないと、頭の回転がおかしくなります。

余談ですが手や足なんかのことをend effectorといいます。
これでもう、腰が座席に行くようになりました。

f:id:machinemaker:20141102004041p:plain


②足をペダルに追従させる

(1)ターゲット作成

足(Foot)のターゲットをIKTargetFootL,Rをペダルの子として配置します。
だいたいペダルの上に足関節が来るように。

f:id:machinemaker:20141028215507p:plain

 

(2)IKの設定
以下のように設定。設定はデフォルトまんまでOKでした。

f:id:machinemaker:20141028222055p:plain


ここまで終えると、座った学ラン君がペダルの位置に足がついてくるようになっています。


このペダル設定、3分くらいで終わりました。

ペダルのモーションをアニメーションでやってた時は、モーション作成3時間、プログラム数日とか苦労したので、あっけなさ過ぎてびっくりします。


③手を操縦桿に合わせる
(1)ターゲット作成

①の時と同じように、飛行機の子として左右の操縦桿あたりにIKTargertHandL,Rを置きます。

f:id:machinemaker:20141028211520p:plain


(2)ターゲット設定

f:id:machinemaker:20141028223438p:plain

 


④頭位置をカメラに追従させる

 今回のキモです。イロイロなインチキ工夫をやっています。そのうえ3割くらいなぜうまく動いてるのかちゃんと理解してないです。そのうちまとめなおします。
実はFinalIKのFullBodyBipedコンポーネントは、頭へのIKを直接設定することができません。代わりに、肩のIKを使ってうまいこと頭位の位置を指定します。このへんはizmさんからヒントを得てます。(ほぼ答え)


鉄騎コンのあれを作ってる時に得られたOculusRift DK2 memo - izm_11's blog


考え方:
Oculusのカメラコントローラは本来人間の目の位置に来るべき。

目の位置から肩の位置を算出する。

⓪カメラコントローラから目の位置姿勢を取得。
①目の位置姿勢からA:頭の姿勢、B:肩の位置を逆算する。

②肩のIKターゲットにBを代入し、キャラの肩までの動きをIKで算出

③キャラの頭の絶対姿勢にAを代入

 

(1)ターゲット配置
人間の左右の目の中心あたりにEyeCenter(Oculusの位置姿勢)を置きます(右眼の前あたりのほうが良いのかも…)。その子にIKTargetHead(ダミーの頭IKターゲット)を置き、更にその子にIKTargetSholderL,Rを配置します。

EyeCenter-IKTargetHead-iKTargetShoulderL,R

という感じです。
そして、肩のIKのターゲットにShoulderL,Rを指定します。(名前に統一感がなくなってますね、直す気力が…)

f:id:machinemaker:20141102002909p:plain

f:id:machinemaker:20141028223438p:plain

こうすることで、目の位置にOculusの位置姿勢を入れると頭の位置姿勢が出ます。

 

※この方式はちょっと欠点があって、頭のヨーイングをすべて体幹のヨーイングとして表現してしまう。(首だけで横に回さない。体幹を回す。ピッチとロールはオフセットをつけるとなんかうまくごまかせる)
しかし意外と違和感がない。違和感が出る姿勢は、首を真横に向けた状態で肩を見る姿勢になるので、通常まずできないかやらない動きだからだろうなぁ。操縦桿で手の位置を固定しているのも大きいかも。見えないところはバレない。 

 

(1.5)インチキを始める

OVRCameraControllerのCameraRightの子にREyeTargetIKを配置。
CameraRightより0,2ほど後ろに配置してオフセットをつける(頭の中が見えるの回避調整)

f:id:machinemaker:20141102001722p:plain


逆に前側ににオフセットすると、頭の後ろから見ている3rdParsonViewっぽくなってこれはこれで面白い。

 

(2)スクリプト

IKTargetEyeの位置・姿勢をEyeCenterのtransformへ渡す。

以下のスクリプトをpilot(キャラクタ)にアタッチする

 

 

using UnityEngine;
using System.Collections;
//using RootMotion.FinalIK;

public class PilotBehaviourScript : MonoBehaviour {
private GameObject player_;
private GameObject head_; //pilot head
public GameObject eye_=null;
public GameObject rEye = null;
private GameObject headTarget;
private GameObject headTargetParent;
private GameObject camera_=null; //camera object

// Use this for initialization
void Start () {
//get Player Component
player_ = gameObject;
headTarget = GameObject.Find("IKTargetHead").gameObject;
rEye = GameObject.Find("IKTargetEyeR").gameObject;
//get head component
head_ = GameObject.Find("20.joint_Head").gameObject;
eye_ = GameObject.Find("eyeCenter").gameObject;

Debug.Log (head_.name);

// Get main camera
camera_ = GameObject.Find("OVRCameraController").transform.FindChild("CameraRight").gameObject;

Debug.Log (camera_.name);

}


// Update is called once per frame
void LateUpdate () {
Debug.Log("Pilot::Update");
Quaternion headRot = camera_.transform.rotation;
head_.transform.rotation = headRot;

eye_.transform.position = rEye.transform.position;
eye_.transform.rotation = headRot;

}

 

だいたいこんな感じです。

両眼の中心位置の取り方をちゃんと把握してないので、ひどい感じですね。最後のほうそのうち修正したい。
突っ込みお願いします。

 

[備考]
検証環境

Unity4.5,4.f1

Oculus Rift SDK 0.4.2