301 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			301 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections.Generic;
 | |
| using UnityEngine;
 | |
| using UnityEngine.InputSystem;
 | |
| using UnityEngine.XR.Interaction.Toolkit;
 | |
| 
 | |
| namespace _PROJECT.NewHandPresence
 | |
| {
 | |
|     public class TutorialController : MonoBehaviour
 | |
|     {
 | |
|         public enum TutorialState
 | |
|         {
 | |
|             Initializing,
 | |
|             Turn,
 | |
|             Teleport,
 | |
|             WaitForGrip,
 | |
|             Grip,
 | |
|             Done,
 | |
|             Move
 | |
|         }
 | |
| 
 | |
|         public ActionBasedController leftHand;
 | |
|         public ActionBasedController rightHand;
 | |
| 
 | |
|         public ActionBasedContinuousMoveProvider moveProvider;
 | |
|         public ActionBasedSnapTurnProvider turnProvider;
 | |
|         public TeleportationProvider teleportProvider;
 | |
| 
 | |
|         public GameObject billboardTemplate;
 | |
|         
 | |
|         private XRControllerHintController _leftHintController;
 | |
|         private XRControllerHintController _rightHintController;
 | |
| 
 | |
|         private SmartHandPresence _leftSmartHandPresence;
 | |
|         private SmartHandPresence _rightSmartHandPresence;
 | |
| 
 | |
|         public TutorialState _state = TutorialState.Initializing;
 | |
| 
 | |
|         private Camera _camera;
 | |
| 
 | |
|         private List<XRGrabInteractable> _grabInteractables = new List<XRGrabInteractable>();
 | |
|         private XRGrabInteractable _grabInteractable;
 | |
| 
 | |
|         private GameObject _billboard;
 | |
| 
 | |
|         private void Update()
 | |
|         {
 | |
|             if (_state == TutorialState.Initializing)
 | |
|             {
 | |
|                 TryInitialize();
 | |
|             }
 | |
| 
 | |
|             if (_state == TutorialState.WaitForGrip)
 | |
|             {
 | |
|                 TryFindXRGrabInteractable();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void TryFindXRGrabInteractable()
 | |
|         {
 | |
|             // Find closest interactable in list
 | |
|             XRGrabInteractable closestInteractable = null;
 | |
|             var closestDistance = float.MaxValue;
 | |
|             foreach (var grabInteractable in _grabInteractables)
 | |
|             {
 | |
|                 var distance = Vector3.Distance(grabInteractable.transform.position, _camera.transform.position);
 | |
|                 if (!(distance < closestDistance)) continue;
 | |
|                 if (grabInteractable.CompareTag("Door")) continue;
 | |
|                 closestDistance = distance;
 | |
|                 closestInteractable = grabInteractable;
 | |
|             }
 | |
|             
 | |
|             if (closestInteractable == null) return;
 | |
|             
 | |
|             _grabInteractable = closestInteractable;
 | |
|             UpdateState(_state.Next());
 | |
|         }
 | |
| 
 | |
|         private void UpdateState(TutorialState newState)
 | |
|         {
 | |
|             _state = newState;
 | |
|             Debug.Log($"Tutorial state: {_state}");
 | |
| 
 | |
|             if (_state == TutorialState.Initializing) return;
 | |
|             SetHandsVisibility(true);
 | |
| 
 | |
|             _rightHintController.HideHint();
 | |
|             _leftHintController.HideHint();
 | |
| 
 | |
|             switch (_state)
 | |
|             {
 | |
|                 case TutorialState.Initializing:
 | |
|                     break;
 | |
|                 case TutorialState.Turn:
 | |
|                     ShowTurnHint();
 | |
|                     break;
 | |
|                 case TutorialState.Move:
 | |
|                     ShowLocomotionHint();
 | |
|                     break;
 | |
|                 case TutorialState.Teleport:
 | |
|                     ShowTeleportHint();
 | |
|                     break;
 | |
|                 case TutorialState.WaitForGrip:
 | |
|                     SetHandsVisibility(true);
 | |
|                     break;
 | |
|                 case TutorialState.Grip:
 | |
|                     SetHandsVisibility(false);
 | |
|                     CreateBillboard(_grabInteractable.gameObject, "Grab me!");
 | |
|                     ShowGripHint();
 | |
|                     break;
 | |
|                 case TutorialState.Done:
 | |
|                     SetHandsVisibility(true);
 | |
|                     DestroyBillboard();
 | |
|                     break;
 | |
|                 default:
 | |
|                     throw new ArgumentOutOfRangeException();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void CreateBillboard(GameObject gmeObject, string gripMe)
 | |
|         {
 | |
|             var number = 1 / gmeObject.transform.localScale.x;
 | |
|             // Create billboard as a child on grab interactable
 | |
|             _billboard = Instantiate(billboardTemplate, gmeObject.transform);
 | |
|             // Set local position above grab interactable
 | |
|             _billboard.transform.localPosition = new Vector3(0, 1, 0);
 | |
|             // Unscale billboard relative to parent
 | |
|             _billboard.transform.localScale = Vector3.one * number;
 | |
|             // Get ControllerTextHint on billboard
 | |
|             var controllerTextHint = _billboard.GetComponentInChildren<Billboard>();
 | |
|             // Set text
 | |
|             controllerTextHint.defaultText = gripMe;
 | |
|             controllerTextHint.ShowHint(gripMe);
 | |
|             // Set interactable to outlined layer
 | |
|             //gmeObject.layer = LayerMask.NameToLayer("Outlined Objects");
 | |
|         }
 | |
| 
 | |
|         private void DestroyBillboard()
 | |
|         {
 | |
|             Destroy(_billboard);
 | |
|         }
 | |
| 
 | |
|         private bool CanInitialize()
 | |
|         {
 | |
|             if (leftHand == null || rightHand == null)
 | |
|             {
 | |
|                 Debug.Log("Left or right hand is null");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (turnProvider.rightHandSnapTurnAction.action == null)
 | |
|             {
 | |
|                 Debug.Log("Right hand snap turn action is null");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (moveProvider.leftHandMoveAction.action == null)
 | |
|             {
 | |
|                 Debug.Log("Left hand move action is null");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (teleportProvider == null)
 | |
|             {
 | |
|                 Debug.Log("Teleport provider is null");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         private void TryInitialize()
 | |
|         {
 | |
|             if (!CanInitialize()) return;
 | |
| 
 | |
|             _camera = Camera.main;
 | |
| 
 | |
|             Debug.Log("Initializing tutorial");
 | |
| 
 | |
|             _leftHintController = leftHand.GetComponentInChildren<XRControllerHintController>();
 | |
|             _rightHintController = rightHand.GetComponentInChildren<XRControllerHintController>();
 | |
| 
 | |
|             Debug.Log($"Left hint controller: {_leftHintController}");
 | |
|             Debug.Log($"Right hint controller: {_rightHintController}");
 | |
| 
 | |
|             _leftSmartHandPresence = leftHand.GetComponentInChildren<SmartHandPresence>();
 | |
|             _rightSmartHandPresence = rightHand.GetComponentInChildren<SmartHandPresence>();
 | |
| 
 | |
|             Debug.Log($"Left smart hand presence: {_leftSmartHandPresence}");
 | |
|             Debug.Log($"Right smart hand presence: {_rightSmartHandPresence}");
 | |
| 
 | |
|             if (_leftHintController == null ||
 | |
|                 _rightHintController == null ||
 | |
|                 _leftSmartHandPresence == null ||
 | |
|                 _rightSmartHandPresence == null)
 | |
|             {
 | |
|                 Debug.Log("Hint controller or smart hand presence is null");
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             _leftHintController.xrBaseController = leftHand;
 | |
|             _rightHintController.xrBaseController = rightHand;
 | |
| 
 | |
|             // Setup action listeners
 | |
|             turnProvider.rightHandSnapTurnAction.action.performed += OnTurnPerformed;
 | |
|             moveProvider.leftHandMoveAction.action.performed += OnMovePerformed;
 | |
|             teleportProvider.endLocomotion += OnTeleportPerformed;
 | |
| 
 | |
|             leftHand.GetComponent<XRDirectInteractor>().selectEntered.AddListener(OnGripPerformed);
 | |
|             rightHand.GetComponent<XRDirectInteractor>().selectEntered.AddListener(OnGripPerformed);
 | |
| 
 | |
|             UpdateState(_state.Next());
 | |
|             Debug.Log("Tutorial initialized");
 | |
|         }
 | |
| 
 | |
|         private void OnGripPerformed(SelectEnterEventArgs arg0)
 | |
|         {
 | |
|             if (_state != TutorialState.Grip) return;
 | |
|             Debug.Log("Grip performed");
 | |
|             UpdateState(_state.Next());
 | |
|         }
 | |
|         
 | |
|         private void OnTeleportPerformed(LocomotionSystem obj)
 | |
|         {
 | |
|             if (_state != TutorialState.Teleport) return;
 | |
|             Debug.Log("Teleport performed");
 | |
|             UpdateState(_state.Next());
 | |
|         }
 | |
| 
 | |
|         private void OnMovePerformed(InputAction.CallbackContext obj)
 | |
|         {
 | |
|             if (_state != TutorialState.Move) return;
 | |
|             Debug.Log("Move performed");
 | |
|             UpdateState(_state.Next());
 | |
|         }
 | |
| 
 | |
|         private void OnTurnPerformed(InputAction.CallbackContext obj)
 | |
|         {
 | |
|             if (_state != TutorialState.Turn) return;
 | |
|             Debug.Log("Turn performed");
 | |
|             UpdateState(_state.Next());
 | |
|         }
 | |
| 
 | |
|         private void ShowTeleportHint()
 | |
|         {
 | |
|             _rightHintController.ShowTeleportHint();
 | |
|             _leftHintController.ShowTeleportHint();
 | |
|         }
 | |
| 
 | |
|         private void ShowTurnHint()
 | |
|         {
 | |
|             _rightHintController.ShowTurnHint();
 | |
|             _leftHintController.ShowTurnHint();
 | |
|         }
 | |
| 
 | |
|         private void ShowLocomotionHint()
 | |
|         {
 | |
|             _rightHintController.ShowSmoothLocomotionHint();
 | |
|             _leftHintController.ShowSmoothLocomotionHint();
 | |
|         }
 | |
| 
 | |
|         private void ShowGripHint()
 | |
|         {
 | |
|             _rightHintController.ShowGripHint();
 | |
|             _leftHintController.ShowGripHint();
 | |
|         }
 | |
| 
 | |
|         private void SetControllerVisibility(bool visible)
 | |
|         {
 | |
|             _leftSmartHandPresence.showController = visible;
 | |
|             _rightSmartHandPresence.showController = visible;
 | |
|         }
 | |
| 
 | |
|         private void SetHandsVisibility(bool visible)
 | |
|         {
 | |
|             _leftSmartHandPresence.showHand = visible;
 | |
|             _rightSmartHandPresence.showHand = visible;
 | |
|         }
 | |
| 
 | |
|         private void OnTriggerEnter(Collider other)
 | |
|         {
 | |
|             // If has component add to list
 | |
|             var grabInteractable = other.GetComponent<XRGrabInteractable>();
 | |
|             if (grabInteractable != null)
 | |
|             {
 | |
|                 _grabInteractables.Add(grabInteractable);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void OnTriggerExit(Collider other)
 | |
|         {
 | |
|             // If has component remove from list
 | |
|             var grabInteractable = other.GetComponent<XRGrabInteractable>();
 | |
|             if (grabInteractable != null)
 | |
|             {
 | |
|                 _grabInteractables.Remove(grabInteractable);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|     }
 | |
| } |