feat: 플레이어 역할별 스킬 프리셋 적용 경로 추가

- 탱커, 지원, 딜러 기본 슬롯 프리셋을 로컬 플레이어에 즉시 적용하는 디버그 메뉴를 추가
- PlayerSkillInput에 7칸 슬롯 자동 보정과 일괄 갱신 API를 넣어 기존 프리팹 직렬화와 호환되도록 정리
- SkillQuickSlotUI가 스킬 슬롯 변경 이벤트를 구독해 프리셋 적용 시 액션바가 즉시 갱신되도록 보강
- 플레이 검증에서 탱커, 지원, 딜러 프리셋이 모두 기대한 슬롯 순서와 Ctrl 회피 슬롯까지 정상 적용되는 것을 확인
This commit is contained in:
2026-03-25 00:14:22 +09:00
parent 7a62e6c631
commit 8d21922e2f
3 changed files with 215 additions and 3 deletions

View File

@@ -1,6 +1,8 @@
using UnityEngine;
using UnityEngine.InputSystem;
using Unity.Netcode;
using System;
using System.Collections.Generic;
using Colosseum.Skills;
using Colosseum.Weapons;
@@ -14,9 +16,11 @@ namespace Colosseum.Player
[RequireComponent(typeof(PlayerActionState))]
public class PlayerSkillInput : NetworkBehaviour
{
private const int ExpectedSkillSlotCount = 7;
[Header("Skill Slots")]
[Tooltip("각 슬롯에 등록할 스킬 데이터 (6개 + 추가 슬롯)")]
[SerializeField] private SkillData[] skillSlots = new SkillData[7];
[SerializeField] private SkillData[] skillSlots = new SkillData[ExpectedSkillSlotCount];
[Header("References")]
[Tooltip("SkillController (없으면 자동 검색)")]
@@ -32,8 +36,15 @@ namespace Colosseum.Player
public SkillData[] SkillSlots => skillSlots;
/// <summary>
/// 스킬 슬롯 구성이 변경되었을 때 호출됩니다.
/// </summary>
public event Action OnSkillSlotsChanged;
public override void OnNetworkSpawn()
{
EnsureSkillSlotCapacity();
if (!IsOwner)
{
enabled = false;
@@ -99,6 +110,16 @@ namespace Colosseum.Player
CleanupInputActions();
}
private void Awake()
{
EnsureSkillSlotCapacity();
}
private void OnValidate()
{
EnsureSkillSlotCapacity();
}
private void OnEnable()
{
if (IsOwner && inputActions != null)
@@ -107,6 +128,28 @@ namespace Colosseum.Player
}
}
/// <summary>
/// 기존 프리팹이나 씬 직렬화 데이터가 6칸으로 남아 있어도
/// 긴급 회피 슬롯까지 포함한 7칸 구성을 항상 보장합니다.
/// </summary>
private void EnsureSkillSlotCapacity()
{
if (skillSlots != null && skillSlots.Length == ExpectedSkillSlotCount)
return;
SkillData[] resizedSlots = new SkillData[ExpectedSkillSlotCount];
if (skillSlots != null)
{
int copyCount = Mathf.Min(skillSlots.Length, resizedSlots.Length);
for (int i = 0; i < copyCount; i++)
{
resizedSlots[i] = skillSlots[i];
}
}
skillSlots = resizedSlots;
}
private void CleanupInputActions()
{
if (inputActions != null)
@@ -227,6 +270,8 @@ namespace Colosseum.Player
/// </summary>
public SkillData GetSkill(int slotIndex)
{
EnsureSkillSlotCapacity();
if (slotIndex < 0 || slotIndex >= skillSlots.Length)
return null;
return skillSlots[slotIndex];
@@ -237,10 +282,37 @@ namespace Colosseum.Player
/// </summary>
public void SetSkill(int slotIndex, SkillData skill)
{
EnsureSkillSlotCapacity();
if (slotIndex < 0 || slotIndex >= skillSlots.Length)
return;
skillSlots[slotIndex] = skill;
OnSkillSlotsChanged?.Invoke();
}
/// <summary>
/// 전체 스킬 슬롯을 한 번에 갱신합니다.
/// </summary>
public void SetSkills(IReadOnlyList<SkillData> skills)
{
EnsureSkillSlotCapacity();
if (skills == null)
return;
int count = Mathf.Min(skillSlots.Length, skills.Count);
for (int i = 0; i < count; i++)
{
skillSlots[i] = skills[i];
}
for (int i = count; i < skillSlots.Length; i++)
{
skillSlots[i] = null;
}
OnSkillSlotsChanged?.Invoke();
}
/// <summary>