FinalIK+LeapMotion Core Assets v4.1.1でMMDの指を動かす
えぇ afjkさんの真似です。
LeapMotionのCore Assets v4.1.1になってからなんか色々とnamespaceや関数が微妙に変わってる気がするのですが如何でしょうか?
(気のせいかなぁ。。。ちゃんと調べろと。。。)
めんどくさいのでFinalIKを直接叩いて動かしてしまえーってのが趣旨です。
指を動かす時の手順やパラメータが複雑なのでメモ書きも兼ねてこの記事です。
1.準備
- mmd4mecanimをいれる
- FinalIKをいれる
- LeapMotion Core Assets v4.1.1を入れる
- LeapMotion Add-on ModulesでHands Module v1.0.0を入れる
※この中で必要なのは - RiggedFinger
- RiggedHand
この2つのスクリプトだけです。 - 好きなモデルを入れてUnityで扱える状態にしておきます
2.LeapHandcontrollerでFinalIKを扱えるスクリプトの作成
こんな感じで継承して作成(かなり適当)
/**
FinalIKを使ったLeapMotion Orion用HandController
Author: MiyuMiyu
*/
using UnityEngine;
using Leap.Unity;
using Leap;
using System.Collections.Generic;
using RootMotion.FinalIK;
public class FinalIKOrionLeapHandController : LeapHandController
{
[SerializeField]
private FullBodyBipedIK fullbodyIK;
public bool ikActive = true;
public bool leftActive = false;
public bool rightActive = false;
public GameObject avatarLeftHand; //keeps track of the left hand gameobject for IK
public GameObject avatarRightHand; //keeps track of the right hand gameobject for IK
[HideInInspector]
public HandModel leftHand;
[HideInInspector]
public HandModel rightHand;
protected virtual void Awake()
{
if(fullbodyIK == null)
{
fullbodyIK = gameObject.transform.root.GetComponent<FullBodyBipedIK>();
}
if(fullbodyIK == null)
{
Debug.LogError("FinalIKOrionLeapHandController:: no FullBodyBipedIK found on GameObject or any of its parent transforms. ");
}
if (leftHand == null && avatarLeftHand != null)
leftHand = avatarLeftHand.GetComponent<RiggedHand>();
if (rightHand == null && avatarRightHand != null)
rightHand = avatarRightHand.GetComponent<RiggedHand>();
if (leftHand == null)
Debug.LogError("IKOrionLeapHandController::Awake::No Rigged Hand set for left hand parameter. You have to set this in the inspector.");
if (rightHand == null)
Debug.LogError("IKOrionLeapHandController::Awake::No Rigged Hand set for right hand parameter. You have to set this in the inspector.");
// Physic Handは使用しないのでDisableにする
physicsEnabled = false;
}
protected override void Start()
{
provider = GetComponent<LeapProvider>();
if (provider == null)
{
Debug.LogError("IKOrionLeapHandController::Start::No Leap Provider component was present on " + gameObject.name);
Debug.Log("Added a Leap Service Provider with default settings.");
gameObject.AddComponent<LeapServiceProvider>();
}
}
void Update()
{
if (graphicsEnabled)
{
UpdateHandRepresentations();
if (ikActive)
{
if (leftActive && leftHand != null)
{
RiggedHand l = leftHand as RiggedHand;
fullbodyIK.solver.leftArmMapping.weight = 1.0f;
fullbodyIK.solver.leftArmMapping.maintainRotationWeight = 1.0f;
fullbodyIK.solver.leftHandEffector.positionWeight = 1.0f;
fullbodyIK.solver.leftHandEffector.rotationWeight = 1.0f;
fullbodyIK.solver.leftHandEffector.position = leftHand.GetPalmPosition();
fullbodyIK.solver.leftHandEffector.rotation = leftHand.GetPalmRotation() * l.Reorientation();
fullbodyIK.solver.leftArmChain.bendConstraint.weight = 1.0f;
}
else
{
fullbodyIK.solver.leftArmMapping.weight = 0.0f;
fullbodyIK.solver.leftArmMapping.maintainRotationWeight = 0.0f;
fullbodyIK.solver.leftHandEffector.positionWeight = 0.0f;
fullbodyIK.solver.leftHandEffector.rotationWeight = 0.0f;
fullbodyIK.solver.leftArmChain.bendConstraint.weight = 0.0f;
}
if (rightActive && rightHand != null)
{
RiggedHand r = rightHand as RiggedHand;
fullbodyIK.solver.rightArmMapping.weight = 1.0f;
fullbodyIK.solver.rightArmMapping.maintainRotationWeight = 1.0f;
fullbodyIK.solver.rightHandEffector.positionWeight = 1.0f;
fullbodyIK.solver.rightHandEffector.rotationWeight = 1.0f;
fullbodyIK.solver.rightHandEffector.position = rightHand.GetPalmPosition();
fullbodyIK.solver.rightHandEffector.rotation = rightHand.GetPalmRotation() * r.Reorientation();
fullbodyIK.solver.rightArmChain.bendConstraint.weight = 1.0f;
}
else
{
fullbodyIK.solver.rightArmMapping.weight = 0.0f;
fullbodyIK.solver.rightArmMapping.maintainRotationWeight = 0.0f;
fullbodyIK.solver.rightHandEffector.positionWeight = 0.0f;
fullbodyIK.solver.rightHandEffector.rotationWeight = 0.0f;
fullbodyIK.solver.rightArmChain.bendConstraint.weight = 0.0f;
}
}
}
}
/// <summary>
/// Tells the hands to update to match the new Leap Motion hand frame data. Also keeps track of
/// which hands are currently active.
/// </summary>
void UpdateHandRepresentations()
{
leftActive = false;
rightActive = false;
foreach (Leap.Hand curHand in provider.CurrentFrame.Hands)
{
if (curHand.IsLeft)
{
leftHand.SetLeapHand(curHand);
leftHand.UpdateHand();
leftActive = true;
}
if (curHand.IsRight)
{
rightHand.SetLeapHand(curHand);
rightHand.UpdateHand();
rightActive = true;
}
}
}
}
3.モデルにFinalIKを入れる
モデルにFinalIKのFullBodyBipedIKを入れます
(コンポーネントをそのままドラック&ドロップでOK)
4.モデルの目(頭のあたり)にLeapMotion関連のスクリプトを置く
モデルのheadの子オブジェクトになるようの空のゲームオブジェクトを作成
そのゲームオブジェクトの名前を【LeapSpacer】という名前にしてその子供にもう一つ空のゲームオブジェクトを作成し名前を【LeapMotion】として、その中に必要なスクリプトを置きます。
(場所の微調整は【LeapSpacer】で行います)
LeapSpacerのポジションは若干前に出して、ローテションは図の通りになります。
LeapMotionに必要なスクリプト
- LeapServiceProvider
- FinalIKOrionLeapHandController(さっき作ったやつ)
を入れます
FinalIKOrionLeapHandControllerのAvater Left Hand 、Avatar Right HandにモデルのWristを入れます。
5.モデルの手と指を地道に設定する
モデルの手にRiggedHand、指それぞれにRiggedFingerを入れて設定していきます
右手の指(親指以外)
(Jointsはいらない。 FingerTypeとElementは指に合わせて適宜変更)
左手の指(親指以外)
(Jointsはいらない。 FingerTypeとElementは指に合わせて適宜変更)
設定完了
これでたぶん動くはず
(アニメーションとかそのあたりの兼ね合いもありますので、動かなかったらごめんなさい)