using UnityEngine; namespace Passer { using Humanoid; /// /// The Menu Manager uses two Interaction Pointers for each hand: /// - An (straight) interaction pointer to interact with the menu when it is active /// - An (curved) interaction pointer to do teleporting when the menu is not active. /// Both pairs of interaction pointers needs to be set in the Inspector to work correctly. /// /// The Menu Manager uses a Trigger Sphere Collider to detect if the user has moved too /// far the the menu to interact with it. /// [RequireComponent(typeof(SphereCollider))] public class MenuManager : MonoBehaviour { public HumanoidControl humanoid; public float menuDistance = 0.5F; public InteractionPointer leftMenuPointer; public InteractionPointer rightMenuPointer; public InteractionPointer leftTeleporter; public InteractionPointer rightTeleporter; /// /// Initialization /// protected virtual void Awake() { // If the humanoid is not set, try to detect the local humanoid player if (humanoid == null) humanoid = FindHumanoid(); if (humanoid != null) { InitInteractionPointers(humanoid); SetControllerInput(humanoid); } // Sphere collider needs to be a trigger collider SphereCollider collider = GetComponent(); if (collider != null) collider.isTrigger = true; // We start with the menu disabled SetMenuActive(false); } /// /// Tries to find the local Humanoid /// /// The found humanoid, null if no local humanoid has been found protected HumanoidControl FindHumanoid() { HumanoidControl[] humanoids = FindObjectsOfType(); for (int i = 0; i < humanoids.Length; i++) { if (humanoids[i].isRemote == false) return humanoids[i]; } return null; } /// /// Detects the Teleporter and Menu Interaction pointer on the humanoid /// /// The humanoid for which the interaction pointers need to be found protected void InitInteractionPointers(HumanoidControl humanoid) { leftTeleporter = humanoid.leftHandTarget.GetComponentInChildren(); rightTeleporter = humanoid.rightHandTarget.GetComponentInChildren(); leftMenuPointer = GetInteractionPointer(humanoid.leftHandTarget, leftTeleporter); rightMenuPointer = GetInteractionPointer(humanoid.rightHandTarget, rightTeleporter); } /// /// Find interaction pointer on the hand /// /// The hand to which the Interaction Pointer should be attached /// (optional) when give, the interaction pointer should be not euqual to the invalidPointer /// The found interaction pointer protected InteractionPointer GetInteractionPointer(HandTarget handTarget, InteractionPointer invalidPointer = null) { InteractionPointer[] interactionPointers = handTarget.GetComponentsInChildren(); foreach (InteractionPointer interactionPointer in interactionPointers) { if (interactionPointer != invalidPointer) return interactionPointer; } return null; } protected void SetControllerInput(HumanoidControl humanoid) { ControllerInput controllerInput = humanoid.GetComponent(); if (controllerInput != null) { //controllerInput.leftOptionInput.SetMethod(SetMenuActive, InputEvent.EventType.Start); //controllerInput.rightOptionInput.SetMethod(SetMenuActive, InputEvent.EventType.Start); controllerInput.SetEventHandler(true, ControllerInput.SideButton.Option, SetMenuActive); controllerInput.SetEventHandler(false, ControllerInput.SideButton.Option, SetMenuActive); } } /// /// Activates or deactivates the menu and updates the interaction pointer behaviour /// /// Indication whether the menu has to be active public void SetMenuActive(bool active) { if (humanoid == null) return; ControllerInput controllerInput = humanoid.GetComponent(); // Hide the menu when it is visble (toggle function) or // when it is set to be inactive explicitly if (MenuActive() || active == false) { HideMenu(); DisableMenuPointer(controllerInput); EnableTeleporter(controllerInput); } else { ShowMenu(); DisableTeleporter(controllerInput); EnableMenuPointer(controllerInput); AdjustOutOfRangeDistance(); } } /// /// Trigger handler for when an object moves out of the sphere collider /// protected void OnTriggerExit(Collider other) { if (MenuActive()) { // Is it the humanoid exiting the range sphere? HumanoidControl triggeringHumanoid = other.GetComponentInParent(); if (triggeringHumanoid == humanoid) SetMenuActive(false); } } /// /// Show the menu at 'distance' from the players' head. /// But take core it is not placed inside objects. /// If this happens, the menu is places closer to the humanoid. /// protected void ShowMenu() { float distance = menuDistance; // check if the menu will intersect with an object RaycastHit hit; Vector3 direction = humanoid.headTarget.transform.forward; Vector3 origin = humanoid.headTarget.transform.position + direction * 0.1F; if (Physics.Raycast(origin, direction, out hit, menuDistance)) distance = hit.distance; // Position the menu at 'distance' from the player's head this.transform.position = humanoid.headTarget.transform.TransformPoint(0, 0, distance); this.transform.rotation = Quaternion.LookRotation(humanoid.headTarget.transform.forward, Vector3.up); this.gameObject.SetActive(true); } /// /// Hide the menu /// protected void HideMenu() { this.gameObject.SetActive(false); } /// /// Is the menu currently visible? /// /// boolean indicating whether the menu is visible protected bool MenuActive() { return this.gameObject.activeInHierarchy; } /// /// Enable the Menu Pointer. This will also set the controller input. /// /// The ControllerInput to update protected void EnableMenuPointer(ControllerInput controllerInput) { // activate the interaction pointer if (leftMenuPointer != null) leftMenuPointer.Activation(true); if (rightMenuPointer != null) rightMenuPointer.Activation(true); // enable the button click if (controllerInput != null) { if (leftMenuPointer != null) controllerInput.SetEventHandler(true, ControllerInput.SideButton.Trigger1, leftMenuPointer.Click); if (rightMenuPointer != null) controllerInput.SetEventHandler(false, ControllerInput.SideButton.Trigger1, rightMenuPointer.Click); } } /// /// Disable the Menu Pointer. This will also disable the controller input. /// /// The ControllerInput to update protected void DisableMenuPointer(ControllerInput controllerInput) { // deactivate the interaction pointer if (leftMenuPointer != null) leftMenuPointer.Activation(false); if (rightMenuPointer != null) rightMenuPointer.Activation(false); // disable the button click if (controllerInput != null) { controllerInput.SetEventHandler(true, ControllerInput.SideButton.Trigger1, null); controllerInput.SetEventHandler(false, ControllerInput.SideButton.Trigger1, null); } } /// /// Enable the teleporter. This will also set the controller input. /// /// The ControllerInput to update protected void EnableTeleporter(ControllerInput controllerInput) { // enable the button activation and click if (controllerInput != null) { if (leftTeleporter != null) { controllerInput.SetEventHandler(true, ControllerInput.SideButton.StickButton, leftTeleporter.Activation); controllerInput.SetEventHandler(true, ControllerInput.SideButton.Trigger1, leftTeleporter.Click); } if (rightTeleporter != null) { controllerInput.SetEventHandler(false, ControllerInput.SideButton.StickButton, rightTeleporter.Activation); controllerInput.SetEventHandler(false, ControllerInput.SideButton.Trigger1, rightTeleporter.Click); } } } /// /// Disable the Teleporter. This will also disable the controller input /// /// The ControllerInput to update protected void DisableTeleporter(ControllerInput controllerInput) { // deactivate the interaction pointer if (leftTeleporter != null) leftTeleporter.Activation(false); if (rightTeleporter != null) rightTeleporter.Activation(false); // disable the button activation if (controllerInput != null) { controllerInput.SetEventHandler(true, ControllerInput.SideButton.StickButton, null); controllerInput.SetEventHandler(false, ControllerInput.SideButton.StickButton, null); } } /// /// Adjust trigger sphere collider radius for out-of-range /// protected void AdjustOutOfRangeDistance() { SphereCollider collider = GetComponent(); if (collider != null) { float radius = collider.radius; if (leftMenuPointer != null) radius = Mathf.Max(radius, leftMenuPointer.maxDistance); if (rightMenuPointer != null) radius = Mathf.Max(radius, rightMenuPointer.maxDistance); collider.radius = radius; } } } }