using DG.Tweening; using UnityEditor; using UnityEngine; using System.Linq; using System.Collections; using System.Collections.Generic; using System; using UnityEngine.Events; #if MIRROR using Mirror; #endif namespace UDE_HAND_INTERACTION { public class InteractableObject : MonoBehaviour { [HideInInspector] public GameObject DefaultVirtualHand; [HideInInspector] public GameObject Hand { get; private set; } private GameObject _VirtualHand; private GameObject CalculateGameobj; private GameObject CalculateGameobjVirtualHand; private Vector3 PosOffset; private Quaternion RotOffset; private bool follow = false; public bool IsFollow => follow; private bool RegularTransfer = true; [HideInInspector] public int InteractPriority; [Space] public UnityEvent WhenObjInteract; public UnityEvent WhenObjDisable; public bool enablePhysicalProperties; public bool enableMotionTrace; public float traceVisibleSeconds = 5f; [Serializable, SerializeField] public struct HandInteractionPlans { [SerializeField] public string Name; [SerializeField] public HandInteraction[] handInteractions; public HandInteractionPlans(string name, HandInteraction[] interactions) { Name = name; handInteractions = interactions; } } [SerializeField, Space] public HandInteraction[] HandInteractions; [HideInInspector] public HandInteraction[] AllInteractions { get; private set; } public bool enableInteractionPlans; [SerializeField] public HandInteractionPlans[] InteractionPlan; [HideInInspector] public ObjectFollowType objectFollowType; [HideInInspector] public float TrackingTime; [HideInInspector] public float TrackingDistance; [HideInInspector] public float MovingOnTime; [HideInInspector] public Transform InsideOperationPart; private GameObject[] TempHand = { null, null }; private Vector3 PreActivePos = Vector3.zero; private Quaternion PreActiveRot = Quaternion.identity; private bool BackReset = false; private Tween[] SlowLocalTween = new Tween[2]; private Tween DistanceCurveTween; private Rigidbody _body; private bool hasRigid = false; private bool onThrow = false; private int FixedTimeIndedx = 1; public enum ObjectFollowType { Instant, SlowMove, Chasing, NoPose, Natural, Distance } public InteractableObjectOutline interactableObjectOutline => GetComponent(); public bool SetInteractSound = false; void Start() { if (CalculateGameobj == null) { CalculateGameobj = new("CalculateGameobj"); CalculateGameobj.transform.parent = transform; } if (CalculateGameobjVirtualHand == null) { CalculateGameobjVirtualHand = new("CalculateGameobjVirtualHand"); CalculateGameobjVirtualHand.transform.parent = CalculateGameobj.transform; } AutoCheckHand(); if(HandInteractions.Length == 0 && !enableInteractionPlans) { objectFollowType = ObjectFollowType.NoPose; } if(enablePhysicalProperties) { _body = GetComponent(); hasRigid = _body != null; } if(TryGetComponent(out var audio) && SetInteractSound) { WhenObjInteract.AddListener(() => { audio.Play(); }); } #if UNITY_EDITOR if (DefaultVirtualHand == null) { DefaultVirtualHand = AssetDatabase.LoadAssetAtPath("Assets/SDK/Interaction SDK/Prefabs/DefaultVirtualHand.prefab"); } #endif } void Update() { if (BackReset && transform.position != PreActivePos) { transform.SetPositionAndRotation(PreActivePos, PreActiveRot); BackReset = false; } } private void FixedUpdate() { if (onThrow && hasRigid && enableMotionTrace) { float time = Time.fixedDeltaTime * FixedTimeIndedx; GetComponent().positionCount = FixedTimeIndedx; GetComponent().SetPosition(FixedTimeIndedx - 1, transform.position); FixedTimeIndedx++; if(time >= traceVisibleSeconds) { onThrow = false; GetComponent().positionCount = 0; } } } public void HandActive(GameObject hand, HandInteraction handInteraction, Vector3 CalculatedRegularPoint) { if (ActiveLock) return; Hand = hand; PreActivePos = transform.position; PreActiveRot = transform.rotation; if (hasRigid && !follow) { FixedTimeIndedx = 1; onThrow = false; _body.useGravity = false; _body.isKinematic = true; Hand.GetComponent().isKinematic = true; } if (handInteraction == null) { transform.parent = Hand.transform; follow = true; WhenObjInteract.Invoke(); DestroyImmediate(_body); Hand.GetComponent().isKinematic = false; return; } _VirtualHand = handInteraction.gameObject; if(CalculatedRegularPoint != Vector3.zero && RegularTransfer) { if(handInteraction.TryGetComponent(out var regular)) { if(regular.SpecialSphere) { _VirtualHand.transform.RotateAround(CalculatedRegularPoint, hand.transform.right, hand.transform.eulerAngles.x - _VirtualHand.transform.eulerAngles.x); _VirtualHand.transform.RotateAround(CalculatedRegularPoint, hand.transform.up, hand.transform.eulerAngles.y - _VirtualHand.transform.eulerAngles.y); _VirtualHand.transform.RotateAround(CalculatedRegularPoint, hand.transform.forward, hand.transform.eulerAngles.z - _VirtualHand.transform.eulerAngles.z); } else if (regular.SpecialCube) { float Angle = 361; Quaternion TargetRotation = Quaternion.identity; Vector3 TargetPos = Vector3.zero; List LoopUp = new() { regular.TargetObject.right, -regular.TargetObject.up, -regular.TargetObject.right, regular.TargetObject.up }; List LoopForward = new() { regular.TargetObject.right, -regular.TargetObject.forward, -regular.TargetObject.right, regular.TargetObject.forward }; regular.CalculateVector3(); void UpdateAngle(Vector3 Axis) { float _angle = Quaternion.Angle(_VirtualHand.transform.rotation, hand.transform.rotation); if (_angle < Angle) { Angle = _angle; _VirtualHand.transform.GetPositionAndRotation(out TargetPos, out TargetRotation); } } for (int i = 0; i < 4; ++i) { _VirtualHand.transform.RotateAround(regular.CenterPoint, transform.forward, 90); for (int j = 0; j < 4; ++j) { _VirtualHand.transform.RotateAround(regular.CenterPoint, LoopUp[j], 90); UpdateAngle(LoopUp[j]); } } for (int i = 0; i < 4; ++i) { _VirtualHand.transform.RotateAround(regular.CenterPoint, transform.up, 90); for (int j = 0; j < 4; ++j) { _VirtualHand.transform.RotateAround(regular.CenterPoint, LoopForward[j], 90); UpdateAngle(LoopForward[j]); } } _VirtualHand.transform.SetPositionAndRotation(TargetPos, TargetRotation); } else { float Angle = 361; float TargetRotation = 0f; for (int i = 1; i < 361; ++i) { _VirtualHand.transform.RotateAround(CalculatedRegularPoint, regular.RealEnd - regular.RealStart, 1); var _angle = Quaternion.Angle(_VirtualHand.transform.rotation, hand.transform.rotation); if (_angle < Angle) { Angle = _angle; TargetRotation = i; } } _VirtualHand.transform.RotateAround(CalculatedRegularPoint, regular.RealEnd - regular.RealStart, TargetRotation); regular.CalculateVector3(); _VirtualHand.transform.position = CalculatedRegularPoint + regular.CenterPointToHand; } RegularTransfer = false; } } if(InsideOperationPart != null && handInteraction.GetComponent().OperationInsideObject) { if (TempHand[0] == null) { TempHand[0] = Instantiate(Hand, Hand.transform.parent); #if MIRROR var net = TempHand[0].GetComponentInChildren(); if (net != null) net.ForbidNet(); #endif TempHand[0].name = handInteraction.name + "(Clone)"; TempHand[0].transform.SetParent(InsideOperationPart); if (hasRigid) { Destroy(TempHand[0].GetComponent()); } TempHand[0].transform.localScale = handInteraction.transform.localScale; TempHand[0].GetComponentInChildren().enabled = false; TempHand[0].GetComponentInChildren().enabled = false; Hand.GetComponent().HandVisionControl(false, TempHand[0].GetComponent()); TempHand[1] = Hand; TempHand[0].transform.SetPositionAndRotation(Hand.transform.position, Hand.transform.rotation); TempHand[0].transform.DOLocalMove(_VirtualHand.transform.localPosition, MovingOnTime); TempHand[0].transform.DOLocalRotate(_VirtualHand.transform.localEulerAngles, MovingOnTime); } return; } transform.parent = Hand.transform; bool SkipMove = Hand.transform.position == _VirtualHand.transform.position && objectFollowType != ObjectFollowType.Chasing; bool SkipRot = Hand.transform.rotation == _VirtualHand.transform.rotation; RotOffset = Quaternion.Inverse(_VirtualHand.transform.localRotation); PosOffset = _VirtualHand.transform.localPosition; CalculateGameobj.transform.parent = transform; CalculateGameobjVirtualHand.transform.parent = CalculateGameobj.transform; Transform CalculateTrans = CalculateGameobj.transform; Transform CalculateTransV = CalculateGameobjVirtualHand.transform; CalculateTrans.SetPositionAndRotation(transform.position, transform.rotation); CalculateTransV.SetPositionAndRotation(_VirtualHand.transform.position, _VirtualHand.transform.rotation); if ((CalculateTransV.localRotation != Quaternion.identity || hand.transform.rotation != Quaternion.identity)) { if (!SkipRot) { CalculateTrans.rotation *= RotOffset; } CalculateTransV.parent = CalculateTrans.parent; CalculateTrans.parent = CalculateTransV; PosOffset = -CalculateTrans.localPosition; } Vector3 WorldTargetPos = Hand.transform.position - _VirtualHand.transform.position + CalculateTrans.position; Quaternion WorldTargetRot = Quaternion.Inverse(_VirtualHand.transform.localRotation * Quaternion.Inverse(Hand.transform.rotation)); if ((SkipMove && SkipRot) || follow) { if (!follow && SkipMove) { follow = true; WhenObjInteract.Invoke(); } if (objectFollowType == ObjectFollowType.Chasing) { transform.DOMove(WorldTargetPos, TrackingTime); transform.DORotateQuaternion(WorldTargetRot, TrackingTime); } return; } switch ((int)objectFollowType) { case 0: transform.DOLocalMove(-PosOffset, 0); transform.DOLocalRotateQuaternion(RotOffset, 0); if(_body != null) { Destroy(_body); Hand.GetComponent().isKinematic = Hand.GetComponent() == null; } break; case 1: if (SlowLocalTween[0] == null) { SlowLocalTween[0] = transform.DOLocalMove(-PosOffset, TrackingTime); SlowLocalTween[1] = transform.DOLocalRotateQuaternion(RotOffset, TrackingTime); } if (_body != null) { Destroy(_body); Hand.GetComponent().isKinematic = Hand.GetComponent() == null; } break; case 2: transform.DOMove(WorldTargetPos, TrackingTime); transform.DORotateQuaternion(WorldTargetRot, TrackingTime); if (_body != null) { Destroy(_body); Hand.GetComponent().isKinematic = Hand.GetComponent() == null; } break; case 4: if (TempHand[0] == null) { transform.parent = null; TempHand[0] = Instantiate(Hand, handInteraction.transform.parent); TempHand[0].transform.localScale = handInteraction.transform.localScale; TempHand[0].name = handInteraction.name + "(Clone)"; TempHand[0].GetComponentInChildren().enabled = false; Hand.GetComponent().HandVisionControl(false, TempHand[0].GetComponent()); TempHand[1] = Hand; TempHand[0].transform.SetPositionAndRotation(Hand.transform.position, Hand.transform.rotation); TempHand[0].transform.DOLocalMove(_VirtualHand.transform.localPosition, TrackingTime).OnComplete(() => { SlowLocalTween[0] = transform.DOLocalMove(-PosOffset, TrackingTime).OnComplete(() => { DestroyImmediate(TempHand[0]); DestroyImmediate(_body); TempHand[1].GetComponent().HandVisionControl(true, null); Hand.GetComponent().isKinematic = Hand.GetComponent() == null; transform.parent = Hand.transform; }); SlowLocalTween[1] = transform.DOLocalRotateQuaternion(RotOffset, TrackingTime); }); TempHand[0].transform.DOLocalRotate(_VirtualHand.transform.localEulerAngles, TrackingTime); } break; case 5: //Distance if(TryGetComponent(out var collider)) { collider.enabled = false; } if (SlowLocalTween[0] == null) { SlowLocalTween[0] = transform.DOBlendableLocalMoveBy(-PosOffset - transform.localPosition, TrackingTime); DistanceCurveTween = transform.DOBlendableMoveBy(Hand.GetComponent().CurveHighestVec, TrackingTime / 2).OnComplete(() => { DistanceCurveTween = transform.DOBlendableMoveBy(-Hand.GetComponent().CurveHighestVec, TrackingTime / 2); }); SlowLocalTween[1] = transform.DOLocalRotateQuaternion(RotOffset, TrackingTime); } if (_body != null) { Destroy(_body); Hand.GetComponent().isKinematic = Hand.GetComponent() == null; } break; } follow = true; WhenObjInteract.Invoke(); } public void HandDisable(Handedness handedness) { if (transform.parent != null && transform.parent.GetComponent() != null && (int)transform.parent.GetComponent().handness == (int)handedness) { if(objectFollowType != ObjectFollowType.Distance) { SlowLocalTween[0].Complete(); SlowLocalTween[1].Complete(); } else { if (TryGetComponent(out var collider)) { collider.enabled = true; } SlowLocalTween[0].Kill(); SlowLocalTween[1].Kill(); DistanceCurveTween.Kill(); } SlowLocalTween[0] = null; BackReset = objectFollowType == ObjectFollowType.SlowMove; transform.parent = null; RegularTransfer = true; } if(Hand != null) { var handRigid = Hand.GetComponent(); handRigid.isKinematic = false; if (hasRigid && !gameObject.TryGetComponent(out _)) { _body = gameObject.AddComponent(); _body.velocity = handRigid.mass / _body.mass * handRigid.velocity; _body.angularVelocity = handRigid.mass / _body.mass * handRigid.angularVelocity; hasRigid = true; onThrow = true; if(InsideOperationPart == null) { TempHand = new GameObject[2] { null, null }; } } } if (TempHand[0] != null && !TempHand[0].name.Contains("Release")) { TempHand[0].name += "(Release)"; TempHand[0].transform.DOMove(TempHand[1].transform.position, MovingOnTime); TempHand[0].transform.DORotateQuaternion(TempHand[1].transform.rotation, MovingOnTime); Invoke(nameof(HandBack), MovingOnTime); } Hand = null; follow = false; WhenObjDisable.Invoke(); } private void HandBack() { Destroy(TempHand[0]); TempHand[1].GetComponent().HandVisionControl(true, null); TempHand = new GameObject[2] { null, null }; } private bool ActiveLock; public void SetActiveLock(bool value) { ActiveLock = value; } public Transform GetReferenceHand(int index) { return TempHand[index]?.transform; } public void AutoCheckHand() { var interactions = GetComponentsInChildren().ToList(); for(int i = interactions.Count - 1; i >= 0; i--) { if (interactions[i].transform.TryGetComponent(out FingerUseInteraction UseInteract)) { interactions.Remove(UseInteract.PreposeInteraction); } } AllInteractions = interactions.ToArray(); if (!Application.isPlaying) { HandInteractions = interactions.ToArray(); } } public void SwitchHandInteractioinPlan(int index) { if (!enableInteractionPlans || index < 0 || index >= InteractionPlan.Length) { return; } HandInteractions = InteractionPlan[index].handInteractions; } public void SwitchHandInteractioinPlan(string name) { if (!enableInteractionPlans || string.IsNullOrEmpty(name)) { return; } foreach (var items in InteractionPlan) { if (items.Name == name) { HandInteractions = items.handInteractions; break; } } } } }