2023-05-08 15:56:10 +03:00

301 lines
9.8 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
{
private enum State
{
Initializing,
Turn,
Move,
Teleport,
WaitForGrip,
Grip,
Done
}
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;
private State _state = State.Initializing;
private Camera _camera;
private List<XRGrabInteractable> _grabInteractables = new List<XRGrabInteractable>();
private XRGrabInteractable _grabInteractable;
private GameObject _billboard;
private void Update()
{
if (_state == State.Initializing)
{
TryInitialize();
}
if (_state == State.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(State newState)
{
_state = newState;
Debug.Log($"Tutorial state: {_state}");
if (_state == State.Initializing) return;
SetHandsVisibility(true);
_rightHintController.HideHint();
_leftHintController.HideHint();
switch (_state)
{
case State.Initializing:
break;
case State.Turn:
ShowTurnHint();
break;
case State.Move:
ShowLocomotionHint();
break;
case State.Teleport:
ShowTeleportHint();
break;
case State.WaitForGrip:
SetHandsVisibility(true);
break;
case State.Grip:
SetHandsVisibility(false);
CreateBillboard(_grabInteractable.gameObject, "Grab me!");
ShowGripHint();
break;
case State.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 != State.Grip) return;
Debug.Log("Grip performed");
UpdateState(_state.Next());
}
private void OnTeleportPerformed(LocomotionSystem obj)
{
if (_state != State.Teleport) return;
Debug.Log("Teleport performed");
UpdateState(_state.Next());
}
private void OnMovePerformed(InputAction.CallbackContext obj)
{
if (_state != State.Move) return;
Debug.Log("Move performed");
UpdateState(_state.Next());
}
private void OnTurnPerformed(InputAction.CallbackContext obj)
{
if (_state != State.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);
}
}
}
}