Files
my-project/Assets/Inventory/Scripts/InventoryDragDrop.cs
2026-04-15 02:56:11 +05:00

236 lines
7.7 KiB
C#

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// Обработка Drag & Drop предметов между слотами и выброс за пределы инвентаря
/// </summary>
public class InventoryDragDrop : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerEnterHandler, IPointerExitHandler
{
public Slot slot;
public int slotIndex;
[Header("Drag Visual")]
[SerializeField] private RectTransform dragIcon;
[SerializeField] private Image dragImage;
[SerializeField] private float dragIconScale = 1.2f;
[Header("Drop Settings")]
[SerializeField] private bool allowDropOutside = true;
[SerializeField] private float dropThreshold = 100f; // пикселей за пределами инвентаря
private Canvas _canvas;
private CanvasGroup _canvasGroup;
private bool _isDragging;
private Vector2 _dragStartPosition;
private RectTransform _inventoryPanelRect;
void Start()
{
_canvas = GetComponentInParent<Canvas>();
_canvasGroup = GetComponent<CanvasGroup>();
if (dragIcon == null)
{
CreateDragIcon();
}
// Находим панель инвентаря
var inventoryManager = FindObjectOfType<InventoryManager>();
if (inventoryManager?.inventoryPanel != null)
{
_inventoryPanelRect = inventoryManager.inventoryPanel.GetComponent<RectTransform>();
}
if (dragIcon != null)
dragIcon.gameObject.SetActive(false);
}
private void CreateDragIcon()
{
GameObject canvasGO = new GameObject("DragIconCanvas");
canvasGO.transform.SetParent(_canvas != null ? _canvas.transform : transform.root);
var dragCanvas = canvasGO.AddComponent<Canvas>();
dragCanvas.overrideSorting = true;
dragCanvas.sortingOrder = 1000;
dragCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvasGO.AddComponent<GraphicRaycaster>();
dragIcon = new GameObject("DragIcon").AddComponent<RectTransform>();
dragIcon.SetParent(canvasGO.transform, false);
dragIcon.sizeDelta = new Vector2(48, 48);
dragImage = dragIcon.gameObject.AddComponent<Image>();
dragImage.raycastTarget = false;
dragIcon.gameObject.SetActive(false);
}
public void OnBeginDrag(PointerEventData eventData)
{
if (slot == null || slot.IsEmpty) return;
_isDragging = true;
_dragStartPosition = eventData.position;
if (_canvasGroup != null)
_canvasGroup.blocksRaycasts = false;
if (dragIcon != null && slot.item?.icon != null)
{
dragIcon.gameObject.SetActive(true);
dragImage.sprite = slot.item.icon;
dragIcon.localScale = Vector3.one * dragIconScale;
dragIcon.position = eventData.position;
}
var bg = GetComponent<Image>();
if (bg != null)
bg.color = new Color(1f, 1f, 1f, 0.5f);
}
public void OnDrag(PointerEventData eventData)
{
if (!_isDragging || dragIcon == null) return;
dragIcon.position = eventData.position;
}
public void OnEndDrag(PointerEventData eventData)
{
if (!_isDragging) return;
_isDragging = false;
if (dragIcon != null)
{
dragIcon.gameObject.SetActive(false);
dragIcon.localScale = Vector3.one;
}
if (_canvasGroup != null)
_canvasGroup.blocksRaycasts = true;
var bg = GetComponent<Image>();
if (bg != null)
bg.color = slot.IsEmpty ? new Color(0.25f, 0.25f, 0.25f, 0.8f) : new Color(0.35f, 0.35f, 0.35f, 0.95f);
// Проверяем - выбросили за пределы инвентаря?
if (allowDropOutside && IsOutsideInventory(eventData.position))
{
DropItemOutside();
return;
}
// Иначе - пробуем переместить в другой слот
var raycastResults = new System.Collections.Generic.List<RaycastResult>();
EventSystem.current.RaycastAll(eventData, raycastResults);
Slot targetSlot = null;
foreach (var result in raycastResults)
{
var slotComponent = result.gameObject.GetComponent<Slot>();
if (slotComponent != null && slotComponent != slot)
{
targetSlot = slotComponent;
break;
}
}
if (targetSlot != null && InventoryManager.Instance != null)
{
InventoryManager.Instance.SwapSlots(slotIndex, targetSlot.slotIndex);
}
}
private bool IsOutsideInventory(Vector2 screenPosition)
{
if (_inventoryPanelRect == null) return false;
// Получаем границы панели инвентаря в экранных координатах
Vector3[] corners = new Vector3[4];
_inventoryPanelRect.GetWorldCorners(corners);
// rect панели в экранных координатах
float minX = corners[0].x;
float maxX = corners[2].x;
float minY = corners[0].y;
float maxY = corners[2].y;
// Добавляем отступ
float threshold = dropThreshold;
// Проверяем находится ли курсор за пределами панели + threshold
bool outside = screenPosition.x < minX - threshold ||
screenPosition.x > maxX + threshold ||
screenPosition.y < minY - threshold ||
screenPosition.y > maxY + threshold;
return outside;
}
private void DropItemOutside()
{
if (slot == null || slot.IsEmpty) return;
var item = slot.item;
int amount = slot.amount;
// Определяем позицию выброса
Vector3 dropPos = Vector3.zero;
if (Camera.main != null)
{
// Выбрасываем перед игроком
dropPos = Camera.main.transform.position + Camera.main.transform.forward * 2f;
}
// Создаём предмет в мире
if (InventoryItemFactory.Instance != null)
{
InventoryItemFactory.Instance.DropItem(item, amount, dropPos);
}
else
{
// Fallback - просто создаём объект
var go = new GameObject($"Dropped_{item.itemName}");
go.transform.position = dropPos;
var visual = GameObject.CreatePrimitive(PrimitiveType.Cube);
visual.transform.SetParent(go.transform);
visual.transform.localPosition = Vector3.zero;
visual.transform.localScale = Vector3.one * 0.3f;
DestroyImmediate(visual.GetComponent<Collider>());
var worldItem = go.AddComponent<WorldItem>();
worldItem.Setup(item, amount);
}
// Очищаем слот
slot.Clear();
Debug.Log($"[DragDrop] Выброшен: {item.itemName} x{amount}");
}
public void OnPointerEnter(PointerEventData eventData)
{
if (!_isDragging && slot != null && !slot.IsEmpty)
{
var bg = GetComponent<Image>();
if (bg != null)
bg.color = new Color(0.5f, 0.5f, 0.5f, 1f);
}
}
public void OnPointerExit(PointerEventData eventData)
{
if (!_isDragging && slot != null)
{
var bg = GetComponent<Image>();
if (bg != null)
bg.color = slot.IsEmpty ?
new Color(0.25f, 0.25f, 0.25f, 0.8f) :
new Color(0.35f, 0.35f, 0.35f, 0.95f);
}
}
}