Files
Genesis_Unity/Assets/02_Scripts/UI/QuickSlot/QuickSlot.cs

234 lines
8.1 KiB
C#

using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class QuickSlot : MonoBehaviour, IDropHandler, IPointerClickHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
{
[Header("Slot Binding")]
public string SlotName; // "UseSlot_Q", "UseSlot_E", ...
[Header("References")]
[SerializeField] private Image _iconImage;
[SerializeField] private TextMeshProUGUI _stackText;
public UseableEntry CurrentEntry { get; private set; }
private Transform _dragTransform;
private Transform _iconOriginalParent;
private CanvasGroup _iconCanvasGroup;
private void Awake()
{
if (_iconImage != null)
{
_iconCanvasGroup = _iconImage.GetComponent<CanvasGroup>();
if (_iconCanvasGroup == null)
_iconCanvasGroup = _iconImage.gameObject.AddComponent<CanvasGroup>();
}
ClearSlot();
}
private void Start()
{
_dragTransform = GameManager.Instance.InGameUI.DragCanvas;
}
private void Update()
{
// 아이템 퀵슬롯은 인벤토리와 동기화: 스택 수량 갱신 및 소진 시 자동 해제
if (CurrentEntry is Item itemData)
{
int total = GameManager.Instance.Inventory.GetTotalStack(itemData);
if (total <= 0)
{
ClearSlot();
return;
}
if (_stackText != null)
{
_stackText.text = itemData.IsStackable ? total.ToString() : "";
_stackText.enabled = itemData.IsStackable;
}
}
else if (_stackText != null)
{
_stackText.enabled = false;
}
}
public void SetEntry(UseableEntry entry)
{
CurrentEntry = entry;
UpdateUI();
}
public void ClearSlot()
{
CurrentEntry = null;
if (_iconImage != null)
{
_iconImage.sprite = null;
_iconImage.enabled = false;
}
if (_stackText != null)
{
_stackText.text = "";
_stackText.enabled = false;
}
}
public void UpdateUI()
{
if (CurrentEntry == null || CurrentEntry.Icon == null)
{
if (_iconImage != null) _iconImage.enabled = false;
return;
}
_iconImage.sprite = CurrentEntry.Icon;
_iconImage.enabled = true;
}
public void Execute(InputState state)
{
if (CurrentEntry == null) return;
if (CurrentEntry is Item itemData)
{
// 아이템은 누를 때(Started)만 사용
if (state == InputState.Started)
GameManager.Instance.Inventory.UseItemByData(itemData);
}
else if (CurrentEntry is SkillData skillData)
{
// 스킬은 Started/Canceled 모두 전달 (Charge/Channel 지원)
var character = GameManager.Instance.Level.CurrentCharacter;
if (character == null) return;
if (character.TryGetComponent<SkillModule>(out var skillModule))
skillModule.SkillInputByData(skillData, state);
}
}
public void OnDrop(PointerEventData eventData)
{
GameObject dropped = eventData.pointerDrag;
if (dropped == null) return;
// 인벤토리에서 드래그
InventoryItemControl invItem = dropped.GetComponent<InventoryItemControl>();
if (invItem != null && invItem.ParentSlot != null && invItem.ParentSlot.currentItem != null)
{
SetEntry(invItem.ParentSlot.currentItem.Data);
return;
}
// 다른 퀵슬롯에서 드래그 → 스왑
QuickSlot otherSlot = dropped.GetComponent<QuickSlot>();
if (otherSlot != null && otherSlot != this)
{
UseableEntry temp = CurrentEntry;
SetEntry(otherSlot.CurrentEntry);
otherSlot.SetEntry(temp);
}
// 스킬창에서 드래그는 스킬창 제작 시 SkillEntryControl 같은 컴포넌트를 여기서 감지
}
public void OnPointerClick(PointerEventData eventData)
{
if (eventData.button == PointerEventData.InputButton.Right)
{
ClearSlot();
}
}
public void OnBeginDrag(PointerEventData eventData)
{
if (CurrentEntry == null || _iconImage == null)
{
eventData.pointerDrag = null;
return;
}
_iconOriginalParent = _iconImage.transform.parent;
_iconImage.transform.SetParent(_dragTransform);
_iconCanvasGroup.blocksRaycasts = false;
}
public void OnDrag(PointerEventData eventData)
{
if (_iconImage == null) return;
_iconImage.transform.position = eventData.position;
}
public void OnEndDrag(PointerEventData eventData)
{
_iconCanvasGroup.blocksRaycasts = true;
_iconImage.transform.SetParent(_iconOriginalParent);
_iconImage.transform.localPosition = Vector3.zero;
UpdateUI();
}
}
/*
변경 요약
1. 입력 시스템 (InputManager, LevelManager)
추가: OnKeyDown_KKeyEvent 이벤트 + OnKeyDown_KKey 핸들러 → K 키로 스킬창 열기
연결: LevelManager에서 K키 → InGameUIManager.SkillWindowToggle에 구독
요구사항: Input Action Asset의 Character와 InGameUI 맵 양쪽에 OnKeyDown_KKey 액션 추가 필요
2. SkillModule (자동 바인딩 제거)
Before: 무기 장착 시 Q/E/R/T가 InputManager에 직접 바인딩 → 누르면 바로 발동
After: 무기 장착 시 _equippedSkills만 채움. 키와는 무관. 퀵슬롯에 등록된 스킬만 발동됨
SkillSlotNames, _skillBindData, DispatchSlot, RegisterSkillsToQuickslot, BindSlotAction 호출 → 삭제
_equippedSkillDataCache[] + GetEquippedSkillDatas() → 추가 (스킬창에서 목록 표시용)
3. QuickSlot (각 슬롯에 부착)
역할: 슬롯 1칸. 스킬이든 아이템이든 UseableEntry 하나를 담음
SlotName 인스펙터 필드 (UseSlot_Q~UseSlot_G)
SetEntry(entry) / ClearSlot() / Execute(state)
Execute(): 담긴 게 Item이면 Inventory.UseItemByData(), SkillData면 SkillModule.SkillInputByData()
드래그 처리:
OnDrop: 인벤토리 아이템 드롭 받으면 등록, 다른 퀵슬롯이면 스왑
OnBeginDrag/OnEndDrag: 자기 슬롯 아이콘을 마우스 따라다니게 (인벤토리와 동일)
OnPointerClick 우클릭: 해제
Update(): 아이템이면 인벤토리 잔여 수량 동기화, 0이면 자동 해제
4. QuickSlotController (QuickSlotRoot에 부착)
역할: 슬롯들 묶어서 입력 시스템에 일괄 등록
Awake(): 자식 QuickSlot[] 수집
Start(): 각 슬롯의 SlotName으로 LevelManager.BindSlotAction() 호출 → 키 누르면 → slot.Execute(state)
RegisterEntry(slotName, entry) / GetSlot(slotName) 외부 API
5. InventoryManager (퀵슬롯 동기화 메서드 추가)
GetTotalStack(Item): 인벤토리 전체에서 해당 아이템 총 개수 (퀵슬롯 카운트 표시용)
UseItemByData(Item): 데이터 기반으로 가장 앞 슬롯의 아이템 사용
6. 스킬창 신규
SkillEntry: 스킬창 한 줄. IBeginDragHandler/IDragHandler/IEndDragHandler로 드래그 → 마우스 위 퀵슬롯에 SetEntry() 호출. 본체는 항상 제자리 복귀
SkillWindowUI: Toggle() 호출되면 활성/비활성 + Refresh()로 SkillModule.GetEquippedSkillDatas() 받아 엔트리 인스턴싱
7. InGameUIManager
QuickSlot, SkillWindow 참조 필드 추가
SkillWindowToggle(): 스킬창 켜고 끄면서 인벤토리와 동일하게 커서/입력맵 토글
데이터 흐름
키 입력 → 발동
키보드 Q
→ InputManager.OnKeyDown_SlotQEvent
→ LevelManager.BindSlotAction이 등록한 콜백
→ QuickSlot.Execute(state)
→ 분기: Item → Inventory.UseItemByData
SkillData → SkillModule.SkillInputByData
스킬 등록
K키 → SkillWindow 열림 → SkillModule에서 장착 스킬 목록 받아 표시
사용자가 SkillEntry 드래그 → QuickSlot 위에서 놓음
QuickSlot.SetEntry(skillData) → 아이콘 갱신
아이템 등록
인벤토리 슬롯의 InventoryItemControl 드래그
QuickSlot.OnDrop → SetEntry(itemData)
인벤토리에서 아이템 소진되면 QuickSlot.Update가 감지해서 자동 ClearSlot
*/