2026-04-13 스킬,퀵슬롯 시스템 수정중
This commit is contained in:
@@ -43,6 +43,7 @@ public class InputManager : MonoBehaviour
|
||||
public event Action OnKeyDown_DownArrowEvent;
|
||||
public event Action OnKeyDown_EnterEvent;
|
||||
public event Action OnKeyDown_IKeyEvent;
|
||||
public event Action OnKeyDown_KKeyEvent;
|
||||
|
||||
|
||||
|
||||
@@ -94,6 +95,7 @@ public void SetUIInputMap(string mapName)
|
||||
BindActionUI("OnKeyDown_DownArrow", OnKeyDown_DownArrow);
|
||||
BindActionUI("OnKeyDown_Enter", OnKeyDown_Enter);
|
||||
BindActionUI("OnkeyDown_IKey", OnKeyDown_IKey);
|
||||
BindActionUI("OnKeyDown_KKey", OnKeyDown_KKey);
|
||||
|
||||
_uiInputActionMap.Disable();
|
||||
|
||||
@@ -128,6 +130,7 @@ public void SetCharacterInputMap(string mapName)
|
||||
BindActionCharacter("Interaction", OnInteraction);
|
||||
|
||||
BindActionCharacter("OnkeyDown_IKey", OnKeyDown_IKey);
|
||||
BindActionCharacter("OnKeyDown_KKey", OnKeyDown_KKey);
|
||||
|
||||
|
||||
_characterInputActionMap.Disable();
|
||||
@@ -307,5 +310,11 @@ private void OnKeyDown_IKey(InputAction.CallbackContext ctx)
|
||||
if(ctx.started)
|
||||
OnKeyDown_IKeyEvent?.Invoke();
|
||||
}
|
||||
|
||||
private void OnKeyDown_KKey(InputAction.CallbackContext ctx)
|
||||
{
|
||||
if (ctx.started)
|
||||
OnKeyDown_KKeyEvent?.Invoke();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -385,4 +385,31 @@ public void ItemUse(int slotIndex)
|
||||
}
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
// 퀵슬롯 동기화용: 해당 아이템 데이터의 총 보유량 합산 (0이면 퀵슬롯에서 자동 해제)
|
||||
public int GetTotalStack(Item itemData)
|
||||
{
|
||||
if (itemData == null) return 0;
|
||||
int total = 0;
|
||||
for (int i = 0; i < Items.Length; i++)
|
||||
{
|
||||
if (Items[i] != null && Items[i].Data == itemData)
|
||||
total += Items[i].CurrentStack;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// 퀵슬롯에서 아이템 사용 시 호출: 해당 데이터가 담긴 가장 앞 슬롯을 사용
|
||||
public void UseItemByData(Item itemData)
|
||||
{
|
||||
if (itemData == null) return;
|
||||
for (int i = 0; i < Items.Length; i++)
|
||||
{
|
||||
if (Items[i] != null && Items[i].Data == itemData)
|
||||
{
|
||||
ItemUse(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@ public void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
|
||||
//UI매핑
|
||||
InputManager.Instance.OnKeyDown_IKeyEvent += GameManager.Instance.InGameUI.InventoryToggle;
|
||||
InputManager.Instance.OnKeyDown_KKeyEvent += GameManager.Instance.InGameUI.SkillWindowToggle;
|
||||
|
||||
//화면 켜기
|
||||
}
|
||||
@@ -204,6 +205,7 @@ private void OnDestroy()
|
||||
if(GameManager.Instance != null)
|
||||
{
|
||||
InputManager.Instance.OnKeyDown_IKeyEvent -= GameManager.Instance.InGameUI.InventoryToggle;
|
||||
InputManager.Instance.OnKeyDown_KKeyEvent -= GameManager.Instance.InGameUI.SkillWindowToggle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ public class InGameUIManager : BaseUIManager
|
||||
public TooltipUI Tooltip;
|
||||
public Transform DragCanvas;
|
||||
public GameObject InventoryRoot;
|
||||
public QuickSlotController QuickSlot;
|
||||
public SkillWindowUI SkillWindow;
|
||||
|
||||
[SerializeField] private GameObject _crosshairRoot;
|
||||
|
||||
@@ -32,6 +34,26 @@ public void InventoryToggle()
|
||||
InventoryOnOff(!InventoryRoot.activeSelf);
|
||||
}
|
||||
|
||||
public void SkillWindowToggle()
|
||||
{
|
||||
if (SkillWindow == null) return;
|
||||
SkillWindow.Toggle();
|
||||
// 인벤토리와 동일한 커서/입력 모드 규칙 적용
|
||||
bool anyOpen = InventoryRoot.activeSelf || SkillWindow.gameObject.activeSelf;
|
||||
if (anyOpen)
|
||||
{
|
||||
InputManager.Instance.ActiveOnlyOneActionMap("InGameUI");
|
||||
Cursor.lockState = CursorLockMode.None;
|
||||
Cursor.visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
InputManager.Instance.ActiveOnlyOneActionMap("Character");
|
||||
Cursor.lockState = CursorLockMode.Locked;
|
||||
Cursor.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void InventoryOnOff(bool isOn)
|
||||
{
|
||||
InventoryRoot.SetActive(isOn);
|
||||
|
||||
@@ -18,6 +18,7 @@ public class SkillModule : MonoBehaviour
|
||||
|
||||
private SkillInstance[] _equippedSkills;
|
||||
private ISkillEffect[] _skillEffects;
|
||||
private SkillData[] _equippedSkillDataCache; // 장착 순서 보존용
|
||||
|
||||
// 차지
|
||||
private int _chargingSlot = -1;
|
||||
@@ -49,13 +50,6 @@ public class SkillModule : MonoBehaviour
|
||||
|
||||
private Dictionary<WeaponType, WeaponSkillSet> _dicSkills = new Dictionary<WeaponType, WeaponSkillSet>();
|
||||
|
||||
// 슬롯 이름 → 런타임 매핑 (Dispatcher 테이블)
|
||||
private Dictionary<string, IUseableRuntime> _skillBindData = new Dictionary<string, IUseableRuntime>();
|
||||
|
||||
// 퀵슬롯에 바인딩할 슬롯 이름 목록 (스킬용)
|
||||
private static readonly string[] SkillSlotNames =
|
||||
{ "UseSlot_Q", "UseSlot_E", "UseSlot_R", "UseSlot_T" };
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_stateMachine = GetComponent<PlayerStateMachine>();
|
||||
@@ -64,6 +58,7 @@ private void Awake()
|
||||
|
||||
_equippedSkills = new SkillInstance[_maxSlots];
|
||||
_skillEffects = new ISkillEffect[_maxSlots];
|
||||
_equippedSkillDataCache = new SkillData[_maxSlots];
|
||||
}
|
||||
|
||||
private void Start()
|
||||
@@ -74,46 +69,17 @@ private void Start()
|
||||
_dicSkills[wss.WeaponType] = wss;
|
||||
}
|
||||
|
||||
// 현재 장착 무기의 스킬셋 로드 (무기 미장착 시 스킬 없음)
|
||||
// 현재 장착 무기의 스킬셋만 "장착 상태"로 로드 (퀵슬롯 자동 바인딩 없음)
|
||||
if (_equippedWeapon != null
|
||||
&& _dicSkills.TryGetValue(_equippedWeapon.WType, out WeaponSkillSet out_wss)
|
||||
&& out_wss != null)
|
||||
{
|
||||
LoadWeaponSkills(out_wss);
|
||||
RegisterSkillsToQuickslot(out_wss);
|
||||
}
|
||||
|
||||
// 각 슬롯에 dispatcher를 한 번만 바인딩 (이후 내용물만 _skillBindData로 교체)
|
||||
foreach (string slotName in SkillSlotNames)
|
||||
{
|
||||
string captured = slotName;
|
||||
GameManager.Instance.Level.BindSlotAction(captured, (state) => DispatchSlot(captured, state));
|
||||
}
|
||||
}
|
||||
|
||||
private void DispatchSlot(string slotName, InputState state)
|
||||
{
|
||||
if (!_skillBindData.TryGetValue(slotName, out IUseableRuntime runtime) || runtime == null)
|
||||
return;
|
||||
|
||||
runtime.Execute(new UseContext
|
||||
{
|
||||
Caster = gameObject,
|
||||
Target = null,
|
||||
UseInputState = state
|
||||
});
|
||||
}
|
||||
|
||||
private void RegisterSkillsToQuickslot(WeaponSkillSet wss)
|
||||
{
|
||||
for (int i = 0; i < SkillSlotNames.Length; i++)
|
||||
{
|
||||
if (i < wss.Skills.Count && wss.Skills[i] != null)
|
||||
_skillBindData[SkillSlotNames[i]] = wss.Skills[i].CreateRuntime();
|
||||
else
|
||||
_skillBindData.Remove(SkillSlotNames[i]);
|
||||
}
|
||||
}
|
||||
// 장착된 스킬 목록 반환 (스킬창 UI에서 사용)
|
||||
public IReadOnlyList<SkillData> GetEquippedSkillDatas() => _equippedSkillDataCache;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
@@ -133,11 +99,13 @@ public void LoadWeaponSkills(WeaponSkillSet skillSet)
|
||||
{
|
||||
_equippedSkills[i] = new SkillInstance(skillSet.Skills[i]);
|
||||
_skillEffects[i] = ResolveEffect(skillSet.Skills[i].TargetType);
|
||||
_equippedSkillDataCache[i] = skillSet.Skills[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
_equippedSkills[i] = null;
|
||||
_skillEffects[i] = null;
|
||||
_equippedSkillDataCache[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
8
Assets/02_Scripts/UI/QuickSlot.meta
Normal file
8
Assets/02_Scripts/UI/QuickSlot.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 619b9f88d08881b4cb478bfd9f27c22a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
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
|
||||
8
Assets/02_Scripts/UI/SkillWindow.meta
Normal file
8
Assets/02_Scripts/UI/SkillWindow.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f4e891fc20854a4f9c63c81ade744c6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
77
Assets/02_Scripts/UI/SkillWindow/SkillEntry.cs
Normal file
77
Assets/02_Scripts/UI/SkillWindow/SkillEntry.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class SkillEntry : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
|
||||
{
|
||||
[SerializeField] private Image _iconImage;
|
||||
[SerializeField] private TextMeshProUGUI _nameText;
|
||||
|
||||
public SkillData Data { get; private set; }
|
||||
|
||||
private Transform _dragTransform;
|
||||
private Transform _originalParent;
|
||||
private CanvasGroup _canvasGroup;
|
||||
private Vector3 _originalLocalPos;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_canvasGroup = GetComponent<CanvasGroup>();
|
||||
if (_canvasGroup == null)
|
||||
_canvasGroup = gameObject.AddComponent<CanvasGroup>();
|
||||
}
|
||||
|
||||
public void SetData(SkillData data)
|
||||
{
|
||||
Data = data;
|
||||
if (_iconImage != null)
|
||||
{
|
||||
_iconImage.sprite = data != null ? data.Icon : null;
|
||||
_iconImage.enabled = data != null && data.Icon != null;
|
||||
}
|
||||
if (_nameText != null)
|
||||
_nameText.text = data != null ? data.EntryName : "";
|
||||
}
|
||||
|
||||
public void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
if (Data == null)
|
||||
{
|
||||
eventData.pointerDrag = null;
|
||||
return;
|
||||
}
|
||||
|
||||
_dragTransform = GameManager.Instance.InGameUI.DragCanvas;
|
||||
_originalParent = transform.parent;
|
||||
_originalLocalPos = transform.localPosition;
|
||||
|
||||
transform.SetParent(_dragTransform);
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
}
|
||||
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
transform.position = eventData.position;
|
||||
}
|
||||
|
||||
public void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
_canvasGroup.blocksRaycasts = true;
|
||||
|
||||
// 드롭된 오브젝트 판정 — 퀵슬롯이면 등록
|
||||
GameObject hovered = eventData.pointerEnter;
|
||||
if (hovered != null)
|
||||
{
|
||||
QuickSlot slot = hovered.GetComponentInParent<QuickSlot>();
|
||||
if (slot != null && Data != null)
|
||||
{
|
||||
slot.SetEntry(Data);
|
||||
}
|
||||
}
|
||||
|
||||
// 엔트리는 항상 제자리로 복귀 (원본은 목록에 남아있어야 함)
|
||||
transform.SetParent(_originalParent);
|
||||
transform.localPosition = _originalLocalPos;
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/UI/SkillWindow/SkillEntry.cs.meta
Normal file
2
Assets/02_Scripts/UI/SkillWindow/SkillEntry.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58eac31de8e96ed4c9a5fa7206bd63f3
|
||||
43
Assets/02_Scripts/UI/SkillWindow/SkillWindowUI.cs
Normal file
43
Assets/02_Scripts/UI/SkillWindow/SkillWindowUI.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class SkillWindowUI : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Transform _listContentRoot;
|
||||
[SerializeField] private GameObject _skillEntryPrefab;
|
||||
|
||||
private readonly List<SkillEntry> _entries = new List<SkillEntry>();
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
bool willOpen = !gameObject.activeSelf;
|
||||
gameObject.SetActive(willOpen);
|
||||
if (willOpen) Refresh();
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
// 기존 엔트리 정리
|
||||
foreach (Transform child in _listContentRoot)
|
||||
Destroy(child.gameObject);
|
||||
_entries.Clear();
|
||||
|
||||
var character = GameManager.Instance.Level.CurrentCharacter;
|
||||
if (character == null) return;
|
||||
if (!character.TryGetComponent<SkillModule>(out var skillModule)) return;
|
||||
|
||||
var skills = skillModule.GetEquippedSkillDatas();
|
||||
for (int i = 0; i < skills.Count; i++)
|
||||
{
|
||||
if (skills[i] == null) continue;
|
||||
|
||||
GameObject entryObj = Instantiate(_skillEntryPrefab, _listContentRoot);
|
||||
SkillEntry entry = entryObj.GetComponent<SkillEntry>();
|
||||
if (entry != null)
|
||||
{
|
||||
entry.SetData(skills[i]);
|
||||
_entries.Add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/UI/SkillWindow/SkillWindowUI.cs.meta
Normal file
2
Assets/02_Scripts/UI/SkillWindow/SkillWindowUI.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0abbd5b063416be4c97a8429d13b4b53
|
||||
Reference in New Issue
Block a user