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 OnRobotGrabbed; public event Action OnRobotReleased; public event Action OnPointClicked; public event Action OnPointDragStart; public event Action OnPointDragUpdate; public event Action 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(); 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(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(); //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 ºñȰ¼ºÈ­ */ } }