using System.Collections; using System.Collections.Generic; using UnityEngine; public class HandController : MonoBehaviour { [SerializeField] private Animator _animator; [SerializeField] private Transform _rightGrabPoint; [SerializeField] private Transform _leftGrabPoint; [SerializeField] private float _interactDistance = 3f; [SerializeField] private float _reachSpeed = 6f; [SerializeField] private float _reachOffset = 0.15f; [SerializeField] private LayerMask _interactMask = ~0; [SerializeField] private float _liquidReturnDistance = 3f; [SerializeField] private float _holdScale = 0.6f; [SerializeField] private Vector3 _liquidLocalOffset = new Vector3(0f, 0.0005f, 0f); private Camera _camera; private Transform _rightHeldObject; private Transform _leftHeldObject; private Vector3 _rightGrabStartLocalPos; private Vector3 _leftGrabStartLocalPos; private bool _isAnimating; private Dictionary _originalPositions = new Dictionary(); private Dictionary _originalRotations = new Dictionary(); private Dictionary _originalScales = new Dictionary(); public Transform RightHeldObject => _rightHeldObject; public Transform LeftHeldObject => _leftHeldObject; void Start() { _camera = Camera.main; _rightGrabStartLocalPos = _rightGrabPoint.localPosition; _leftGrabStartLocalPos = _leftGrabPoint.localPosition; } void Update() { if (_isAnimating) return; if (Input.GetKeyDown(KeyCode.R)) HandleRight(); if (Input.GetKeyDown(KeyCode.L)) HandleLeft(); } void HandleRight() { if (_rightHeldObject != null) { Release(_rightHeldObject, _rightGrabPoint, _rightGrabStartLocalPos, "RightOpen"); _rightHeldObject = null; return; } TryGrab(_rightGrabPoint, _rightGrabStartLocalPos, "RightGrab", true); } void HandleLeft() { if (_leftHeldObject != null) { Release(_leftHeldObject, _leftGrabPoint, _leftGrabStartLocalPos, "LeftOpen"); _leftHeldObject = null; return; } TryGrab(_leftGrabPoint, _leftGrabStartLocalPos, "LeftGrab", false); } void TryGrab(Transform grabPoint, Vector3 grabStartLocalPos, string animName, bool isRight) { Ray ray = _camera.ScreenPointToRay(Input.mousePosition); RaycastHit[] hits = Physics.RaycastAll(ray, _interactDistance, _interactMask); foreach (var hit in hits) { Transform target = FindTarget(hit.collider.transform); if (target == null) continue; StartCoroutine(ReachAndGrab(target, grabPoint, grabStartLocalPos, animName, isRight)); return; } } Transform FindTarget(Transform current) { while (current != null) { if (current.CompareTag("Target")) return current; if (current.CompareTag("Liquid")) return current; if (current.CompareTag("Breakable")) return current; current = current.parent; } return null; } IEnumerator ReachAndGrab(Transform target, Transform grabPoint, Vector3 grabStartLocalPos, string animName, bool isRight) { _isAnimating = true; NotifyScaleRemoved(target); _originalPositions[target] = target.position; _originalRotations[target] = target.rotation; _originalScales[target] = target.lossyScale; Vector3 direction = (target.position - grabPoint.position).normalized; Vector3 reachTargetPos = grabPoint.position + direction * _reachOffset; Vector3 startPos = grabPoint.position; float t = 0f; while (t < 1f) { t += Time.deltaTime * _reachSpeed; grabPoint.position = Vector3.Lerp(startPos, reachTargetPos, t); yield return null; } Vector3 originalScale = target.lossyScale; SetupPhysics(target, true); target.SetParent(grabPoint, true); Vector3 parentScale = grabPoint.lossyScale; Vector3 holdScale = originalScale * _holdScale; target.localScale = new Vector3( holdScale.x / parentScale.x, holdScale.y / parentScale.y, holdScale.z / parentScale.z ); target.rotation = Quaternion.Euler(0f, grabPoint.rotation.eulerAngles.y, 0f); if (target.CompareTag("Liquid")) target.localPosition = _liquidLocalOffset; else target.localPosition = Vector3.zero; if (isRight) _rightHeldObject = target; else _leftHeldObject = target; _animator.Play(animName); yield return ReturnGrabPoint(grabPoint, grabStartLocalPos); _isAnimating = false; } IEnumerator ReturnGrabPoint(Transform grabPoint, Vector3 startLocalPos) { Vector3 startPos = grabPoint.localPosition; float t = 0f; while (t < 1f) { t += Time.deltaTime * _reachSpeed; grabPoint.localPosition = Vector3.Lerp(startPos, startLocalPos, t); yield return null; } } void Release(Transform obj, Transform grabPoint, Vector3 grabStartLocalPos, string animName) { Ray ray = _camera.ScreenPointToRay(Input.mousePosition); bool placedOnScale = false; RaycastHit[] hits = Physics.RaycastAll(ray, _interactDistance); foreach (var hit in hits) { ScaleSurface scale = hit.collider.GetComponentInParent(); if (scale != null) { obj.SetParent(null, true); SetupPhysics(obj, false); if (_originalScales.ContainsKey(obj)) obj.localScale = _originalScales[obj]; scale.PlaceObject(obj); placedOnScale = true; break; } BalanceScale balance = hit.collider.GetComponentInParent(); if (balance != null) { obj.SetParent(null, true); SetupPhysics(obj, false); if (_originalScales.ContainsKey(obj)) obj.localScale = _originalScales[obj]; bool isLeft = Vector3.Dot( hit.point - balance.transform.position, balance.transform.right) < 0; if (isLeft) balance.PlaceLeft(obj); else balance.PlaceRight(obj); placedOnScale = true; break; } } if (!placedOnScale) { obj.SetParent(null, true); SetupPhysics(obj, false); if (_originalScales.ContainsKey(obj)) obj.localScale = _originalScales[obj]; if (obj.CompareTag("Liquid") && _originalPositions.ContainsKey(obj)) { float dist = Vector3.Distance(obj.position, _originalPositions[obj]); if (dist <= _liquidReturnDistance) { obj.position = _originalPositions[obj]; obj.rotation = _originalRotations[obj]; Rigidbody rb = obj.GetComponent(); if (rb != null) StartCoroutine(FreezeOnPlace(rb)); } } } _originalPositions.Remove(obj); _originalRotations.Remove(obj); _originalScales.Remove(obj); StartCoroutine(ReturnGrabPoint(grabPoint, grabStartLocalPos)); _animator.Play(animName); } private IEnumerator FreezeOnPlace(Rigidbody rb) { rb.isKinematic = true; yield return new WaitForSeconds(0.5f); rb.isKinematic = false; } void NotifyScaleRemoved(Transform obj) { MassController[] allMass = FindObjectsOfType(); foreach (var mc in allMass) mc.RemoveObject(obj.GetComponent()); ScaleSurface[] allSurfaces = FindObjectsOfType(); foreach (var surface in allSurfaces) surface.RemoveObject(obj); BalanceScale[] allBalances = FindObjectsOfType(); foreach (var balance in allBalances) balance.RemoveObject(obj); } void SetupPhysics(Transform obj, bool disable) { Rigidbody rb = obj.GetComponent(); Collider[] colliders = obj.GetComponentsInChildren(); if (rb != null) { rb.isKinematic = disable; rb.useGravity = !disable; } foreach (var col in colliders) col.enabled = !disable; } public bool IsHoldingAny() { return _rightHeldObject != null || _leftHeldObject != null; } }