176 lines
6.6 KiB
C#
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) { }
|
|
}
|