219 lines
6.7 KiB
C#

/**
[EasyMotionRecorder]
Copyright (c) 2018 Duo.inc
This software is released under the MIT License.
http://opensource.org/licenses/mit-license.php
*/
using UnityEngine;
using System;
namespace Entum
{
/// <summary>
/// モーションデータ再生クラス
/// SpringBone, DynamicBone, BulletPhysicsImplなどの揺れ物アセットのScript Execution Orderを20000など
/// 大きな値にしてください。
/// DefaultExecutionOrder(11000) はVRIK系より処理順を遅くする、という意図です
/// </summary>
[DefaultExecutionOrder(11000)]
public class MotionDataPlayer : MonoBehaviour
{
[SerializeField]
private KeyCode _playStartKey = KeyCode.S;
[SerializeField]
private KeyCode _playStopKey = KeyCode.T;
[SerializeField]
protected HumanoidPoses RecordedMotionData;
[SerializeField]
private Animator _animator;
[SerializeField, Tooltip("再生開始フレームを指定します。0だとファイル先頭から開始です")]
private int _startFrame;
[SerializeField]
private bool _playing;
[SerializeField]
private int _frameIndex;
[SerializeField, Tooltip("普段はOBJECTROOTで問題ないです。特殊な機材の場合は変更してください")]
private MotionDataSettings.Rootbonesystem _rootBoneSystem = MotionDataSettings.Rootbonesystem.Objectroot;
[SerializeField, Tooltip("rootBoneSystemがOBJECTROOTの時は使われないパラメータです。")]
private HumanBodyBones _targetRootBone = HumanBodyBones.Hips;
[HideInInspector, SerializeField] private string instanceID = "";
[HideInInspector, SerializeField] private bool useDontDestroyOnLoad = false;
private HumanPoseHandler _poseHandler;
private Action _onPlayFinish;
private float _playingTime;
private void Awake()
{
if (_animator == null)
{
Debug.LogError("MotionDataPlayerにanimatorがセットされていません。MotionDataPlayerを削除します。");
Destroy(this);
return;
}
// 인스턴스 ID가 비어있으면 자동 생성
if (string.IsNullOrEmpty(instanceID))
{
instanceID = System.Guid.NewGuid().ToString().Substring(0, 8);
}
// DontDestroyOnLoad 설정 (선택적)
if (useDontDestroyOnLoad)
{
DontDestroyOnLoad(gameObject);
}
_poseHandler = new HumanPoseHandler(_animator.avatar, _animator.transform);
_onPlayFinish += StopMotion;
}
// Update is called once per frame
private void Update()
{
if (Input.GetKeyDown(_playStartKey))
{
PlayMotion();
}
if (Input.GetKeyDown(_playStopKey))
{
StopMotion();
}
}
private void LateUpdate()
{
if (!_playing)
{
return;
}
_playingTime += Time.deltaTime;
SetHumanPose();
}
/// <summary>
/// モーションデータ再生開始
/// </summary>
private void PlayMotion()
{
if (_playing)
{
return;
}
if (RecordedMotionData == null)
{
Debug.LogError("재생할 모션 데이터가 없습니다.");
return;
}
_playing = true;
_frameIndex = _startFrame;
_playingTime = 0f;
Debug.Log($"모션 재생 시작 - 인스턴스: {instanceID}, 시작 프레임: {_startFrame}");
}
private void StopMotion()
{
if (!_playing)
{
return;
}
_playing = false;
Debug.Log($"모션 재생 중지 - 인스턴스: {instanceID}, 재생된 프레임: {_frameIndex}");
}
private void SetHumanPose()
{
if (RecordedMotionData == null || RecordedMotionData.Poses == null || RecordedMotionData.Poses.Count == 0)
{
StopMotion();
return;
}
if (_frameIndex >= RecordedMotionData.Poses.Count)
{
StopMotion();
_onPlayFinish?.Invoke();
return;
}
var pose = RecordedMotionData.Poses[_frameIndex];
var humanPose = new HumanPose();
// 본 데이터 설정
if (pose.HumanoidBones != null)
{
foreach (var bone in pose.HumanoidBones)
{
// HumanBodyBones enum으로 변환
HumanBodyBones bodyBone;
if (System.Enum.TryParse(bone.Name, out bodyBone))
{
var boneTransform = _animator.GetBoneTransform(bodyBone);
if (boneTransform != null)
{
boneTransform.localPosition = bone.LocalPosition;
boneTransform.localRotation = bone.LocalRotation;
}
}
}
}
// 근육 데이터 설정
if (pose.Muscles != null && pose.Muscles.Length > 0)
{
humanPose.muscles = pose.Muscles;
}
// 루트 본 설정
switch (_rootBoneSystem)
{
case MotionDataSettings.Rootbonesystem.Objectroot:
_animator.transform.localPosition = pose.BodyRootPosition;
_animator.transform.localRotation = pose.BodyRootRotation;
break;
case MotionDataSettings.Rootbonesystem.Hipbone:
var hipBone = _animator.GetBoneTransform(_targetRootBone);
if (hipBone != null)
{
hipBone.position = pose.BodyRootPosition;
hipBone.rotation = pose.BodyRootRotation;
}
break;
}
// HumanPose 적용
_poseHandler.SetHumanPose(ref humanPose);
_frameIndex++;
}
public void SetInstanceID(string id)
{
instanceID = id;
}
public void SetUseDontDestroyOnLoad(bool use)
{
useDontDestroyOnLoad = use;
}
public string GetInstanceID()
{
return instanceID;
}
}
}