4dad9e5d5b
- 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>
238 lines
8.5 KiB
C#
238 lines
8.5 KiB
C#
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;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|