using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
namespace BNG {
///
/// An object that can be picked up by a Grabber
///
public class Grabbable : MonoBehaviour {
///
/// Is this object currently being held by a Grabber
///
public bool BeingHeld = false;
///
/// Is this object currently being held by more than one Grabber
///
public bool BeingHeldWithTwoHands {
get {
if (heldByGrabbers != null && heldByGrabbers.Count > 1 && SecondaryGrabBehavior == OtherGrabBehavior.DualGrab) {
return true;
}
// Being Held and a defined SecondaryGrabbable is also being held
else if (BeingHeld && SecondaryGrabbable != null && SecondaryGrabbable.BeingHeld == true) {
return true;
}
return false;
}
}
List validGrabbers;
///
/// The grabber that is currently holding us. Null if not being held
///
protected List heldByGrabbers;
public List HeldByGrabbers {
get {
return heldByGrabbers;
}
}
///
/// Save whether or not the RigidBody was kinematic on Start.
///
protected bool wasKinematic;
protected bool usedGravity;
protected CollisionDetectionMode initialCollisionMode;
protected RigidbodyInterpolation initialInterpolationMode;
///
/// Is the object being pulled towards the Grabber
///
public bool RemoteGrabbing {
get {
return remoteGrabbing;
}
}
protected bool remoteGrabbing;
[Header("Grab Settings")]
///
/// Configure which button is used to initiate the grab
///
[Tooltip("Configure which button is used to initiate the grab")]
public GrabButton GrabButton = GrabButton.Inherit;
///
/// 'Inherit' will inherit this setting from the Grabber. 'Hold' requires the user to hold the GrabButton down. 'Toggle' will drop / release the Grabbable on button activation.
///
[Tooltip("'Inherit' will inherit this setting from the Grabber. 'Hold' requires the user to hold the GrabButton down. 'Toggle' will drop / release the Grabbable on button activation.")]
public HoldType Grabtype = HoldType.Inherit;
///
/// Kinematic Physics locks the object in place on the hand / grabber. PhysicsJoint allows collisions with the environment.
///
[Tooltip("Kinematic Physics locks the object in place on the hand / grabber. Physics Joint and Velocity types allow collisions with the environment.")]
public GrabPhysics GrabPhysics = GrabPhysics.Velocity;
///
/// Snap to a location or grab anywhere on the object
///
[Tooltip("Snap to a location or grab anywhere on the object")]
public GrabType GrabMechanic = GrabType.Precise;
///
/// How fast to Lerp the object to the hand
///
[Tooltip("How fast to Lerp the object to the hand")]
public float GrabSpeed = 15f;
///
/// Can the object be picked up from far away. Must be within RemoteGrabber Trigger
///
[Header("Remote Grab")]
[Tooltip("Can the object be picked up from far away. Must be within RemoteGrabber Trigger")]
public bool RemoteGrabbable = false;
public RemoteGrabMovement RemoteGrabMechanic = RemoteGrabMovement.Linear;
///
/// Max Distance Object can be Remote Grabbed. Not applicable if RemoteGrabbable is false
///
[Tooltip("Max Distance Object can be Remote Grabbed. Not applicable if RemoteGrabbable is false")]
public float RemoteGrabDistance = 2f;
///
/// Multiply controller's velocity times this when throwing
///
[Header("Throwing")]
[Tooltip("Multiply controller's velocity times this when throwing")]
public float ThrowForceMultiplier = 2f;
///
/// Multiply controller's angular velocity times this when throwing
///
[Tooltip("Multiply controller's angular velocity times this when throwing")]
public float ThrowForceMultiplierAngular = 1.5f; // Multiply Angular Velocity times this
///
/// Drop the item if object's center travels this far from the Grabber's Center (in meters). Set to 0 to disable distance break.
///
[Tooltip("Drop the item if object's center travels this far from the Grabber's Center (in meters). Set to 0 to disable distance break.")]
public float BreakDistance = 0;
///
/// Enabling this will hide the Transform specified in the Grabber's HandGraphics property
///
[Header("Hand Options")]
[Tooltip("Enabling this will hide the Transform specified in the Grabber's HandGraphics property")]
public bool HideHandGraphics = false;
///
/// Parent this object to the hands for better stability.
/// Not recommended for child grabbers
///
[Tooltip("Parent this object to the hands for instantaneous movement. Object will travel 1:1 with the controller but may have trouble detecting fast moving collisions.")]
public bool ParentToHands = false;
///
/// If true, the hand model will be attached to the grabbed object
///
[Tooltip("If true, the hand model will be attached to the grabbed object. This separates it from a 1:1 match with the controller, but may look more realistic.")]
public bool ParentHandModel = true;
[Tooltip("If true, the hand model will snap to the nearest GrabPoint. Otherwise the hand model will stay with the Grabber.")]
public bool SnapHandModel = true;
///
/// Set to false to disable dropping. If false, will be permanently attached to whatever grabs this.
///
[Header("Misc")]
[Tooltip("Set to false to disable dropping. If false, will be permanently attached to whatever grabs this.")]
public bool CanBeDropped = true;
///
/// Can this object be snapped to snap zones? Set to false if you never want this to be snappable. Further filtering can be done on the SnapZones
///
[Tooltip("Can this object be snapped to snap zones? Set to false if you never want this to be snappable. Further filtering can be done on the SnapZones")]
public bool CanBeSnappedToSnapZone = true;
[Tooltip("If true, the object will always have kinematic disabled when dropped, even if it was initially kinematic.")]
public bool ForceDisableKinematicOnDrop = false;
[Tooltip("If true, the object will instantly position / rotate to the grabber instead of using velocity / force. This will only happen if no collisions have recently occurred. When using this method, the Grabbable's Rigidbody willbe instantly rotated / moved, taking in to account the interpolation settings. May clip through objects if moving fast enough.")]
public bool InstantMovement = false;
[Tooltip("If true, all child colliders will be considered Grabbable. If false, you will need to add the 'GrabbableChild' component to any child colliders that you wish to also be considered grabbable.")]
public bool MakeChildCollidersGrabbable = false;
[Header("Default Hand Pose")]
[Tooltip("A hand controller can read this value to determine how to animate when grabbing this object. 'AnimatorID' = specify an Animator ID to be set on the hand animator after grabbing this object. 'HandPose' = use a HandPose scriptable object. 'AutoPoseOnce' = DO an auto pose one time upon grabbing this object. 'AutoPoseContinuous' = Keep running attempting an autopose while grabbing this item.")]
public HandPoseType handPoseType = HandPoseType.HandPose;
protected HandPoseType initialHandPoseType;
[Tooltip("If HandPoseType = 'HandPose', this HandPose object will be applied to the hand on pickup")]
public HandPose SelectedHandPose;
protected HandPose initialHandPose;
///
/// Animator ID of the Hand Pose to use
///
[Tooltip("This HandPose Id will be passed to the Hand Animator when equipped. You can add new hand poses in the HandPoseDefinitions.cs file.")]
public HandPoseId CustomHandPose = HandPoseId.Default;
protected HandPoseId initialHandPoseId;
///
/// What to do if another grabber grabs this while equipped. DualGrab is currently unsupported.
///
[Header("Two-Handed Grab Behavior")]
[Tooltip("What to do if another grabber grabs this while equipped.")]
public OtherGrabBehavior SecondaryGrabBehavior = OtherGrabBehavior.None;
[Tooltip("How to behave when two hands are grabbing this object. LookAt = Have the primary Grabber 'LookAt' the secondary grabber. For example, holding a rifle in the right controller will have it rotate towards the left controller. AveragePositionRotation = Use a point and rotation in space that is half-way between both grabbers.")]
public TwoHandedPositionType TwoHandedPosition = TwoHandedPositionType.Lerp;
[Tooltip("How far to lerp between grabber positions. For example, 0.5 = halfway between the primary and secondary grabber. 0 = use the primary grabber's position, 1 = use the secondary grabber's position.")]
[Range(0.0f, 1f)]
public float TwoHandedPostionLerpAmount = 0.5f;
[Tooltip("How to behave when two hands are grabbing this object. LookAt = Have the primary Grabber 'LookAt' the secondary grabber. For example, holding a rifle in the right controller will have it rotate towards the left controller. AveragePositionRotation = Use a point and rotation in space that is half-way between both grabbers.")]
public TwoHandedRotationType TwoHandedRotation = TwoHandedRotationType.Slerp;
[Tooltip("How far to lerp / slerp between grabber rotation. For example, 0.5 = halfway between the primary and secondary grabber. 0 = use the primary grabber's rotation, 1 = use the secondary grabber's position.")]
[Range(0.0f, 1f)]
public float TwoHandedRotationLerpAmount = 0.5f;
[Tooltip("How to repond if you are holding an object with two hands, and then drop the primary grabber. For example, you may want to drop the object, transfer it to the second hand, or do nothing at all.")]
public TwoHandedDropMechanic TwoHandedDropBehavior = TwoHandedDropMechanic.Drop;
[Tooltip("Which vector to use when TwoHandedRotation = LookAtSecondary. Ex : Horizontal = A rifle type setup where you want to aim down the sites; Vertical = A melee type setup where the object is vertical")]
public TwoHandedLookDirection TwoHandedLookVector = TwoHandedLookDirection.Horizontal;
[Tooltip("How quickly to Lerp towards the SecondaryGrabbable if TwoHandedGrabBehavior = LookAt")]
public float SecondHandLookSpeed = 40f;
[Header("Secondary Grabbale Object")]
[Tooltip("If specified, this object will be used as a secondary grabbable instead of relying on grab points on this object. If 'TwoHandedGrabBehavior' is specified as LookAt, this is the object the grabber will be rotated towards. If 'TwoHandedGrabBehavior' is specified as AveragePositionRotation, this is the object the grabber use to calculate position.")]
public Grabbable SecondaryGrabbable;
///
/// The Grabbable can only be grabbed if this grabbable is being held.
/// Example : If you only want a weapon part to be grabbable if the weapon itself is being held.
///
[Header("Grab Restrictions")]
[Tooltip("The Grabbable can only be grabbed if this grabbable is being held. Example : If you only want a weapon part to be grabbable if the weapon itself is being held.")]
public Grabbable OtherGrabbableMustBeGrabbed = null;
[Header("Physics Joint Settings")]
///
/// How much Spring Force to apply to the joint when something comes in contact with the grabbable
/// A higher Spring Force will make the Grabbable more rigid
///
[Tooltip("A higher Spring Force will make the Grabbable more rigid")]
public float CollisionSpring = 3000;
///
/// How much Slerp Force to apply to the joint when something is in contact with the grabbable
///
[Tooltip("How much Slerp Force to apply to the joint when something is in contact with the grabbable")]
public float CollisionSlerp = 500;
[Tooltip("How to restrict the Configurable Joint's xMotion when colliding with an object. Position can be free, completely locked, or limited.")]
public ConfigurableJointMotion CollisionLinearMotionX = ConfigurableJointMotion.Free;
[Tooltip("How to restrict the Configurable Joint's yMotion when colliding with an object. Position can be free, completely locked, or limited.")]
public ConfigurableJointMotion CollisionLinearMotionY = ConfigurableJointMotion.Free;
[Tooltip("How to restrict the Configurable Joint's zMotion when colliding with an object. Position can be free, completely locked, or limited.")]
public ConfigurableJointMotion CollisionLinearMotionZ = ConfigurableJointMotion.Free;
[Tooltip("Restrict the rotation around the X axes to be Free, completely Locked, or Limited when colliding with an object.")]
public ConfigurableJointMotion CollisionAngularMotionX = ConfigurableJointMotion.Free;
[Tooltip("Restrict the rotation around the Y axes to be Free, completely Locked, or Limited when colliding with an object.")]
public ConfigurableJointMotion CollisionAngularMotionY = ConfigurableJointMotion.Free;
[Tooltip("Restrict the rotation around Z axes to be Free, completely Locked, or Limited when colliding with an object.")]
public ConfigurableJointMotion CollisionAngularMotionZ = ConfigurableJointMotion.Free;
[Tooltip("If true, the object's velocity will be adjusted to match the grabber. This is in addition to any forces added by the configurable joint.")]
public bool ApplyCorrectiveForce = true;
[Header("Velocity Grab Settings")]
public float MoveVelocityForce = 3000f;
public float MoveAngularVelocityForce = 90f;
///
/// Time in seconds (Time.time) when we last grabbed this item
///
[HideInInspector]
public float LastGrabTime;
///
/// Time in seconds (Time.time) when we last dropped this item
///
[HideInInspector]
public float LastDropTime;
///
/// Set to True to throw the Grabbable by applying the controller velocity to the grabbable on drop.
/// Set False if you don't want the object to be throwable, or want to apply your own force manually
///
[HideInInspector]
public bool AddControllerVelocityOnDrop = true;
// Total distance between the Grabber and Grabbable.
float journeyLength;
public Vector3 OriginalScale { get; private set; }
// Keep track of objects that are colliding with us
[Header("Shown for Debug : ")]
[SerializeField]
public List collisions;
// Last time in seconds (Time.time) since we had a valid collision
public float lastCollisionSeconds { get; protected set; }
///
/// How many seconds we've gone without collisions
///
public float lastNoCollisionSeconds { get; protected set; }
///
/// Have we recently collided with an object
///
public bool RecentlyCollided {
get {
if(Time.time - lastCollisionSeconds <= 0.1f) {
return true;
}
if(collisions != null && collisions.Count > 0) {
return true;
}
return false;
}
}
// If Time.time < requestSpringTime, force joint to be springy
public float requestSpringTime { get; protected set; }
///
/// If Grab Mechanic is set to Snap, set position and rotation to this Transform on the primary Grabber
///
protected Transform primaryGrabOffset;
protected Transform secondaryGrabOffset;
///
/// Returns the active GrabPoint component if object is held and a GrabPoint has been assigneed
///
[HideInInspector]
public GrabPoint ActiveGrabPoint;
[HideInInspector]
public Vector3 SecondaryLookOffset;
[HideInInspector]
public Transform SecondaryLookAtTransform;
[HideInInspector]
public Transform LocalOffsetTransform;
Vector3 grabPosition {
get {
if (primaryGrabOffset != null) {
return primaryGrabOffset.position;
}
else {
return transform.position;
}
}
}
[HideInInspector]
public Vector3 GrabPositionOffset {
get {
if (primaryGrabOffset) {
return primaryGrabOffset.transform.localPosition;
}
return Vector3.zero;
}
}
[HideInInspector]
public Vector3 GrabRotationOffset {
get {
if (primaryGrabOffset) {
return primaryGrabOffset.transform.localEulerAngles;
}
return Vector3.zero;
}
}
private Transform _grabTransform;
// Position this on the grabber to get a precise location
public Transform grabTransform {
get {
if (_grabTransform != null) {
return _grabTransform;
}
_grabTransform = new GameObject().transform;
_grabTransform.parent = this.transform;
_grabTransform.name = "Grab Transform";
_grabTransform.localPosition = Vector3.zero;
// _grabTransform.hideFlags = HideFlags.HideInHierarchy;
return _grabTransform;
}
}
private Transform _grabTransformSecondary;
// Position this on the grabber to get a precise location
public Transform grabTransformSecondary {
get {
if (_grabTransformSecondary != null) {
return _grabTransformSecondary;
}
_grabTransformSecondary = new GameObject().transform;
_grabTransformSecondary.parent = this.transform;
_grabTransformSecondary.name = "Grab Transform Secondary";
_grabTransformSecondary.localPosition = Vector3.zero;
_grabTransformSecondary.hideFlags = HideFlags.HideInHierarchy;
return _grabTransformSecondary;
}
}
[Header("Grab Points")]
///
/// If Grab Mechanic is set to Snap, the closest GrabPoint will be used. Add a SnapPoint Component to a GrabPoint to specify custom hand poses and rotation.
///
[Tooltip("If Grab Mechanic is set to Snap, the closest GrabPoint will be used. Add a SnapPoint Component to a GrabPoint to specify custom hand poses and rotation.")]
public List GrabPoints;
///
/// Can the object be moved towards a Grabber.
/// Levers, buttons, doorknobs, and other types of objects cannot be moved because they are attached to another object or are static.
///
public bool CanBeMoved {
get {
return _canBeMoved;
}
}
private bool _canBeMoved;
protected Transform originalParent;
protected InputBridge input;
protected ConfigurableJoint connectedJoint;
protected Vector3 previousPosition;
protected float lastItemTeleportTime;
protected bool recentlyTeleported;
///
/// Set this to false if you need to see Debug field or don't want to use the custom inspector
///
[HideInInspector]
public bool UseCustomInspector = true;
///
/// If a BNGPlayerController is provided we can check for player movements and make certain adjustments to physics.
///
protected BNGPlayerController player {
get {
return GetBNGPlayerController();
}
}
private BNGPlayerController _player;
protected Collider col;
protected Rigidbody rigid;
public Grabber FlyingToGrabber {
get {
return flyingTo;
}
}
protected Grabber flyingTo;
protected List events;
public bool DidParentHands {
get {
return didParentHands;
}
}
protected bool didParentHands = false;
protected void Awake() {
col = GetComponent();
rigid = GetComponent();
input = InputBridge.Instance;
events = GetComponents().ToList();
collisions = new List();
// Try parent if no rigid found here
if (rigid == null && transform.parent != null) {
rigid = transform.parent.GetComponent();
}
// Store initial rigidbody properties so we can reset them later as needed
if (rigid) {
initialCollisionMode = rigid.collisionDetectionMode;
initialInterpolationMode = rigid.interpolation;
wasKinematic = rigid.isKinematic;
usedGravity = rigid.useGravity;
// Allow our rigidbody to rotate quickly
rigid.maxAngularVelocity = 25f;
}
// Store initial parent so we can reset later if needed
UpdateOriginalParent(transform.parent);
validGrabbers = new List();
// Set Original Scale based in World coordinates if available
if (transform.parent != null) {
OriginalScale = transform.parent.TransformVector(transform.localScale);
}
else {
OriginalScale = transform.localScale;
}
initialHandPoseId = CustomHandPose;
initialHandPose = SelectedHandPose;
initialHandPoseType = handPoseType;
// Store movement status
_canBeMoved = canBeMoved();
// Set up any Child Grabbable Objects
if(MakeChildCollidersGrabbable) {
Collider[] cols = GetComponentsInChildren();
for(int x = 0; x < cols.Length; x++) {
// Make child Grabbable if it isn't already
if (cols[x].GetComponent() == null && cols[x].GetComponent() == null) {
var gc = cols[x].gameObject.AddComponent();
gc.ParentGrabbable = this;
}
}
}
}
public virtual void Update() {
if (BeingHeld) {
// ResetLockResets();
// Something happened to our Grabber. Drop the item
if (heldByGrabbers == null) {
DropItem(null, true, true);
return;
}
// Make sure all collisions are valid
filterCollisions();
// Cache PrimaryGrabber designation
_priorPrimaryGrabber = GetPrimaryGrabber();
// Update collision time
if (collisions != null && collisions.Count > 0) {
lastCollisionSeconds = Time.time;
lastNoCollisionSeconds = 0;
}
else if (collisions != null && collisions.Count <= 0) {
lastNoCollisionSeconds += Time.deltaTime;
}
// Update item recently teleported time
if (Vector3.Distance(transform.position, previousPosition) > 0.1f) {
lastItemTeleportTime = Time.time;
}
recentlyTeleported = Time.time - lastItemTeleportTime < 0.2f;
// Loop through held grabbers and see if we need to drop the item, fire off events, etc.
for (int x = 0; x < heldByGrabbers.Count; x++) {
Grabber g = heldByGrabbers[x];
// Should we drop the item if it's too far away?
if (!recentlyTeleported && BreakDistance > 0 && Vector3.Distance(grabPosition, g.transform.position) > BreakDistance) {
Debug.Log("Break Distance Exceeded. Dropping item.");
DropItem(g, true, true);
break;
}
// Should we drop the item if no longer holding the required Grabbable?
if (OtherGrabbableMustBeGrabbed != null && !OtherGrabbableMustBeGrabbed.BeingHeld) {
// Fixed joints work ok. Configurable Joints have issues
if (GetComponent() != null) {
DropItem(g, true, true);
break;
}
}
// Fire off any relevant events
callEvents(g);
}
// Check to parent the hand models to the Grabbable
if(ParentHandModel && !didParentHands) {
checkParentHands(GetPrimaryGrabber());
}
// Position Hands in proper place
positionHandGraphics(GetPrimaryGrabber());
// Rotate the grabber to look at our secondary object
// JPTODO : Move this to physics updates
if(TwoHandedRotation == TwoHandedRotationType.LookAtSecondary && GrabPhysics == GrabPhysics.PhysicsJoint) {
checkSecondaryLook();
}
// Keep track of where we were each frame
previousPosition = transform.position;
}
}
public virtual void FixedUpdate() {
if (remoteGrabbing) {
UpdateRemoteGrab();
}
if (BeingHeld) {
// Reset all collisions every physics update
// These are then populated in OnCollisionEnter / OnCollisionStay to make sure we have the most up to date collision info
// This can create garbage so only do this if we are holding the object
if (BeingHeld && collisions != null && collisions.Count > 0) {
collisions = new List();
}
// Update any physics properties here
if (GrabPhysics == GrabPhysics.PhysicsJoint) {
UpdatePhysicsJoints();
}
else if (GrabPhysics == GrabPhysics.FixedJoint) {
UpdateFixedJoints();
}
else if (GrabPhysics == GrabPhysics.Kinematic) {
UpdateKinematicPhysics();
}
else if (GrabPhysics == GrabPhysics.Velocity) {
UpdateVelocityPhysics();
}
}
}
public virtual Vector3 GetGrabberWithGrabPointOffset(Grabber grabber, Transform grabPoint) {
// Sanity check
if(grabber == null) {
return Vector3.zero;
}
// Get the Grabber's position, offset by a grab point
Vector3 grabberPosition = grabber.transform.position;
if (grabPoint != null) {
grabberPosition += transform.position - grabPoint.position;
}
return grabberPosition;
}
public virtual Quaternion GetGrabberWithOffsetWorldRotation(Grabber grabber) {
if(grabber != null) {
return grabber.transform.rotation;
}
return Quaternion.identity;
}
protected void positionHandGraphics(Grabber g) {
if (ParentHandModel && didParentHands) {
if (GrabMechanic == GrabType.Snap) {
if(g != null) {
g.HandsGraphics.localPosition = g.handsGraphicsGrabberOffset;
g.HandsGraphics.localEulerAngles = Vector3.zero;
}
}
}
}
///
/// Is this object able to be grabbed. Does not check for valid Grabbers, only if it isn't being held, is active, etc.
///
///
public virtual bool IsGrabbable() {
// Not valid if not active
if (!isActiveAndEnabled) {
return false;
}
// Not valid if being held and the object has no secondary grab behavior
if (BeingHeld == true && SecondaryGrabBehavior == OtherGrabBehavior.None) {
return false;
}
// Not Grabbable if set as DualGrab, but secondary grabbable has been specified. This means we can't use a grab point on this object
if (BeingHeld == true && SecondaryGrabBehavior == OtherGrabBehavior.DualGrab && SecondaryGrabbable != null) {
return false;
}
// Make sure grabbed conditions are met
if (OtherGrabbableMustBeGrabbed != null && !OtherGrabbableMustBeGrabbed.BeingHeld) {
return false;
}
return true;
}
public virtual void UpdateRemoteGrab() {
// Linear Movement
if(RemoteGrabMechanic == RemoteGrabMovement.Linear) {
CheckRemoteGrabLinear();
}
else if (RemoteGrabMechanic == RemoteGrabMovement.Velocity) {
CheckRemoteGrabVelocity();
}
else if (RemoteGrabMechanic == RemoteGrabMovement.Flick) {
CheckRemoteGrabFlick();
}
}
public virtual void CheckRemoteGrabLinear() {
// Bail early if we're not remote grabbing this item
if (!remoteGrabbing) {
return;
}
// Move the object linearly as a kinematic rigidbody
if (rigid && !rigid.isKinematic) {
rigid.collisionDetectionMode = CollisionDetectionMode.Discrete;
rigid.isKinematic = true;
}
Vector3 grabberPosition = GetGrabberWithGrabPointOffset(flyingTo, GetClosestGrabPoint(flyingTo));
Quaternion remoteRotation = getRemoteRotation(flyingTo);
float distance = Vector3.Distance(transform.position, grabberPosition);
// reached destination, snap to final transform position
// Typically this won't be hit as the Grabber trigger will pick it up first
if (distance <= 0.002f) {
movePosition(grabberPosition);
moveRotation(grabTransform.rotation);
if (rigid) {
rigid.velocity = Vector3.zero;
}
if (flyingTo != null) {
flyingTo.GrabGrabbable(this);
}
}
// Getting close so speed up
else if (distance < 0.03f) {
movePosition(Vector3.MoveTowards(transform.position, grabberPosition, Time.fixedDeltaTime * GrabSpeed * 2f));
moveRotation(Quaternion.Slerp(transform.rotation, remoteRotation, Time.fixedDeltaTime * GrabSpeed * 2f));
}
// Normal Lerp
else {
movePosition(Vector3.Lerp(transform.position, grabberPosition, Time.fixedDeltaTime * GrabSpeed));
moveRotation(Quaternion.Slerp(transform.rotation, remoteRotation, Time.fixedDeltaTime * GrabSpeed));
}
}
public virtual void CheckRemoteGrabVelocity() {
if (remoteGrabbing) {
Vector3 grabberPosition = GetGrabberWithGrabPointOffset(flyingTo, GetClosestGrabPoint(flyingTo));
Quaternion remoteRotation = getRemoteRotation(flyingTo);
float distance = Vector3.Distance(transform.position, grabberPosition);
// Move the object with velocity, without using gravity
if (rigid && rigid.useGravity) {
rigid.useGravity = false;
// Snap rotation once
// transform.rotation = remoteRotation;
}
// reached destination, snap to final transform position
// Typically this won't be hit as the Grabber trigger will pick it up first
if (distance <= 0.0025f) {
movePosition(grabberPosition);
moveRotation(grabTransform.rotation);
if (rigid) {
rigid.velocity = Vector3.zero;
}
if (flyingTo != null) {
flyingTo.GrabGrabbable(this);
}
}
else {
// Move with velocity
Vector3 positionDelta = grabberPosition - transform.position;
// Move towards hand using velocity
rigid.velocity = Vector3.MoveTowards(rigid.velocity, (positionDelta * MoveVelocityForce) * Time.fixedDeltaTime, 1f);
rigid.MoveRotation(Quaternion.Slerp(rigid.rotation, GetGrabbersAveragedRotation(), Time.fixedDeltaTime * GrabSpeed));
//rigid.angularVelocity = Vector3.zero;
//moveRotation(Quaternion.Slerp(transform.rotation, remoteRotation, Time.fixedDeltaTime * GrabSpeed));
}
}
}
bool initiatedFlick = false;
// Angular Velocity required to start the flick force
float flickStartVelocity = 1.5f;
///
/// How long in seconds the object should take to jump to the grabber when using the Flick remote grab type
///
float FlickSpeed = 0.5f;
public float lastFlickTime;
public virtual void InitiateFlick() {
initiatedFlick = true;
lastFlickTime = Time.time;
Vector3 grabberPosition = flyingTo.transform.position;// GetGrabberWithGrabPointOffset(flyingTo, GetClosestGrabPoint(flyingTo));
Quaternion remoteRotation = getRemoteRotation(flyingTo);
float distance = Vector3.Distance(transform.position, grabberPosition);
// Defauult to 1, but speed up if close
float timeToGrab = FlickSpeed;
if (distance < 1f) {
timeToGrab = FlickSpeed / 1.5f;
}
else if (distance < 0.5f) {
timeToGrab = FlickSpeed / 3;
}
Vector3 vel = GetVelocityToHitTargetByTime(transform.position, grabberPosition, Physics.gravity * 1.1f, timeToGrab);
rigid.velocity = vel;
// rigid.AddForce(vel, ForceMode.VelocityChange);
// No longer initiated flick
initiatedFlick = false;
}
public Vector3 GetVelocityToHitTargetByTime(Vector3 startPosition, Vector3 targetPosition, Vector3 gravityBase, float timeToTarget) {
Vector3 direction = targetPosition - startPosition;
Vector3 horizontal = Vector3.Project(direction, Vector3.Cross(gravityBase, Vector3.Cross(direction, gravityBase)));
float horizontalDistance = horizontal.magnitude;
float horizontalSpeed = horizontalDistance / timeToTarget;
Vector3 vertical = Vector3.Project(direction, gravityBase);
float verticalDistance = vertical.magnitude * Mathf.Sign(Vector3.Dot(vertical, -gravityBase));
float verticalSpeed = (verticalDistance + ((0.5f * gravityBase.magnitude) * (timeToTarget * timeToTarget))) / timeToTarget;
return (horizontal.normalized * horizontalSpeed) - (gravityBase.normalized * verticalSpeed);
}
public virtual void CheckRemoteGrabFlick() {
if(remoteGrabbing) {
// Have we initiated a flick yet?
if(!initiatedFlick) {
// Get angular velocity from controller
if(InputBridge.Instance.GetControllerAngularVelocity(flyingTo.HandSide).magnitude >= flickStartVelocity) {
// Must be at least some time between flicks
if(Time.time - lastFlickTime >= 0.1f) {
InitiateFlick();
}
}
}
}
else {
initiatedFlick = false;
}
}
public float FlickForce = 1f;
public virtual void UpdateFixedJoints() {
// Set to continuous dynamic while being held
if (rigid != null && rigid.isKinematic) {
rigid.collisionDetectionMode = CollisionDetectionMode.Discrete;
}
else {
rigid.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
}
// Adjust item velocity. This smooths out forces while becoming rigid
if (ApplyCorrectiveForce) {
moveWithVelocity();
}
}
public virtual void UpdatePhysicsJoints() {
// Bail if no joint connected
if (connectedJoint == null || rigid == null) {
return;
}
// Set to continuous dynamic while being held
if (rigid.isKinematic) {
rigid.collisionDetectionMode = CollisionDetectionMode.Discrete;
}
else {
rigid.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
}
// Update Joint poisition in real time
if (GrabMechanic == GrabType.Snap) {
connectedJoint.anchor = Vector3.zero;
connectedJoint.connectedAnchor = GrabPositionOffset;
}
// Check if something is requesting a springy joint
// For example, a gun may wish to make the joint springy in order to apply recoil to a weapon via AddForce
bool forceSpring = Time.time < requestSpringTime;
// Only snap to a rigid grip if it's been a short delay after our last collision
// This prevents the joint from rapidly becoming stiff / springy which will cause jittery behaviour
bool afterCollision = collisions.Count == 0 && lastNoCollisionSeconds >= 0.1f;
// Nothing touching it so we can stick to hand rigidly
// Two-Handed weapons currently react much more smoothly if the joint is rigid, due to how the LookAt system works
if ((BeingHeldWithTwoHands || afterCollision) && !forceSpring) {
// Lock Angular, XYZ Motion
// Make joint very rigid
connectedJoint.rotationDriveMode = RotationDriveMode.Slerp;
connectedJoint.xMotion = ConfigurableJointMotion.Limited;
connectedJoint.yMotion = ConfigurableJointMotion.Limited;
connectedJoint.zMotion = ConfigurableJointMotion.Limited;
connectedJoint.angularXMotion = ConfigurableJointMotion.Limited;
connectedJoint.angularYMotion = ConfigurableJointMotion.Limited;
connectedJoint.angularZMotion = ConfigurableJointMotion.Limited;
SoftJointLimit sjl = connectedJoint.linearLimit;
sjl.limit = 15f;
SoftJointLimitSpring sjlsp = connectedJoint.linearLimitSpring;
sjlsp.spring = 3000;
sjlsp.damper = 10f;
// Set X,Y, and Z drive to our values
// Set X,Y, and Z drive to our values
setPositionSpring(CollisionSpring, 10f);
// Slerp drive used for rotation
setSlerpDrive(CollisionSlerp, 10f);
// Adjust item velocity. This smooths out forces while becoming rigid
if (ApplyCorrectiveForce) {
moveWithVelocity();
}
}
else {
// Make Springy
connectedJoint.rotationDriveMode = RotationDriveMode.Slerp;
connectedJoint.xMotion = CollisionLinearMotionX;
connectedJoint.yMotion = CollisionLinearMotionY;
connectedJoint.zMotion = CollisionLinearMotionZ;
connectedJoint.angularXMotion = CollisionAngularMotionX;
connectedJoint.angularYMotion = CollisionAngularMotionY;
connectedJoint.angularZMotion = CollisionAngularMotionZ;
SoftJointLimitSpring sp = connectedJoint.linearLimitSpring;
sp.spring = 5000;
sp.damper = 5;
// Set X,Y, and Z drive to our values
setPositionSpring(CollisionSpring, 5f);
// Slerp drive used for rotation
setSlerpDrive(CollisionSlerp, 5f);
}
if(BeingHeldWithTwoHands && SecondaryLookAtTransform != null) {
connectedJoint.angularXMotion = ConfigurableJointMotion.Free;
setSlerpDrive(1000f, 2f);
connectedJoint.angularYMotion = ConfigurableJointMotion.Limited;
connectedJoint.angularZMotion = ConfigurableJointMotion.Limited;
if (TwoHandedRotation == TwoHandedRotationType.LookAtSecondary) {
checkSecondaryLook();
}
}
}
void setPositionSpring(float spring, float damper) {
if(connectedJoint == null) {
return;
}
JointDrive xDrive = connectedJoint.xDrive;
xDrive.positionSpring = spring;
xDrive.positionDamper = damper;
connectedJoint.xDrive = xDrive;
JointDrive yDrive = connectedJoint.yDrive;
yDrive.positionSpring = spring;
yDrive.positionDamper = damper;
connectedJoint.yDrive = yDrive;
JointDrive zDrive = connectedJoint.zDrive;
zDrive.positionSpring = spring;
zDrive.positionDamper = damper;
connectedJoint.zDrive = zDrive;
}
void setSlerpDrive(float slerp, float damper) {
if(connectedJoint) {
JointDrive slerpDrive = connectedJoint.slerpDrive;
slerpDrive.positionSpring = slerp;
slerpDrive.positionDamper = damper;
connectedJoint.slerpDrive = slerpDrive;
}
}
public virtual Vector3 GetGrabberVector3(Grabber grabber, bool isSecondary) {
// Snap
if (GrabMechanic == GrabType.Snap) {
return GetGrabberWithGrabPointOffset(grabber, isSecondary ? secondaryGrabOffset : primaryGrabOffset);
}
// Precise
else {
if (isSecondary) {
return grabTransformSecondary.position;
}
return grabTransform.position;
}
}
public virtual Quaternion GetGrabberQuaternion(Grabber grabber, bool isSecondary) {
if (GrabMechanic == GrabType.Snap) {
return GetGrabberWithOffsetWorldRotation(grabber);
}
else {
if (isSecondary) {
return grabTransformSecondary.rotation;
}
return grabTransform.rotation;
}
}
///
/// Apply a velocity on our Grabbable towards our Grabber
///
void moveWithVelocity() {
if(rigid == null) { return; }
Vector3 destination = GetGrabbersAveragedPosition();
float distance = Vector3.Distance(transform.position, destination);
if (distance > 0.002f) {
Vector3 positionDelta = destination - transform.position;
// Move towards hand using velocity
rigid.velocity = Vector3.MoveTowards(rigid.velocity, (positionDelta * MoveVelocityForce) * Time.fixedDeltaTime, 1f);
}
else {
// Very close - just move object right where it needs to be and set velocity to 0 so it doesn't overshoot
rigid.MovePosition(destination);
rigid.velocity = Vector3.zero;
}
}
float angle;
Vector3 axis, angularTarget, angularMovement;
void rotateWithVelocity() {
if(rigid == null) {
return;
}
bool noRecentCollisions = collisions != null && collisions.Count == 0 && lastNoCollisionSeconds >= 0.5f;
bool moveInstantlyOneHand = InstantMovement; // MoveAngularVelocityForce >= 200f;
bool moveInstantlyTwoHands = BeingHeldWithTwoHands && InstantMovement; // TwoHandedRotation == TwoHandedRotationType.LookAtSecondary && SecondHandLookSpeed > 20;
if (InstantMovement == true && noRecentCollisions && (moveInstantlyOneHand || moveInstantlyTwoHands)) {
//rigid.rotation = GetGrabbersAveragedRotation();
rigid.MoveRotation(Quaternion.Slerp(rigid.rotation, GetGrabbersAveragedRotation(), Time.fixedDeltaTime * SecondHandLookSpeed));
// Can exit immediately
return;
}
Quaternion rotationDelta = GetGrabbersAveragedRotation() * Quaternion.Inverse(transform.rotation);
rotationDelta.ToAngleAxis(out angle, out axis);
// Use closest rotation. If over 180 degrees, rotate the other way
if (angle > 180) {
angle -= 360;
}
if (angle != 0) {
angularTarget = angle * axis;
angularTarget = (angularTarget * MoveAngularVelocityForce) * Time.fixedDeltaTime;
angularMovement = Vector3.MoveTowards(rigid.angularVelocity, angularTarget, MoveAngularVelocityForce);
if (angularMovement.magnitude > 0.05f) {
// rigid.centerOfMass = transform.InverseTransformPoint(GetGrabbersAveragedPosition());
rigid.angularVelocity = angularMovement;
}
// Snap in place if very close
if(angle < 1) {
rigid.MoveRotation(GetGrabbersAveragedRotation());
rigid.angularVelocity = Vector3.zero;
}
}
}
///
/// Get the estimated world position of the grabber(s) holding this object. Position factors in 2-Handed grabbing options
///
/// World position og the grabber, with two handed behavior factored in.
public Vector3 GetGrabbersAveragedPosition() {
// Start with our primary Grabber
Vector3 destination = GetGrabberVector3(GetPrimaryGrabber(), false);
// Add secondary grabber position
if (SecondaryGrabBehavior == OtherGrabBehavior.DualGrab && TwoHandedPosition == TwoHandedPositionType.Lerp) {
// Check Secondary Grabbable first
if (SecondaryGrabbable != null && SecondaryGrabbable.BeingHeld) {
// Add secondary grab position
destination = Vector3.Lerp(destination, SecondaryGrabbable.GetGrabberVector3(SecondaryGrabbable.GetPrimaryGrabber(), false), TwoHandedPostionLerpAmount);
}
// Check if a grabber is holding this object
else if (heldByGrabbers != null && heldByGrabbers.Count > 1) {
destination = Vector3.Lerp(destination, GetGrabberVector3(heldByGrabbers[1], true), TwoHandedPostionLerpAmount);
}
}
// Return primary grabber position as default
return destination;
}
public Quaternion GetGrabbersAveragedRotation() {
// Start with our primary Grabber's rotation
Quaternion destination = GetGrabberQuaternion(GetPrimaryGrabber(), false);
// Add secondary grabber position
// Check Lerp / Slerp Setting
if (SecondaryGrabBehavior == OtherGrabBehavior.DualGrab && TwoHandedRotation == TwoHandedRotationType.Lerp || TwoHandedRotation == TwoHandedRotationType.Slerp) {
// Check Secondary Grabbable first
if (SecondaryGrabbable != null && SecondaryGrabbable.BeingHeld) {
if (TwoHandedRotation == TwoHandedRotationType.Lerp) {
destination = Quaternion.Lerp(destination, SecondaryGrabbable.GetGrabberQuaternion(SecondaryGrabbable.GetPrimaryGrabber(), false), TwoHandedRotationLerpAmount);
}
else {
destination = Quaternion.Slerp(destination, SecondaryGrabbable.GetGrabberQuaternion(SecondaryGrabbable.GetPrimaryGrabber(), false), TwoHandedRotationLerpAmount);
}
}
// Check if a grabber is holding this object
else if (heldByGrabbers != null && heldByGrabbers.Count > 1) {
if (TwoHandedRotation == TwoHandedRotationType.Lerp) {
destination = Quaternion.Lerp(destination, GetGrabberQuaternion(heldByGrabbers[1], true), TwoHandedRotationLerpAmount);
}
else {
destination = Quaternion.Slerp(destination, GetGrabberQuaternion(heldByGrabbers[1], true), TwoHandedRotationLerpAmount);
}
}
}
// LookAt type
else if (SecondaryGrabBehavior == OtherGrabBehavior.DualGrab && TwoHandedRotation == TwoHandedRotationType.LookAtSecondary) {
// Rotate our primary grabber towards our secondary grabber
// Check Secondary Grabbable first
if (SecondaryGrabbable != null && SecondaryGrabbable.BeingHeld) {
Vector3 targetVector = GetGrabberVector3(SecondaryGrabbable.GetPrimaryGrabber(), false) - GetGrabberVector3(GetPrimaryGrabber(), false);
// Forward Direction
if(TwoHandedLookVector == TwoHandedLookDirection.Horizontal) {
destination = Quaternion.LookRotation(targetVector, -GetPrimaryGrabber().transform.up) * Quaternion.AngleAxis(180f, Vector3.up) * Quaternion.AngleAxis(180f, Vector3.forward);
}
// Do up / down
else if(TwoHandedLookVector == TwoHandedLookDirection.Vertical) {
destination = Quaternion.LookRotation(targetVector, -GetPrimaryGrabber().transform.right) * Quaternion.AngleAxis(90f, Vector3.right) * Quaternion.AngleAxis(180f, Vector3.forward) * Quaternion.AngleAxis(-90f, Vector3.up);
}
}
// Check if a grabber is holding this object
else if (heldByGrabbers != null && heldByGrabbers.Count > 1) {
// destination = Quaternion.Lerp(destination, GetGrabberQuaternion(heldByGrabbers[1], true), TwoHandedRotationLerpAmount);
}
}
return destination;
}
public virtual void UpdateKinematicPhysics() {
// Distance moved equals elapsed time times speed.
float distCovered = (Time.time - LastGrabTime) * GrabSpeed;
// How far along have we traveled
float fractionOfJourney = distCovered / journeyLength;
Vector3 destination = GetGrabbersAveragedPosition();
Quaternion destRotation = grabTransform.rotation;
// Realtime update position to make it easier to preview grab transforms
bool realtime = Application.isEditor;
if(realtime) {
destination = getRemotePosition(GetPrimaryGrabber());
//destRotation = getRemoteRotation(GetPrimaryGrabber());
rotateGrabber(false);
}
if (GrabMechanic == GrabType.Snap) {
// Set our position as a fraction of the distance between the markers.
Grabber g = GetPrimaryGrabber();
// Update local transform in real time
if (g != null) {
if (ParentToHands) {
transform.localPosition = Vector3.Lerp(transform.localPosition, Vector3.zero - GrabPositionOffset, fractionOfJourney);
transform.localRotation = Quaternion.Lerp(transform.localRotation, grabTransform.localRotation, Time.deltaTime * 10);
}
// Position the object in world space using physics
else {
movePosition(Vector3.Lerp(transform.position, destination, fractionOfJourney));
moveRotation(Quaternion.Lerp(transform.rotation, destRotation, Time.deltaTime * 20));
}
}
else {
movePosition(destination);
transform.localRotation = grabTransform.localRotation;
}
}
else if (GrabMechanic == GrabType.Precise) {
movePosition(grabTransform.position);
moveRotation(grabTransform.rotation);
}
}
public virtual void UpdateVelocityPhysics() {
// Make sure rotation is always free
if(connectedJoint != null) {
connectedJoint.xMotion = ConfigurableJointMotion.Free;
connectedJoint.yMotion = ConfigurableJointMotion.Free;
connectedJoint.zMotion = ConfigurableJointMotion.Free;
connectedJoint.angularXMotion = ConfigurableJointMotion.Free;
connectedJoint.angularYMotion = ConfigurableJointMotion.Free;
connectedJoint.angularZMotion = ConfigurableJointMotion.Free;
}
// Make sure linear spring is off
// Set X,Y, and Z drive to our values
setPositionSpring(0, 0.5f);
// Slerp drive used for rotation
setSlerpDrive(5, 0.5f);
// Update collision detection mode to ContinuousDynamic while being held
if (rigid && rigid.isKinematic) {
rigid.collisionDetectionMode = CollisionDetectionMode.Discrete;
}
else if(rigid) {
rigid.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
}
moveWithVelocity();
rotateWithVelocity();
//Parent to our hands if no colliisions present
// This makes our object move 1:1 with our controller
if (ParentToHands) {
// Parent to hands if no collisions
bool afterCollision = collisions.Count == 0 && lastNoCollisionSeconds >= 0.2f;
// Set parent to us to keep movement smoothed
if (afterCollision) {
Grabber g = GetPrimaryGrabber();
transform.parent = g.transform;
}
else {
transform.parent = null;
}
}
}
void checkParentHands(Grabber g) {
if (ParentHandModel && g != null) {
// Precise - Go ahead and parent hands model immediately
if (GrabMechanic == GrabType.Precise) {
parentHandGraphics(g);
}
// Snap - Hand Models if close enough
else {
// Vector3 grabberPosition = g.transform.position;
Vector3 grabberPosition = grabTransform.position;
Vector3 grabbablePosition = transform.position;
float distance = Vector3.Distance(grabbablePosition, grabberPosition);
// If object can be moved towards the grabber, wait until the item is close before snapping hand to it
if (CanBeMoved) {
// Close enough to snap hand graphics
if (distance < 0.001f ) {
// Snap position
parentHandGraphics(g);
// Snap Hand Model Position
if (g.HandsGraphics != null) {
g.HandsGraphics.localEulerAngles = Vector3.zero;
g.HandsGraphics.localPosition = g.handsGraphicsGrabberOffset;
}
}
}
else {
// Can't be moved so go ahead and snap
if (grabTransform != null && distance < 0.1f) {
// Snap position
parentHandGraphics(g);
positionHandGraphics(g);
if (g.HandsGraphics != null) {
g.HandsGraphics.localEulerAngles = Vector3.zero;
g.HandsGraphics.localPosition = g.handsGraphicsGrabberOffset;
}
}
}
}
}
}
// Can this object be moved towards an object, or is it fixed in place / attached to something else
bool canBeMoved() {
if (GetComponent() == null) {
return false;
}
if (GetComponent() != null) {
return false;
}
return true;
}
void checkSecondaryLook() {
// Create transform to look at if we are looking at a precise grab
if (BeingHeldWithTwoHands) {
if (SecondaryLookAtTransform == null) {
Grabber thisGrabber = GetPrimaryGrabber();
Grabber secondaryGrabber = SecondaryGrabbable.GetPrimaryGrabber();
GameObject o = new GameObject();
SecondaryLookAtTransform = o.transform;
SecondaryLookAtTransform.name = "LookAtTransformTemp";
// Precise grab can use current grabber position
if (SecondaryGrabbable.GrabMechanic == GrabType.Precise) {
SecondaryLookAtTransform.position = secondaryGrabber.transform.position;
}
// Otherwise use snap point
else {
Transform grabPoint = SecondaryGrabbable.GetGrabPoint();
if (grabPoint) {
SecondaryLookAtTransform.position = grabPoint.position;
}
else {
SecondaryLookAtTransform.position = SecondaryGrabbable.transform.position;
}
SecondaryLookAtTransform.position = SecondaryGrabbable.transform.position;
}
if (SecondaryLookAtTransform && thisGrabber) {
SecondaryLookAtTransform.parent = thisGrabber.transform;
SecondaryLookAtTransform.localEulerAngles = Vector3.zero;
SecondaryLookAtTransform.localPosition = new Vector3(0, 0, SecondaryLookAtTransform.localPosition.z);
// Move parent back to grabber
SecondaryLookAtTransform.parent = secondaryGrabber.transform;
}
}
}
// We should not be aiming at anything if a Grabbable was specified
if (SecondaryGrabbable != null && !SecondaryGrabbable.BeingHeld && SecondaryLookAtTransform != null) {
clearLookAtTransform();
}
Grabber heldBy = GetPrimaryGrabber();
if (heldBy) {
Transform grabberTransform = heldBy.transform;
if (SecondaryLookAtTransform != null) {
Vector3 initialRotation = grabberTransform.localEulerAngles;
Quaternion dest = Quaternion.LookRotation(SecondaryLookAtTransform.position - grabberTransform.position, Vector3.up);
grabberTransform.rotation = Quaternion.Slerp(grabberTransform.rotation, dest, Time.deltaTime * SecondHandLookSpeed);
// Exclude rotations to only x and y
grabberTransform.localEulerAngles = new Vector3(grabberTransform.localEulerAngles.x, grabberTransform.localEulerAngles.y, initialRotation.z);
}
else {
rotateGrabber(true);
}
}
}
void rotateGrabber(bool lerp = false) {
Grabber heldBy = GetPrimaryGrabber();
if (heldBy != null) {
Transform grabberTransform = heldBy.transform;
if (lerp) {
grabberTransform.localRotation = Quaternion.Slerp(grabberTransform.localRotation, Quaternion.Inverse(Quaternion.Euler(GrabRotationOffset)), Time.deltaTime * 20);
}
else {
grabberTransform.localRotation = Quaternion.Inverse(Quaternion.Euler(GrabRotationOffset));
}
}
}
public Transform GetGrabPoint() {
return primaryGrabOffset;
}
public virtual void GrabItem(Grabber grabbedBy) {
// Make sure we release this item
if (BeingHeld && SecondaryGrabBehavior != OtherGrabBehavior.DualGrab) {
DropItem(false, true);
}
bool isPrimaryGrab = !BeingHeld;
bool isSecondaryGrab = BeingHeld && SecondaryGrabBehavior == OtherGrabBehavior.DualGrab;
// Officially being held
BeingHeld = true;
LastGrabTime = Time.time;
// Primary Grabber just grabbed this item
if (isPrimaryGrab) {
// Make sure all values are reset first
ResetGrabbing();
// Set where the item will move to on the grabber
primaryGrabOffset = GetClosestGrabPoint(grabbedBy);
secondaryGrabOffset = null;
// Set the active Grab Point that we will be using
if (primaryGrabOffset) {
ActiveGrabPoint = primaryGrabOffset.GetComponent();
}
else {
ActiveGrabPoint = null;
}
// Update Hand Pose Id
if (primaryGrabOffset != null && ActiveGrabPoint != null) {
CustomHandPose = primaryGrabOffset.GetComponent().HandPose;
SelectedHandPose = primaryGrabOffset.GetComponent().SelectedHandPose;
handPoseType = primaryGrabOffset.GetComponent().handPoseType;
}
else {
CustomHandPose = initialHandPoseId;
SelectedHandPose = initialHandPose;
handPoseType = initialHandPoseType;
}
// Update held by properties
addGrabber(grabbedBy);
grabTransform.parent = grabbedBy.transform;
rotateGrabber(false);
// Use center of grabber if snapping
if (GrabMechanic == GrabType.Snap) {
grabTransform.localEulerAngles = Vector3.zero;
grabTransform.localPosition = -GrabPositionOffset;
}
// Precision hold can use position of what we're grabbing
else if (GrabMechanic == GrabType.Precise) {
grabTransform.position = transform.position;
grabTransform.rotation = transform.rotation;
}
// First remove any connected joints if necessary
var projectile = GetComponent();
if (projectile) {
var fj = GetComponent();
if (fj) {
Destroy(fj);
}
}
// Setup any relevant joints or required components
if (GrabPhysics == GrabPhysics.PhysicsJoint) {
setupConfigJointGrab(grabbedBy, GrabMechanic);
}
else if (GrabPhysics == GrabPhysics.Velocity) {
setupVelocityGrab(grabbedBy, GrabMechanic);
}
else if (GrabPhysics == GrabPhysics.FixedJoint) {
setupFixedJointGrab(grabbedBy, GrabMechanic);
}
else if (GrabPhysics == GrabPhysics.Kinematic) {
setupKinematicGrab(grabbedBy, GrabMechanic);
}
// Stop our object on initial grab
if(rigid) {
rigid.velocity = Vector3.zero;
rigid.angularVelocity = Vector3.zero;
}
// Let events know we were grabbed
for (int x = 0; x < events.Count; x++) {
events[x].OnGrab(grabbedBy);
}
checkParentHands(grabbedBy);
// Move Hand Model
if (GrabMechanic == GrabType.Precise && SnapHandModel && primaryGrabOffset != null && grabbedBy.HandsGraphics != null) {
grabbedBy.HandsGraphics.transform.parent = primaryGrabOffset;
grabbedBy.HandsGraphics.localPosition = grabbedBy.handsGraphicsGrabberOffset;
grabbedBy.HandsGraphics.localEulerAngles = grabbedBy.handsGraphicsGrabberOffsetRotation;
}
SubscribeToMoveEvents();
}
else if (isSecondaryGrab) {
// Set where the item will move to on the grabber
secondaryGrabOffset = GetClosestGrabPoint(grabbedBy);
// Update held by properties
addGrabber(grabbedBy);
grabTransformSecondary.parent = grabbedBy.transform;
// Use center of grabber if snapping
if (GrabMechanic == GrabType.Snap) {
grabTransformSecondary.localEulerAngles = Vector3.zero;
grabTransformSecondary.localPosition = GrabPositionOffset;
}
// Precision hold can use position of what we're grabbing
else if (GrabMechanic == GrabType.Precise) {
grabTransformSecondary.position = transform.position;
grabTransformSecondary.rotation = transform.rotation;
}
checkParentHands(grabbedBy);
// Move Hand Model if snap hands and precise
if (GrabMechanic == GrabType.Precise && SnapHandModel && secondaryGrabOffset != null && grabbedBy.HandsGraphics != null) {
grabbedBy.HandsGraphics.transform.parent = secondaryGrabOffset;
grabbedBy.HandsGraphics.localPosition = grabbedBy.handsGraphicsGrabberOffset;
grabbedBy.HandsGraphics.localEulerAngles = grabbedBy.handsGraphicsGrabberOffsetRotation;
}
}
// Hide the hand graphics if necessary
if (HideHandGraphics) {
grabbedBy.HideHandGraphics();
}
journeyLength = Vector3.Distance(grabPosition, grabbedBy.transform.position);
}
protected virtual void setupConfigJointGrab(Grabber grabbedBy, GrabType grabType) {
// Set up the new connected joint
if (GrabMechanic == GrabType.Precise) {
connectedJoint = grabbedBy.GetComponent();
connectedJoint.connectedBody = rigid;
// Just let the autoconfigure handle the calculations for us
connectedJoint.autoConfigureConnectedAnchor = true;
}
// Set up the physics joint for snapping
else if (GrabMechanic == GrabType.Snap) {
// Need to Fix Rotation on Snap Physics when close by
transform.rotation = grabTransform.rotation;
// Setup joint
setupConfigJoint(grabbedBy);
rigid.MoveRotation(grabTransform.rotation);
}
}
protected virtual void setupFixedJointGrab(Grabber grabbedBy, GrabType grabType) {
FixedJoint joint = grabbedBy.gameObject.AddComponent();
joint.connectedBody = rigid;
// Setup Fixed Joint in place
if (GrabMechanic == GrabType.Precise) {
// Just let the autoconfigure handle the calculations for us
joint.autoConfigureConnectedAnchor = true;
}
// Setup the snap point manually
else if (GrabMechanic == GrabType.Snap) {
joint.autoConfigureConnectedAnchor = false;
joint.anchor = Vector3.zero;
joint.connectedAnchor = GrabPositionOffset;
}
}
protected virtual void setupKinematicGrab(Grabber grabbedBy, GrabType grabType) {
if (ParentToHands) {
transform.parent = grabbedBy.transform;
}
if (rigid != null) {
// Update detection mode if necessary
if (rigid.collisionDetectionMode == CollisionDetectionMode.Continuous || rigid.collisionDetectionMode == CollisionDetectionMode.ContinuousDynamic) {
rigid.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
}
rigid.isKinematic = true;
}
}
protected virtual void setupVelocityGrab(Grabber grabbedBy, GrabType grabType) {
// Setup joint to be used when moving with velocity
bool addJointToVelocityGrabbable = false;
if(addJointToVelocityGrabbable) {
if (GrabMechanic == GrabType.Precise) {
connectedJoint = grabbedBy.GetComponent();
connectedJoint.connectedBody = rigid;
// Just let the autoconfigure handle the calculations for us
connectedJoint.autoConfigureConnectedAnchor = true;
}
// Set up the connected joint for snapping
else if (GrabMechanic == GrabType.Snap) {
transform.rotation = grabTransform.rotation;
// Setup joint
setupConfigJoint(grabbedBy);
rigid.MoveRotation(grabTransform.rotation);
}
}
// Disable Gravity to prevent fighting physics with the hand object
rigid.useGravity = false;
}
public virtual void GrabRemoteItem(Grabber grabbedBy) {
flyingTo = grabbedBy;
grabTransform.parent = grabbedBy.transform;
grabTransform.localEulerAngles = Vector3.zero;
grabTransform.localPosition = -GrabPositionOffset;
grabTransform.localEulerAngles = GrabRotationOffset;
remoteGrabbing = true;
}
public virtual void ResetGrabbing() {
if (rigid) {
rigid.isKinematic = wasKinematic;
}
flyingTo = null;
remoteGrabbing = false;
collisions = new List();
}
public virtual void DropItem(Grabber droppedBy, bool resetVelocity, bool resetParent) {
// Nothing holding us
if (heldByGrabbers == null) {
BeingHeld = false;
return;
}
bool isPrimaryGrabber = droppedBy == GetPrimaryGrabber();
bool isSecondaryGrabber = !isPrimaryGrabber && heldByGrabbers.Count > 1;
if(isPrimaryGrabber) {
// Keep track of if we were being held with two hands or not before dropping the item
bool wasHeldWithTwoHands = BeingHeldWithTwoHands;
// Should we release this item
bool releaseItem = true;
if (resetParent) {
ResetParent();
}
// disconnect all joints and set the connected object to null
removeConfigJoint();
// Remove Fixed Joint
if (GrabPhysics == GrabPhysics.FixedJoint && droppedBy != null) {
FixedJoint joint = droppedBy.gameObject.GetComponent();
if (joint) {
GameObject.Destroy(joint);
}
}
// If something called drop on this item we want to make sure the parent knows about it
// Reset's Grabber position, grabbable state, etc.
if (droppedBy) {
droppedBy.DidDrop();
}
// No longer need move events
UnsubscribeFromMoveEvents();
// No longer have a primary Grab Offset set
primaryGrabOffset = null;
// No longer looking at a 2h object
clearLookAtTransform();
removeGrabber(droppedBy);
didParentHands = false;
// This object is being held by another grabber. Should we drop the item, transfer it over, or do nothing.
if (wasHeldWithTwoHands) {
// Force Release
if(TwoHandedDropBehavior == TwoHandedDropMechanic.Drop) {
// Drop Secondary Object
if(SecondaryGrabbable != null && SecondaryGrabbable.BeingHeld) {
SecondaryGrabbable.DropItem(false, false);
}
else {
// Drop our own object
DropItem(heldByGrabbers[0]);
}
}
// Swap To other Hand Side
else if (TwoHandedDropBehavior == TwoHandedDropMechanic.Transfer) {
// We are going to transfer this item, so no need to release
releaseItem = false;
// Swap to new grabber
var newGrabber = heldByGrabbers[0];
Vector3 localHandsPos = Vector3.zero;
Vector3 localHandsRot = Vector3.zero;
if (newGrabber.HandsGraphics != null) {
Transform prev = newGrabber.HandsGraphics.parent;
newGrabber.HandsGraphics.parent = transform;
localHandsPos = newGrabber.HandsGraphics.localPosition;
localHandsRot = newGrabber.HandsGraphics.localEulerAngles;
newGrabber.HandsGraphics.parent = prev;
}
DropItem(newGrabber);
newGrabber.GrabGrabbable(this);
// Call Transfer Events
// OnTransferGrabber(Grabber from, Grabber to);
// Fix Hands position
if (newGrabber.HandsGraphics != null && ParentHandModel == true && GrabMechanic == GrabType.Precise) {
Transform prev = newGrabber.HandsGraphics.parent;
newGrabber.HandsGraphics.parent = transform;
newGrabber.HandsGraphics.localPosition = localHandsPos;
newGrabber.HandsGraphics.localEulerAngles = localHandsRot;
newGrabber.HandsGraphics.parent = prev;
}
}
}
// Release the object
if(releaseItem) {
// We know the item is no longer being held. Can set this before calling any drop events
BeingHeld = false;
LastDropTime = Time.time;
// Release item and apply physics force to it
if (rigid != null && GrabPhysics != GrabPhysics.None) {
rigid.isKinematic = wasKinematic;
rigid.useGravity = usedGravity;
rigid.interpolation = initialInterpolationMode;
rigid.collisionDetectionMode = initialCollisionMode;
}
// Override Kinematic status if specified
if (ForceDisableKinematicOnDrop) {
rigid.isKinematic = false;
// Free of constraints if they were set
if (rigid.constraints == RigidbodyConstraints.FreezeAll) {
rigid.constraints = RigidbodyConstraints.None;
}
}
// On release event
if (events != null) {
for (int x = 0; x < events.Count; x++) {
events[x].OnRelease();
}
}
// Reset hand pose
CustomHandPose = initialHandPoseId;
SelectedHandPose = initialHandPose;
handPoseType = initialHandPoseType;
// Apply velocity last
if (rigid && resetVelocity && droppedBy && AddControllerVelocityOnDrop&& GrabPhysics != GrabPhysics.None) {
// Make sure velocity is passed on
Vector3 velocity = droppedBy.GetGrabberAveragedVelocity() + droppedBy.GetComponent().velocity;
Vector3 angularVelocity = droppedBy.GetGrabberAveragedAngularVelocity() + droppedBy.GetComponent().angularVelocity;
if (gameObject.activeSelf) {
Release(velocity, angularVelocity);
}
}
}
}
else if (isSecondaryGrabber) {
// If something called drop on this item we want to make sure the parent knows about it
// Reset's Grabber position, grabbable state, etc.
if (droppedBy) {
droppedBy.DidDrop();
}
removeGrabber(droppedBy);
secondaryGrabOffset = null;
// didParentHands = false;
}
BeingHeld = heldByGrabbers != null && heldByGrabbers.Count > 0;
}
void clearLookAtTransform() {
if (SecondaryLookAtTransform != null && SecondaryLookAtTransform.transform.name == "LookAtTransformTemp") {
GameObject.Destroy(SecondaryLookAtTransform.gameObject);
}
SecondaryLookAtTransform = null;
}
void callEvents(Grabber g) {
if (events.Any()) {
ControllerHand hand = g.HandSide;
// Right Hand Controls
if (hand == ControllerHand.Right) {
foreach (var e in events) {
e.OnGrip(input.RightGrip);
e.OnTrigger(input.RightTrigger);
if (input.RightTriggerUp) {
e.OnTriggerUp();
}
if (input.RightTriggerDown) {
e.OnTriggerDown();
}
if (input.AButton) {
e.OnButton1();
}
if (input.AButtonDown) {
e.OnButton1Down();
}
if (input.AButtonUp) {
e.OnButton1Up();
}
if (input.BButton) {
e.OnButton2();
}
if (input.BButtonDown) {
e.OnButton2Down();
}
if (input.BButtonUp) {
e.OnButton2Up();
}
}
}
// Left Hand Controls
if (hand == ControllerHand.Left) {
for (int x = 0; x < events.Count; x++) {
GrabbableEvents e = events[x];
e.OnGrip(input.LeftGrip);
e.OnTrigger(input.LeftTrigger);
if (input.LeftTriggerUp) {
e.OnTriggerUp();
}
if (input.LeftTriggerDown) {
e.OnTriggerDown();
}
if (input.XButton) {
e.OnButton1();
}
if (input.XButtonDown) {
e.OnButton1Down();
}
if (input.XButtonUp) {
e.OnButton1Up();
}
if (input.YButton) {
e.OnButton2();
}
if (input.YButtonDown) {
e.OnButton2Down();
}
if (input.YButtonUp) {
e.OnButton2Up();
}
}
}
}
}
public virtual void DropItem(Grabber droppedBy) {
DropItem(droppedBy, true, true);
}
public virtual void DropItem(bool resetVelocity, bool resetParent) {
DropItem(GetPrimaryGrabber(), resetVelocity, resetParent);
}
public void ResetScale() {
transform.localScale = OriginalScale;
}
public void ResetParent() {
transform.parent = originalParent;
}
public void UpdateOriginalParent(Transform newOriginalParent) {
originalParent = newOriginalParent;
}
public void UpdateOriginalParent() {
UpdateOriginalParent(transform.parent);
}
public ControllerHand GetControllerHand(Grabber g) {
if(g != null) {
return g.HandSide;
}
return ControllerHand.None;
}
///
/// Returns the Grabber that first grabbed this item. Return null if not being held.
///
///
public virtual Grabber GetPrimaryGrabber() {
if(heldByGrabbers != null) {
for (int x = 0; x < heldByGrabbers.Count; x++) {
if (heldByGrabbers[x] != null && heldByGrabbers[x].HeldGrabbable == this) {
return heldByGrabbers[x];
}
}
}
return null;
}
///
/// Get the closest valid grabber.
///
/// Returns null if no valid Grabbers in range
public virtual Grabber GetClosestGrabber() {
Grabber closestGrabber = null;
float lastDistance = 9999;
if (validGrabbers != null) {
for (int x = 0; x < validGrabbers.Count; x++) {
Grabber g = validGrabbers[x];
if (g != null) {
float dist = Vector3.Distance(grabPosition, g.transform.position);
if(dist < lastDistance) {
closestGrabber = g;
}
}
}
}
return closestGrabber;
}
public virtual Transform GetClosestGrabPoint(Grabber grabber) {
Transform grabPoint = null;
float lastDistance = 9999;
float lastAngle = 360;
if(GrabPoints != null) {
int grabCount = GrabPoints.Count;
for (int x = 0; x < grabCount; x++) {
Transform g = GrabPoints[x];
// Transform may have been destroyed
if (g == null) {
continue;
}
float thisDist = Vector3.Distance(g.transform.position, grabber.transform.position);
if (thisDist <= lastDistance) {
// Check for GrabPoint component that may override some values
GrabPoint gp = g.GetComponent();
if (gp) {
// Not valid for this hand side
if((grabber.HandSide == ControllerHand.Left && !gp.LeftHandIsValid) || (grabber.HandSide == ControllerHand.Right && !gp.RightHandIsValid)) {
continue;
}
// Angle is too great
float currentAngle = Quaternion.Angle(grabber.transform.rotation, g.transform.rotation);
if (currentAngle > gp.MaxDegreeDifferenceAllowed) {
continue;
}
// Last angle was better, don't use this one
if (currentAngle > lastAngle && gp.MaxDegreeDifferenceAllowed != 360) {
continue;
}
lastAngle = currentAngle;
}
grabPoint = g;
lastDistance = thisDist;
}
}
}
return grabPoint;
}
///
/// Throw the object by applying velocity
///
/// How much velocity to apply to the grabbable. Multiplied by ThrowForceMultiplier
/// How much angular velocity to apply to the grabbable.
public virtual void Release(Vector3 velocity, Vector3 angularVelocity) {
Vector3 releaseVelocity = velocity * ThrowForceMultiplier;
// Make sure this is a valid velocity
if (float.IsInfinity(releaseVelocity.x) || float.IsNaN(releaseVelocity.x)) {
return;
}
rigid.velocity = releaseVelocity;
rigid.angularVelocity = angularVelocity;
}
public virtual bool IsValidCollision(Collision collision) {
return IsValidCollision(collision.collider);
}
public virtual bool IsValidCollision(Collider col) {
// Ignore Projectiles from grabbable collision
// This way our grabbable stays rigid when projectils come in contact
string transformName = col.transform.name;
if (transformName.Contains("Projectile") || transformName.Contains("Bullet") || transformName.Contains("Clip")) {
return false;
}
// Ignore Character Joints as these cause jittery issues
if (transformName.Contains("Joint")) {
return false;
}
// Ignore Character Controllers
CharacterController cc = col.gameObject.GetComponent();
if (cc && col) {
Physics.IgnoreCollision(col, cc, true);
return false;
}
return true;
}
public virtual void parentHandGraphics(Grabber g) {
if (g.HandsGraphics != null) {
// Set to specified Grab Transform
if (primaryGrabOffset != null) {
g.HandsGraphics.transform.parent = primaryGrabOffset;
didParentHands = true;
}
else {
g.HandsGraphics.transform.parent = transform;
didParentHands = true;
}
}
}
void setupConfigJoint(Grabber g) {
connectedJoint = g.GetComponent();
connectedJoint.autoConfigureConnectedAnchor = false;
connectedJoint.connectedBody = rigid;
connectedJoint.anchor = Vector3.zero;
connectedJoint.connectedAnchor = GrabPositionOffset;
}
void removeConfigJoint() {
if (connectedJoint != null) {
connectedJoint.anchor = Vector3.zero;
connectedJoint.connectedBody = null;
}
}
public UnityEvent OnUniqueGrabbed;
public UnityEvent OnReleased;
void addGrabber(Grabber g) {
if (heldByGrabbers == null) {
heldByGrabbers = new List();
}
if (!heldByGrabbers.Contains(g)) {
heldByGrabbers.Add(g);
OnUniqueGrabbed?.Invoke(g);
}
}
void removeGrabber(Grabber g) {
if (heldByGrabbers == null) {
heldByGrabbers = new List();
}
else if (heldByGrabbers.Contains(g)) {
heldByGrabbers.Remove(g);
OnReleased?.Invoke(g);
}
Grabber removeGrabber = null;
// Clean up any other latent grabbers
for (int x = 0; x < heldByGrabbers.Count; x++) {
Grabber grab = heldByGrabbers[x];
if (grab.HeldGrabbable == null || grab.HeldGrabbable != this) {
removeGrabber = grab;
}
}
if (removeGrabber) {
heldByGrabbers.Remove(removeGrabber);
}
}
///
/// Moves the Grabbable using MovePosition if rigidbody present. Otherwise use transform.position
///
void movePosition(Vector3 worldPosition) {
if (rigid) {
rigid.MovePosition(worldPosition);
}
else {
transform.position = worldPosition;
}
}
///
/// Rotates the Grabbable using MoveRotation if rigidbody present. Otherwise use transform.rotation
///
void moveRotation(Quaternion worldRotation) {
if (rigid) {
rigid.MoveRotation(worldRotation);
}
else {
transform.rotation = worldRotation;
}
}
protected Vector3 getRemotePosition(Grabber toGrabber) {
return GetGrabberWithGrabPointOffset(toGrabber, GetClosestGrabPoint(toGrabber));
//if (toGrabber != null) {
// Transform pointPosition = GetClosestGrabPoint(toGrabber);
// if(pointPosition) {
// Vector3 grabberPosition = toGrabber.transform.position;
// if (pointPosition != null) {
// grabberPosition += transform.position - pointPosition.position;
// //Vector3 offset = toGrabber.transform.InverseTransformPoint(pointPosition.position);
// //grabberPosition += offset;
// }
// return grabberPosition;
// }
// return grabTransform.position;
//}
//return grabTransform.position;
}
protected Quaternion getRemoteRotation(Grabber grabber) {
if (grabber != null) {
Transform point = GetClosestGrabPoint(grabber);
if (point) {
Quaternion originalRot = grabTransform.rotation;
grabTransform.localRotation *= Quaternion.Inverse(point.localRotation);
Quaternion result = grabTransform.rotation;
grabTransform.rotation = originalRot;
return result;
}
}
return grabTransform.rotation;
}
void filterCollisions() {
for (int x = 0; x < collisions.Count; x++) {
if (collisions[x] == null || !collisions[x].enabled || !collisions[x].gameObject.activeSelf) {
collisions.Remove(collisions[x]);
break;
}
}
}
///
/// A BNGPlayerController is optional, but if one is available we can check the last moved time in order to strengthen the physics joint during quick movements. This helps prevent jitter or flying objects in certain situations.
///
///
public virtual BNGPlayerController GetBNGPlayerController() {
if (_player != null) {
return _player;
}
// The player object can be used to determine if the object is about to move rapidly
if (GameObject.FindGameObjectWithTag("Player")) {
return _player = GameObject.FindGameObjectWithTag("Player").GetComponentInChildren();
}
else {
return _player = FindObjectOfType();
}
}
///
/// Request the Grabbable to use a springy joint for the next X seconds
///
/// How many seconds to make the Grabbable springy.
public virtual void RequestSpringTime(float seconds) {
float requested = Time.time + seconds;
// Only apply if our request is longer than the current request
if(requested > requestSpringTime) {
requestSpringTime = requested;
}
}
public virtual void AddValidGrabber(Grabber grabber) {
if (validGrabbers == null) {
validGrabbers = new List();
}
if (!validGrabbers.Contains(grabber)) {
validGrabbers.Add(grabber);
}
}
public virtual void RemoveValidGrabber(Grabber grabber) {
if (validGrabbers != null && validGrabbers.Contains(grabber)) {
validGrabbers.Remove(grabber);
}
}
bool subscribedToEvents = false;
bool grabbableIsLocked = false;
///
/// Subscribe to any movement-related events that might cause our Grabbable to suddenly move far away.
/// By subscribing to these events before they occur we can then respond better to these positional updates
///
public virtual void SubscribeToMoveEvents() {
// Object can't be moved, so no need for subscription
if(!CanBeMoved || subscribedToEvents == true || GrabPhysics == GrabPhysics.None) {
return;
}
// Lock the slide in place when teleporting or snap turning
PlayerTeleport.OnBeforeTeleport += LockGrabbableWithRotation;
PlayerTeleport.OnAfterTeleport += UnlockGrabbable;
PlayerRotation.OnBeforeRotate += LockGrabbableWithRotation;
PlayerRotation.OnAfterRotate += UnlockGrabbable;
// Only needed for velocity and physics type movement
if(GrabPhysics == GrabPhysics.Velocity || GrabPhysics == GrabPhysics.PhysicsJoint) {
SmoothLocomotion.OnBeforeMove += LockGrabbable;
SmoothLocomotion.OnAfterMove += UnlockGrabbable;
}
// Kinematic can use parenting
if (GrabPhysics == GrabPhysics.Kinematic && ParentToHands == true) {
SmoothLocomotion.OnBeforeMove += LockGrabbableWithRotation;
SmoothLocomotion.OnAfterMove += UnlockGrabbable;
}
else if (GrabPhysics == GrabPhysics.Kinematic && ParentToHands == false) {
SmoothLocomotion.OnBeforeMove += LockGrabbable;
SmoothLocomotion.OnAfterMove += UnlockGrabbable;
}
subscribedToEvents = true;
}
public virtual void UnsubscribeFromMoveEvents() {
if(subscribedToEvents) {
PlayerTeleport.OnBeforeTeleport -= LockGrabbableWithRotation;
PlayerTeleport.OnAfterTeleport -= UnlockGrabbable;
PlayerRotation.OnBeforeRotate -= LockGrabbableWithRotation;
PlayerRotation.OnAfterRotate -= UnlockGrabbable;
// Specific lock types
if (GrabPhysics == GrabPhysics.Velocity || GrabPhysics == GrabPhysics.PhysicsJoint) {
SmoothLocomotion.OnBeforeMove -= LockGrabbable;
SmoothLocomotion.OnAfterMove -= UnlockGrabbable;
}
// Kinematic can use parenting
if (GrabPhysics == GrabPhysics.Kinematic && ParentToHands == true) {
SmoothLocomotion.OnBeforeMove -= LockGrabbableWithRotation;
SmoothLocomotion.OnAfterMove -= UnlockGrabbable;
}
else if (GrabPhysics == GrabPhysics.Kinematic && ParentToHands == false) {
SmoothLocomotion.OnBeforeMove -= LockGrabbable;
SmoothLocomotion.OnAfterMove -= UnlockGrabbable;
}
// Reset Lock Events
lockRequests = 0;
subscribedToEvents = false;
}
}
private Transform _priorParent;
private Vector3 _priorLocalOffsetPosition;
private Quaternion _priorLocalOffsetRotation;
private Grabber _priorPrimaryGrabber;
bool lockPos, lockRot;
int lockRequests = 0;
public virtual void LockGrabbable() {
// By default only lock position
LockGrabbable(true, false, false);
}
// Lock both position and rotation
public virtual void LockGrabbableWithRotation() {
LockGrabbable(true, true, true);
}
public virtual void RequestLockGrabbable() {
// Don't do anything if recent collision
if(RecentlyCollided) {
return;
}
lockRequests++;
if (lockRequests == 1) {
if (_priorPrimaryGrabber != null) {
// Lock via parenting
// Store position as well as parenting
_priorParent = transform.parent;
transform.parent = _priorPrimaryGrabber.transform;
}
}
if (lockRequests > 0) {
if (_priorPrimaryGrabber != null) {
_priorParent = transform.parent;
transform.parent = _priorPrimaryGrabber.transform;
// Store latest position offset
_priorLocalOffsetPosition = _priorPrimaryGrabber.transform.InverseTransformPoint(transform.position);
}
}
}
public virtual void RequestUnlockGrabbable() {
// Don't do anything if recent collision
if (RecentlyCollided) {
return;
}
ResetLockResets();
}
public virtual void ResetLockResets() {
if (lockRequests > 0) {
if (transform.parent != _priorParent) {
transform.parent = _priorParent;
}
lockRequests = 0;
}
}
///
/// Keep the Grabbable's position and /or rotation in place
///
public virtual void LockGrabbable(bool lockPosition, bool lockRotation, bool overridePriorLock) {
if (BeingHeld && (!grabbableIsLocked || overridePriorLock)) {
if (_priorPrimaryGrabber != null) {
lockPos = lockPosition;
lockRot = lockRotation;
// Lock via parenting
if (lockPosition && lockRotation) {
// Store position as well as parenting
_priorLocalOffsetPosition = _priorPrimaryGrabber.transform.InverseTransformPoint(transform.position);
_priorParent = transform.parent;
transform.parent = _priorPrimaryGrabber.transform;
}
// Individual locking
else {
if (lockPos) {
_priorLocalOffsetPosition = _priorPrimaryGrabber.transform.InverseTransformPoint(transform.position);
}
if (lockRot) {
_priorLocalOffsetRotation = Quaternion.FromToRotation(transform.forward, _priorPrimaryGrabber.transform.forward);
}
}
grabbableIsLocked = true;
}
}
}
///
/// Allow the Grabbable to move
///
public virtual void UnlockGrabbable() {
if (BeingHeld && grabbableIsLocked) {
// Use parenting if both position and rotation are to be locked
if(lockPos && lockRot) {
Vector3 dest = _priorPrimaryGrabber.transform.TransformPoint(_priorLocalOffsetPosition);
float dist = Vector3.Distance(transform.position, dest);
// Only move if gone far enough
if (dist > 0.001f) {
transform.position = _priorPrimaryGrabber.transform.TransformPoint(_priorLocalOffsetPosition);
}
// Only reparent if necessary
if(transform.parent != _priorParent) {
transform.parent = _priorParent;
}
}
else {
if (lockPos) {
Vector3 dest = _priorPrimaryGrabber.transform.TransformPoint(_priorLocalOffsetPosition);
float dist = Vector3.Distance(transform.position, dest);
// Only move if gone far enough
if (dist > 0.0005f) {
transform.position = dest;
}
}
if (lockRot) {
transform.rotation = _priorPrimaryGrabber.transform.rotation * _priorLocalOffsetRotation;
}
}
grabbableIsLocked = false;
}
}
///
/// You can comment this function out if you don't need precise contacts. Otherwise this is necessary to check for world collisions while being held
///
///
private void OnCollisionStay(Collision collision) {
// Can bail early
if (!BeingHeld) {
return;
}
for (int x = 0; x < collision.contacts.Length; x++) {
ContactPoint contact = collision.contacts[x];
// Keep track of how many objects we are colliding with
if (BeingHeld && IsValidCollision(contact.otherCollider) && !collisions.Contains(contact.otherCollider)) {
collisions.Add(contact.otherCollider);
}
}
}
private void OnCollisionEnter(Collision collision) {
// Keep track of how many objects we are colliding with
if (BeingHeld && IsValidCollision(collision) && !collisions.Contains(collision.collider)) {
collisions.Add(collision.collider);
}
}
private void OnCollisionExit(Collision collision) {
// We only care about collisions when being held, so we can skip this check otherwise
if (BeingHeld && collisions.Contains(collision.collider)) {
collisions.Remove(collision.collider);
}
}
bool quitting = false;
void OnApplicationQuit() {
quitting = true;
}
void OnDestroy() {
if(BeingHeld && !quitting) {
DropItem(false, false);
}
}
void OnDrawGizmosSelected() {
// Show Grip Points
Gizmos.color = new Color(0, 1, 0, 0.5f);
if (GrabPoints != null && GrabPoints.Count > 0) {
for (int i = 0; i < GrabPoints.Count; i++) {
Transform p = GrabPoints[i];
if (p != null) {
Gizmos.DrawSphere(p.position, 0.02f);
}
}
}
else {
Gizmos.DrawSphere(transform.position, 0.02f);
}
}
}
#region enums
public enum GrabType {
Snap,
Precise
}
public enum RemoteGrabMovement {
Linear,
Velocity,
Flick
}
public enum GrabPhysics {
None = 2,
PhysicsJoint = 0,
FixedJoint = 3,
Velocity = 4,
Kinematic = 1
}
public enum OtherGrabBehavior {
None,
SwapHands,
DualGrab
}
public enum TwoHandedPositionType {
Lerp,
None
}
public enum TwoHandedRotationType {
Lerp,
Slerp,
LookAtSecondary,
None
}
public enum TwoHandedDropMechanic {
Drop,
Transfer,
None
}
public enum TwoHandedLookDirection {
Horizontal,
Vertical
}
public enum HandPoseType {
AnimatorID,
HandPose,
AutoPoseOnce,
AutoPoseContinuous,
None
}
#endregion
}