2026-06-01 씬전환시 무기 사라지는 현상 수정

This commit is contained in:
2026-06-01 13:06:31 +09:00
parent 964383c59c
commit 11a6a4f983
7 changed files with 67 additions and 13 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -6,8 +6,16 @@
// PlayerWeaponInventory
// ----------------------------------------------------------------------------
// 플레이어가 보유한 무기 목록 + 현재 장착 무기 관리.
// 씬 전환 시에도 데이터를 유지하기 위해 DontDestroyOnLoad 싱글톤으로 동작.
// PlayerController가 OnWeaponChanged 이벤트를 구독해서 애니메이션/공격 콤보 교체.
//
// 사용법:
// - 어디서든 PlayerWeaponInventory.Instance 또는 EnsureInstance()로 접근.
// - 인스턴스가 없으면 EnsureInstance()가 새 GameObject로 자동 생성 → 첫 씬에
// 수동 배치하지 않아도 됨.
// - Player 프리팹에는 이 컴포넌트를 더 이상 붙이지 않는다 (중복 시엔 자동 폐기되긴 하지만,
// 프리팹과 같은 GameObject가 영속화되는 부작용을 피하려면 제거 권장).
//
// 슬롯 매핑 (외부 코드의 EquipSlot 인덱스):
// -1 → 맨손 (CurrentWeapon == null)
// 0 → 첫 번째로 픽업한 무기
@@ -21,9 +29,40 @@
// ============================================================================
public class PlayerWeaponInventory : MonoBehaviour
{
// ─── 싱글톤 ──────────────────────────────────────────────────────────
public static PlayerWeaponInventory Instance { get; private set; }
// 어디서든 호출하면 인스턴스를 보장한다. 없으면 새 GameObject로 생성.
// (PlayerController.Awake에서 호출해 첫 진입을 보장하는 용도)
public static PlayerWeaponInventory EnsureInstance()
{
if (Instance != null) return Instance;
GameObject go = new GameObject(nameof(PlayerWeaponInventory));
return go.AddComponent<PlayerWeaponInventory>();
// AddComponent가 Awake를 동기 호출 → Instance 세팅 + DontDestroyOnLoad 적용 완료 후 반환.
}
private readonly List<WeaponData> _weapons = new();
private int _currentIndex = -1; // -1 = 맨손
private void Awake()
{
// 중복 생성 방지 — 이미 Instance가 있으면 이 컴포넌트만 폐기.
// (GameObject 전체를 지우지 않는 이유: Player 프리팹 등에 잘못 붙어 있어도 Player가 사라지면 안 되니까.)
if (Instance != null && Instance != this)
{
Destroy(this);
return;
}
Instance = this;
}
private void OnDestroy()
{
// 앱 종료 등으로 영속 인스턴스가 파괴될 때 정적 참조 정리.
if (Instance == this) Instance = null;
}
// 무기 교체 시 발화. null이면 맨손.
public event Action<WeaponData> OnWeaponChanged;

View File

@@ -43,13 +43,16 @@ private void ApplyVisual()
}
// 플레이어가 트리거 영역에 진입 시 발화.
// Player(또는 자식)에 PlayerWeaponInventory 컴포넌트가 있어야 픽업 됨.
// 인벤토리는 영속 싱글톤(PlayerWeaponInventory.Instance)이므로 Player 본인이 들어왔는지만 확인.
// 이미 보유 중이면 Pickup이 false 반환 → pickup 오브젝트 그대로 유지.
private void OnTriggerEnter2D(Collider2D other)
{
if (_weapon == null) return;
PlayerWeaponInventory inventory = other.GetComponentInParent<PlayerWeaponInventory>();
// 플레이어 본인만 픽업 (적/투사체 등이 트리거 들어와도 무시).
if (other.GetComponentInParent<PlayerController>() == null) return;
PlayerWeaponInventory inventory = PlayerWeaponInventory.Instance;
if (inventory == null) return;
if (inventory.Pickup(_weapon))

View File

@@ -150,10 +150,9 @@ private void Awake()
// 공격이 enemy를 hit하면 _lastHitEnemy를 기억 → 다음 잡기에서 그 enemy를 우선 타겟팅.
_attackHitbox.OnHit += OnAttackHit;
// 무기 인벤토리: 같은 GameObject에 컴포넌트가 없으면 자동 부착.
_weaponInventory = GetComponent<PlayerWeaponInventory>();
if (_weaponInventory == null)
_weaponInventory = gameObject.AddComponent<PlayerWeaponInventory>();
// 무기 인벤토리: 영속 싱글톤 (DontDestroyOnLoad). 없으면 EnsureInstance가 새로 생성.
// 씬 전환 시 PlayerController는 파괴되지만 인벤토리는 살아남아 무기 목록을 유지한다.
_weaponInventory = PlayerWeaponInventory.EnsureInstance();
_weaponInventory.OnWeaponChanged += OnWeaponChanged;
}

View File

@@ -13,7 +13,7 @@
//
// _slots는 데이터가 아니라 생성한 화면 오브젝트 캐시일 뿐 (재사용 풀).
// ============================================================================
public class WeaponInventory : MonoBehaviour
public class WeaponInventory : MonoBehaviour, ISceneInitializable
{
[SerializeField] private PlayerWeaponInventory _wInvenSource; // 비워두면 런타임에 자동 탐색
[SerializeField] private Transform _slotRoot; // 슬롯이 생성될 부모
@@ -33,6 +33,7 @@ private void Start()
Refresh();
}
private void OnDestroy()
{
if (_wInvenSource != null)
@@ -40,6 +41,17 @@ private void OnDestroy()
}
public void OnSceneLoaded()
{
if (_wInvenSource == null)
_wInvenSource = FindFirstObjectByType<PlayerWeaponInventory>();
if (_wInvenSource != null)
_wInvenSource.OnInventoryChanged += Refresh;
Refresh();
}
// 인벤토리 상태를 화면에 반영. 슬롯 개수를 무기 수에 맞추고 내용/하이라이트 갱신.
private void Refresh()
{
@@ -61,4 +73,5 @@ private void Refresh()
_slots[i].SetSelected(i == _wInvenSource.CurrentIndex);
}
}
}