Files
HDRobotics/Assets/Scripts/View/InteractionView.cs
2025-11-06 15:28:16 +09:00

298 lines
9.3 KiB
C#

using System;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Interactables;
using UnityEngine.XR.Interaction.Toolkit.Interactors;
public interface IInteractionView
{
}
public class InteractionView : MonoBehaviour, IInteractionView
{
public event Action<Vector3, Quaternion> OnRobotGrabbed;
public event Action<RobotData> OnRobotReleased;
public event Action<int> OnPointClicked;
public event Action<int> OnPointDragStart;
public event Action<int, Vector3, Quaternion> OnPointDragUpdate;
public event Action<int> OnPointDragEnd;
[SerializeField]private NearFarInteractor nearFarInteractor;
private XRBaseInteractor baseInteractor;
private IXRRayProvider rayProvider;
private bool isInitialized = false;
private bool isGrabbingPoint = false;
private int currentGrabbedPointIndex = -1;
public bool isGrabbingRobot = false;
void Start()
{
nearFarInteractor = GetComponent<NearFarInteractor>();
if (nearFarInteractor == null)
{
Debug.LogError("InteractionView: 'nearFarInteractor'가 할당되지 않았습니다", this);
return;
}
baseInteractor = nearFarInteractor as XRBaseInteractor;
rayProvider = nearFarInteractor as IXRRayProvider;
if (baseInteractor == null)
{
Debug.LogError("NearFarInteractor를 XRBaseInteractor로 변환할 수 없습니다.", this);
return;
}
InitializeInteraction();
}
void Update()
{
if (!isInitialized || rayProvider == null)
{
if (nearFarInteractor == null)
{
nearFarInteractor = GetComponentInChildren<NearFarInteractor>(true);
baseInteractor = nearFarInteractor as XRBaseInteractor;
rayProvider = nearFarInteractor as IXRRayProvider;
}
if (nearFarInteractor != null && nearFarInteractor.gameObject.activeInHierarchy)
{
InitializeInteraction();
}
}
else
{
Vector3 currentTargetPosition = Vector3.zero;
Quaternion currentTargetRotation = Quaternion.identity;
currentTargetPosition = rayProvider.rayEndPoint;
currentTargetRotation = baseInteractor.attachTransform.rotation;
if (isGrabbingPoint)
{
OnPointDragUpdate?.Invoke(currentGrabbedPointIndex, currentTargetPosition, currentTargetRotation);
}
else if (isGrabbingRobot)
{
OnRobotGrabbed?.Invoke(currentTargetPosition, currentTargetRotation);
}
}
}
private void InitializeInteraction()
{
// XRI 이벤트 구독
baseInteractor.selectEntered.AddListener(HandleGrabStart);
baseInteractor.selectExited.AddListener(HandleGrabEnd);
//interactor.activated.AddListener(OnActivated);
//interactor.deactivated.AddListener(OnDeactivated);
isInitialized = true;
AppManager.Instance.RegisterView(this);
}
private void OnDestroy()
{
if (baseInteractor != null)
{
baseInteractor.selectEntered.RemoveListener(HandleGrabStart);
baseInteractor.selectExited.RemoveListener(HandleGrabEnd);
//interactor.activated.RemoveListener(OnActivated);
//interactor.deactivated.RemoveListener(OnDeactivated);
}
}
// --- XRI 이벤트 핸들러 ---
// "잡기" 버튼을 눌렀을 때
private void HandleGrabStart(SelectEnterEventArgs args)
{
GameObject grabbedGO = args.interactableObject.transform.gameObject;
// 잡은 것이 "포인트"인지 확인
//RobotPoint point = grabbedGO.GetComponent<RobotPoint>();
//if (point != null)
//{
// isGrabbingPoint = true;
// isGrabbingRobot = false;
// currentGrabbedPointIndex = point.pointIndex;
// // (Presenter의 HandlePointDragStart 호출)
// OnPointDragStart?.Invoke(currentGrabbedPointIndex);
//}
// 2. 잡은 것이 "로봇 핸들"인지 확인
if (grabbedGO.CompareTag("RobotArm"))
{
isGrabbingPoint = false;
isGrabbingRobot = true;
// (로봇 자체를 잡는 것은 -1 인덱스로 Presenter에게 알림)
currentGrabbedPointIndex = -1;
// (로봇 잡기도 "PointDragStart" 이벤트로 통합하여 Presenter에 알림)
OnPointDragStart?.Invoke(currentGrabbedPointIndex);
}
//if (!enableSelectToDrag) return;
//BeginDrag(args.interactorObject);
}
// "잡기" 버튼을 뗐을 때
private void HandleGrabEnd(SelectExitEventArgs args)
{
//if (!enableSelectToDrag) return;
//EndDrag();
//// "포인트"를 놓고 있는 중이었다면
//if (isGrabbingPoint)
//{
// // Presenter에게 "드래그 끝" 이벤트 전송 (팝업 트리거용)
// OnPointDragEnd?.Invoke(currentGrabbedPointIndex);
//}
//// "로봇"을 놓고 있는 중이었다면
//else if (isGrabbingRobot)
//{
// // Presenter에게 "로봇 놓기" 이벤트 전송 (팝업 트리거용)
// RobotData currentPose = GetCurrentRobotPoseFromController();
// OnRobotReleased?.Invoke(currentPose);
//}
// 상태 초기화
isGrabbingPoint = false;
isGrabbingRobot = false;
currentGrabbedPointIndex = -1;
}
// ---- Drag lifecycle ----
//private void BeginDrag(IXRInteractor interactor)
//{
// currentInteractor = interactor;
// isDragging = true;
// startPosition = transform.position;
// //pathLine.enabled = true;
// uiController?.UpdateStatus("드래그 중...");
// if (communicationScript != null) communicationScript.isMoving = false;
// // ← 드래그 루프 시작
// StartDragMoveLoop();
//}
//private void EndDrag()
//{
// if (!isDragging) return;
// isDragging = false;
// uiController?.UpdateStatus("완료!");
// communicationScript.isMoving = false;
// // ← 루프 중지
// StopDragMoveLoop();
// currentInteractor = null;
//}
//private void StopDragMoveLoop()
//{
// if (dragLoopCts != null)
// {
// dragLoopCts.Cancel();
// dragLoopCts.Dispose();
// dragLoopCts = null;
// }
//}
//private void StartDragMoveLoop()
//{
// StopDragMoveLoop(); // 중복 방지
// dragLoopCts = new CancellationTokenSource();
// _ = DragMoveLoopAsync(dragLoopCts.Token);
//}
//private async Task DragMoveLoopAsync(CancellationToken token)
//{
// // 드래그 동안 100ms 주기로 전송
// while (!token.IsCancellationRequested && isDragging)
// {
// try
// {
// // 로봇이 아직 이동 중이 아니라면(대기 상태) 최신 dragPosition으로 지령 전송
// if (communicationScript != null && !communicationScript.isMoving)
// {
// await MovingTask(); // 내부에서 StratMovement(dragPosition) 호출 및 isMoving 갱신 :contentReference[oaicite:4]{index=4}
// }
// await Task.Delay(10, token); // 100ms 주기
// }
// catch (TaskCanceledException) { /* 정상 취소 */ }
// }
//}
//// ---- Per-frame ----
//void Update()
//{
// if (isDragging && currentInteractor != null)
// {
// communicationScript.isMoving = false;
// // 1) Ray Interactor: 현재 3D Raycast 히트 포인트가 있으면 그대로 사용
// if (currentInteractor is XRRayInteractor ray &&
// ray.TryGetCurrent3DRaycastHit(out RaycastHit hit))
// {
// dragPosition = hit.point;
// }
// else
// {
// // 2) Direct Interactor 또는 Ray 히트가 없을 때: 컨트롤러(또는 attachTransform) 위치 사용
// var t = currentInteractor.transform;
// var attach = currentInteractor.GetAttachTransform(null);
// dragPosition = attach != null ? attach.position : t.position;
// }
// uiController?.UpdateCoordinates(dragPosition); // 기존 UI 갱신 유지 :contentReference[oaicite:2]{index=2}
// }
//}
//private async Task MovingTask()
//{
// if (communicationScript == null) return;
// bool ok = await communicationScript.StratMovement(dragPosition); // 기존 호출 그대로 사용 :contentReference[oaicite:3]{index=3}
// if (ok) communicationScript.isMoving = true;
//}
private RobotData GetCurrentRobotPoseFromController()
{
// 이 View는 Presenter의 변환 로직(ConvertVrToRobotPose)을 알 수 없으므로,
// 현재 컨트롤러 위치/회전을 기반으로 '가짜' RobotData를 만듦
// Presenter가 이 데이터를 받아 어차피 다시 변환(또는 무시)해야 함
return new RobotData(); // 임시 데이터
}
// --- Presenter가 호출할 함수들 ---
public void ShowGhostRobot(RobotData pose)
{
/* 반투명 로봇2 활성화 및 위치 설정 */
}
public void HideGhostRobot()
{
/* 반투명 로봇2 비활성화 */
}
public void ShowDragArrow(Vector3 position)
{
/* 드래그용 화살표 UI 활성화 및 위치 설정 */
}
public void HideDragArrow()
{
/* 드래그용 화살표 UI 비활성화 */
}
}