概要
Oculus TouchとVRIKで動かしたモデルのモーションを保存して再生できる機能を作ってみたので紹介します。自分が試したのはOculusですが、HTCViveでも同じ話だと思います。
VRIK設定
下記サイト等を参考にして設定してください。
http://qiita.com/halne369/items/daadf0e00330a1019830
モーションデータ取り扱い
Humanoidタイプのモデルのアニメーションを編集するには、AnimationClipを動的に生成するのではなくてHumanPoseHandlerクラスを使うといいらしい。
HumanPhoseHandlerの使い方は以下の通り。
test.cs
HumanPose m_pose;
//ハンドラー取得
var handler = new HumanPoseHandler(m_animator.avatar, m_animator.transform);
//HumanPose取得
handler.GetHumanPose(ref m_pose)
//HumanPoseセット
handler.SetHumanPose(ref m_pose)
これだけ。
モーションデータ取得→保存
やり方は簡単で、毎フレームHumanPoseを取得してScriptableObjectに保存するだけ。
HumanPoses.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class HumanPoses : ScriptableObject {
[System.SerializableAttribute]
public class SerializeHumanPose
{
public Vector3 bodyPosition;
public Quaternion bodyRotation;
public float[] muscles;
public int frameCount;
}
public List<SerializeHumanPose> poses = new List<SerializeHumanPose>();
}
RecordHumanPose.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using UniRx.Triggers;
using System;
using LitJson;
using System.IO;
using UnityEditor;
public class RecordHumanPose : MonoBehaviour {
/// <summary>
/// 対象のアニメーター
/// </summary>
[SerializeField] Animator m_animator;
int m_frameCount;
HumanPose m_pose;
HumanPoseHandler m_handler;
Action m_recordEndCB;
/// <summary>
/// 録画開始
/// </summary>
public void RecordStart()
{
m_frameCount = 0;
HumanPoses humanposes = ScriptableObject.CreateInstance<HumanPoses>();
var dispose = this.UpdateAsObservable().Subscribe(p =>
{
m_handler.GetHumanPose(ref m_pose);
var seripose = new HumanPoses.SerializeHumanPose();
seripose.bodyPosition = m_pose.bodyPosition;
seripose.bodyRotation = m_pose.bodyRotation;
seripose.frameCount = m_frameCount;
seripose.muscles = new float[m_pose.muscles.Length];
seripose.frameCount = m_frameCount;
for (int i = 0; i < seripose.muscles.Length; i++)
{
seripose.muscles[i] = m_pose.muscles[i];
}
humanposes.poses.Add(seripose);
}
);
var timeCountObservable = this.UpdateAsObservable().Subscribe(p => m_frameCount++);
m_recordEndCB = () =>
{
string path = AssetDatabase.GenerateUniqueAssetPath("Assets/Resources/HumanPoses.asset");
AssetDatabase.CreateAsset(humanposes, path);
AssetDatabase.Refresh();
m_frameCount = 0;
timeCountObservable.Dispose();
dispose.Dispose();
};
}
/// <summary>
/// 録画終了
/// </summary>
public void RecordEnd()
{
if (m_recordEndCB != null)
{
m_recordEndCB();
m_recordEndCB = null;
}
}
private void Awake()
{
m_handler = new HumanPoseHandler(m_animator.avatar, transform);
}
}
モーションデータ再生
ScriptableObjectに保存したHumanPoseを読み取ってセットする。
PlayHumanPoses.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using UniRx.Triggers;
public class PlayHumanPoses : MonoBehaviour {
[SerializeField]
HumanPoses m_poses;
[SerializeField]
Animator m_animator;
HumanPoseHandler m_handler;
System.Action m_finishPlayCB;
int m_index = 0;
public void Play()
{
m_index = 0;
var disposable = this.UpdateAsObservable().Where(p=>m_index < m_poses.poses.Count).Select(p => 1).Scan((now, count) =>
{
Debug.Log(now);
return now + count;
}).Subscribe(xx=>
{
DoSetHumanPose(xx);
});
m_finishPlayCB = () =>
{
disposable.Dispose();
m_index = 0;
};
}
public void Stop()
{
if (m_finishPlayCB != null)
{
m_finishPlayCB();
m_finishPlayCB = null;
}
}
void DoSetHumanPose(int frame)
{
if (m_poses.poses[m_index].frameCount == frame-1)
{
var pose = new HumanPose();
pose.bodyPosition = m_poses.poses[m_index].bodyPosition;
pose.bodyRotation = m_poses.poses[m_index].bodyRotation;
pose.muscles = m_poses.poses[m_index].muscles;
m_handler.SetHumanPose(ref pose);
m_index++;
}
}
// Use this for initialization
void Awake () {
m_handler = new HumanPoseHandler(m_animator.avatar, transform);
}
}
最後に
後はOculusかHTCViveでVRIKをセットしたモデルを動かして上記方法でモーションを記録+再生をすればOKです。