feat: SongCreator 씬 완성 — Beat Sage URL 지원, info.dat 메타데이터 자동 추출
- BeatSageUploader: audio_url 지원(UploadFromUrl), PollAndDownload 공통화, ZIP 500 오류 3회 재시도 - BeatSageConverter: info.dat 파싱(SongMetadata), BPM 자동 감지 → 노트 타이밍 변환에 적용 - SongCreatorManager: title/BPM 필수 입력 제거, 난이도 4개 자동 선택, GenerateFlowFromUrl 버그 수정 - NasPublisher: audioPath null 허용(URL 흐름에서 로컬 파일 없는 경우 스킵) - .gitignore/.gitattributes 초기 설정 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace VRSDK
|
||||
{
|
||||
public class VR_TwoHandGrabbable : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private VR_Grabbable dominantGrabbable = null;
|
||||
[SerializeField] private VR_Grabbable secondaryGrabbable = null;
|
||||
[SerializeField] private UnityEvent onTwoHandGrabStart = null;
|
||||
[SerializeField] private Vector3 rightHandRotOffset = Vector3.zero;
|
||||
[SerializeField] private Vector3 leftHandRotOffset = Vector3.zero;
|
||||
[SerializeField] private UnityEvent onTwoHandGrabEnd = null;
|
||||
|
||||
|
||||
|
||||
private VR_Controller secondaryController = null;
|
||||
private Quaternion desireRotation = Quaternion.identity;
|
||||
private Vector3 dir = Vector3.zero;
|
||||
private bool applyTwoHandTransformManually = false;
|
||||
|
||||
public UnityEvent OnTwoHandGrabStart { get { return onTwoHandGrabStart; } }
|
||||
public UnityEvent OnTwoHandGrabEnd { get { return onTwoHandGrabEnd; } }
|
||||
|
||||
private Quaternion HandRotOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
if (dominantGrabbable.GrabController.ControllerType == VR_ControllerType.Right)
|
||||
return Quaternion.Euler( rightHandRotOffset );
|
||||
return Quaternion.Euler( leftHandRotOffset );
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
//disable secondary grabbable while dominat grabbable is dropped
|
||||
secondaryGrabbable.enabled = false;
|
||||
|
||||
dominantGrabbable.OnGrabStateChange.AddListener( OnDominantGrabbableGrabStateChange );
|
||||
secondaryGrabbable.OnGrabStateChange.AddListener( OnSecondaryGrabbableGrabStateChange );
|
||||
//prevent the VR_Grabbable for making changes in the object, so we can control it
|
||||
secondaryGrabbable.PreventDefault();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (CanUpdate())
|
||||
return;
|
||||
|
||||
//set the desire rotation to the dominanthand
|
||||
dominantGrabbable.GrabController.transform.rotation = desireRotation;
|
||||
}
|
||||
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (!applyTwoHandTransformManually)
|
||||
{
|
||||
TwoHandUpdate();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void TwoHandUpdate()
|
||||
{
|
||||
if (CanUpdate())
|
||||
{
|
||||
//for some reason the secondary controller should be dropped now
|
||||
if (SecondaryGrabbableShouldBeDropped())
|
||||
{
|
||||
ReleaseSecondaryGrabbable();
|
||||
}
|
||||
|
||||
//dominantGrabbable.IsUsingTwoHandGrabbable = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//dominantGrabbable.IsUsingTwoHandGrabbable = true;
|
||||
desireRotation = CalculateDesireRotation();
|
||||
|
||||
/*
|
||||
if (dominantGrabbable.GrabMode == GrabMode.Physics)
|
||||
{
|
||||
dominantGrabbable.TwoHandGrabbableDesireRotation = desireRotation;
|
||||
}*/
|
||||
|
||||
if (dominantGrabbable.GrabController.transform.parent == null)
|
||||
{
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Quaternion rotOffset = dominantGrabbable.GrabController.RotationOffset;
|
||||
|
||||
|
||||
|
||||
if (rotOffset.eulerAngles.magnitude > 0.001f)
|
||||
{
|
||||
desireRotation = desireRotation * dominantGrabbable.RotationOffset;
|
||||
}
|
||||
//desireRotation = desireRotation * Quaternion.Euler( dominantGrabbable.GetCurrentHandInteractSettings().rotationOffset);
|
||||
|
||||
|
||||
|
||||
//set the desire rotation to the dominanthand
|
||||
dominantGrabbable.GrabController.transform.rotation = desireRotation;
|
||||
dominantGrabbable.GrabController.transform.localPosition = dominantGrabbable.GrabController.InitialPosition + ( dominantGrabbable.GrabController.transform.parent.InverseTransformDirection( dir * -1.0f ) * ( dominantGrabbable.GrabController.PositionOffset.magnitude ) );
|
||||
}
|
||||
|
||||
private bool CanUpdate()
|
||||
{
|
||||
return dominantGrabbable.CurrentGrabState != GrabState.Grab || secondaryGrabbable.CurrentGrabState != GrabState.Grab;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private Quaternion CalculateDesireRotation()
|
||||
{
|
||||
dir = ( secondaryGrabbable.GrabController.OriginalParent.transform.position - GetDominatGrabbableGrabPosition() ).normalized;
|
||||
|
||||
Vector3 customDir = dominantGrabbable.GrabController.OriginalParent.forward;
|
||||
float angle = Vector2.Angle( new Vector2( dir.x, dir.z ), new Vector2( customDir.x, customDir.z ) );
|
||||
|
||||
Quaternion result = Quaternion.LookRotation( dir ) * Quaternion.Euler( 0.0f, 0.0f, dominantGrabbable.GrabController.OriginalParent.localEulerAngles.z * ( angle > 90.0f && angle < 180.0f ? -1.0f : 1.0f ) );
|
||||
|
||||
return result * HandRotOffset;
|
||||
}
|
||||
|
||||
private bool SecondaryGrabbableShouldBeDropped()
|
||||
{
|
||||
return dominantGrabbable.CurrentGrabState == GrabState.UnGrab && secondaryGrabbable.CurrentGrabState == GrabState.Grab;
|
||||
}
|
||||
|
||||
private bool ShouldCalculateRotationFromOriginalPosition()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private Vector3 GetDominatGrabbableGrabPosition()
|
||||
{
|
||||
return ShouldCalculateRotationFromOriginalPosition() ? dominantGrabbable.GrabController.OriginalParent.position : dominantGrabbable.GrabController.transform.position;
|
||||
}
|
||||
|
||||
private void OnDominantGrabbableGrabStateChange(GrabState state)
|
||||
{
|
||||
if (state == GrabState.Drop)
|
||||
{
|
||||
if (secondaryGrabbable.CurrentGrabState == GrabState.Grab || secondaryGrabbable.CurrentGrabState == GrabState.Drop)
|
||||
{
|
||||
ReleaseSecondaryGrabbable();
|
||||
}
|
||||
|
||||
secondaryGrabbable.enabled = false;
|
||||
}
|
||||
else if (state == GrabState.Grab)
|
||||
{
|
||||
secondaryGrabbable.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReleaseSecondaryGrabbable()
|
||||
{
|
||||
if (secondaryController == null)
|
||||
{
|
||||
secondaryGrabbable.ForceDrop();
|
||||
if(OnTwoHandGrabEnd != null)
|
||||
OnTwoHandGrabEnd.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
secondaryGrabbable.ForceDrop();
|
||||
|
||||
|
||||
secondaryController.SetVisibility( true );
|
||||
|
||||
//dominantGrabbable.GrabController.UsePositionOffset = true;
|
||||
|
||||
secondaryController.SetPositionControlMode( MotionControlMode.Engine );
|
||||
secondaryController.SetRotationControlMode( MotionControlMode.Engine );
|
||||
|
||||
if(OnTwoHandGrabEnd != null)
|
||||
OnTwoHandGrabEnd.Invoke();
|
||||
}
|
||||
|
||||
private void OnSecondaryGrabbableGrabStateChange( GrabState state)
|
||||
{
|
||||
if (state == GrabState.Grab)
|
||||
{
|
||||
dominantGrabbable.GrabController.UsePositionOffset = false;
|
||||
|
||||
|
||||
secondaryController = secondaryGrabbable.GrabController;
|
||||
Transform interactPoint = secondaryGrabbable.GetCurrentHandInteractSettings().interactPoint;
|
||||
|
||||
secondaryController.SetVisibility( !secondaryGrabbable.GetCurrentHandAnimationSettings().hideHandOnGrab );
|
||||
|
||||
secondaryController.SetPositionControlMode( MotionControlMode.Free );
|
||||
secondaryController.SetRotationControlMode( MotionControlMode.Free );
|
||||
|
||||
secondaryController.transform.parent = interactPoint;
|
||||
secondaryController.transform.localPosition = Vector3.zero;
|
||||
secondaryController.transform.rotation = interactPoint.rotation * Quaternion.Euler( secondaryGrabbable.GetCurrentHandInteractSettings().rotationOffset );
|
||||
|
||||
if (OnTwoHandGrabStart != null)
|
||||
OnTwoHandGrabStart.Invoke();
|
||||
|
||||
}
|
||||
|
||||
else if(state == GrabState.Drop && dominantGrabbable.CurrentGrabState == GrabState.Grab)
|
||||
{
|
||||
|
||||
dominantGrabbable.GrabController.UsePositionOffset = true;
|
||||
|
||||
secondaryController.SetPositionControlMode( MotionControlMode.Engine );
|
||||
secondaryController.SetRotationControlMode( MotionControlMode.Engine );
|
||||
|
||||
secondaryController.SetVisibility( true);
|
||||
|
||||
if (OnTwoHandGrabEnd != null)
|
||||
OnTwoHandGrabEnd.Invoke();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void SetApplyTwoHandTransformManually(bool value)
|
||||
{
|
||||
applyTwoHandTransformManually = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user