|
|
- // Player Climb|Locomotion|20120
- namespace VRTK
- {
- using GrabAttachMechanics;
- using UnityEngine;
-
- /// <summary>
- /// Event Payload
- /// </summary>
- /// <param name="controllerReference">The reference to the controller doing the interaction.</param>
- /// <param name="target">The GameObject of the interactable object that is being interacted with by the controller.</param>
- public struct PlayerClimbEventArgs
- {
- public VRTK_ControllerReference controllerReference;
- public GameObject target;
- }
-
- /// <summary>
- /// Event Payload
- /// </summary>
- /// <param name="sender">this object</param>
- /// <param name="e"><see cref="PlayerClimbEventArgs"/></param>
- public delegate void PlayerClimbEventHandler(object sender, PlayerClimbEventArgs e);
-
- /// <summary>
- /// Provides the ability for the SDK Camera Rig to be moved around based on whether an Interact Grab is interacting with a Climbable Interactable Object to simulate climbing.
- /// </summary>
- /// <remarks>
- /// **Required Components:**
- /// * `VRTK_BodyPhysics` - A Body Physics script to deal with the effects of physics and gravity on the play area.
- ///
- /// **Optional Components:**
- /// * `VRTK_BasicTeleport` - A Teleporter script to use when snapping the play area to the nearest floor when releasing from grab.
- /// * `VRTK_HeadsetCollision` - A Headset Collision script to determine when the headset is colliding with geometry to know when to reset to a valid location.
- /// * `VRTK_PositionRewind` - A Position Rewind script to utilise when resetting to a valid location upon ungrabbing whilst colliding with geometry.
- ///
- /// **Script Usage:**
- /// * Place the `VRTK_PlayerClimb` script on any active scene GameObject.
- ///
- /// **Script Dependencies:**
- /// * The controller Script Alias GameObject requires the Interact Touch and Interact Grab scripts to allow for touching and grabbing of Interactable Objects.
- /// * An Interactable Object in the scene that has the Climbable Grab Attach Mechanic.
- /// </remarks>
- /// <example>
- /// `VRTK/Examples/037_CameraRig_ClimbingFalling` shows how to set up a scene with player climbing. There are many different examples showing how the same system can be used in unique ways.
- /// </example>
- [AddComponentMenu("VRTK/Scripts/Locomotion/VRTK_PlayerClimb")]
- public class VRTK_PlayerClimb : MonoBehaviour
- {
- [Header("Climb Settings")]
-
- [Tooltip("Will scale movement up and down based on the player transform's scale.")]
- public bool usePlayerScale = true;
-
- [Header("Custom Settings")]
-
- [Tooltip("The Body Physics script to use for dealing with climbing and falling. If this is left blank then the script will need to be applied to the same GameObject.")]
- public VRTK_BodyPhysics bodyPhysics;
- [Tooltip("The Teleport script to use when snapping to nearest floor on release. If this is left blank then a Teleport script will need to be applied to the same GameObject.")]
- public VRTK_BasicTeleport teleporter;
- [Tooltip("The Headset Collision script to use for determining if the user is climbing inside a collidable object. If this is left blank then the script will need to be applied to the same GameObject.")]
- public VRTK_HeadsetCollision headsetCollision;
- [Tooltip("The Position Rewind script to use for dealing resetting invalid positions. If this is left blank then the script will need to be applied to the same GameObject.")]
- public VRTK_PositionRewind positionRewind;
-
- /// <summary>
- /// Emitted when player climbing has started.
- /// </summary>
- public event PlayerClimbEventHandler PlayerClimbStarted;
- /// <summary>
- /// Emitted when player climbing has ended.
- /// </summary>
- public event PlayerClimbEventHandler PlayerClimbEnded;
-
- protected Transform playArea;
- protected Vector3 startControllerScaledLocalPosition;
- protected Vector3 startGrabPointLocalPosition;
- protected Vector3 startPlayAreaWorldOffset;
- protected GameObject grabbingController;
- protected GameObject climbingObject;
- protected Quaternion climbingObjectLastRotation;
- protected bool isClimbing;
- protected bool useGrabbedObjectRotation;
-
- /// <summary>
- /// The IsClimbing method will return if climbing is currently taking place or not.
- /// </summary>
- /// <returns>Returns `true` if climbing is currently taking place.</returns>
- public virtual bool IsClimbing()
- {
- return isClimbing;
- }
-
- protected virtual void Awake()
- {
- bodyPhysics = (bodyPhysics != null ? bodyPhysics : FindObjectOfType<VRTK_BodyPhysics>());
-
- if (bodyPhysics == null)
- {
- VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.REQUIRED_COMPONENT_MISSING_FROM_SCENE, "VRTK_PlayerClimb", "VRTK_BodyPhysics"));
- }
-
- teleporter = (teleporter != null ? teleporter : FindObjectOfType<VRTK_BasicTeleport>());
- headsetCollision = (headsetCollision != null ? headsetCollision : FindObjectOfType<VRTK_HeadsetCollision>());
- positionRewind = (positionRewind != null ? positionRewind : FindObjectOfType<VRTK_PositionRewind>());
-
- VRTK_SDKManager.AttemptAddBehaviourToToggleOnLoadedSetupChange(this);
- }
-
- protected virtual void OnEnable()
- {
- playArea = VRTK_DeviceFinder.PlayAreaTransform();
- InitListeners(true);
- }
-
- protected virtual void OnDisable()
- {
- Ungrab(false, null, climbingObject);
- InitListeners(false);
- }
-
- protected virtual void OnDestroy()
- {
- VRTK_SDKManager.AttemptRemoveBehaviourToToggleOnLoadedSetupChange(this);
- }
-
- protected virtual void Update()
- {
- if (isClimbing)
- {
- Vector3 controllerLocalOffset = GetScaledLocalPosition(grabbingController.transform) - startControllerScaledLocalPosition;
- Vector3 grabPointWorldPosition = climbingObject.transform.TransformPoint(startGrabPointLocalPosition);
- playArea.position = grabPointWorldPosition + startPlayAreaWorldOffset - controllerLocalOffset;
-
- if (useGrabbedObjectRotation)
- {
- Vector3 lastRotationVec = climbingObjectLastRotation * Vector3.forward;
- Vector3 currentObectRotationVec = climbingObject.transform.rotation * Vector3.forward;
- Vector3 axis = Vector3.Cross(lastRotationVec, currentObectRotationVec);
- float angle = Vector3.Angle(lastRotationVec, currentObectRotationVec);
-
- playArea.RotateAround(grabPointWorldPosition, axis, angle);
- climbingObjectLastRotation = climbingObject.transform.rotation;
- }
-
- if (positionRewind != null && !IsHeadsetColliding())
- {
- positionRewind.SetLastGoodPosition();
- }
- }
- }
-
- protected virtual void OnPlayerClimbStarted(PlayerClimbEventArgs e)
- {
- if (PlayerClimbStarted != null)
- {
- PlayerClimbStarted(this, e);
- }
- }
-
- protected virtual void OnPlayerClimbEnded(PlayerClimbEventArgs e)
- {
- if (PlayerClimbEnded != null)
- {
- PlayerClimbEnded(this, e);
- }
- }
-
- protected virtual PlayerClimbEventArgs SetPlayerClimbEvent(VRTK_ControllerReference controllerReference, GameObject target)
- {
- PlayerClimbEventArgs e;
- e.controllerReference = controllerReference;
- e.target = target;
- return e;
- }
-
- protected virtual void InitListeners(bool state)
- {
- InitControllerListeners(VRTK_DeviceFinder.GetControllerLeftHand(), state);
- InitControllerListeners(VRTK_DeviceFinder.GetControllerRightHand(), state);
-
- InitTeleportListener(state);
- }
-
- protected virtual void InitTeleportListener(bool state)
- {
- if (teleporter != null)
- {
- if (state)
- {
- teleporter.Teleporting += new TeleportEventHandler(OnTeleport);
- }
- else
- {
- teleporter.Teleporting -= new TeleportEventHandler(OnTeleport);
- }
- }
- }
-
- protected virtual void OnTeleport(object sender, DestinationMarkerEventArgs e)
- {
- if (isClimbing)
- {
- Ungrab(false, e.controllerReference, e.target.gameObject);
- }
- }
-
- protected virtual Vector3 GetScaledLocalPosition(Transform objTransform)
- {
- if (usePlayerScale)
- {
- return (playArea.localRotation * Vector3.Scale(objTransform.localPosition, playArea.localScale));
- }
-
- return (playArea.localRotation * objTransform.localPosition);
- }
-
- protected virtual void OnGrabObject(object sender, ObjectInteractEventArgs e)
- {
- if (IsClimbableObject(e.target))
- {
- GameObject controller = ((VRTK_InteractGrab)sender).gameObject;
- GameObject actualController = VRTK_DeviceFinder.GetActualController(controller);
- Grab(actualController, e.controllerReference, e.target);
- }
- }
-
- protected virtual void OnUngrabObject(object sender, ObjectInteractEventArgs e)
- {
- GameObject controller = ((VRTK_InteractGrab)sender).gameObject;
- GameObject actualController = VRTK_DeviceFinder.GetActualController(controller);
- if (e.target != null && IsClimbableObject(e.target) && IsActiveClimbingController(actualController))
- {
- Ungrab(true, e.controllerReference, e.target);
- }
- }
-
- protected virtual void Grab(GameObject currentGrabbingController, VRTK_ControllerReference controllerReference, GameObject target)
- {
- if (bodyPhysics == null)
- {
- return;
- }
-
- bodyPhysics.ResetFalling();
- bodyPhysics.TogglePreventSnapToFloor(true);
- bodyPhysics.enableBodyCollisions = false;
- bodyPhysics.ToggleOnGround(false);
-
- isClimbing = true;
- climbingObject = target;
- grabbingController = currentGrabbingController;
- startControllerScaledLocalPosition = GetScaledLocalPosition(grabbingController.transform);
- startGrabPointLocalPosition = climbingObject.transform.InverseTransformPoint(grabbingController.transform.position);
- startPlayAreaWorldOffset = playArea.transform.position - grabbingController.transform.position;
- climbingObjectLastRotation = climbingObject.transform.rotation;
- useGrabbedObjectRotation = climbingObject.GetComponent<VRTK_ClimbableGrabAttach>().useObjectRotation;
-
- OnPlayerClimbStarted(SetPlayerClimbEvent(controllerReference, climbingObject));
- }
-
- protected virtual void Ungrab(bool carryMomentum, VRTK_ControllerReference controllerReference, GameObject target)
- {
- if (bodyPhysics == null)
- {
- return;
- }
-
- isClimbing = false;
- if (positionRewind != null && IsHeadsetColliding())
- {
- positionRewind.RewindPosition();
- }
- if (IsBodyColliding() && !IsHeadsetColliding())
- {
- bodyPhysics.ForceSnapToFloor();
- }
-
- bodyPhysics.enableBodyCollisions = true;
-
- if (carryMomentum)
- {
- Vector3 velocity = Vector3.zero;
-
- if (VRTK_ControllerReference.IsValid(controllerReference))
- {
- velocity = -VRTK_DeviceFinder.GetControllerVelocity(controllerReference);
- if (usePlayerScale)
- {
- velocity = playArea.TransformVector(velocity);
- }
- else
- {
- velocity = playArea.TransformDirection(velocity);
- }
- }
-
- bodyPhysics.ApplyBodyVelocity(velocity, true, true);
- }
-
- grabbingController = null;
- climbingObject = null;
-
- OnPlayerClimbEnded(SetPlayerClimbEvent(controllerReference, target));
- }
-
- protected virtual bool IsActiveClimbingController(GameObject controller)
- {
- return (controller == grabbingController);
- }
-
- protected virtual bool IsClimbableObject(GameObject obj)
- {
- VRTK_InteractableObject interactObject = obj.GetComponent<VRTK_InteractableObject>();
- return (interactObject != null && interactObject.grabAttachMechanicScript && interactObject.grabAttachMechanicScript.IsClimbable());
- }
-
- protected virtual void InitControllerListeners(GameObject controller, bool state)
- {
- if (controller != null)
- {
- VRTK_InteractGrab grabScript = controller.GetComponentInChildren<VRTK_InteractGrab>();
- if (grabScript != null)
- {
- if (state)
- {
- grabScript.ControllerGrabInteractableObject += new ObjectInteractEventHandler(OnGrabObject);
- grabScript.ControllerUngrabInteractableObject += new ObjectInteractEventHandler(OnUngrabObject);
- }
- else
- {
- grabScript.ControllerGrabInteractableObject -= new ObjectInteractEventHandler(OnGrabObject);
- grabScript.ControllerUngrabInteractableObject -= new ObjectInteractEventHandler(OnUngrabObject);
- }
- }
- }
- }
-
- protected virtual bool IsBodyColliding()
- {
- return (bodyPhysics != null && bodyPhysics.GetCurrentCollidingObject() != null);
- }
-
- protected virtual bool IsHeadsetColliding()
- {
- return (headsetCollision != null && headsetCollision.IsColliding());
- }
- }
- }
|