Files
ScienceLab.WeightAndMass/Assets/Scripts/HandController.cs
2026-04-07 03:14:32 +03:00

261 lines
8.8 KiB
C#

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<Transform, Vector3> _originalPositions = new Dictionary<Transform, Vector3>();
private Dictionary<Transform, Quaternion> _originalRotations = new Dictionary<Transform, Quaternion>();
private Dictionary<Transform, Vector3> _originalScales = new Dictionary<Transform, Vector3>();
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<ScaleSurface>();
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<BalanceScale>();
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<Rigidbody>();
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<MassController>();
foreach (var mc in allMass)
mc.RemoveObject(obj.GetComponent<Rigidbody>());
ScaleSurface[] allSurfaces = FindObjectsOfType<ScaleSurface>();
foreach (var surface in allSurfaces)
surface.RemoveObject(obj);
BalanceScale[] allBalances = FindObjectsOfType<BalanceScale>();
foreach (var balance in allBalances)
balance.RemoveObject(obj);
}
void SetupPhysics(Transform obj, bool disable)
{
Rigidbody rb = obj.GetComponent<Rigidbody>();
Collider[] colliders = obj.GetComponentsInChildren<Collider>();
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;
}
}