주요 구현 내용 요약
1. 플레이어 상태 시스템 일원화
2. 적 충돌/데미지 처리
3. 카메라 따라가기
4.양손 장비 스왑(Swap) 로직 구현
5. 인벤토리 UI에서 장착 상태(체크 마크) 동기화
구현 코드 구조 요약
PlayerStatus.cs
public class PlayerStatus : MonoBehaviour
{
[SerializeField] private StatusData health, food, mana;
// 자동 회복/감소
void Update()
{
food.Add(food.PassiveValue * Time.deltaTime);
mana.Add(mana.PassiveValue * Time.deltaTime);
}
// 자원 소모/획득/적 처치 보상
public bool UseFood(float amount) { ... }
public bool UseMana(float amount) { ... }
public void GainFood(float amount) { ... }
public void OnEnemyKilled(float reward) { GainFood(reward); }
// 적 충돌 시 데미지 코루틴 등...
}
StatusData.cs
[Serializable]
public class StatusData
{
[SerializeField] private float curValue, maxValue, passiveValue;
public float CurValue { get; set; }
public float MaxValue => maxValue;
public float PassiveValue => passiveValue;
public void Add(float v) => CurValue += v;
public void Subtract(float v) => CurValue -= v;
// 저장/불러오기용 함수도 추가!
public StatusSaveData ToSaveData() { ... }
public void FromSaveData(StatusSaveData data) { ... }
}
CameraFollow.cs
public class CameraFollow : MonoBehaviour
{
public Transform target;
public Vector3 offset = new Vector3(0, 3, -10); // y값 조절로 플레이어 위치 아래 배치
public float smoothSpeed = 7f;
void LateUpdate()
{
if (target == null) return;
Vector3 desired = target.position + offset;
Vector3 smooth = Vector3.Lerp(transform.position, desired, smoothSpeed * Time.deltaTime);
transform.position = smooth;
}
}
고민했던 부분 & 해결법
- 자원이 많아질수록 저장/불러오기 복잡해질까?
- StatusData/SaveData 구조화로 깔끔하게 해결할수있음.
- 카메라 중앙이 아니라 아래쪽에 플레이어 배치하는 법?
- offset.y 조절로 직관적으로 위치 제어
- PassiveValue 활용(양수=자동 회복, 음수=자동 소모)
- 주석/region 적극 활용: 협업, 문서화, 가독성 모두 UP
배운 점 & 느낀점
- 상태/자원/저장 구조를 일관성 있게 잡으니
확장(스탯 추가), 유지보수, 협업 모두 편해진다! - 카메라 연출도 작은 offset/y값 조정만으로 원하는 느낌을 손쉽게 낼 수 있다.
- 클린코드(주석/region), 팀원/외주 협업 문서화의 중요성 재확인!
4.양손 장비 스왑(Swap) 로직 구현
- 기존에는 EquipRight(무기), EquipLeft(방패) 메서드에서
이미 장착 중인 아이템과 동일한 아이템을 다른 손에 장착하려 할 때
단순히 기존 손의 장비를 해제(=None) 시켰음. - 이렇게 하면, 양손에 같은 아이템을 번갈아 끼우려 할 때 한쪽이 비어버리는(장비가 None이 되는) 문제가 발생함.
- 이 문제를 해결하기 위해,
- 장착하려는 아이템이 반대 손에 이미 장착 중이면, 두 손의 아이템을 swap(교환)하는 로직을 추가함.
- 즉, 오른손에 칼, 왼손에 방패 상태에서 왼손에 칼을 장착하면
→ 오른손엔 방패, 왼손엔 칼로 서로 바뀌도록 구현.
// 오른손 장착 메서드 예시
public void EquipRight(EquipmentItem item)
{
if (equippedWeapon == item) return;
// 반대 손(왼손)에 이미 같은 아이템이 있으면 swap
if (equippedShield == item)
{
var temp = equippedWeapon;
equippedWeapon = item;
equippedShield = temp;
// 비주얼 처리
characterVisual.EquipWeaponRight(item.equippedSprite);
if (temp != null)
characterVisual.EquipShieldLeft(temp.equippedSprite);
else
characterVisual.UnequipShieldLeft();
FindObjectOfType<InventoryUI>()?.SyncEquippedUI();
ApplyTotalStats();
return;
}
// ... 이하 기존 로직
}
EquipLeft도 같은 원리로 swap 구현!
배운 점
- 양손 장비가 존재하는 경우, 장비를 단순 해제하는 것이 아니라 swap을 구현해야 UX가 자연스러움.
5. 인벤토리 UI에서 장착 상태(체크 마크) 동기화
- 슬롯 오브젝트에서 **MarkAsEquipped()**를 호출해 장착 체크 마크/버튼 비활성화,
**ResetVisual()**로 해제(체크 해제+버튼 활성화)를 구현함. - **SyncEquippedUI()**를 통해 현재 장착 상태와 슬롯 UI를 동기화.
- 장착 상태(EquipManager의 equippedWeapon, equippedShield)와 슬롯의 아이템이 같으면 체크
- 아니면 해제
public void SyncEquippedUI()
{
foreach (var slot in slots)
{
if (equipManager.equippedWeapon == slot.Item || equipManager.equippedShield == slot.Item)
slot.MarkAsEquipped();
else
slot.ResetVisual();
}
}
장비를 장착/해제/스왑할 때마다 SyncEquippedUI()를 호출하여
UI가 항상 실제 장착 상태와 일치하게 만듦.
배운 점
- Start(), OnEnable(), Awake() 등 Unity 라이프사이클 순서를 정확히 이해하고, 슬롯/동기화 타이밍을 제어하는 게 중요함.
- UI와 실제 데이터가 항상 동기화되도록, 장착/해제 시 SyncEquippedUI를 반드시 호출하는 습관을 들여야 한다.
Unity MonoBehaviour 라이프사이클 주요 순서
1. 오브젝트가 생성되어 Hierarchy에 등장할 때
-
- Awake()
- 컴포넌트가 인스턴스화되면 제일 먼저 실행
- 다른 모든 컴포넌트/스크립트가 아직 Awake 중일 수 있음
- DontDestroyOnLoad 등 초기 세팅에 많이 사용
- OnEnable()
- 오브젝트가 활성화(Enable) 상태가 될 때 호출
- 비활성화 → 활성화 될 때마다 실행됨
- (비활성화된 오브젝트는 Enable해도 Awake/Start는 다시 안 탄다!)
- Awake()
2. 씬이 처음 실행되거나, 오브젝트가 활성화된 직후
-
- Start()
- 모든 Awake/OnEnable이 끝난 뒤,
- 프레임이 시작되기 직전에 한 번만 호출
- "초기화가 다 끝난 후에 무언가를 하고 싶을 때" 사용
- Start()
3. 게임 루프가 돌기 시작하면…
-
- Update() (매 프레임마다)
- LateUpdate() (Update 후)
- FixedUpdate() (물리 루프마다)
- …(이후 OnDisable, OnDestroy 등)
실제 호출 순서 예시
-
- 씬에 오브젝트가 활성화된 상태로 등장 →
- Awake()
- OnEnable()
- Start()
- 오브젝트가 비활성화됐다가 다시 활성화되면 →
- OnEnable() (Awake, Start는 한 번만!)
- 씬 전환 등으로 오브젝트가 삭제될 때 →
- OnDisable()
- OnDestroy()
- 씬에 오브젝트가 활성화된 상태로 등장 →
정리 표
| 오브젝트 활성화 시 | 호출 순서 |
| 최초 생성(활성화 O) | Awake → OnEnable → Start |
| 비활성화 후 다시 활성화 | OnEnable |
| 비활성화 | OnDisable |
| 파괴 | OnDestroy |
느낀점
이번 양손 장비 시스템을 구현하면서,
처음엔 "그냥 장착 해제하고 새로 착용"하는 방식만 생각했다가
실제 사용해보니 UX가 굉장히 불편해지는 걸 직접 체감했다.
그래서 “swap” 방식을 도입해보니 사용감이 훨씬 자연스러워졌고,
실제로 게임에서도 자주 쓰는 “드래그&스왑” 패턴의 필요성을 깨달았다.
또한 UI 체크 표시와 실제 데이터 동기화를 꼼꼼하게 신경써야
사용자 입장에서 “내가 장비를 바꾼 게 바로바로 보인다”는
즉각적인 피드백이 가능하다는 점을 다시 느꼈다.
UI 오브젝트(특히 InventoryUI)가 SetActive(false) 상태로 시작하면
Start()가 늦게(=실제 켜질 때) 호출된다는 Unity 라이프사이클 특성을 명확히 알게 됐다.
덕분에 OnEnable/Start 타이밍 꼬임,
그리고 “왜 내 슬롯이 안 만들어지지?” 하는 문제를 직접 로그로 확인하며
“초기화 타이밍과 UI 활성화 상태”를 정말 신경써야 함을 깨달았다.