/**
[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
{
///
/// 모션 데이터 재생 클래스
/// SpringBone, DynamicBone, BulletPhysicsImpl 등의 흔들리는 에셋의 Script Execution Order를 20000 등
/// 큰 값으로 설정해주세요.
/// DefaultExecutionOrder(11000)은 VRIK계보다 처리 순서를 느리게 한다는 의도입니다
///
[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;
}
// 매 프레임마다 호출
private void Update()
{
if (Input.GetKeyDown(_playStartKey))
{
PlayMotion();
}
if (Input.GetKeyDown(_playStopKey))
{
StopMotion();
}
}
private void LateUpdate()
{
if (!_playing)
{
return;
}
_playingTime += Time.deltaTime;
SetHumanPose();
}
///
/// 모션 데이터 재생 시작
///
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;
}
}
}