// Base Object Control Action|ObjectControlActions|25000 namespace VRTK { using UnityEngine; /// /// Provides a base that all object control actions can inherit from. /// /// /// **Script Usage:** /// > This is an abstract class that is to be inherited to a concrete class that provides object control action functionality, therefore this script should not be directly used. /// public abstract class VRTK_BaseObjectControlAction : MonoBehaviour { /// /// The axis to listen to changes on. /// public enum AxisListeners { /// /// Listen for changes on the horizontal X axis. /// XAxisChanged, /// /// Listen for changes on the vertical y axis. /// YAxisChanged } [Tooltip("The Object Control script to receive axis change events from.")] public VRTK_ObjectControl objectControlScript; [Tooltip("Determines which Object Control Axis event to listen to.")] public AxisListeners listenOnAxisChange; protected Collider centerCollider; protected Vector3 colliderCenter = Vector3.zero; protected float colliderRadius = 0f; protected float colliderHeight = 0f; protected Transform controlledTransform; protected Transform playArea; protected VRTK_BodyPhysics internalBodyPhysics; protected Vector3 playerHeadPositionBeforeRotation; protected Transform headsetTransform; protected bool validPlayerObject; protected abstract void Process(GameObject controlledGameObject, Transform directionDevice, Vector3 axisDirection, float axis, float deadzone, bool currentlyFalling, bool modifierActive); protected virtual void Awake() { VRTK_SDKManager.AttemptAddBehaviourToToggleOnLoadedSetupChange(this); } protected virtual void OnEnable() { playArea = VRTK_DeviceFinder.PlayAreaTransform(); if (objectControlScript) { switch (listenOnAxisChange) { case AxisListeners.XAxisChanged: objectControlScript.XAxisChanged += AxisChanged; break; case AxisListeners.YAxisChanged: objectControlScript.YAxisChanged += AxisChanged; break; } } internalBodyPhysics = (internalBodyPhysics == null ? VRTK_SharedMethods.FindEvenInactiveComponent(true) : internalBodyPhysics); } protected virtual void OnDisable() { if (objectControlScript) { switch (listenOnAxisChange) { case AxisListeners.XAxisChanged: objectControlScript.XAxisChanged -= AxisChanged; break; case AxisListeners.YAxisChanged: objectControlScript.YAxisChanged -= AxisChanged; break; } } } protected virtual void OnDestroy() { VRTK_SDKManager.AttemptRemoveBehaviourToToggleOnLoadedSetupChange(this); } protected virtual void AxisChanged(object sender, ObjectControlEventArgs e) { Process(e.controlledGameObject, e.directionDevice, e.axisDirection, e.axis, e.deadzone, e.currentlyFalling, e.modifierActive); } protected virtual void RotateAroundPlayer(GameObject controlledGameObject, float angle) { Vector3 objectCenter = GetObjectCenter(controlledGameObject.transform); Vector3 objectPosition = controlledGameObject.transform.TransformPoint(objectCenter); controlledGameObject.transform.Rotate(Vector3.up, angle); objectPosition -= controlledGameObject.transform.TransformPoint(objectCenter); controlledGameObject.transform.position += objectPosition; } protected virtual void Blink(float blinkSpeed) { if (blinkSpeed > 0f) { VRTK_SDK_Bridge.HeadsetFade(Color.black, 0); ReleaseBlink(blinkSpeed); } } protected virtual void ReleaseBlink(float blinkSpeed) { VRTK_SDK_Bridge.HeadsetFade(Color.clear, blinkSpeed); } protected virtual Vector3 GetObjectCenter(Transform checkObject) { if (centerCollider == null || checkObject != controlledTransform) { controlledTransform = checkObject; if (checkObject == playArea) { bool centerColliderSet = false; if (internalBodyPhysics != null && internalBodyPhysics.GetBodyColliderContainer() != null) { CapsuleCollider playAreaCollider = internalBodyPhysics.GetBodyColliderContainer().GetComponent(); centerCollider = playAreaCollider; if (playAreaCollider != null) { centerColliderSet = true; colliderRadius = playAreaCollider.radius; colliderHeight = playAreaCollider.height; colliderCenter = playAreaCollider.center; } } if (!centerColliderSet) { VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT, "PlayArea", "CapsuleCollider", "the same or child")); } } else { centerCollider = checkObject.GetComponentInChildren(); if (centerCollider == null) { VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_GAMEOBJECT, "CheckObject", "Collider", "the same or child")); } colliderRadius = 0.1f; colliderHeight = 0.1f; } } return colliderCenter; } protected virtual int GetAxisDirection(float axis) { int axisDirection = 0; if (axis < 0) { axisDirection = -1; } else if (axis > 0) { axisDirection = 1; } return axisDirection; } protected virtual bool CanMove(VRTK_BodyPhysics givenBodyPhysics, Vector3 currentPosition, Vector3 proposedPosition) { if (givenBodyPhysics == null) { return true; } Vector3 proposedDirection = (proposedPosition - currentPosition).normalized; float distance = Vector3.Distance(currentPosition, proposedPosition); return !givenBodyPhysics.SweepCollision(proposedDirection, distance); } /// /// Since rotation scripts may rotate the game object '[CameraRig]' in order to rotate the player and the player's head does not always have the local position (0,0,0), the rotation will result in a position offset of player's head. The game object '[CameraRig]' will moved relativly to compensate that. Therefore it will save the player's head position in this method. /// Call 'CheckForPlayerAfterRotation()' to correct the player's head position offset after rotation. /// /// protected virtual void CheckForPlayerBeforeRotation(GameObject controlledGameObject) { VRTK_PlayerObject playerObject = controlledGameObject.GetComponent(); if (headsetTransform == null) { headsetTransform = VRTK_DeviceFinder.HeadsetTransform(); } validPlayerObject = (playerObject != null && playerObject.objectType == VRTK_PlayerObject.ObjectTypes.CameraRig && headsetTransform != null); if (validPlayerObject) { //Save the player's head position for use in method 'CheckForPlayerAfterRotation'. playerHeadPositionBeforeRotation = headsetTransform.position; } } /// /// Corrects the player's head position offset after rotation. Call 'CheckForPlayerBeforeRotation' before execute rotation. /// /// protected virtual void CheckForPlayerAfterRotation(GameObject controlledGameObject) { //If necessary the player's head position will be corrected by translate the Gameobject [CameraRig] relativly. if (validPlayerObject) { controlledGameObject.transform.position += playerHeadPositionBeforeRotation - headsetTransform.position; //Prevents multiple calls of this method without call of 'CheckForPlayerBeforeRotation'. validPlayerObject = false; } } } }