using UnityEngine;
#if hNW_UNET
using UnityEngine.Networking;
#endif
namespace Passer {
using Humanoid;
/// Component to specify behaviour when grabbing a GameObject
/// A Handle can be used to give direction on how an object can be grabbed.
/// A sword is usually grabbed by the hilt, a gun by the grip.
/// When a Rigidbody with a %Handle is grabbed,
/// the Rigidbody will move into the hand such that the Handle will fit in the palm of the hand.
/// When a static object with a Handle is grabbed,
/// the hand itself will move such that the Handle fits in the palm of the hand.
///
/// Sockets
/// -------
/// %Humanoid hands actually have a socket inside which receives the handle.
/// Sockets can also be used at different places to receive handles.
///
/// \image html HandleInspector.png
///
/// * \ref Handle::hand "Hand"
/// * \ref Handle::grabType "Grab Type"
/// * \ref Handle::range "Range"
/// * \ref Handle::pose "Hand Pose"
/// * \ref Handle::handTarget "Hand Target"
///
/// Events
/// ======
/// * \ref Handle::grabbedEvents "Grabbed Events"
///
/// %Controller Input
/// ================
/// If a hand grabs an handle you can override the ControllerInput for the side
/// of the hand which grabbed the handle during the time it is held.
/// This enables you to assign a button to shoot a weapon when it is held for example.
/// The configuration is similar to the normal ControllerInput.
/// The difference is that empty entries do not override the ControllerInput configuration.
/// When the handle is released by the hand,
/// the ControllerInput is restored to the original configuration.
[HelpURL("https://passervr.com/apis/HumanoidControl/Unity/class_passer_1_1_handle.html")]
public class Handle : MonoBehaviour {
/// The way in which the hand can grab the handle
public enum GrabType {
DefaultGrab, ///< Same as BarGrab
BarGrab, ///< The hand will grab the handle in the specified position and rotation
BallGrab, ///< The hand will grab the handle in the specified position, the rotation is free
RailGrab, ///< The hand will grab the handle along the the specified position, the rotation around the rail is free
AnyGrab, ///< The hand will grab the handle in any position or rotation
NoGrab ///< The hand cannot grab the handle or the gameObject
}
/// Select how the hand will grab the handle
public GrabType grabType;
///
/// Sticky handles will not release unless release sticky is used
///
public bool sticky = false;
/// The range within the handle will work. Outside this range normal grabbing is used.
public float range = 0.2f;
/// The Controller input which will be active while the Handle is grabbed.
/// \version v3
public ControllerEventHandlers[] controllerInputEvents = {
new ControllerEventHandlers() { label = "Vertical", id = 0 },
new ControllerEventHandlers() { label = "Horizontal", id = 1 },
new ControllerEventHandlers() { label = "Stick Button", id = 2 },
new ControllerEventHandlers() { label = "Vertical", id = 3 },
new ControllerEventHandlers() { label = "Horizontal", id = 4 },
new ControllerEventHandlers() { label = "Stick Button", id = 5 },
new ControllerEventHandlers() { label = "Button 1", id = 6 },
new ControllerEventHandlers() { label = "Button 2", id = 7 },
new ControllerEventHandlers() { label = "Button 3", id = 8 },
new ControllerEventHandlers() { label = "Button 4", id = 9 },
new ControllerEventHandlers() { label = "Trigger 1", id = 10 },
new ControllerEventHandlers() { label = "Trigger 2", id = 11 },
new ControllerEventHandlers() { label = "Option", id = 12 },
};
//public ControllerEventHandlers[] mouseInputEvents = {
// new ControllerEventHandlers() { label = "Mouse Vertical", id = 0 },
// new ControllerEventHandlers() { label = "Mouse Horizontal", id = 1 },
// new ControllerEventHandlers() { label = "Mouse Scroll", id = 2 },
// new ControllerEventHandlers() { label = "Left Button", id = 3 },
// new ControllerEventHandlers() { label = "Middle button", id = 4 },
// new ControllerEventHandlers() { label = "Right Button", id = 5 },
//};
/// The socket holding the handle
/// This parameter contains the socket holding the handle
/// when it is held by a socket.
public Socket socket;
/// Which hand can pick up this handle?
public enum Hand {
Both, ///< The handle can be picked up by any hand
Left, ///< The handle can only be grabbed by the left hand
Right ///< The handle can only be grabbed by the right hand
}
///
/// Selects which hand can pick up this handle
///
/// Some handles may only be grabbed by the left or right hand.
public Hand hand;
///
/// The Hand Pose which will be active while the Handle is grabbed.
///
/// See also: Hand Pose
public Pose pose;
#if hNEARHANDLE
public bool useNearPose;
public int nearPose;
#endif
/// The hand target which grabbed the handle.
/// When the Handle is grabbed this will contain the HandTarget of the grabbing hand.
/// When the HandTarget of a Handle is set in the editor while editing the scene
/// the applicable hand will try to grab the Handle.
/// This is null when the handle is not grabbed by a hand.
public Humanoid.HandTarget handTarget;
///
/// The Handle is held by a socket
///
public bool isHeld;
public Vector3 TranslationTo(Vector3 position) {
Vector3 handlePosition = transform.position;
Vector3 translation = position - handlePosition;
return translation;
}
public Quaternion RotationTo(Quaternion orientation) {
Quaternion handleOrientation = transform.rotation;
Quaternion rotation = orientation * Quaternion.Inverse(handleOrientation);
return rotation;
}
// public static void Create(GameObject gameObject, Pawn.PawnHand controllerTarget) {
// GameObject handleObject = new GameObject("Handle");
// handleObject.transform.parent = gameObject.transform;
// handleObject.transform.localRotation = controllerTarget.transform.rotation * gameObject.transform.rotation;
// handleObject.transform.localPosition = gameObject.transform.InverseTransformPoint(controllerTarget.transform.position);
// Handle handle = gameObject.AddComponent();
// handle.grabType = GrabType.BarGrab;
// }
public static void Create(GameObject gameObject, Humanoid.HandTarget handTarget) {
GameObject handleObject = new GameObject("Handle");
handleObject.transform.parent = gameObject.transform;
handleObject.transform.localRotation = Quaternion.Inverse(Quaternion.Inverse(handTarget.handPalm.rotation * gameObject.transform.rotation));
handleObject.transform.localPosition = gameObject.transform.InverseTransformPoint(handTarget.handPalm.position);
Handle handle = gameObject.AddComponent();
handle.grabType = GrabType.BarGrab;
handle.handTarget = handTarget;
}
/// Finds the handle on the transform closest to the given position
public static Handle GetClosestHandle(Transform transform, Vector3 position, Hand hand, float range = float.PositiveInfinity) {
Handle[] handles = transform.GetComponentsInChildren();
Handle closestHandle = null;
float closestDistance = float.PositiveInfinity;
foreach (Handle handle in handles) {
bool correctHand = CheckHand(handle, hand);
if (!correctHand)
continue;
if (handle.grabType == GrabType.RailGrab) {
// Determine the closest position on the rail in local space
Vector3 localSocketPosition = handle.transform.InverseTransformPoint(position);
Vector3 projectedOnRail = Vector3.Project(localSocketPosition, Vector3.up);
Vector3 clampedOnRail = projectedOnRail;
float railLength = handle.transform.lossyScale.y;
if (projectedOnRail.magnitude > railLength / 2)
clampedOnRail = projectedOnRail.normalized * (railLength / 2);
// Now convert it back to world space
Vector3 targetPosition = handle.transform.TransformPoint(clampedOnRail);
// And determine the distance to of the grab position to the closest point on the rail
float distance = Vector3.Distance(position, targetPosition);
if (distance < closestDistance && distance < range) {
closestHandle = handle;
closestDistance = distance;
}
}
else {
float distance = Vector3.Distance(handle.transform.position, position);
if (distance < closestDistance && distance < range) {
closestHandle = handle;
closestDistance = distance;
}
}
}
return closestHandle;
}
/// Finds the handle on the transform closest to the given position
public static Handle GetClosestHandle(Transform transform, Vector3 position, float range = float.PositiveInfinity) {
Handle[] handles = transform.GetComponentsInChildren();
Handle closestHandle = null;
float closestDistance = float.PositiveInfinity;
foreach (Handle handle in handles) {
if (handle.grabType == GrabType.RailGrab) {
// Determine the closest position on the rail in local space
Vector3 localSocketPosition = handle.transform.InverseTransformPoint(position);
Vector3 projectedOnRail = Vector3.Project(localSocketPosition, Vector3.up);
Vector3 clampedOnRail = projectedOnRail;
float railLength = handle.transform.lossyScale.y;
if (projectedOnRail.magnitude > railLength / 2)
clampedOnRail = projectedOnRail.normalized * (railLength / 2);
// Now convert it back to world space
Vector3 targetPosition = handle.transform.TransformPoint(clampedOnRail);
// And determine the distance to of the grab position to the closest point on the rail
float distance = Vector3.Distance(position, targetPosition);
if (distance < closestDistance && distance < range) {
closestHandle = handle;
closestDistance = distance;
}
}
else {
float distance = Vector3.Distance(handle.transform.position, position);
if (distance < closestDistance && distance < range) {
closestHandle = handle;
closestDistance = distance;
}
}
}
return closestHandle;
}
/// Finds the handle on the transform closest to the given position
/// Handles not in socket have lower priority
public static Handle GetClosestHandle(Transform transform, Vector3 position, Hand hand) {
Handle[] handles = transform.GetComponentsInChildren();
Handle closestHandle = GetClosestHandle(handles, position, hand);
if (closestHandle != null)
return closestHandle;
handles = transform.GetComponentsInParent();
closestHandle = GetClosestHandle(handles, position, hand);
return closestHandle;
}
protected static Handle GetClosestHandle(Handle[] handles, Vector3 position, Hand hand) {
bool closestInSocket = true;
Handle closestHandle = null;
float closestDistance = float.PositiveInfinity;
foreach (Handle handle in handles) {
bool correctHand = CheckHand(handle, hand);
if (!correctHand)
continue;
float distance = Vector3.Distance(handle.transform.position, position);
if (closestInSocket || (distance < closestDistance && handle.socket == null)) {
closestHandle = handle;
closestDistance = distance;
closestInSocket = handle.socket != null;
}
//Debug.Log("Closest: " + closestHandle + " " + closestInSocket);
}
return closestHandle;
}
protected static bool CheckHand(Handle handle, Hand hand) {
if (handle.hand == Hand.Both)
return true;
else
return (handle.hand == hand);
}
/// Releases this handle from the socket
public void ReleaseFromSocket() {
if (socket == null)
return;
socket.Release();
}
#region Update
protected virtual void Update() {
UpdateGrabbed();
}
#endregion
#region Events
public GameObjectEventHandlers grabbedEvent = new GameObjectEventHandlers() {
label = "Grab Event",
tooltip =
"Call functions using the grabbing status\n" +
"Parameter: the grabbed object",
eventTypeLabels = new string[] {
"Nothing",
"On Grab Start",
"On Let Go",
"While Holding",
"While Not Holding",
"On Grab Change",
"Always"
},
//fromEventLabelBool = "Handle.isHeld",
fromEventLabel = "socket.gameObject"
};
public virtual void UpdateGrabbed() {
isHeld = socket != null;
if (isHeld)
grabbedEvent.value = socket.gameObject;
else
grabbedEvent.value = null;
}
#endregion Update
#if hNEARHANDLE
private BasicHandPhysics nearHand;
public void OnTriggerEnter(Collider other) {
Rigidbody rigidbody = other.attachedRigidbody;
if (rigidbody == null)
return;
nearHand = rigidbody.GetComponent();
}
private void Update() {
if (nearHand != null) {
Vector3 handlePosition = transform.TransformPoint(position);
float distance = Vector3.Distance(nearHand.target.handPalm.position, handlePosition) * 2;
float f = Mathf.Clamp01((distance + 0.25F) / range);
f = f * f * f;
nearHand.target.SetHandPose(nearPose, 1 - f);
if (1 - f <= 0) {
nearHand.target.SetHandPose1(1);
nearHand = null;
}
}
}
#endif
#region Gizmos
protected Mesh gizmoMesh;
void OnDrawGizmosSelected() {
if (gizmoMesh == null)
gizmoMesh = Socket.GenerateGizmoMesh();
if (enabled) {
Matrix4x4 m = Matrix4x4.identity;
Vector3 p = transform.position; // TransformPoint(position);
Quaternion q = Quaternion.identity; // Quaternion.Euler(rotation);
m.SetTRS(p, transform.rotation * q, transform.localScale);//Vector3.one);
Gizmos.color = Color.yellow;
Gizmos.matrix = m;
switch (grabType) {
case GrabType.DefaultGrab:
case GrabType.BarGrab:
Gizmos.DrawMesh(gizmoMesh);
break;
case GrabType.BallGrab:
Gizmos.DrawSphere(Vector3.zero, 0.04f);
break;
case GrabType.RailGrab:
Gizmos.DrawCube(Vector3.zero, new Vector3(0.03F, transform.lossyScale.y, 0.03F));
break;
}
//if (grabType != GrabType.NoGrab)
// Gizmos.DrawWireSphere(Vector3.zero, range);
}
}
#endregion
}
}