// VR Simulator|Prefabs|0005 namespace VRTK { using UnityEngine; using UnityEngine.UI; using System; using System.Collections.Generic; /// /// The `VRSimulatorCameraRig` prefab is a mock Camera Rig set up that can be used to develop with VRTK without the need for VR Hardware. /// /// /// Use the mouse and keyboard to move around both play area and hands and interacting with objects without the need of a hmd or VR controls. /// public class SDK_InputSimulator : MonoBehaviour { /// /// Mouse input mode types /// /// Mouse movement is always treated as mouse input. /// Mouse movement is only treated as movement when a button is pressed. public enum MouseInputMode { Always, RequiresButtonPress } #region Public fields [Tooltip("Show control information in the upper left corner of the screen.")] public bool showControlHints = true; [Tooltip("Hide hands when disabling them.")] public bool hideHandsAtSwitch = false; [Tooltip("Reset hand position and rotation when enabling them.")] public bool resetHandsAtSwitch = true; [Tooltip("Whether mouse movement always acts as input or requires a button press.")] public MouseInputMode mouseMovementInput = MouseInputMode.Always; [Tooltip("Lock the mouse cursor to the game window when the mouse movement key is pressed.")] public bool lockMouseToView = true; [Header("Adjustments")] [Tooltip("Adjust hand movement speed.")] public float handMoveMultiplier = 0.002f; [Tooltip("Adjust hand rotation speed.")] public float handRotationMultiplier = 0.5f; [Tooltip("Adjust player movement speed.")] public float playerMoveMultiplier = 5; [Tooltip("Adjust player rotation speed.")] public float playerRotationMultiplier = 0.5f; [Tooltip("Adjust player sprint speed.")] public float playerSprintMultiplier = 2; [Header("Operation Key Bindings")] [Tooltip("Key used to enable mouse input if a button press is required.")] public KeyCode mouseMovementKey = KeyCode.Mouse1; [Tooltip("Key used to toggle control hints on/off.")] public KeyCode toggleControlHints = KeyCode.F1; [Tooltip("Key used to switch between left and righ hand.")] public KeyCode changeHands = KeyCode.Tab; [Tooltip("Key used to switch hands On/Off.")] public KeyCode handsOnOff = KeyCode.LeftAlt; [Tooltip("Key used to switch between positional and rotational movement.")] public KeyCode rotationPosition = KeyCode.LeftShift; [Tooltip("Key used to switch between X/Y and X/Z axis.")] public KeyCode changeAxis = KeyCode.LeftControl; [Tooltip("Key used to distance pickup with left hand.")] public KeyCode distancePickupLeft = KeyCode.Mouse0; [Tooltip("Key used to distance pickup with right hand.")] public KeyCode distancePickupRight = KeyCode.Mouse1; [Tooltip("Key used to enable distance pickup.")] public KeyCode distancePickupModifier = KeyCode.LeftControl; [Header("Movement Key Bindings")] [Tooltip("Key used to move forward.")] public KeyCode moveForward = KeyCode.W; [Tooltip("Key used to move to the left.")] public KeyCode moveLeft = KeyCode.A; [Tooltip("Key used to move backwards.")] public KeyCode moveBackward = KeyCode.S; [Tooltip("Key used to move to the right.")] public KeyCode moveRight = KeyCode.D; [Tooltip("Key used to sprint.")] public KeyCode sprint = KeyCode.LeftShift; [Header("Controller Key Bindings")] [Tooltip("Key used to simulate trigger button.")] public KeyCode triggerAlias = KeyCode.Mouse1; [Tooltip("Key used to simulate grip button.")] public KeyCode gripAlias = KeyCode.Mouse0; [Tooltip("Key used to simulate touchpad button.")] public KeyCode touchpadAlias = KeyCode.Q; [Tooltip("Key used to simulate button one.")] public KeyCode buttonOneAlias = KeyCode.E; [Tooltip("Key used to simulate button two.")] public KeyCode buttonTwoAlias = KeyCode.R; [Tooltip("Key used to simulate start menu button.")] public KeyCode startMenuAlias = KeyCode.F; [Tooltip("Key used to switch between button touch and button press mode.")] public KeyCode touchModifier = KeyCode.T; [Tooltip("Key used to switch between hair touch mode.")] public KeyCode hairTouchModifier = KeyCode.H; #endregion #region Private fields private bool isHand = false; private GameObject hintCanvas; private Text hintText; private Transform rightHand; private Transform leftHand; private Transform currentHand; private Vector3 oldPos; private Transform neck; private SDK_ControllerSim rightController; private SDK_ControllerSim leftController; private static GameObject cachedCameraRig; private static bool destroyed = false; private float sprintMultiplier = 1; private GameObject crossHairPanel; #endregion /// /// The FindInScene method is used to find the `VRSimulatorCameraRig` GameObject within the current scene. /// /// Returns the found `VRSimulatorCameraRig` GameObject if it is found. If it is not found then it prints a debug log error. public static GameObject FindInScene() { if (cachedCameraRig == null && !destroyed) { cachedCameraRig = VRTK_SharedMethods.FindEvenInactiveGameObject(); if (!cachedCameraRig) { VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_SCENE, "VRSimulatorCameraRig", "SDK_InputSimulator", ". check that the `VRTK/Prefabs/VRSimulatorCameraRig` prefab been added to the scene.")); } } return cachedCameraRig; } private void Awake() { VRTK_SDKManager.instance.AddBehaviourToToggleOnLoadedSetupChange(this); } private void OnEnable() { hintCanvas = transform.Find("Canvas/Control Hints").gameObject; crossHairPanel = transform.Find("Canvas/CrosshairPanel").gameObject; hintText = hintCanvas.GetComponentInChildren(); hintCanvas.SetActive(showControlHints); rightHand = transform.Find("RightHand"); rightHand.gameObject.SetActive(false); leftHand = transform.Find("LeftHand"); leftHand.gameObject.SetActive(false); currentHand = rightHand; oldPos = Input.mousePosition; neck = transform.Find("Neck"); leftHand.Find("Hand").GetComponent().material.color = Color.red; rightHand.Find("Hand").GetComponent().material.color = Color.green; rightController = rightHand.GetComponent(); leftController = leftHand.GetComponent(); rightController.Selected = true; leftController.Selected = false; destroyed = false; var controllerSDK = VRTK_SDK_Bridge.GetControllerSDK() as SDK_SimController; if (controllerSDK != null) { Dictionary keyMappings = new Dictionary() { {"Trigger", triggerAlias }, {"Grip", gripAlias }, {"TouchpadPress", touchpadAlias }, {"ButtonOne", buttonOneAlias }, {"ButtonTwo", buttonTwoAlias }, {"StartMenu", startMenuAlias }, {"TouchModifier", touchModifier }, {"HairTouchModifier", hairTouchModifier } }; controllerSDK.SetKeyMappings(keyMappings); } rightHand.gameObject.SetActive(true); leftHand.gameObject.SetActive(true); crossHairPanel.SetActive(false); } private void OnDestroy() { VRTK_SDKManager.instance.RemoveBehaviourToToggleOnLoadedSetupChange(this); destroyed = true; } private void Update() { if (Input.GetKeyDown(toggleControlHints)) { showControlHints = !showControlHints; hintCanvas.SetActive(showControlHints); } if (mouseMovementInput == MouseInputMode.RequiresButtonPress) { if (lockMouseToView) { Cursor.lockState = Input.GetKey(mouseMovementKey) ? CursorLockMode.Locked : CursorLockMode.None; } else if (Input.GetKeyDown(mouseMovementKey)) { oldPos = Input.mousePosition; } } if (Input.GetKeyDown(handsOnOff)) { if (isHand) { SetMove(); } else { SetHand(); } } if (Input.GetKeyDown(changeHands)) { if (currentHand.name == "LeftHand") { currentHand = rightHand; rightController.Selected = true; leftController.Selected = false; } else { currentHand = leftHand; rightController.Selected = false; leftController.Selected = true; } } if (isHand) { UpdateHands(); } else { UpdateRotation(); if(Input.GetKeyDown(distancePickupRight) && Input.GetKey(distancePickupModifier)) { TryPickup(true); } else if(Input.GetKeyDown(distancePickupLeft) && Input.GetKey(distancePickupModifier)) { TryPickup(false); } if(Input.GetKey(sprint)) { sprintMultiplier = playerSprintMultiplier; } else { sprintMultiplier = 1; } if(Input.GetKeyDown(distancePickupModifier)) { crossHairPanel.SetActive(true); } else if(Input.GetKeyUp(distancePickupModifier)) { crossHairPanel.SetActive(false); } } UpdatePosition(); if (showControlHints) { UpdateHints(); } } private void TryPickup(bool rightHand) { Ray screenRay = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0f)); RaycastHit hit; if(Physics.Raycast(screenRay, out hit)) { VRTK_InteractableObject io = hit.collider.gameObject.GetComponent(); if(io) { GameObject hand; if(rightHand) { hand = VRTK_DeviceFinder.GetControllerRightHand(); } else { hand = VRTK_DeviceFinder.GetControllerLeftHand(); } VRTK_InteractGrab grab = hand.GetComponent(); if(grab.GetGrabbedObject() == null) { hand.GetComponent().ForceTouch(hit.collider.gameObject); grab.AttemptGrab(); } } } } private void UpdateHands() { Vector3 mouseDiff = GetMouseDelta(); if (IsAcceptingMouseInput()) { if (Input.GetKey(rotationPosition)) //Rotation { if (Input.GetKey(changeAxis)) { Vector3 rot = Vector3.zero; rot.x += (mouseDiff * handRotationMultiplier).y; rot.y += (mouseDiff * handRotationMultiplier).x; currentHand.transform.Rotate(rot * Time.deltaTime); } else { Vector3 rot = Vector3.zero; rot.z += (mouseDiff * handRotationMultiplier).x; rot.x += (mouseDiff * handRotationMultiplier).y; currentHand.transform.Rotate(rot * Time.deltaTime); } } else //Position { if (Input.GetKey(changeAxis)) { Vector3 pos = Vector3.zero; pos += mouseDiff * handMoveMultiplier; currentHand.transform.Translate(pos * Time.deltaTime); } else { Vector3 pos = Vector3.zero; pos.x += (mouseDiff * handMoveMultiplier).x; pos.z += (mouseDiff * handMoveMultiplier).y; currentHand.transform.Translate(pos * Time.deltaTime); } } } } private void UpdateRotation() { Vector3 mouseDiff = GetMouseDelta(); if (IsAcceptingMouseInput()) { Vector3 rot = transform.localRotation.eulerAngles; rot.y += (mouseDiff * playerRotationMultiplier).x; transform.localRotation = Quaternion.Euler(rot); rot = neck.rotation.eulerAngles; if (rot.x > 180) { rot.x -= 360; } if (rot.x < 80 && rot.x > -80) { rot.x += (mouseDiff * playerRotationMultiplier).y * -1; rot.x = Mathf.Clamp(rot.x, -79, 79); neck.rotation = Quaternion.Euler(rot); } } } private void UpdatePosition() { float moveMod = Time.deltaTime * playerMoveMultiplier * sprintMultiplier; if (Input.GetKey(moveForward)) { transform.Translate(transform.forward * moveMod, Space.World); } else if (Input.GetKey(moveBackward)) { transform.Translate(-transform.forward * moveMod, Space.World); } if (Input.GetKey(moveLeft)) { transform.Translate(-transform.right * moveMod, Space.World); } else if (Input.GetKey(moveRight)) { transform.Translate(transform.right * moveMod, Space.World); } } private void SetHand() { Cursor.visible = false; isHand = true; rightHand.gameObject.SetActive(true); leftHand.gameObject.SetActive(true); oldPos = Input.mousePosition; if (resetHandsAtSwitch) { rightHand.transform.localPosition = new Vector3(0.2f, 1.2f, 0.5f); rightHand.transform.localRotation = Quaternion.identity; leftHand.transform.localPosition = new Vector3(-0.2f, 1.2f, 0.5f); leftHand.transform.localRotation = Quaternion.identity; } } private void SetMove() { Cursor.visible = true; isHand = false; if (hideHandsAtSwitch) { rightHand.gameObject.SetActive(false); leftHand.gameObject.SetActive(false); } } private void UpdateHints() { string hints = ""; Func key = (k) => "" + k.ToString() + ""; string mouseInputRequires = ""; if (mouseMovementInput == MouseInputMode.RequiresButtonPress) { mouseInputRequires = " (" + key(mouseMovementKey) + ")"; } // WASD Movement string movementKeys = moveForward.ToString() + moveLeft.ToString() + moveBackward.ToString() + moveRight.ToString(); hints += "Toggle Control Hints: " + key(toggleControlHints) + "\n\n"; hints += "Move Player/Playspace: " + movementKeys + "\n"; hints += "Sprint Modifier: (" + key(sprint) + ")\n\n"; if (isHand) { // Controllers if (Input.GetKey(rotationPosition)) { hints += "Mouse: Controller Rotation" + mouseInputRequires + "\n"; } else { hints += "Mouse: Controller Position" + mouseInputRequires + "\n"; } hints += "Modes: HMD (" + key(handsOnOff) + "), Rotation (" + key(rotationPosition) + ")\n"; hints += "Controller Hand: " + currentHand.name.Replace("Hand", "") + " (" + key(changeHands) + ")\n"; string axis = Input.GetKey(changeAxis) ? "X/Y" : "X/Z"; hints += "Axis: " + axis + " (" + key(changeAxis) + ")\n"; // Controller Buttons string pressMode = "Press"; if (Input.GetKey(hairTouchModifier)) { pressMode = "Hair Touch"; } else if (Input.GetKey(touchModifier)) { pressMode = "Touch"; } hints += "\nButton Press Mode Modifiers: Touch (" + key(touchModifier) + "), Hair Touch (" + key(hairTouchModifier) + ")\n"; hints += "Trigger " + pressMode + ": " + key(triggerAlias) + "\n"; hints += "Grip " + pressMode + ": " + key(gripAlias) + "\n"; if (!Input.GetKey(hairTouchModifier)) { hints += "Touchpad " + pressMode + ": " + key(touchpadAlias) + "\n"; hints += "Button One " + pressMode + ": " + key(buttonOneAlias) + "\n"; hints += "Button Two " + pressMode + ": " + key(buttonTwoAlias) + "\n"; hints += "Start Menu " + pressMode + ": " + key(startMenuAlias) + "\n"; } } else { // HMD Input hints += "Mouse: HMD Rotation" + mouseInputRequires + "\n"; hints += "Modes: Controller (" + key(handsOnOff) + ")\n"; hints += "Distance Pickup Modifier: (" + key(distancePickupModifier) + ")\n"; hints += "Distance Pickup Left Hand: (" + key(distancePickupLeft) + ")\n"; hints += "Distance Pickup Right Hand: (" + key(distancePickupRight) + ")\n"; } hintText.text = hints.TrimEnd(); } private bool IsAcceptingMouseInput() { return mouseMovementInput == MouseInputMode.Always || Input.GetKey(mouseMovementKey); } private Vector3 GetMouseDelta() { if (Cursor.lockState == CursorLockMode.Locked) { return new Vector3(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")); } else { Vector3 mouseDiff = Input.mousePosition - oldPos; oldPos = Input.mousePosition; return mouseDiff; } } } }