2026-04-13 스킬,퀵슬롯 시스템 수정중
This commit is contained in:
234
Assets/02_Scripts/UI/QuickSlot/QuickSlot.cs
Normal file
234
Assets/02_Scripts/UI/QuickSlot/QuickSlot.cs
Normal file
@@ -0,0 +1,234 @@
|
||||
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
|
||||
*/
|
||||
2
Assets/02_Scripts/UI/QuickSlot/QuickSlot.cs.meta
Normal file
2
Assets/02_Scripts/UI/QuickSlot/QuickSlot.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b986e78d40a4ec4ebe640f5dc4defdc
|
||||
41
Assets/02_Scripts/UI/QuickSlot/QuickSlotController.cs
Normal file
41
Assets/02_Scripts/UI/QuickSlot/QuickSlotController.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class QuickSlotController : MonoBehaviour
|
||||
{
|
||||
private QuickSlot[] _slots;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_slots = GetComponentsInChildren<QuickSlot>(true);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
foreach (var slot in _slots)
|
||||
{
|
||||
if (string.IsNullOrEmpty(slot.SlotName)) continue;
|
||||
QuickSlot captured = slot;
|
||||
GameManager.Instance.Level.BindSlotAction(captured.SlotName, (state) => captured.Execute(state));
|
||||
}
|
||||
}
|
||||
|
||||
// 외부(스킬창/SkillModule 등)에서 슬롯에 등록할 때 호출
|
||||
public void RegisterEntry(string slotName, UseableEntry entry)
|
||||
{
|
||||
foreach (var slot in _slots)
|
||||
{
|
||||
if (slot.SlotName == slotName)
|
||||
{
|
||||
slot.SetEntry(entry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public QuickSlot GetSlot(string slotName)
|
||||
{
|
||||
foreach (var slot in _slots)
|
||||
if (slot.SlotName == slotName) return slot;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0150a6644db52824c9b546e9f34c221d
|
||||
Reference in New Issue
Block a user