Files
PIM_6/Assets/_Scripts/DraggableObject.cs
T

268 lines
6.3 KiB
C#
Raw Normal View History

2026-06-04 14:27:16 +03:00
using UnityEngine;
[RequireComponent(typeof(Collider))]
public class DraggableObject : MonoBehaviour
{
[Header("Item")]
public ItemType itemType;
public Material normalMaterial;
public Material highlightMaterial;
public Material dragMaterial;
public Material inactiveMaterial;
public Vector3 startPosition;
public bool isLocked;
[Header("Drag")]
public float dragLift = 0.35f;
public float snapDistance = 1.25f;
public bool isInteractable = true;
private Camera mainCamera;
private Renderer itemRenderer;
private Plane dragPlane;
private Vector3 dragOffset;
private Vector3 returnPosition;
private Slot highlightedSlot;
private bool isDragging;
private bool isMouseOver;
private void Awake()
{
mainCamera = Camera.main;
itemRenderer = GetComponent<Renderer>();
startPosition = transform.position;
returnPosition = startPosition;
if (itemRenderer != null && normalMaterial == null)
{
normalMaterial = itemRenderer.sharedMaterial;
}
}
private void Start()
{
ApplyNormalMaterial();
}
private void OnMouseEnter()
{
if (!CanInteract())
{
return;
}
isMouseOver = true;
ApplyMaterial(highlightMaterial);
}
private void OnMouseExit()
{
isMouseOver = false;
if (!isDragging && !isLocked)
{
ApplyNormalMaterial();
}
}
private void OnMouseDown()
{
if (!CanInteract())
{
return;
}
if (mainCamera == null)
{
mainCamera = Camera.main;
}
// Запоминаем позицию перед перетаскиванием, чтобы вернуть предмет при ошибке.
returnPosition = transform.position;
dragPlane = new Plane(Vector3.up, new Vector3(0f, startPosition.y, 0f));
if (TryGetMousePointOnDragPlane(out Vector3 mouseWorldPoint))
{
dragOffset = transform.position - mouseWorldPoint;
dragOffset.y = 0f;
}
else
{
dragOffset = Vector3.zero;
}
isDragging = true;
ApplyMaterial(dragMaterial != null ? dragMaterial : highlightMaterial);
}
private void OnMouseDrag()
{
if (!isDragging || !CanInteract())
{
return;
}
if (!TryGetMousePointOnDragPlane(out Vector3 mouseWorldPoint))
{
return;
}
// Offset сохраняет относительное положение курсора и предмета без резких скачков.
Vector3 targetPosition = mouseWorldPoint + dragOffset;
targetPosition.y = startPosition.y + dragLift;
transform.position = targetPosition;
UpdateSlotPreview();
}
private void OnMouseUp()
{
if (!isDragging)
{
return;
}
isDragging = false;
// Предмет фиксируется только в ближайшем подходящем слоте.
Slot nearestSlot = FindNearestSlot();
ClearHighlightedSlot();
if (nearestSlot != null && nearestSlot.CanAccept(this))
{
nearestSlot.PlaceItem(this);
return;
}
transform.position = returnPosition;
ApplyNormalMaterial();
}
public void LockToSlot(Vector3 slotPosition)
{
isLocked = true;
isInteractable = false;
transform.position = slotPosition;
ApplyNormalMaterial();
}
public void SetInteractable(bool value)
{
if (isLocked)
{
isInteractable = false;
return;
}
isInteractable = value;
ApplyNormalMaterial();
}
public void SetNormalMaterial(Material material)
{
normalMaterial = material;
ApplyNormalMaterial();
}
private bool CanInteract()
{
return isInteractable && !isLocked;
}
private bool TryGetMousePointOnDragPlane(out Vector3 worldPoint)
{
worldPoint = Vector3.zero;
if (mainCamera == null)
{
return false;
}
// Raycast в математическую плоскость делает drag стабильным для ортографической камеры.
Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
if (!dragPlane.Raycast(ray, out float distance))
{
return false;
}
worldPoint = ray.GetPoint(distance);
return true;
}
private void UpdateSlotPreview()
{
Slot nearestSlot = FindNearestSlot();
if (nearestSlot == highlightedSlot)
{
return;
}
ClearHighlightedSlot();
highlightedSlot = nearestSlot;
if (highlightedSlot != null)
{
highlightedSlot.ShowPreview(this);
}
}
private Slot FindNearestSlot()
{
Slot[] slots = FindObjectsOfType<Slot>();
Slot nearest = null;
float bestDistance = snapDistance;
foreach (Slot slot in slots)
{
float distance = Vector3.Distance(transform.position, slot.transform.position);
if (distance <= bestDistance)
{
bestDistance = distance;
nearest = slot;
}
}
return nearest;
}
private void ClearHighlightedSlot()
{
if (highlightedSlot != null)
{
highlightedSlot.ClearPreview();
highlightedSlot = null;
}
}
private void ApplyNormalMaterial()
{
if (isLocked)
{
ApplyMaterial(normalMaterial);
return;
}
if (isMouseOver && CanInteract())
{
ApplyMaterial(highlightMaterial);
return;
}
if (!isInteractable && inactiveMaterial != null)
{
ApplyMaterial(inactiveMaterial);
return;
}
ApplyMaterial(normalMaterial);
}
private void ApplyMaterial(Material material)
{
if (itemRenderer != null && material != null)
{
itemRenderer.sharedMaterial = material;
}
}
}