1
0
forked from cgvr/DeltaVR
Files
DeltaVR3DModelGeneration/Assets/_PROJECT/Components/NewHandPresence/TutorialController.cs

367 lines
13 KiB
C#

using System;
using System.Collections;
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;
public event Action<XRGrabInteractable> OnGrab;
private GameObject _billboard;
public enum TutorialInfoKey
{
Initialized,
LeftHintController,
RightHintController,
LeftSmartHandPresence,
RightSmartHandPresence
}
protected Dictionary<TutorialInfoKey, bool> initializationInfoStatus = new Dictionary<TutorialInfoKey, bool>();
protected Coroutine initializationInfoCoroutine;
private void Awake()
{
initializationInfoStatus.Add(TutorialInfoKey.Initialized, false);
initializationInfoStatus.Add(TutorialInfoKey.LeftHintController, false);
initializationInfoStatus.Add(TutorialInfoKey.RightHintController, false);
initializationInfoStatus.Add(TutorialInfoKey.LeftSmartHandPresence, false);
initializationInfoStatus.Add(TutorialInfoKey.RightSmartHandPresence, false);
}
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();
SetControllerVisibility(true);
break;
case TutorialState.Move:
ShowLocomotionHint();
SetControllerVisibility(true);
break;
case TutorialState.Teleport:
ShowTeleportHint();
SetControllerVisibility(true);
break;
case TutorialState.WaitForGrip:
SetControllerVisibility(false);
break;
case TutorialState.Grip:
//SetHandsVisibility(false);
CreateBillboard(_grabInteractable.gameObject, "Grab me!");
ShowGripHint();
SetControllerVisibility(true);
break;
case TutorialState.Done:
SetControllerVisibility(false);
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;
if (null == initializationInfoCoroutine)
{
initializationInfoCoroutine = StartCoroutine(InitializationInfoCoroutine());
}
_camera = Camera.main;
//Debug.Log("Initializing tutorial");
_leftHintController = leftHand.GetComponentInChildren<XRControllerHintController>();
_rightHintController = rightHand.GetComponentInChildren<XRControllerHintController>();
initializationInfoStatus[TutorialInfoKey.LeftHintController] = null != _leftHintController;
initializationInfoStatus[TutorialInfoKey.RightHintController] = null != _rightHintController;
_leftSmartHandPresence = leftHand.GetComponentInChildren<SmartHandPresence>();
_rightSmartHandPresence = rightHand.GetComponentInChildren<SmartHandPresence>();
initializationInfoStatus[TutorialInfoKey.LeftSmartHandPresence] = null != _leftSmartHandPresence;
initializationInfoStatus[TutorialInfoKey.RightSmartHandPresence] = null != _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");
StopCoroutine(initializationInfoCoroutine);
}
private void OnGripPerformed(SelectEnterEventArgs arg0)
{
var grab = arg0.interactableObject as XRGrabInteractable;
// Notify any listeners
OnGrab?.Invoke(grab);
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);
}
}
IEnumerator InitializationInfoCoroutine()
{
string CombineMessage(TutorialInfoKey key, object value, string prefix)
{
bool isAvailable = initializationInfoStatus.GetValueOrDefault(key);
return prefix + ": " + (isAvailable ? value.ToString() : "NULL") + "\r\n";
}
bool isInitialized = initializationInfoStatus.GetValueOrDefault(TutorialInfoKey.Initialized);
while (!isInitialized)
{
isInitialized = initializationInfoStatus.GetValueOrDefault(TutorialInfoKey.Initialized);
string infoMessage = "Tutorial not yet initialized!" + "\r\n";
infoMessage += "(click me for more info)" + "\r\n";
infoMessage += "Hint controllers" + "\r\n";
infoMessage += CombineMessage(TutorialInfoKey.LeftHintController, _leftHintController, "Left");
infoMessage += CombineMessage(TutorialInfoKey.RightHintController, _rightHintController, "Right");
infoMessage += "Smart hand presence" + "\r\n";
infoMessage += CombineMessage(TutorialInfoKey.LeftSmartHandPresence, _leftSmartHandPresence, "Left");
infoMessage += CombineMessage(TutorialInfoKey.RightSmartHandPresence, _rightSmartHandPresence, "Right");
Debug.Log(infoMessage);
yield return new WaitForSeconds(7);
}
}
}
}