mirror of
https://github.com/nothke/quality-control.git
synced 2025-08-11 08:03:44 +00:00
Added interaction, hand, rigidbody dragging, picking up hammer
This commit is contained in:
141
Assets/Plugins/Interaction/Runtime/Items/GenericItem.cs
Normal file
141
Assets/Plugins/Interaction/Runtime/Items/GenericItem.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nothke.Interaction.Items
|
||||
{
|
||||
public class GenericItem : Interactable,
|
||||
ITakeable, IDroppable, IThrowable, INicePlaceable,
|
||||
IExaminable, ICustomHoldPivot
|
||||
{
|
||||
[System.Serializable]
|
||||
public class ItemSettings
|
||||
{
|
||||
//public Vector3 customHoldPosition = new Vector3(0.15f, -0.15f, 0.1f);
|
||||
public Transform holdPivot;
|
||||
|
||||
public Collider[] collidersToDisable;
|
||||
public bool autoPopulateColliderListFromChildren;
|
||||
|
||||
public bool useCustomExaminePosition;
|
||||
public Vector3 customExaminePosition = new Vector3(0, 0, 0.15f);
|
||||
public float examineHorizontalAngle = 90;
|
||||
public float examineVerticalAngle = 90;
|
||||
|
||||
public bool canBeThrown = true;
|
||||
public float minThrowVelocity = 5;
|
||||
public float maxThrowVelocity = 10;
|
||||
|
||||
public Transform nicePlacePivot;
|
||||
}
|
||||
|
||||
public ItemSettings itemSettings;
|
||||
|
||||
public Transform Transform => transform;
|
||||
public Rigidbody Rigidbody => rb;
|
||||
|
||||
public bool DelayDrop => false;
|
||||
|
||||
public float HorizontalAngle => itemSettings.examineHorizontalAngle;
|
||||
public float VerticalAngle => itemSettings.examineVerticalAngle;
|
||||
|
||||
protected Rigidbody rb;
|
||||
Collider col;
|
||||
|
||||
public bool canThrow => itemSettings.canBeThrown;
|
||||
public float minThrowAcceleration => itemSettings.minThrowVelocity;
|
||||
public float maxThrowAcceleration => itemSettings.maxThrowVelocity;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
rb = GetComponentInParent<Rigidbody>();
|
||||
col = GetComponent<Collider>();
|
||||
|
||||
Debug.Assert(rb, "GenericItem has no Rigidbody", this);
|
||||
|
||||
if ((!itemSettings.holdPivot && transform.IsNonUniform()) ||
|
||||
(itemSettings.holdPivot && itemSettings.holdPivot.IsNonUniform()))
|
||||
Debug.LogError("GenericItem's scale (or its hold pivot's scale) is non-uniform, this is not allowed. Placement will be skewed.", this);
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
if (itemSettings == null)
|
||||
itemSettings = new ItemSettings();
|
||||
|
||||
if (itemSettings.autoPopulateColliderListFromChildren)
|
||||
{
|
||||
itemSettings.collidersToDisable = GetComponentsInChildren<Collider>(true);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnDropped(IHands hands)
|
||||
{
|
||||
SetCollisions(true);
|
||||
FixRigidbody(false);
|
||||
}
|
||||
|
||||
public virtual void OnTaken(IHands hands)
|
||||
{
|
||||
SetCollisions(false);
|
||||
FixRigidbody(true);
|
||||
}
|
||||
|
||||
public virtual void OnStartedPlacing(IHands hands)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void OnNicePlaced(IHands hands)
|
||||
{
|
||||
SetCollisions(true);
|
||||
rb.isKinematic = false;
|
||||
}
|
||||
|
||||
protected void SetCollisions(bool enable)
|
||||
{
|
||||
if (col)
|
||||
col.enabled = enable;
|
||||
|
||||
foreach (Collider col in itemSettings.collidersToDisable)
|
||||
col.enabled = enable;
|
||||
}
|
||||
|
||||
public void FixRigidbody(bool fix)
|
||||
{
|
||||
rb.interpolation = fix ? RigidbodyInterpolation.None : RigidbodyInterpolation.Interpolate;
|
||||
rb.isKinematic = fix;
|
||||
}
|
||||
|
||||
public void GetHoldPivot(out Vector3 holdPos, out Quaternion holdRot)
|
||||
{
|
||||
if (!itemSettings.holdPivot)
|
||||
{
|
||||
holdPos = Vector3.zero;
|
||||
holdRot = Quaternion.identity;
|
||||
}
|
||||
else
|
||||
{
|
||||
holdPos = itemSettings.holdPivot.localPosition;
|
||||
holdRot = itemSettings.holdPivot.localRotation;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetPlacePivot(out Vector3 placePos, out Quaternion placeRot)
|
||||
{
|
||||
if (!itemSettings.nicePlacePivot)
|
||||
{
|
||||
Bounds bounds = Utils.BoundsUtils.GetObjectSpaceColliderBounds(gameObject, true);
|
||||
placePos = bounds.center;
|
||||
placePos.y = bounds.min.y;
|
||||
placeRot = Quaternion.identity;
|
||||
//Debug.Log("Got bounds offset: " + placePos.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
placePos = itemSettings.nicePlacePivot.localPosition;
|
||||
placeRot = itemSettings.nicePlacePivot.localRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Plugins/Interaction/Runtime/Items/GenericItem.cs.meta
Normal file
11
Assets/Plugins/Interaction/Runtime/Items/GenericItem.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84817382e549d8b4797caa3a4891df6b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
579
Assets/Plugins/Interaction/Runtime/Items/Hands.cs
Normal file
579
Assets/Plugins/Interaction/Runtime/Items/Hands.cs
Normal file
@@ -0,0 +1,579 @@
|
||||
using System.Collections.Generic;
|
||||
using Nothke.Interaction.Integration;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Nothke.Interaction.Items
|
||||
{
|
||||
public class Hands : MonoBehaviour, IHands
|
||||
{
|
||||
#region Inspector variables
|
||||
|
||||
public Transform hand;
|
||||
|
||||
public Vector3 handAimPos = new Vector3(0, 0, 0.8f);
|
||||
|
||||
public float examinationMouseSensitivity = 1;
|
||||
|
||||
[Header("Smooth taking")]
|
||||
public bool smoothTake;
|
||||
public float smoothMoveFactor = 0.1f;
|
||||
public float smoothRotateRate = 10;
|
||||
|
||||
[Header("Throwing")]
|
||||
public bool throwFromCenter = true;
|
||||
public float throwUpFactor = 0.2f;
|
||||
public float throwAngularVelocity = 10;
|
||||
public Transform throwTransform;
|
||||
|
||||
public float handNoiseGain = 1;
|
||||
|
||||
[System.NonSerialized] public MonoBehaviour[] mouseLooks; // TODO: Remove
|
||||
public MonoBehaviour lockableFreeLookComponent;
|
||||
ILockableFreeLook lockableFreeLook;
|
||||
public MonoBehaviour focusEffectComponent;
|
||||
IFocusableEffect focusableEffect;
|
||||
|
||||
public bool nicePlacement;
|
||||
|
||||
public bool disableShadowsOnTakenObjects = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
public bool isFixed { get; set; }
|
||||
[System.NonSerialized] public Interactable item;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private variables
|
||||
|
||||
const float smoothPlaceTimeLimit = 1;
|
||||
|
||||
Transform itemInHands;
|
||||
|
||||
Vector3 handStartPos;
|
||||
Vector3 handTargetPos;
|
||||
Quaternion handStartRot;
|
||||
Quaternion handTargetRot;
|
||||
|
||||
RaycastHit hit;
|
||||
|
||||
Vector3 mouseSpeed;
|
||||
Vector3 handRefVelo;
|
||||
|
||||
#if FOV
|
||||
float originalFoV;
|
||||
float FoVRefVelo;
|
||||
#endif
|
||||
|
||||
Interactable IHands.item => item;
|
||||
public InteractionController controller { get; set; }
|
||||
|
||||
Vector3 placeTLocalPos;
|
||||
Quaternion placeTLocalRot;
|
||||
Vector3 placeHandOffset;
|
||||
Quaternion placeHandRotation;
|
||||
Transform placeT;
|
||||
bool placing;
|
||||
bool placingIsNice;
|
||||
float placeStartTime;
|
||||
|
||||
Transform handParent;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public methods
|
||||
|
||||
public void Take(ITakeable _item)
|
||||
{
|
||||
if (itemInHands) return;
|
||||
|
||||
item = _item as Interactable;
|
||||
|
||||
if (_item.Rigidbody)
|
||||
_item.Rigidbody.isKinematic = true;
|
||||
|
||||
var itemT = _item.Transform;
|
||||
|
||||
Vector3 targetHoldPos = Vector3.zero;
|
||||
Quaternion targetHoldRot = Quaternion.identity;
|
||||
|
||||
if (item is ICustomHoldPivot)
|
||||
(item as ICustomHoldPivot).GetHoldPivot(out targetHoldPos, out targetHoldRot);
|
||||
|
||||
if (smoothTake)
|
||||
{
|
||||
hand.transform.SetPositionAndRotation(
|
||||
itemT.TransformPoint(targetHoldPos),
|
||||
itemT.rotation * targetHoldRot);
|
||||
}
|
||||
|
||||
itemT.parent = hand;
|
||||
|
||||
if (!smoothTake)
|
||||
{
|
||||
itemT.localPosition = -targetHoldPos;
|
||||
itemT.localRotation = Quaternion.Inverse(targetHoldRot);
|
||||
}
|
||||
|
||||
if (disableShadowsOnTakenObjects)
|
||||
EnableShadowcasting(item, true);
|
||||
|
||||
itemInHands = _item.Transform;
|
||||
|
||||
_item.OnTaken(this);
|
||||
}
|
||||
|
||||
public void Drop()
|
||||
{
|
||||
if (!itemInHands) return;
|
||||
|
||||
var droppable = item as IDroppable;
|
||||
|
||||
if (droppable == null) Debug.LogError("Attempting to drop undroppable item");
|
||||
|
||||
Debug.Assert(droppable != null, "Droppable is null");
|
||||
//Debug.Assert(droppable.Rigidbody != null, "Item rigidbody is null");
|
||||
|
||||
if (droppable.Rigidbody)
|
||||
droppable.Rigidbody.isKinematic = false;
|
||||
|
||||
itemInHands.parent = null;
|
||||
|
||||
if (disableShadowsOnTakenObjects)
|
||||
EnableShadowcasting(item, true);
|
||||
|
||||
Debug.Log($"Dropped {item.name}");
|
||||
|
||||
itemInHands = null;
|
||||
item = null;
|
||||
}
|
||||
|
||||
public void DropFixed()
|
||||
{
|
||||
if (!itemInHands) return;
|
||||
|
||||
itemInHands.parent = null;
|
||||
|
||||
if (disableShadowsOnTakenObjects)
|
||||
EnableShadowcasting(item, true);
|
||||
|
||||
Debug.Log($"Dropped fixed {item.name}");
|
||||
|
||||
if (item is IDroppable droppable)
|
||||
droppable.OnDropped(this);
|
||||
|
||||
itemInHands = null;
|
||||
item = null;
|
||||
}
|
||||
|
||||
void EnableShadowcasting(Interactable item, bool enable)
|
||||
{
|
||||
Renderer[] renderers = item.transform.GetComponentsInChildren<Renderer>(); // alloc!
|
||||
for (int i = 0; i < renderers.Length; i++)
|
||||
renderers[i].shadowCastingMode =
|
||||
enable ?
|
||||
UnityEngine.Rendering.ShadowCastingMode.On :
|
||||
UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||
}
|
||||
|
||||
IItemReceivable placingIntoSlot;
|
||||
|
||||
public void Place(Vector3 position, Quaternion rotation, Transform relativeParent, IItemReceivable intoSlot = null)
|
||||
{
|
||||
placeT = relativeParent;
|
||||
placeTLocalPos = position;
|
||||
placeTLocalRot = rotation;
|
||||
placing = true;
|
||||
|
||||
if (item is ICustomHoldPivot)
|
||||
(item as ICustomHoldPivot).GetHoldPivot(out placeHandOffset, out placeHandRotation);
|
||||
else
|
||||
{
|
||||
placeHandOffset = Vector3.zero;
|
||||
placeHandRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
if (item is INicePlaceable placeable)
|
||||
placeable.OnStartedPlacing(this);
|
||||
else if (item is ISlottable slottable)
|
||||
slottable.OnStartedPlacing(this);
|
||||
|
||||
placingIsNice = placeT == null;
|
||||
placingIntoSlot = intoSlot;
|
||||
|
||||
// TODO: Add movement velocity to smoothing speed
|
||||
|
||||
placeStartTime = Time.time;
|
||||
|
||||
hand.parent = null;
|
||||
}
|
||||
|
||||
public void OverrideHandPositionAndRotation(Vector3 pos, Quaternion rot)
|
||||
{
|
||||
handTargetPos = pos;
|
||||
handTargetRot = rot;
|
||||
}
|
||||
|
||||
public void SetHandVelocity(Vector3 velo)
|
||||
{
|
||||
handRefVelo = velo;
|
||||
}
|
||||
|
||||
public void ResetOffset()
|
||||
{
|
||||
handTargetPos = handStartPos;
|
||||
handTargetRot = handStartRot;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (hand == null)
|
||||
hand = transform;
|
||||
|
||||
handStartPos = hand.transform.localPosition;
|
||||
handTargetPos = handStartPos;
|
||||
handStartRot = hand.transform.localRotation;
|
||||
handTargetRot = handStartRot;
|
||||
|
||||
if (lockableFreeLookComponent)
|
||||
{
|
||||
lockableFreeLook = lockableFreeLookComponent.GetComponent<ILockableFreeLook>();
|
||||
if (lockableFreeLook != null)
|
||||
Debug.Log("Found lockable free look on GameObject");
|
||||
}
|
||||
|
||||
if (focusEffectComponent)
|
||||
{
|
||||
focusableEffect = focusEffectComponent.GetComponent<IFocusableEffect>();
|
||||
if (focusableEffect != null)
|
||||
Debug.Log("Found focusable effect on GameObject");
|
||||
}
|
||||
|
||||
handParent = hand.parent;
|
||||
}
|
||||
|
||||
#region Update
|
||||
|
||||
private void Update()
|
||||
{
|
||||
UpdatePosition();
|
||||
}
|
||||
|
||||
public void UpdatePosition()
|
||||
{
|
||||
// sanity check
|
||||
if (item == null)
|
||||
if (placing)
|
||||
{
|
||||
this.EndPlacing();
|
||||
}
|
||||
|
||||
float dt = Time.deltaTime;
|
||||
mouseSpeed = new Vector3(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"), 0);
|
||||
|
||||
Vector3 targetPos = handTargetPos;
|
||||
|
||||
if (item is IOverrideHandPositon ohp)
|
||||
{
|
||||
targetPos = ohp.HandPosition;
|
||||
}
|
||||
|
||||
if (item is IOffsetHandPosition offhp)
|
||||
{
|
||||
targetPos += offhp.HandPositionOffset;
|
||||
}
|
||||
|
||||
float lrRot = (-0.5f + Mathf.PerlinNoise(Time.time * 3.34f, 0.234f)) * 5 * handNoiseGain;
|
||||
float udRot = (-0.5f + Mathf.PerlinNoise(Time.time * 3.34f, 34.783f)) * 5 * handNoiseGain;
|
||||
|
||||
Quaternion targetRot = handTargetRot * Quaternion.Euler(lrRot, udRot, 0);
|
||||
|
||||
if (placing)
|
||||
{
|
||||
// Get hold pivot offset and rotation
|
||||
Vector3 holdPos = Vector3.zero;
|
||||
Quaternion holdRot = Quaternion.identity;
|
||||
if (item is ICustomHoldPivot chp)
|
||||
{
|
||||
chp.GetHoldPivot(out holdPos, out holdRot);
|
||||
float slotScale = placeT ? 1.0f / placeT.lossyScale.x : 1;
|
||||
holdPos = holdPos * item.transform.lossyScale.x * slotScale;
|
||||
}
|
||||
|
||||
Vector3 placeWorldPos;
|
||||
Quaternion placeWorldRot;
|
||||
|
||||
if (!placeT) // calcaulte in world space
|
||||
{
|
||||
Matrix4x4 worldLocationMatrix = Matrix4x4.TRS(placeTLocalPos, placeTLocalRot, Vector3.one);
|
||||
Matrix4x4 handPivotMatrix = Matrix4x4.TRS(holdPos, holdRot, Vector3.one);
|
||||
Vector3 posOff = worldLocationMatrix * holdPos;
|
||||
|
||||
//var mat = handPivotMatrix * worldLocationMatrix;
|
||||
placeWorldPos = placeTLocalPos + (Vector3)(worldLocationMatrix * holdPos);
|
||||
placeWorldRot = placeTLocalRot * holdRot;
|
||||
//placeWorldRot = mat.rotation;
|
||||
}
|
||||
else // if placeT, calculate in local space of target object
|
||||
{
|
||||
placeWorldPos = placeT.TransformPoint(placeTLocalPos + holdPos);
|
||||
placeWorldRot = placeT.rotation * placeTLocalRot * holdRot;
|
||||
}
|
||||
|
||||
// Calculate targets in world space
|
||||
targetPos = placeWorldPos;
|
||||
targetRot = placeWorldRot;
|
||||
|
||||
bool isCloseToTarget = Vector3.Distance(hand.position, targetPos) < 0.02f;
|
||||
bool isAngleCloseToTarget = Quaternion.Angle(hand.rotation, targetRot) < 1;
|
||||
bool isSafetyTimeout = Time.time - placeStartTime > smoothPlaceTimeLimit;
|
||||
|
||||
// END PLACING
|
||||
if ((isCloseToTarget && isAngleCloseToTarget) || isSafetyTimeout)
|
||||
{
|
||||
EndPlacing();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Assign hand position and rotation:
|
||||
if (placing) // while placing is happening, lerping is in world space, otherwise it should be in localspace
|
||||
{
|
||||
hand.position = Vector3.SmoothDamp(hand.position, targetPos, ref handRefVelo, smoothMoveFactor);
|
||||
hand.rotation = Quaternion.Slerp(hand.rotation, targetRot, dt * smoothRotateRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
hand.localPosition = Vector3.SmoothDamp(hand.localPosition, targetPos, ref handRefVelo, smoothMoveFactor);
|
||||
hand.localRotation = Quaternion.Slerp(hand.localRotation, targetRot, dt * smoothRotateRate);
|
||||
}
|
||||
|
||||
if (examining)
|
||||
{
|
||||
if (itemInHands)
|
||||
{
|
||||
float x = mouseSpeed.x * examinationMouseSensitivity;
|
||||
float y = mouseSpeed.y * examinationMouseSensitivity;
|
||||
|
||||
IExaminable examinable = item as IExaminable;
|
||||
|
||||
if (examinable != null)
|
||||
{
|
||||
if (examinable.HorizontalAngle > 0)
|
||||
hand.Rotate(hand.transform.parent.up, x, Space.World);
|
||||
|
||||
if (examinable.VerticalAngle > 0)
|
||||
hand.Rotate(hand.transform.parent.right, y, Space.World);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EndPlacing()
|
||||
{
|
||||
placing = false;
|
||||
|
||||
// Temp, make it get velocity from target:
|
||||
handRefVelo = Vector3.zero;
|
||||
|
||||
var _item = itemInHands;
|
||||
if (_item != null)
|
||||
{
|
||||
DropFixed();
|
||||
_item.parent = placeT;
|
||||
_item.localPosition = placeTLocalPos;
|
||||
_item.localRotation = placeTLocalRot;
|
||||
}
|
||||
else Debug.LogError("Ending placement but item was null! This shouldn't happen");
|
||||
|
||||
if (placingIsNice && _item.GetComponent<Interactable>() is INicePlaceable placeable)
|
||||
placeable.OnNicePlaced(this);
|
||||
|
||||
//Debug.Log("Ending placeming");
|
||||
if (placingIntoSlot != null)
|
||||
Debug.Log("PLACINGINTOSLOT");
|
||||
|
||||
if (placingIntoSlot != null && _item.GetComponent<ISlottable>() != null)
|
||||
placingIntoSlot.SetItemInSlot(_item.GetComponent<ISlottable>());
|
||||
|
||||
placeT = null;
|
||||
placingIsNice = false;
|
||||
|
||||
hand.parent = handParent;
|
||||
}
|
||||
|
||||
//bool gunning = false;
|
||||
//public Transform gunningThing;
|
||||
|
||||
public struct HandsInput
|
||||
{
|
||||
public bool useDown;
|
||||
public bool useUp;
|
||||
public bool examineDown;
|
||||
public bool examineUp;
|
||||
public bool throwDown;
|
||||
public bool throwUp;
|
||||
public bool dropDown;
|
||||
public bool placeDown;
|
||||
}
|
||||
|
||||
HandsInput input;
|
||||
|
||||
public void SetInput(HandsInput input)
|
||||
{
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
public void UpdateInteraction()
|
||||
{
|
||||
#region Unused
|
||||
/*
|
||||
if (input.examineDown)
|
||||
{
|
||||
if (!gunning)
|
||||
{
|
||||
gunning = true;
|
||||
OverrideHandPositionAndRotation(Vector3.forward * 0.3f, Quaternion.LookRotation(-Vector3.right + Vector3.forward * 0.4f));
|
||||
GetComponent<InteractionController>().SetRayMode(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
gunning = false;
|
||||
ResetOffset();
|
||||
GetComponent<InteractionController>().SetRayMode(false);
|
||||
}
|
||||
}
|
||||
|
||||
gunningThing.localPosition = Vector3.Lerp(gunningThing.localPosition, !gunning ? Vector3.down : Vector3.zero, Time.deltaTime * 10);
|
||||
*/
|
||||
|
||||
// RMB
|
||||
/*
|
||||
if (input.useDown)
|
||||
{
|
||||
if (item is ISecondaryUsable usable)
|
||||
{
|
||||
usable.UseSecondary();
|
||||
|
||||
if (usable.AllowExamination)
|
||||
StartAim();
|
||||
}
|
||||
else if (item is IExaminable examinable)
|
||||
{
|
||||
StartAim();
|
||||
}
|
||||
}*/
|
||||
#endregion
|
||||
|
||||
if (placing)
|
||||
return;
|
||||
|
||||
// RMB up
|
||||
if (input.examineUp)
|
||||
{
|
||||
EndAim();
|
||||
}
|
||||
if (input.useDown)
|
||||
{
|
||||
if (item is IUsable usable)
|
||||
usable.Use();
|
||||
}
|
||||
|
||||
if (input.useUp && item is IHoldUsable holdUsable)
|
||||
{
|
||||
holdUsable.UseEnd();
|
||||
}
|
||||
|
||||
if (Input.mouseScrollDelta.y != 0)
|
||||
{
|
||||
if (item is IScrollableInHand scrollable)
|
||||
scrollable.ScrollInHand(Input.mouseScrollDelta.y);
|
||||
}
|
||||
|
||||
if (input.dropDown && item is IDroppable)
|
||||
{
|
||||
IDroppable droppable = item as IDroppable;
|
||||
droppable.OnDropped(this);
|
||||
|
||||
if (!droppable.DelayDrop)
|
||||
Drop();
|
||||
}
|
||||
|
||||
if (input.throwDown && item is IThrowable)
|
||||
{
|
||||
IThrowable throwable = item as IThrowable;
|
||||
|
||||
if (throwFromCenter)
|
||||
{
|
||||
hand.localPosition = new Vector3(0, 0, hand.localPosition.z);
|
||||
|
||||
if (throwTransform)
|
||||
hand.position = throwTransform.position;
|
||||
}
|
||||
|
||||
Drop();
|
||||
throwable.OnDropped(this);
|
||||
|
||||
Vector3 throwFrw = throwTransform ? throwTransform.forward : transform.forward;
|
||||
|
||||
float throwSpeed = throwable.minThrowAcceleration;
|
||||
Vector3 force = throwFrw * throwSpeed + Vector3.up * throwUpFactor;
|
||||
|
||||
throwable.Rigidbody.AddForce(force, ForceMode.VelocityChange);
|
||||
throwable.Rigidbody.AddTorque(transform.forward * throwAngularVelocity, ForceMode.VelocityChange);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Aiming
|
||||
|
||||
bool examining;
|
||||
|
||||
void StartAim()
|
||||
{
|
||||
examining = true;
|
||||
|
||||
if (item && item is ICustomExaminePosition cep)
|
||||
handTargetPos = cep.ExaminePosition;
|
||||
else
|
||||
handTargetPos = handAimPos;
|
||||
|
||||
handTargetRot = Quaternion.identity;
|
||||
|
||||
LockFreeLook(true);
|
||||
focusableEffect?.FocusEffect(true, handTargetPos.z);
|
||||
}
|
||||
|
||||
void EndAim()
|
||||
{
|
||||
examining = false;
|
||||
|
||||
if (item && item is ICustomHandPosition chp)
|
||||
handTargetPos = chp.HandsOffset;
|
||||
else
|
||||
handTargetPos = handStartPos;
|
||||
|
||||
handTargetRot = handStartRot;
|
||||
|
||||
LockFreeLook(false);
|
||||
focusableEffect?.FocusEffect(false, 1000);
|
||||
}
|
||||
|
||||
public void LockFreeLook(bool b)
|
||||
{
|
||||
if (mouseLooks != null)
|
||||
for (int i = 0; i < mouseLooks.Length; i++)
|
||||
mouseLooks[i].enabled = !b;
|
||||
|
||||
lockableFreeLook?.LockFreeLook(b);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
11
Assets/Plugins/Interaction/Runtime/Items/Hands.cs.meta
Normal file
11
Assets/Plugins/Interaction/Runtime/Items/Hands.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff7c610785ea24144a1af4b97eb2ba39
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
196
Assets/Plugins/Interaction/Runtime/Items/Slot.cs
Normal file
196
Assets/Plugins/Interaction/Runtime/Items/Slot.cs
Normal file
@@ -0,0 +1,196 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using Nothke.Interaction.Items;
|
||||
using Nothke.Interaction.UI;
|
||||
|
||||
namespace Nothke.Interaction
|
||||
{
|
||||
public class Slot : Interactable, IItemReceivable, IItemInHandsInteractionDependable
|
||||
{
|
||||
public Transform slotPivot;
|
||||
|
||||
public string slotTag;
|
||||
public virtual string SlotTag => slotTag;
|
||||
|
||||
public ISlottable itemInSlot;
|
||||
public bool Occupied => itemInSlot != null;
|
||||
|
||||
public override string Label
|
||||
{
|
||||
get
|
||||
{
|
||||
if (itemInSlot != null)
|
||||
return (itemInSlot as Interactable).Label;
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void Awake()
|
||||
{
|
||||
if (slotPivot.IsNonUniform())
|
||||
Debug.LogError("Slot lossy scale is not uniform, this is not allowed.", slotPivot);
|
||||
}
|
||||
#endif
|
||||
|
||||
public virtual bool IsInteractableIfHolding(Interactable item)
|
||||
{
|
||||
// if occupied and holding an item, not able to interact
|
||||
if (item != null && itemInSlot != null)
|
||||
return false;
|
||||
|
||||
// if occupied and not holding anything, able to take
|
||||
if (item == null && itemInSlot != null)
|
||||
return true;
|
||||
|
||||
var slottable = item as ISlottable;
|
||||
|
||||
if (slottable == null) return false;
|
||||
|
||||
if (!slottable.IsSlottable)
|
||||
return false;
|
||||
|
||||
if (CanReceive(slottable))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CanReceive(ISlottable slottable)
|
||||
{
|
||||
if (itemInSlot != null) return false;
|
||||
|
||||
Debug.Assert(slottable != null, "CanReceive slottable is null");
|
||||
|
||||
if (slottable.SlotTag == SlotTag)
|
||||
return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
public override void Use(InteractionController im)
|
||||
{
|
||||
manager = im;
|
||||
SlotOrTake(im);
|
||||
}
|
||||
|
||||
public void SlotOrTake(InteractionController im)
|
||||
{
|
||||
var hands = im.hands;
|
||||
|
||||
if (!hands.item && itemInSlot != null)
|
||||
{
|
||||
if (IsTakeable)
|
||||
Take();
|
||||
}
|
||||
else
|
||||
if (hands.item && hands.item is ISlottable)
|
||||
{
|
||||
SlotItemFromHands(hands);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsTakeable => true;
|
||||
|
||||
public void Take()
|
||||
{
|
||||
if (itemInSlot == null)
|
||||
{
|
||||
Debug.LogError("Attempting to take an item from slot but there is none");
|
||||
return;
|
||||
}
|
||||
|
||||
var takeable = itemInSlot as ITakeable;
|
||||
|
||||
Debug.Assert(takeable != null, "Item is slottable but not takeable", this);
|
||||
|
||||
OnBeforeTaken();
|
||||
|
||||
manager.hands.Take(takeable);
|
||||
|
||||
itemInSlot.OnRemovedFromSlot();
|
||||
itemInSlot.SlottedIn = null;
|
||||
itemInSlot = null;
|
||||
|
||||
manager.Recast();
|
||||
}
|
||||
|
||||
public void SlotItemFromHands(IHands hands)
|
||||
{
|
||||
if (CanReceive(hands.item as ISlottable))
|
||||
{
|
||||
manager.hands.Place(Vector3.zero, Quaternion.identity, slotPivot, this);
|
||||
|
||||
var item = hands.item;
|
||||
|
||||
// if not smooth placement:
|
||||
//itemInSlot = item as ISlottable;
|
||||
//itemInSlot.SlottedIn = this;
|
||||
//itemInSlot.OnSlotted();
|
||||
//OnSlot();
|
||||
|
||||
// if smooth placement, hands will callback the slot:
|
||||
(item as ISlottable).OnStartedPlacing(hands);
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetItemInSlot(ISlottable item)
|
||||
{
|
||||
Debug.Assert(item != null, "ISlottable passed is null");
|
||||
//Debug.Log("Slotted " + (item as Component).gameObject.name + " into " + gameObject.name, this);
|
||||
|
||||
if (CanReceive(item))
|
||||
{
|
||||
var itemT = (item as Component).transform;
|
||||
itemT.parent = slotPivot;
|
||||
itemT.localPosition = Vector3.zero;
|
||||
itemT.localRotation = Quaternion.identity;
|
||||
|
||||
itemInSlot = item;
|
||||
item.SlottedIn = this;
|
||||
|
||||
item.OnSlotted();
|
||||
OnSlot();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("Slot can't receive this item, tags don't match OR already holding item", item as Component);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (manager)
|
||||
manager.Recast();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public ISlottable RemoveItemFromSlot()
|
||||
{
|
||||
var takeable = itemInSlot as ITakeable;
|
||||
|
||||
OnBeforeTaken();
|
||||
|
||||
var _item = itemInSlot;
|
||||
itemInSlot = null;
|
||||
|
||||
(_item as Component).transform.SetParent(null);
|
||||
_item.OnRemovedFromSlot();
|
||||
_item.SlottedIn = null;
|
||||
|
||||
return _item;
|
||||
}
|
||||
|
||||
public virtual void OnBeforeTaken() { }
|
||||
public virtual void OnSlot() { }
|
||||
|
||||
public ReticleUI.State GetCustomReticle()
|
||||
{
|
||||
if (itemInSlot != null)
|
||||
return ReticleUI.State.Take;
|
||||
else
|
||||
return ReticleUI.State.Slot;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Plugins/Interaction/Runtime/Items/Slot.cs.meta
Normal file
11
Assets/Plugins/Interaction/Runtime/Items/Slot.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 668243f6f6db5be499bd28639ba7c669
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
32
Assets/Plugins/Interaction/Runtime/Items/Slots.cs
Normal file
32
Assets/Plugins/Interaction/Runtime/Items/Slots.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using Nothke.Interaction.Items;
|
||||
|
||||
namespace Nothke.Interaction
|
||||
{
|
||||
public interface ISlottable
|
||||
{
|
||||
string SlotTag { get; }
|
||||
|
||||
bool IsSlottable { get; }
|
||||
void OnStartedPlacing(IHands hands);
|
||||
void OnSlotted();
|
||||
|
||||
IItemReceivable SlottedIn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called before IsSlottable is nulled
|
||||
/// </summary>
|
||||
void OnRemovedFromSlot();
|
||||
}
|
||||
|
||||
public interface IItemReceivable
|
||||
{
|
||||
string SlotTag { get; }
|
||||
|
||||
bool CanReceive(ISlottable slottable);
|
||||
bool SetItemInSlot(ISlottable slottable);
|
||||
}
|
||||
}
|
11
Assets/Plugins/Interaction/Runtime/Items/Slots.cs.meta
Normal file
11
Assets/Plugins/Interaction/Runtime/Items/Slots.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8738e65b425e1df4492657193fd2b9fd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user