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

176 lines
6.6 KiB
C#

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// Drag & Drop предметов между слотами.
/// Один общий DragIconCanvas на все слоты — создаётся при первом старте.
/// </summary>
public class InventoryDragDrop : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerEnterHandler, IPointerExitHandler
{
public Slot slot;
public int slotIndex;
[SerializeField] private float dragIconScale = 1.2f;
[SerializeField] private bool allowDropOutside = true;
[SerializeField] private float dropThreshold = 100f;
// ── Общий на все слоты drag-icon ─────────────────────────────────────────
private static GameObject _sharedCanvas;
private static RectTransform _sharedIcon;
private static Image _sharedImage;
// ── Per-instance поля ─────────────────────────────────────────────────────
private CanvasGroup _canvasGroup;
private bool _isDragging;
private RectTransform _inventoryPanelRect;
// ── Unity lifecycle ───────────────────────────────────────────────────────
void Start()
{
_canvasGroup = GetComponent<CanvasGroup>();
// Создаём общий DragCanvas один раз для всех слотов
if (_sharedCanvas == null)
CreateSharedDragCanvas();
// Ищем rect инвентарной панели для определения «вне панели»
if (InventoryManager.Instance?.inventoryPanel != null)
_inventoryPanelRect = InventoryManager.Instance.inventoryPanel
.GetComponent<RectTransform>();
}
private static void CreateSharedDragCanvas()
{
_sharedCanvas = new GameObject("DragIconCanvas");
DontDestroyOnLoad(_sharedCanvas);
var c = _sharedCanvas.AddComponent<Canvas>();
c.renderMode = RenderMode.ScreenSpaceOverlay;
c.sortingOrder = 1000;
c.overrideSorting = true;
_sharedCanvas.AddComponent<GraphicRaycaster>();
var iconGO = new GameObject("DragIcon");
iconGO.transform.SetParent(_sharedCanvas.transform, false);
_sharedIcon = iconGO.AddComponent<RectTransform>();
_sharedIcon.sizeDelta = new Vector2(48, 48);
_sharedImage = iconGO.AddComponent<Image>();
_sharedImage.raycastTarget = false;
iconGO.SetActive(false);
}
// ── Drag handlers ─────────────────────────────────────────────────────────
public void OnBeginDrag(PointerEventData eventData)
{
if (slot == null || slot.IsEmpty) return;
_isDragging = true;
if (_canvasGroup != null)
_canvasGroup.blocksRaycasts = false;
if (_sharedIcon != null && slot.item?.icon != null)
{
_sharedImage.sprite = slot.item.icon;
_sharedIcon.localScale = Vector3.one * dragIconScale;
_sharedIcon.position = eventData.position;
_sharedIcon.gameObject.SetActive(true);
}
var bg = GetComponent<Image>();
if (bg != null)
bg.color = new Color(1f, 1f, 1f, 0.5f);
}
public void OnDrag(PointerEventData eventData)
{
if (!_isDragging || _sharedIcon == null) return;
_sharedIcon.position = eventData.position;
}
public void OnEndDrag(PointerEventData eventData)
{
if (!_isDragging) return;
_isDragging = false;
if (_sharedIcon != null)
{
_sharedIcon.gameObject.SetActive(false);
_sharedIcon.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);
// Сначала ищем целевой слот (инвентарь ИЛИ хотбар)
var raycastResults = new System.Collections.Generic.List<RaycastResult>();
EventSystem.current.RaycastAll(eventData, raycastResults);
Slot targetSlot = null;
foreach (var result in raycastResults)
{
var s = result.gameObject.GetComponent<Slot>();
if (s != null && s != slot) { targetSlot = s; break; }
}
if (targetSlot != null)
{
// Нашли слот — перемещаем (работает и для хотбара)
InventoryManager.Instance?.SwapSlots(slot, targetSlot);
return;
}
// Слот не найден — выбросить в мир если за пределами инвентаря
if (allowDropOutside && IsOutsideInventory(eventData.position))
DropItemOutside();
}
// ── Вспомогательные ───────────────────────────────────────────────────────
private bool IsOutsideInventory(Vector2 screenPos)
{
if (_inventoryPanelRect == null) return false;
Vector3[] corners = new Vector3[4];
_inventoryPanelRect.GetWorldCorners(corners);
return screenPos.x < corners[0].x - dropThreshold ||
screenPos.x > corners[2].x + dropThreshold ||
screenPos.y < corners[0].y - dropThreshold ||
screenPos.y > corners[2].y + dropThreshold;
}
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);
slot.Clear();
Debug.Log($"[DragDrop] Выброшен: {item.itemName} x{amount}");
}
public void OnPointerEnter(PointerEventData eventData) { }
public void OnPointerExit(PointerEventData eventData) { }
}