using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; /// /// Обработка Drag & Drop предметов между слотами и выброс за пределы инвентаря /// 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(); _canvasGroup = GetComponent(); if (dragIcon == null) { CreateDragIcon(); } // Находим панель инвентаря var inventoryManager = FindObjectOfType(); if (inventoryManager?.inventoryPanel != null) { _inventoryPanelRect = inventoryManager.inventoryPanel.GetComponent(); } 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(); dragCanvas.overrideSorting = true; dragCanvas.sortingOrder = 1000; dragCanvas.renderMode = RenderMode.ScreenSpaceOverlay; canvasGO.AddComponent(); dragIcon = new GameObject("DragIcon").AddComponent(); dragIcon.SetParent(canvasGO.transform, false); dragIcon.sizeDelta = new Vector2(48, 48); dragImage = dragIcon.gameObject.AddComponent(); 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(); 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(); 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(); EventSystem.current.RaycastAll(eventData, raycastResults); Slot targetSlot = null; foreach (var result in raycastResults) { var slotComponent = result.gameObject.GetComponent(); 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()); var worldItem = go.AddComponent(); 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(); 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(); 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); } } }