first commit
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user