261 lines
8.8 KiB
C#
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;
|
|
}
|
|
}
|