no image
내일배움캠프 32일차 TIL [Project_Up]
🗓️ 오늘 하루 일정✅ 오전09:00 ~ 11:00 : 점프 애니메이션 추가 (OnTriggerEnter에 JumpTrigger 트리거 연동, AddForce로 물리 튕김 적용)11:00 ~ 13:00 : 맵 제작 진행 (지형 구성, 오브젝트 배치 일부)🍽️ 점심시간13:00 ~ 14:00 : 점심시간✅ 오후14:00 ~ 16:00 : 맵 배치 마무리 작업16:00 ~ 18:00 : 챌린지반 강의 수강 (주제: 델리게이트)18:00 ~ 19:00 : 자유시간 및 휴식🍽️ 저녁시간19:00 ~ 20:00 : 맵 최종 점검 및 세부 수정20:00 ~ 21:00 : 맵 제작 완성, 마무리 테스트✅ 오늘 학습 키워드Unity OnTriggerEnter 활용Rigidbody 물리 처리 (AddForce, y..
20:24:44
no image
내일배움캠프 31일차 TIL [Project_Up]
🗓️ 오늘 하루 일정✅ 오전09:00 ~ 13:00 : 현재 진행 중인 프로젝트 작업 이어서 진행플레이어 이동/카메라 로직 점검1인칭에서 3인칭 카메라 전환 시 로직 테스트🍽️ 점심시간13:00 ~ 14:00 : 점심시간✅ 오후14:00 ~ 18:00 : 프로젝트 오류 수정카메라 회전 관련 불필요한 캐릭터 회전 코드 제거달리기(OnRun) 입력 처리 시 스태미나 감소가 한 번만 되는 문제 확인지속적인 스태미나 감소 로직 구현 아이디어 구상🍽️ 저녁시간18:00 ~ 19:00 : 저녁시간✅ 오후19:00 ~ 21:00 : 기능 추가 및 개선스태미나 감소 로직을 프레임 기반(Update)으로 작동하도록 개선카메라 시야 유지 및 캐릭터 스트레이프 이동 적용일부 Input System 액션 상태 처리 방식..
2025.08.11
no image
WIL - 본캠프 6주차(25.08.04~08.08)
1) 이번 주 가장 인상 깊었던 배움디자인 패턴 학습옵저버 패턴, 팩토리 패턴, 이벤트 버스, 상태 패턴(FSM 비교 포함), 커맨드 패턴, 전략 패턴, 중재자 패턴을 Unity 예시 코드로 구현하며 개념과 활용 방법을 정리했다.특히 상태 패턴과 FSM의 차이를 직접 코드로 확인하며, 상황별로 어떤 구조를 쓰는 것이 유지보수와 확장성 측면에서 유리한지 감을 잡았다.Unity Input System 이해 심화InputAction.CallbackContext.phase가 입력 상태 변화 시점에만 호출된다는 점을 이해했고, 지속적인 행동(달리기, 스태미나 소모)은 Update()와 상태 플래그로 처리해야 한다는 것을 알게 됐다.이를 1인칭 이동 시스템과 3인칭 오비트 카메라 전환 작업에 적용했다.플레이어 컨트..
2025.08.10
no image
내일배움캠프 30일차 TIL [Project_Up]
📝 TIL - 2025년 8월 8일 (금)🗓️ 오늘 하루 일정✅ 오전09:00 ~ 13:00 : 3D 서바이벌 프로젝트 개발플레이어 애니메이션 시스템 설계 및 구현Blend Tree 애니메이션 전환 테스트 및 디버깅PlayerAnimation, PlayerController 스크립트 수정Speed 값 기반으로 Idle, Walk, Run 상태 구분 구현Rigidbody.velocity 기반의 실제 이동속도 반영애니메이션 끊김 현상 지속적으로 디버깅🍽️ 점심시간13:00 ~ 14:00 : 점심시간✅ 오후14:00 ~ 18:00 : 인벤토리 및 상태 시스템 검토UIInventory, ItemSlot, Equip, ItemObject 등 주요 시스템 확인 및 정리PlayerCondition, Condit..
2025.08.08
no image
Unity - 3D Survival Game(인벤토리 기능)
# 🧭 3D Survival Game (Unity Project)Unity를 사용하여 제작한 3D 생존 게임입니다. 플레이어는 자원을 수집하고, 적과 전투하며, 아이템을 활용해 생존을 이어갑니다. **불, 날씨, 사운드 등 환경 요소까지 구현하여 몰입감을 강화했습니다.**---## 📌 주요 기능### 🕹️ 플레이어 조작- `Input System` 기반 WASD 이동 + Space 점프- 마우스 카메라 회전 (`LookSensitivity`)- 체력, 허기, 스태미나 관리 (`PlayerCondition`)---### 🛠️ 자원 채집 & 전투 시스템- `EquipTool` 장비를 통해: - 자원 채집 (`Resource.cs`) - 적 NPC 공격 (`IDamageable`)- 공격 시 스..
2025.08.08
no image
내일배움캠프 29일차 TIL [인벤토리 시스템 구현]
🗓️ 오늘 하루 일정✅ 오전09:00 ~ 13:00 : 3D 서바이벌 프로젝트 진행 (인벤토리 시스템 구현 및 디버깅)ItemData 구조 점검[System.Serializable] 누락 문제 해결inventoryWindow.activeInHierarchy 활용한 UI 상태 확인 방법 학습🍽️ 점심시간13:00 ~ 14:00 : 점심시간✅ 오후14:00 ~ 16:00 : 인벤토리 UI 개선 및 아이템 장착 시스템 구현아이템 클릭 → 정보 출력 → 버튼 기능 연결 흐름 정리장착된 무기에 따라 공격 애니메이션 동작하도록 연동16:00 ~ 17:00 : 챌린지반 강의 수강 (3D 장착 시스템 및 애니메이션 연동)17:00 ~ 18:00 : 인벤토리 기능 블로그용 정리, 유니티 12강 까지 마무리🍽️ 저녁..
2025.08.07
no image
Unity - Inventory 인벤토리 시스템 구현 정리
🎒 Unity 3D 서바이벌 게임 개발: 인벤토리 시스템 구현 정리 Unity로 서바이벌 게임을 만들면서 인벤토리 시스템을 구현해봤습니다. 생각보다 복잡했지만, 구현하면서 많이 배운 기능이라 정리해두면 좋을 것 같았습니다!✔️ 구현 목표아이템을 줍고 인벤토리에 추가하기인벤토리 창을 열고 닫기아이템을 클릭해서 정보 확인하기아이템 사용/버리기/장착/해제 기능 만들기장착 아이템에 따라 공격 애니메이션 실행하기🧩 1. 아이템 데이터 구조: ScriptableObject 활용아이템은 ScriptableObject로 데이터를 따로 관리하게 했습니다! 이렇게 하면 인스펙터에서 편하게 데이터를 만들고, 재활용할 수도 있어서 좋습니다![CreateAssetMenu(fileName = "Item", menuName =..
2025.08.07
no image
Unity - Unity의 TryGetComponent
🎯 Unity의 TryGetComponent 제대로 알기Unity 개발을 하다 보면 오브젝트의 컴포넌트를 가져와야 할 일이 자주 있습니다. 이때 흔히 사용하는 방식이 GetComponent()입니다. 하지만 이 방식은 컴포넌트가 존재하지 않을 경우 NullReferenceException을 발생시킬 수 있다는 단점이 있습니다.이런 위험을 피하고자 Unity에서는 보다 안전한 방식으로 컴포넌트를 가져올 수 있는 TryGetComponent() 메서드를 제공합니다. 이 글에서는 TryGetComponent의 기본 개념, 사용법, 실제 활용 예시, 그리고 GetComponent와의 차이점까지 상세히 정리해보겠습니다.📌 TryGetComponent란?TryGetComponent(out T component)..
2025.08.07
반응형

🗓️ 오늘 하루 일정

✅ 오전

  • 09:00 ~ 11:00 : 점프 애니메이션 추가 (OnTriggerEnter에 JumpTrigger 트리거 연동, AddForce로 물리 튕김 적용)
  • 11:00 ~ 13:00 : 맵 제작 진행 (지형 구성, 오브젝트 배치 일부)

🍽️ 점심시간

  • 13:00 ~ 14:00 : 점심시간

✅ 오후

  • 14:00 ~ 16:00 : 맵 배치 마무리 작업
  • 16:00 ~ 18:00 : 챌린지반 강의 수강 (주제: 델리게이트)
  • 18:00 ~ 19:00 : 자유시간 및 휴식

🍽️ 저녁시간

  • 19:00 ~ 20:00 : 맵 최종 점검 및 세부 수정
  • 20:00 ~ 21:00 : 맵 제작 완성, 마무리 테스트

✅ 오늘 학습 키워드

  • Unity OnTriggerEnter 활용
  • Rigidbody 물리 처리 (AddForce, y속도 초기화)
  • Animator Trigger 파라미터 활용 (SetTrigger)
  • 맵 제작 및 오브젝트 배치
  • C# 델리게이트(Delegate) 개념 및 활용

✅ 오늘 학습 한 내용을 나만의 언어로 정리하기

오늘 오전에는 OnTriggerEnter를 이용해 플레이어가 특정 트리거 존에 닿았을 때 점프 애니메이션과 물리적인 튕김 효과가 동시에 적용되도록 구현했다.

Rigidbody의 y속도를 0으로 초기화해 낙하 중에도 깔끔하게 위로 튀어 오를 수 있도록 처리했고, anim.SetTrigger("JumpTrigger")를 사용해 애니메이션을 재생하도록 했다. 물리 처리는 AddForce(Vector3.up * jumpForce, forceMode)로 구현하여 위쪽으로 힘을 가했다.

오후에는 맵 제작을 이어서 진행했다. 지형과 오브젝트를 배치하고, 동선과 분위기를 고려한 레이아웃을 구성했다. 배치 작업은 생각보다 시간이 오래 걸렸지만, 어느 정도 완성도를 갖춘 상태로 마무리할 수 있었다.

또한 챌린지반 강의에서 C# 델리게이트에 대해 학습했다. 델리게이트가 메서드를 변수처럼 다룰 수 있게 해주는 타입이라는 점, 그리고 멀티캐스트, 익명 메서드, 람다식, 이벤트와의 관계까지 배우면서 이벤트 처리나 콜백 구조를 만들 때 얼마나 유용한지 이해할 수 있었다.


🧩 학습하며 겪었던 문제점 & 에러

1) 점프 애니메이션과 물리 힘 적용 타이밍

  • 문제정의
  • 트리거 존에 닿았을 때 점프 애니메이션은 실행되지만, 물리적으로 위로 튀는 타이밍이 애니메이션과 완전히 맞지 않아 시각적으로 어색했다.
  • 시도
  • 단순히 OnTriggerEnter에서 SetTrigger와 AddForce를 동시에 호출하여 처리했다.
  • 해결방법이후에는 Animation Event를 이용하면 타이밍을 더 정확히 맞출 수 있다는 점을 메모해 두었다.
  • 우선 애니메이션 트리거는 그대로 유지하되, 물리 힘은 y속도를 초기화한 후 즉시 적용하여 낙하 중에도 부드럽게 튀어 오르게 했다.
  • 예시 코드
private void OnTriggerEnter(Collider other)
{
    if (!string.IsNullOrEmpty(targetTag) && !other.CompareTag(targetTag)) return;

    Rigidbody rb = other.attachedRigidbody;
    if (rb != null)
    {
        if (resetVerticalVelocity)
        {
            Vector3 v = rb.velocity;
            v.y = 0f;
            rb.velocity = v;
        }

        anim.SetTrigger("JumpTrigger"); // 애니메이션 재생
        rb.AddForce(Vector3.up * jumpForce, forceMode); // 물리 힘 적용
    }
}
  • 새롭게 알게 된 점
  • 애니메이션과 물리 동작을 정확히 동기화하려면 Animator의 트리거만 사용하는 것이 아니라 애니메이션 이벤트코루틴 지연 실행으로 타이밍을 조절하는 것이 좋다.
  • 다시 만나게 된다면
  • 다음번에는 애니메이션 클립에 이벤트를 추가해, 특정 프레임에서만 힘을 적용하도록 구현할 것이다.

📝 메모

오늘은 오전에 점프 애니메이션과 물리 튕김 처리를 구현하고, 오후에는 맵 배치를 마무리했다. 사실 맵 제작은 하다 보면 끝이 없는데, 오늘은 적당한 선에서 마무리하기로 결정했다. 완벽하게 만들려고 욕심내기보다 지금 상태에서 충분히 플레이가 가능한 수준이라면 더 이상 붙잡지 않는 게 오히려 효율적이라는 걸 느꼈다.

델리게이트 강의를 들으면서, 메서드를 변수처럼 다루고 콜백 구조를 쉽게 만들 수 있다는 점이 인상 깊었다. 앞으로 이벤트나 UI 버튼 클릭 처리, 플레이어 상태 변화 알림 등 다양한 곳에 적용할 수 있을 것 같아 기대된다.

내일은 오늘 만든 맵에서 직접 플레이 테스트를 하면서, 필요한 최소한의 수정만 하고 다른 기능 구현에 들어가야겠다.


반응형
반응형

🗓️ 오늘 하루 일정

✅ 오전

  • 09:00 ~ 13:00 : 현재 진행 중인 프로젝트 작업 이어서 진행
    • 플레이어 이동/카메라 로직 점검
    • 1인칭에서 3인칭 카메라 전환 시 로직 테스트

🍽️ 점심시간

  • 13:00 ~ 14:00 : 점심시간

✅ 오후

  • 14:00 ~ 18:00 : 프로젝트 오류 수정
    • 카메라 회전 관련 불필요한 캐릭터 회전 코드 제거
    • 달리기(OnRun) 입력 처리 시 스태미나 감소가 한 번만 되는 문제 확인
    • 지속적인 스태미나 감소 로직 구현 아이디어 구상

🍽️ 저녁시간

  • 18:00 ~ 19:00 : 저녁시간

✅ 오후

  • 19:00 ~ 21:00 : 기능 추가 및 개선
    • 스태미나 감소 로직을 프레임 기반(Update)으로 작동하도록 개선
    • 카메라 시야 유지 및 캐릭터 스트레이프 이동 적용
    • 일부 Input System 액션 상태 처리 방식 개선

✅ 오늘 학습 키워드

  • Unity Input System의 CallbackContext.phase 동작 방식
  • 1인칭 → 3인칭 카메라 전환 로직
  • 이동 중 카메라와 캐릭터 회전 분리
  • 스태미나 지속 소모 처리 방식 (Update + 상태 플래그)

✅ 오늘 학습 한 내용을 나만의 언어로 정리하기

오늘은 기능을 많이 확장하진 못했지만, Input System에서 입력이 "눌림/뗌" 상태 변화에서만 이벤트가 호출된다는 점을 다시 확인했다.

특히 OnRun 같이 지속적인 처리가 필요한 기능은 Update()

에서 상태를 체크하면서 처리해야 한다는 걸 깨달았다.

또한 3인칭 카메라 전환 시 이동 방향과 시야 회전을 분리해, 스트레이프 방식으로 움직일 수 있게 만들었다.

이 과정에서 카메라 피벗과 캐릭터 본체 회전을 나누는 구조의 필요성을 다시 느꼈다.


🧩 학습하며 겪었던 문제점 & 에러

  • 문제정의
    • OnRun이 Shift 누를 때 한 번만 호출되어, 스태미나가 1회만 줄어드는 문제.
  • 시도
    • OnRun 내부에서 UseStamina() 호출
    • Performed 상태일 때만 처리
  • 해결방법
    • isRunning 상태 변수를 두고, Update()에서 지속적으로 스태미나를 감소시키는 방식으로 변경.
    • Time.deltaTime을 곱해 초당 감소량을 일정하게 유지.
    private bool isRunning;
    
    public void OnRun(InputAction.CallbackContext context)
    {
        if (context.phase == InputActionPhase.Performed)
        {
            isRunning = true;
            moveSpeed = runSpeed;
        }
        else if (context.phase == InputActionPhase.Canceled)
        {
            isRunning = false;
            moveSpeed = walkSpeed;
        }
    }
    
    private void Update()
    {
        if (isRunning && curMovementInput.magnitude > 0.1f)
            CharacterManager.Instance.Player.condition.UseStamina(Time.deltaTime * 5f);
    }
    
  • 새롭게 알게 된 점
    • Input System은 "상태 변화" 이벤트라, 지속 동작은 Update() 기반으로 처리해야 함.
  • 다시 만나게 된다면
    • 초당 소모량과 회복량을 조절해, 달리기 시스템에 자연스러운 제약을 줄 예정.

📝 메모

오늘은 작업량이 많진 않았지만, 그만큼 구조를 다시 점검하고 다음에 진행할 방향을 명확히 잡을 수 있었다. 특히 Input System의 이벤트 구조를 이해한 건 앞으로 개발할 때 꽤 큰 도움이 될 것 같다. 내일부터는 스태미나 회복 로직과 카메라 충돌 보정까지 마무리해서 기본 이동 시스템을 완성하고 싶다! 💪


반응형
반응형

1) 이번 주 가장 인상 깊었던 배움

  • 디자인 패턴 학습
    옵저버 패턴, 팩토리 패턴, 이벤트 버스, 상태 패턴(FSM 비교 포함), 커맨드 패턴, 전략 패턴, 중재자 패턴을 Unity 예시 코드로 구현하며 개념과 활용 방법을 정리했다.
    특히 상태 패턴과 FSM의 차이를 직접 코드로 확인하며, 상황별로 어떤 구조를 쓰는 것이 유지보수와 확장성 측면에서 유리한지 감을 잡았다.
  • Unity Input System 이해 심화
    InputAction.CallbackContext.phase가 입력 상태 변화 시점에만 호출된다는 점을 이해했고, 지속적인 행동(달리기, 스태미나 소모)은 Update()와 상태 플래그로 처리해야 한다는 것을 알게 됐다.
    이를 1인칭 이동 시스템과 3인칭 오비트 카메라 전환 작업에 적용했다.
  • 플레이어 컨트롤 시스템 개선
    카메라 피벗과 캐릭터 회전을 분리해 스트레이프 이동이 가능하도록 변경했고, 마우스로만 시야를 제어하도록 구현했다.
    1인칭 시점에서 3인칭 시점으로 전환하며 줌, 카메라 충돌 보정, 시야각 제한 등 다양한 기능을 적용했다.
  • 현대세계: 최후의 궁수 프로젝트 마무리
    팀 프로젝트의 마지막 주로, 스킬/업그레이드 시스템과 몬스터 스폰 매니저 구현을 마무리했다.
    각 기능을 통합하고 최종 버그 수정, UI/사운드 조정까지 완료하며 빌드 테스트까지 진행했다.
    프로젝트를 마치면서 협업 과정에서의 커뮤니케이션과 GitHub 브랜치 전략의 중요성을 다시 한번 느꼈다.

2) 그 배움을 얻기까지 어떤 어려움이 있었나요?

  • 디자인 패턴 적용의 난이도
    패턴 자체 개념은 이해했지만, Unity 환경에서 구체적으로 적용하려다 보니 GameObject, 컴포넌트 구조와 어떻게 결합해야 하는지가 쉽지 않았다.
    특히 상태 패턴과 FSM을 비교하며 설계하려니, 상태 전환 로직과 Update 흐름 관리에서 혼동이 있었다.
  • Input System의 한계 이해
    기존에는 OnRun 같은 콜백 메서드 안에서만 처리하려다 보니, Shift를 누른 순간 한 번만 실행되고 계속된 입력이 반영되지 않는 문제가 있었다.
    이 구조를 개선하려고 상태 플래그 + Update() 방식을 도입하는 과정에서 입력 상태 관리 코드를 전반적으로 수정해야 했다.
  • 1인칭 → 3인칭 전환 시 충돌
    카메라 전환 후에도 기존 1인칭 로직이 일부 남아 있어, 캐릭터가 이동할 때 카메라도 같이 회전해 버리는 문제가 있었다.
    이를 해결하려면 이동과 시야 제어를 완전히 분리해야 했고, 관련 코드를 리팩토링하는 데 시간이 꽤 걸렸다.
  • 팀 프로젝트 막바지 통합 작업
    현대세계: 최후의 궁수 프로젝트의 마지막 빌드 과정에서, UI와 사운드 타이밍이 어긋나거나 몬스터 스폰 타이밍이 잘못되는 문제가 발생했다.
    각자 구현한 기능을 통합하면서 충돌이 많았고, GitHub 브랜치 병합 후 테스트 과정에서 예상치 못한 버그를 여러 번 수정해야 했다.

3) 그 과정에서 나는 무엇을 깨달았고, 어떤 감정/생각이 들었나요?

  • 개념 이해보다 적용이 더 어렵다는 걸 다시 한번 느꼈다. 디자인 패턴이나 Input System 같은 기술은 책이나 문서로만 볼 때는 단순해 보이지만, 실제 프로젝트 상황에 맞게 녹여내는 과정에서 많은 변수가 생긴다.
  • 특히 Unity에서는 객체지향 설계와 게임 오브젝트 구조가 맞물려야 유지보수하기 좋은 코드가 된다는 걸 배웠다. 단순히 “작동하는 코드”가 아니라, 앞으로도 수정하기 편한 코드를 만드는 것이 중요하다고 생각하게 됐다.
  • Input System 문제를 해결하면서, 프레임 기반(Update) 처리와 이벤트 기반 처리의 차이를 명확히 구분하게 됐다. 덕분에 앞으로는 입력 구조를 설계할 때 이 두 가지 흐름을 처음부터 고려할 수 있을 것 같다.
  • 팀 프로젝트 막바지에는 기능을 붙이는 것보다 협업과 조율이 더 어렵다는 걸 실감했다. 기능을 합칠수록 충돌이 많아지고, 의도치 않은 버그가 생기는 과정에서 팀원과의 소통이 정말 중요했다.
  • 마지막 빌드를 마쳤을 때, 아쉬움도 있었지만 “이제는 이런 규모의 프로젝트를 처음부터 끝까지 완주할 수 있다”는 자신감이 생겼다.

4) 메모 (느낀 점, 다짐)

이번 주는 새로운 기술을 배우는 것보다 배운 것을 실제로 녹여내고 마무리하는 주였다.
디자인 패턴, Input System, 카메라 전환 등 각각 따로 보면 어렵지 않아 보이지만,
실제 프로젝트 상황에 맞춰 적용하려니 예상치 못한 문제와 구조상의 제약이 많았다.

특히 팀 프로젝트를 완주하면서, 기능 구현보다 중요한 건 협업과 일정 관리라는 걸 다시 느꼈다.
코드 품질과 구조 설계를 미리 잘 잡아놓아야 막판 통합 과정에서 버그와 충돌을 최소화할 수 있다는 교훈도 얻었다.

다음 주에는 이번에 익힌 구조와 패턴을 개인 프로젝트에도 적극적으로 적용해,
기능뿐 아니라 유지보수성과 확장성을 갖춘 코드를 작성하는 습관을 들이고 싶다.
“빨리”보다 “오래” 가는 개발을 목표로 한 주를 보내겠다. 💪

 

 

 

📅이번주 TIL 목록

 

내일배움캠프 26일차 TIL [팀프로젝트(Archero)]

🗓️ 오늘 하루 일정✅ 오전09:00 ~ 11:00 : StageChangeTrigger 조건문 로직 디버깅11:00 ~ 12:30 : MonsterSpawnManager 개선 (스폰 포인트별로 몬스터 순차 소환 / 프리팹 랜덤)🍽️ 점심시간13:00 ~ 14:00 : 점심시

dev-jen.tistory.com

 

내일배움캠프 27일차 TIL [팀프로젝트 마무리 + 발표]

🗓️ 오늘 하루 일정✅ 오전09:00 ~ 13:00 :프로젝트 시연 영상 2종 촬영GitHub README 파일 최종 수정결과보고서 최종 점검프로젝트 기능 최종 확인 및 점검최종 발표 준비 진행🍽️ 점심시간13:00 ~ 14:

dev-jen.tistory.com

 

내일배움캠프 28일차 TIL [Input System과 디자인 패턴 정리]

🗓️ 오늘 하루 일정✅ 오전09:00 ~ 10:00 : 팀원들과 나머지 인사 및 소통 시간10:00 ~ 10:30 : Unity 숙련 주차 발제 강의 수강10:30 ~ 11:00 : 팀원들과 추가 인사 및 친목 시간11:00 ~ 13:00 : 개인 프로젝트 3D_

dev-jen.tistory.com

 

내일배움캠프 29일차 TIL [인벤토리 시스템 구현]

🗓️ 오늘 하루 일정✅ 오전09:00 ~ 13:00 : 3D 서바이벌 프로젝트 진행 (인벤토리 시스템 구현 및 디버깅)ItemData 구조 점검[System.Serializable] 누락 문제 해결inventoryWindow.activeInHierarchy 활용한 UI 상태 확

dev-jen.tistory.com

 

내일배움캠프 30일차 TIL [Project_Up]

📝 TIL - 2025년 8월 8일 (금)🗓️ 오늘 하루 일정✅ 오전09:00 ~ 13:00 : 3D 서바이벌 프로젝트 개발플레이어 애니메이션 시스템 설계 및 구현Blend Tree 애니메이션 전환 테스트 및 디버깅PlayerAnimation, Pla

dev-jen.tistory.com

 

📅이번주 Study 목록

 

Unity - Unity의 메인 카메라(Main Camera) & Projection

🎥 Unity의 메인 카메라(Main Camera)란?✅ 1. 기본 정의Unity 씬(Scene)에서 **플레이어가 보는 시점(Viewport)**을 결정하는 렌더링 카메라기본적으로 Main Camera 태그를 가진 GameObject로 자동 생성됨Camera.main

dev-jen.tistory.com

 

프로젝트 소개 - 「현대세계 최후의 궁수」

🎮 1. 게임 소개이번 프로젝트는 '궁수의 전설(Archero)'에서 영감을 받아 제작한 탑다운 슈팅 기반의 액션 게임입니다.현대 세계를 배경으로, 마지막 남은 궁수가 되어 적들을 물리치고 스테이지

dev-jen.tistory.com

 

Unity - Input System - InputAction

🎮 Unity Input System - InputAction 완전 정복1. 🧭 InputAction이란?InputAction은 Unity의 새 Input System에서 사용자의 입력을 추상화하여 "행동" 단위로 정의하는 구성 요소입니다.예를 들어 Jump, Move, Shoot 같은

dev-jen.tistory.com

 

DesignPattern - 옵저버(Observer) 패턴

🔭 Unity 디자인 패턴 - 옵저버(Observer) 패턴1. 옵저버 패턴이란?옵저버(Observer) 패턴은 어떤 객체의 상태가 변경될 때, 그 객체를 "구독"하고 있는 다른 객체들에게 자동으로 알림을 보내는 구조입

dev-jen.tistory.com

 

DesignPattern - 팩토리(Factory) 패턴

🏭 Unity 디자인 패턴 - 팩토리(Factory) 패턴1. 팩토리 패턴이란?팩토리(Factory) 패턴은 객체의 생성 과정을 감추고, 생성 책임을 별도의 팩토리 클래스에 위임하는 디자인 패턴입니다. Unity에서는 다

dev-jen.tistory.com

 

DesignPattern - 이벤트 버스(Event Bus) 패턴

📡 Unity 디자인 패턴 - 이벤트 버스(Event Bus) 패턴1. 이벤트 버스란?이벤트 버스(Event Bus) 패턴은 객체 간 직접적인 참조 없이, 전역 이벤트 허브를 통해 메시지를 주고받는 구조입니다."A가 B를 직

dev-jen.tistory.com

 

DesignPattern - 상태 패턴(State Pattern) vs FSM

🔄 Unity 디자인 패턴 - 상태 패턴(State Pattern) vs FSM1. 상태를 관리하는 두 가지 방식게임 캐릭터의 행동이나 UI의 전환처럼 상태에 따라 동작이 달라져야 할 때, 우리는 다음 두 가지 접근 방식을

dev-jen.tistory.com

 

DesignPattern - 커맨드(Command) 패턴

🎮 Unity 디자인 패턴 - 커맨드(Command) 패턴1. 커맨드 패턴이란?커맨드(Command) 패턴은 요청(행동)을 하나의 객체로 캡슐화하여,나중에 실행하거나취소하거나저장하거나여러 명령을 큐에 쌓아두었

dev-jen.tistory.com

 

DesignPattern - 전략(Strategy) 패턴

🧠 Unity 디자인 패턴 - 전략(Strategy) 패턴1. 전략 패턴이란?전략(Strategy) 패턴은 행동(알고리즘)을 객체로 분리하여, 런타임에 자유롭게 교체할 수 있도록 하는 패턴입니다."동일한 행동 인터페이

dev-jen.tistory.com

 

DesignPattern - 중재자(Mediator) 패턴

🧩 Unity 디자인 패턴 - 중재자(Mediator) 패턴1. 중재자 패턴이란?중재자(Mediator) 패턴은 객체 간의 직접적인 소통을 피하고, 모든 통신을 중앙 관리자(중재자)를 통해 진행하도록 설계하는 패턴입니

dev-jen.tistory.com

 

Unity - Unity의 TryGetComponent

🎯 Unity의 TryGetComponent 제대로 알기Unity 개발을 하다 보면 오브젝트의 컴포넌트를 가져와야 할 일이 자주 있습니다. 이때 흔히 사용하는 방식이 GetComponent()입니다. 하지만 이 방식은 컴포넌트가

dev-jen.tistory.com

 

Unity - Inventory 인벤토리 시스템 구현 정리

🎒 Unity 3D 서바이벌 게임 개발: 인벤토리 시스템 구현 정리 Unity로 서바이벌 게임을 만들면서 인벤토리 시스템을 구현해봤습니다. 생각보다 복잡했지만, 구현하면서 많이 배운 기능이라 정리해

dev-jen.tistory.com

 

Unity - 3D Survival Game(인벤토리 기능)

# 🧭 3D Survival Game (Unity Project)Unity를 사용하여 제작한 3D 생존 게임입니다. 플레이어는 자원을 수집하고, 적과 전투하며, 아이템을 활용해 생존을 이어갑니다. **불, 날씨, 사운드 등 환경 요소까

dev-jen.tistory.com

 

반응형
반응형

📝 TIL - 2025년 8월 8일 (금)

🗓️ 오늘 하루 일정

✅ 오전

  • 09:00 ~ 13:00 : 3D 서바이벌 프로젝트 개발
    • 플레이어 애니메이션 시스템 설계 및 구현
    • Blend Tree 애니메이션 전환 테스트 및 디버깅
    • PlayerAnimation, PlayerController 스크립트 수정
    • Speed 값 기반으로 Idle, Walk, Run 상태 구분 구현
    • Rigidbody.velocity 기반의 실제 이동속도 반영
    • 애니메이션 끊김 현상 지속적으로 디버깅

🍽️ 점심시간

  • 13:00 ~ 14:00 : 점심시간

✅ 오후

  • 14:00 ~ 18:00 : 인벤토리 및 상태 시스템 검토
    • UIInventory, ItemSlot, Equip, ItemObject 등 주요 시스템 확인 및 정리
    • PlayerCondition, Condition, UICondition 스크립트 정상 작동 확인
    • 상태 UI와 플레이어의 체력 연동 점검
    • FootSteps, DamageIndicator, Interaction 시스템 파일 구성 확인

🍽️ 저녁시간

  • 18:00 ~ 19:00 : 저녁식사

✅ 저녁

  • 19:00 ~ 21:00 : 챌린지반 강의 수강

✅ 오늘 학습 키워드

  • Rigidbody.velocity 기반 애니메이션 전환
  • Blend Tree의 전환 조건 및 루프 설정
  • Animator 파라미터 조정 (Speed, Grounded, Jump, FreeFall)
  • PlayerController에서 실제 움직임 계산 방식
  • 인벤토리 연동 구조 및 상태 UI 연동

✅ 오늘 학습 한 내용을 나만의 언어로 정리하기

 

Unity - 3D Survival Game(인벤토리 기능)

# 🧭 3D Survival Game (Unity Project)Unity를 사용하여 제작한 3D 생존 게임입니다. 플레이어는 자원을 수집하고, 적과 전투하며, 아이템을 활용해 생존을 이어갑니다. **불, 날씨, 사운드 등 환경 요소까

dev-jen.tistory.com

 

오늘은 플레이어의 이동 속도를 기반으로 애니메이션을 자연스럽게 전환해보려 했다. 기존에는 단순히 설정된 moveSpeed를 사용해 애니메이션 속도를 반영했지만, 실제로는 물리적으로 이동하는 velocity 값을 활용해야 보다 현실감 있는 전환이 가능하다는 것을 배웠다.

Speed 파라미터를 Rigidbody.velocity를 통해 계산하고, Idle, Walk, Run 상태로 구분하여 애니메이션 트리거를 조정했다. 하지만 여전히 끊김 현상이 남아있어 다른 방식(FSM, 단일 애니메이션 관리)도 고려하게 됐다.

또한 인벤토리 시스템, 상태 시스템이 잘 정리되어 있는지 확인하고 구조를 복습하면서 내가 어떤 구조로 코드를 짜왔는지 다시 점검할 수 있는 시간이었다.


🧩 학습하며 겪었던 문제점 & 에러

1. 애니메이션 끊김 현상

  • 문제정의 : 걷기/뛰기 애니메이션이 자연스럽게 전환되지 않고 끊기는 문제가 발생
  • 시도 : Blend Tree 속도 조정, 루프타임 확인, Speed 값 변화 확인, 로그 출력
  • 해결방법 : Rigidbody.velocity에서 y축 제외한 수평 속도만 추출하여 사용
  • 새롭게 알게 된 점 : 물리 기반 움직임을 애니메이션에 반영할 땐 실제 움직이는 속도를 써야 자연스럽다
  • 다시 만나게 된다면 : FSM 패턴을 도입하거나 CrossFade 방식으로 스무스한 전환 시도해볼 것

📝 메모

  • 생각보다 애니메이션과 실제 속도의 연동이 까다롭다. 수치를 확인하며 디버깅하는 습관이 정말 중요하다는 걸 다시 느꼈다.
  • 문제를 계속 시도해보다가 “일단은 이렇게 해두자”라는 결론도 중요한 선택이다. 완벽보다 우선순위에 따라 유연하게 넘어가는 게 실력이다.
  • 저녁 강의는 집중해서 듣고, 내일은 스테미너 시스템이나 몬스터와의 상호작용 쪽도 해보면 좋을 것 같다!

 

반응형
반응형

# 🧭 3D Survival Game (Unity Project)

Unity를 사용하여 제작한 3D 생존 게임입니다.  
플레이어는 자원을 수집하고, 적과 전투하며, 아이템을 활용해 생존을 이어갑니다.  
**불, 날씨, 사운드 등 환경 요소까지 구현하여 몰입감을 강화했습니다.**


---

## 📌 주요 기능

### 🕹️ 플레이어 조작
- `Input System` 기반 WASD 이동 + Space 점프
- 마우스 카메라 회전 (`LookSensitivity`)
- 체력, 허기, 스태미나 관리 (`PlayerCondition`)

---

### 🛠️ 자원 채집 & 전투 시스템
- `EquipTool` 장비를 통해:
  - 자원 채집 (`Resource.cs`)
  - 적 NPC 공격 (`IDamageable`)
- 공격 시 스태미나 소모 (스태미나 부족 시 공격 불가)

---

### 🤖 적 AI (NPC)
- FSM 상태 기반 (Idle → Wandering → Attacking)
- `NavMeshAgent`로 자동 이동 및 경로 탐색
- 플레이어 감지 후 추적 + 공격
- 사망 시 아이템 드랍 (`ItemData.dropPrefab`)

---

### 🎒 인벤토리 시스템

> **ScriptableObject 기반의 강력한 아이템/장비 관리 기능**

- `ItemData`를 ScriptableObject로 설계 (이름, 설명, 아이콘, 드랍 프리팹, 스탯 등 포함)
- 인벤토리 창(`UIInventory`)에서 다음 기능 제공:
  - ✅ **아이템 획득** (중복 시 스택, 최대 수량 초과 시 드랍)
  - ✅ **아이템 장비/해제** (장비형: `equipPrefab` 연결)
  - ✅ **아이템 사용** (소모형: 체력/허기 회복 등)
  - ✅ **아이템 버리기** (지정 위치에 드랍)
  - ✅ **선택 아이템 정보창** (이름, 설명, 스탯 등 표시)
- `ItemSlot.cs` 기반의 슬롯 UI로 정리됨

---

### 🔥 환경 시스템

#### 🌙 낮/밤 사이클 (`DayNightCycle.cs`)
- 태양과 달의 위치, 색상, 세기 등을 시간에 따라 자동 조정
- `Gradient` + `AnimationCurve` 활용한 자연스러운 변화

#### 🔥 캠프파이어 데미지 (`CampFire.cs`)
- 불에 들어가면 `IDamageable`에게 지속 피해
- `OnTriggerEnter/Exit` + `InvokeRepeating()` 사용

#### 🎵 음악 존 (`MusicZone.cs`)
- 특정 구역에 들어가면 음악이 서서히 켜지고
- 나가면 다시 꺼지는 `Fade In/Out` 사운드 구현

---

### 👣 사운드 연출
- 플레이어가 일정 속도로 움직일 때마다
- 발걸음 소리(`FootSteps.cs`)를 무작위로 재생

---

## 📂 프로젝트 구조

```text
Assets/
├── 01_Scripts/                    → 전체 스크립트 폴더
│   ├── Character/                → 플레이어 컨트롤, 상태 시스템 등 핵심 로직
│   │   ├── CharacterManager.cs   → 싱글톤 플레이어 관리자
│   │   ├── Player.cs             → 입력 & 상태 연결
│   │   ├── PlayerController.cs   → 이동 및 점프 처리
│   │   ├── PlayerCondition.cs    → 체력, 허기, 스태미나 관리
│   │   └── Condition.cs          → 상태 수치 정의용 데이터 클래스
│   │
│   ├── Equipments/               → 장비 및 아이템 사용 관련
│   │   ├── Equip.cs              → 장비 착용
│   │   ├── EquipTool.cs          → 채집 및 공격 도구 처리
│   │   └── Equipment.cs          → 장착 아이템 프리팹 관리
│   │
│   ├── Environment/              → 환경 오브젝트 관련
│   │   ├── CampFire.cs           → 캠프파이어 데미지 처리
│   │   ├── DayNightCycle.cs      → 낮/밤 주기 시스템
│   │   └── MusicZone.cs          → 음악 재생 구역 (페이드 인/아웃)
│   │
│   ├── Interaction/              → 플레이어와 상호작용 가능한 오브젝트
│   │   ├── Interaction.cs        → Raycast 기반 상호작용 처리
│   │   └── Resource.cs           → 자원 채집 가능 오브젝트
│   │
│   ├── Items/                    → 아이템 데이터 및 관련 기능
│   │   ├── ItemData.cs           → ScriptableObject 기반 아이템 정의
│   │   ├── ItemObject.cs         → 인게임 드랍용 오브젝트
│   │   ├── ItemSlot.cs           → 인벤토리 슬롯 UI 처리
│   │   └── Equipment.cs          → 장비 프리팹과 연결되는 아이템 기능
│   │
│   ├── NPCs/                     → 적 AI 관련 스크립트
│   │   └── NPC.cs                → NavMesh 기반 상태 머신 AI (Wander, Attack)
│   │
│   └── UI/                       → 사용자 인터페이스 관련
│       ├── UICondition.cs        → 체력, 허기, 스태미나 UI 연동
│       ├── UIInventory.cs        → 인벤토리 UI 처리
│       └── DamageIndicator.cs    → 피격 시 화면 이펙트
'''












반응형
반응형

🗓️ 오늘 하루 일정

✅ 오전

  • 09:00 ~ 13:00 : 3D 서바이벌 프로젝트 진행 (인벤토리 시스템 구현 및 디버깅)
    • ItemData 구조 점검
    • [System.Serializable] 누락 문제 해결
    • inventoryWindow.activeInHierarchy 활용한 UI 상태 확인 방법 학습

🍽️ 점심시간

  • 13:00 ~ 14:00 : 점심시간

✅ 오후

  • 14:00 ~ 16:00 : 인벤토리 UI 개선 및 아이템 장착 시스템 구현
    • 아이템 클릭 → 정보 출력 → 버튼 기능 연결 흐름 정리
    • 장착된 무기에 따라 공격 애니메이션 동작하도록 연동
  • 16:00 ~ 17:00 : 챌린지반 강의 수강 (3D 장착 시스템 및 애니메이션 연동)
  • 17:00 ~ 18:00 : 인벤토리 기능 블로그용 정리, 유니티 12강 까지 마무리

🍽️ 저녁시간

  • 18:00 ~ 19:00 : 저녁시간

✅ 저녁

  • 19:00 ~ 21:00 : TIL 작성 및 인벤토리 정리 문서 마무리

✅ 오늘 학습 키워드

  • ScriptableObject를 활용한 아이템 데이터 구성
  • ItemDataConsumable 배열에 [System.Serializable] 속성 필요성
  • IInteractable 인터페이스 구현을 통한 상호작용 처리
  • TextMeshPro 오토 사이즈(Auto Size) 기능
  • inventoryWindow.activeInHierarchy를 통한 UI 활성 상태 판별
  • 인벤토리 슬롯 구성 및 아이템 스택 처리
  • InputAction으로 인벤토리 열고 닫기 구현
  • 아이템 장착 및 장착된 무기 애니메이션 트리거 연결
  • Non-convex MeshCollider 오류 해결 방법
  • UIInventory, ItemSlot, SelectedItemWindow 연동 흐름 이해

✅ 오늘 학습 한 내용을 나만의 언어로 정리하기

 

Unity - Unity의 TryGetComponent

🎯 Unity의 TryGetComponent 제대로 알기Unity 개발을 하다 보면 오브젝트의 컴포넌트를 가져와야 할 일이 자주 있습니다. 이때 흔히 사용하는 방식이 GetComponent()입니다. 하지만 이 방식은 컴포넌트가

dev-jen.tistory.com

 

Unity - Inventory 인벤토리 시스템 구현 정리

🎒 Unity 3D 서바이벌 게임 개발: 인벤토리 시스템 구현 정리 Unity로 서바이벌 게임을 만들면서 인벤토리 시스템을 구현해봤습니다. 생각보다 복잡했지만, 구현하면서 많이 배운 기능이라 정리해

dev-jen.tistory.com

 

오늘은 인벤토리 시스템을 본격적으로 구현했다. 처음에는 단순히 아이템을 담는 창을 만든다고 생각했지만, 막상 구현을 시작하니 생각보다 훨씬 많은 것들이 얽혀 있었다.

먼저 ScriptableObject를 활용해 아이템 데이터 구조를 설계했다. 이름, 설명, 아이콘, 스택 가능 여부뿐 아니라 소비형 아이템의 효과 정보까지 넣어야 했고, 장착 가능한 아이템은 장착용 프리팹까지 연결해줘야 했다. 여기서 내가 [System.Serializable]을 빼먹는 바람에 Inspector에서 ItemDataConsumable[]이 보이지 않는 일이 있었는데, 이걸 찾느라 꽤 시간을 썼다. 이처럼 사소한 부분 하나로 진행이 막히는 경험을 다시 했다.

아이템을 줍는 기능은 IInteractable 인터페이스를 통해 Raycast로 구현했다. 플레이어가 F 키를 누르면 아이템을 인벤토리에 추가하고, 슬롯에 아이콘과 수량이 표시되도록 구성했다. TextMeshPro 컴포넌트로 아이콘 수량을 보여주는 부분도 처음에는 연결이 안 되어 헤맸지만, 드래그로 연결해주니 잘 작동했다.

가장 어려웠던 건 인벤토리 UI를 구성하고, 아이템의 상태에 따라 버튼의 동작을 다르게 하는 부분이었다. 특히 장착 기능은 장착 여부에 따라 상태를 바꿔줘야 했고, 장착한 아이템의 애니메이션까지 연동해줘야 했다. Attack 트리거를 Animator에 추가하고, InputAction으로 공격 키를 눌렀을 때 무기 애니메이션이 나오도록 하는 과정을 구현했다. 이 과정에서 Non-convex MeshCollider 오류도 발생했는데, Equip된 무기에 Rigidbody가 달려있어서 생긴 문제였다. 이건 Rigidbody를 제거하거나 Kinematic으로 설정해서 해결했다.

이번 작업은 단순한 UI를 넘어서, 아이템의 속성과 상태에 따라 실제 월드와 상호작용하는 흐름까지 전반적으로 이해하고 구현해야 했던 시간이었다. 기능 하나를 넣기 위해 생각보다 많은 곳에서 서로 연결되어 있다는 걸 깨달았고, 그래서 더 어려웠던 것 같다.


🧩 학습하며 겪었던 문제점 & 에러

1. Inspector에 ItemDataConsumable[] 배열이 안 보임

  • 문제정의
  • ItemData에 ItemDataConsumable[] consumables 필드를 추가했지만, Inspector 창에서 해당 배열이 나타나지 않음.
  • 시도
  • Unity를 재시작하거나 다시 컴파일해봤지만 여전히 Inspector에 보이지 않음.
  • 해결방법
  • ItemDataConsumable 클래스에 [System.Serializable] 어트리뷰트를 추가함으로써 해결됨.
  • 예시 코드
[System.Serializable]
public class ItemDataConsumable
{
    public ConsumableType type;
    public float value;
}
  • 새롭게 알게 된 점
  • 커스텀 클래스가 Inspector에 표시되기 위해서는 반드시 [System.Serializable]을 선언해줘야 한다는 것을 다시 한 번 확인함.
  • 다시 만나게 된다면
  • ScriptableObject에 배열이나 리스트를 사용할 때는 무조건 [System.Serializable]을 먼저 확인할 것!

2. 아이템이 획득되지 않고 사라지는 현상

  • 문제정의
  • 아이템을 획득했는데, 인벤토리에 들어오지 않고 그냥 사라져버림.
  • 시도
  • AddItem() 로직을 따라가면서 GetEmptySlot()이 null을 반환하는지 확인함.
  • 해결방법
  • 인벤토리 슬롯이 가득 차 있을 경우 ThrowItem() 메서드가 자동으로 실행되어 아이템이 땅에 떨어지게 되어 있었음. 하지만 땅에 떨어지는 위치가 카메라에서 잘 안 보여서 사라진 것처럼 느껴졌던 것.
  • 새롭게 알게 된 점
  • 예상치 못한 분기 처리(슬롯이 없을 경우 자동 드롭)도 충분히 인지하고 있어야 디버깅이 쉬워진다는 점을 배움.

3. Non-convex MeshCollider 오류

  • 문제정의"Non-convex MeshCollider with non-kinematic Rigidbody is no longer supported since Unity 5..."
  • 무기를 장착한 후 게임 실행 시 콘솔에 다음과 같은 에러 발생:
  • 시도
  • 메시가 잘못됐나 싶어 FBX 파일을 다시 임포트해보기도 했지만 여전히 오류 발생.
  • 해결방법Rigidbody 제거 또는 Kinematic 체크로 해결됨.
  • Equip_Sword(Clone) 안의 무기 모델에 Rigidbody가 붙어 있었고, Kinematic이 체크되어 있지 않아서 발생한 문제.
  • 새롭게 알게 된 점
  • Unity는 Non-convex MeshCollider에 대해 일반 Rigidbody를 허용하지 않으며, 반드시 Kinematic으로 설정하거나 Collider를 Convex로 바꿔야 한다는 점을 다시 확실히 알게 됨.

📝 메모

오늘은 정말 많은 걸 구현했지만, 그만큼 어려움도 많았던 하루였다. 처음에는 단순히 아이템을 저장하는 UI 창만 만들면 되는 줄 알았는데, 아이템을 줍는 것부터 시작해서, 장착하고 애니메이션까지 연결하는 흐름이 하나로 연결되어 있다는 걸 직접 부딪히며 체감했다.

특히 인벤토리는 보이는 것보다 훨씬 복잡한 구조였다. 아이템을 주울 때마다 슬롯을 확인하고, 겹칠 수 있는 아이템이면 스택을 쌓고, 장착 상태도 관리하고, UI도 갱신해야 하고… 구현하면서 머리가 지끈했지만, 그래도 하나씩 해결해가니까 재미도 있었던 것 같다.

중간에 오류나 디버깅이 많아서 멈추기도 했지만, 그 과정에서 Unity의 동작 원리를 더 깊이 이해할 수 있었고, 이제는 아이템을 클릭했을 때 어떤 동작이 이어지는지, 그리고 그걸 어떻게 연결해야 할지 감이 조금씩 잡히고 있다. 그만큼 또 한 걸음 나아간 하루였다고 생각한다. 오늘은 여기까지! 내일도 화이팅!!!!!

오늘 만든 인벤토리 시스템!

 

반응형
반응형

🎒 Unity 3D 서바이벌 게임 개발: 인벤토리 시스템 구현 정리

Unity로 서바이벌 게임을 만들면서 인벤토리 시스템을 구현해봤습니다. 생각보다 복잡했지만, 구현하면서 많이 배운 기능이라 정리해두면 좋을 것 같았습니다!


✔️ 구현 목표

  • 아이템을 줍고 인벤토리에 추가하기
  • 인벤토리 창을 열고 닫기
  • 아이템을 클릭해서 정보 확인하기
  • 아이템 사용/버리기/장착/해제 기능 만들기
  • 장착 아이템에 따라 공격 애니메이션 실행하기

🧩 1. 아이템 데이터 구조: ScriptableObject 활용

아이템은 ScriptableObject로 데이터를 따로 관리하게 했습니다! 이렇게 하면 인스펙터에서 편하게 데이터를 만들고, 재활용할 수도 있어서 좋습니다!

[CreateAssetMenu(fileName = "Item", menuName = "New Item")]
public class ItemData : ScriptableObject
{
    public string displayName;
    public string description;
    public ItemType type; // Equipable, Consumable, Resource
    public Sprite icon;
    public GameObject dropPrefab;

    public bool canStack;
    public int maxStackAmount;

    public ItemDataConsumable[] consumables; // 회복 효과 등
    public GameObject equipPrefab; // 장착 아이템일 경우 연결할 프리팹
}

💡 헷갈렸던 점: ItemDataConsumable이 Inspector에 안 보이길래 왜 그런가 했는데, [System.Serializable]을 안 붙여서 그런 거였어요. 이거 빠뜨리면 아무리 배열이어도 인스펙터에 안 뜨더라고요.


🖱️ 2. 아이템과 상호작용: Raycast + IInteractable

화면 가운데서 Ray를 쏴서 아이템을 감지하고, IInteractable 인터페이스를 통해 상호작용할 수 있도록 했습니다!

public interface IInteractable
{
    string GetInteractPrompt();
    void OnInteract();
}

아이템 오브젝트에 이 인터페이스를 붙이고, 플레이어가 가까이 가서 E 키를 누르면 OnInteract()가 실행되도록 구성했어요. 이때 아이템 데이터를 Player.itemData로 넘기고, 인벤토리에 추가됩니다.


🧭 3. 인벤토리 UI 구성

인벤토리는 다음과 같은 구조로 만들었습니다:

  • UIInventory.cs: 전체 인벤토리 관리 (슬롯 생성, 아이템 추가, 정보 표시 등)
  • ItemSlot.cs: 슬롯 한 칸에 대한 개별 관리 (아이콘, 수량 표시 등)
  • SelectedItemWindow: 아이템 이름, 설명, 스탯, 버튼들 표시
public void Set()
{
    icon.sprite = item.icon;
    quatityText.text = quantity > 1 ? quantity.ToString() : string.Empty;
}

아이템이 있는 경우 아이콘을 보여주고, 수량이 1보다 크면 숫자를 표시합니다.


➕ 4. 아이템 획득 로직

플레이어가 아이템을 획득하면 Player.itemData에 데이터가 저장되고, 인벤토리에서 AddItem() 메서드를 호출해서 슬롯에 추가됩니다!

public void AddItem()
{
    ItemData data = CharacterManager.Instance.Player.itemData;

    if (data.canStack)
    {
        ItemSlot slot = GetItemStack(data);
        if (slot != null)
        {
            slot.quantity++;
            UpdateUI();
            return;
        }
    }

    ItemSlot emptySlot = GetEmptySlot();
    if (emptySlot != null)
    {
        emptySlot.item = data;
        emptySlot.quantity = 1;
        UpdateUI();
        return;
    }

    // 슬롯이 꽉 찼다면, 아이템을 땅에 떨어뜨림
    ThrowItem(data);
}

🍽️ 5. 아이템 사용하기

아이템이 소비 아이템(예: 당근, 고기 등)일 경우, 버튼을 눌러서 사용할 수 있습니다!

public void OnUseButton()
{
    foreach (var effect in selectedItem.item.consumables)
    {
        switch (effect.type)
        {
            case ConsumableType.Health:
                condition.Heal(effect.value); break;
            case ConsumableType.Hunger:
                condition.Eat(effect.value); break;
        }
    }
    RemoveSelectedItem();
}

사용하고 나면 수량을 줄이고, 0개가 되면 슬롯을 비워주는 식으로 처리돼요!


🧹 6. 아이템 삭제 및 드롭

버튼을 눌러서 아이템을 버릴 수도 있어요. 이때는 아이템을 Instantiate() 해서 실제 땅에 떨어뜨리는 방식으로 처리했습니다!

public void OnDropButton()
{
    ThrowItem(selectedItem.item);
    RemoveSelectedItem();
}

처음엔 그냥 리스트에서만 지우면 끝인 줄 알았는데, 직접 프리팹을 땅에 생성해야 눈에도 보이고 다시 주울 수도 있겠더라고요.


📌 7. 인벤토리 열고 닫기 (InputSystem)

새로운 InputSystem을 이용해서 Tab 키(또는 설정한 키)로 인벤토리를 열고 닫을 수 있어요.

public void OnInventoryButton(InputAction.CallbackContext context)
{
    if (context.phase == InputActionPhase.Started)
    {
        inventory?.Invoke();
        ToggleCursor();
    }
}

ToggleCursor()도 함께 써서 인벤토리를 열 때는 마우스가 보이도록 설정했어요. 이거 안 하면 클릭이 안 되더라고요!

 

🔁 8. 선택한 아이템 처리 로직

아이템 슬롯을 클릭하면 해당 아이템이 선택되고, 우측 정보창에 이름/설명/스탯이 표시돼요. 이때 어떤 아이템을 선택 중인지 저장해두는 selectedItemIndex, selectedItem도 함께 관리해줘야 해요.

public void SelectItem(int index)
{
    selectedItemIndex = index;
    selectedItem = slots[index];
    UpdateSelectedItemUI();
}

처음에는 NullReferenceException 오류가 났었는데, 알고 보니 선택한 슬롯에 아이템이 없는데 접근하려고 해서 그랬어요. 꼭 조건 체크도 같이 해줘야 해요!


✅ 마무리하며

처음에는 생각보다 구현할 게 많아서 막막했는데, 하나씩 만들어가다 보니까 점점 구조가 잡히더라고요. 특히 ScriptableObjectInputSystem을 잘 활용하면 유지보수도 편하고, UI도 깔끔하게 정리할 수 있었습니다!

 

스파르타 코딩클럽에서 배운 내용인데 기록을 안해놓으면 까먹을거 같아서 작성해요!

실행화면입니다!

 

반응형
반응형

🎯 Unity의 TryGetComponent 제대로 알기

Unity 개발을 하다 보면 오브젝트의 컴포넌트를 가져와야 할 일이 자주 있습니다. 이때 흔히 사용하는 방식이 GetComponent<T>()입니다. 하지만 이 방식은 컴포넌트가 존재하지 않을 경우 NullReferenceException을 발생시킬 수 있다는 단점이 있습니다.

이런 위험을 피하고자 Unity에서는 보다 안전한 방식으로 컴포넌트를 가져올 수 있는 TryGetComponent<T>() 메서드를 제공합니다. 이 글에서는 TryGetComponent의 기본 개념, 사용법, 실제 활용 예시, 그리고 GetComponent와의 차이점까지 상세히 정리해보겠습니다.


📌 TryGetComponent란?

TryGetComponent<T>(out T component)는 특정 컴포넌트가 현재 GameObject에 존재할 경우 해당 컴포넌트를 안전하게 가져오는 메서드입니다. 존재하지 않을 경우 false를 반환하고, 컴포넌트를 반환하지 않기 때문에 예외가 발생하지 않습니다.

✅ 문법

public bool TryGetComponent<T>(out T component) where T : Component;
  • T : 가져오려는 컴포넌트 타입 (예: Rigidbody, Collider, Animator, 사용자 정의 컴포넌트 등)
  • component : 컴포넌트를 담을 out 매개변수
  • 리턴값 : 해당 컴포넌트가 존재하면 true, 없으면 false

🔍 왜 TryGetComponent를 써야 하나요?

기존의 GetComponent는 다음과 같은 단점이 있습니다:

Rigidbody rb = GetComponent<Rigidbody>();
rb.AddForce(Vector3.up * 10f); // Rigidbody가 없으면 NullReferenceException!

이 코드에서 Rigidbody가 붙어있지 않다면 NullReferenceException이 발생합니다. 이를 방지하려면 다음과 같이 조건문을 넣어야 하죠:

Rigidbody rb = GetComponent<Rigidbody>();
if (rb != null)
{
    rb.AddForce(Vector3.up * 10f);
}

하지만 TryGetComponent를 사용하면 이런 코드를 더 깔끔하고 안전하게 작성할 수 있습니다:

if (TryGetComponent<Rigidbody>(out Rigidbody rb))
{
    rb.AddForce(Vector3.up * 10f);
}
  • 장점: 예외 처리 필요 없음
  • 성능: 내부적으로 GetComponent보다 성능이 약간 더 나은 것으로 알려져 있음 (Unity 공식 문서 기준)
  • 가독성: 조건문 안에서 바로 사용할 수 있어 코드가 간결해짐

💡 실전 예제

🎯 예제 1: 총알이 적에게 부딪혔을 때 데미지 주기

private void OnTriggerEnter(Collider other)
{
    if (other.TryGetComponent<Enemy>(out Enemy enemy))
    {
        enemy.TakeDamage(10f);
        Destroy(gameObject); // 총알 삭제
    }
}

총알이 부딪힌 오브젝트가 Enemy 컴포넌트를 가지고 있다면 데미지를 주고, 총알은 파괴합니다. 만약 적이 아닌 오브젝트라면 아무 일도 일어나지 않습니다.

🎯 예제 2: 인터랙션 가능한 오브젝트 찾기

private void Interact(GameObject target)
{
    if (target.TryGetComponent<IInteractable>(out IInteractable interactable))
    {
        interactable.Interact();
    }
}

IInteractable 인터페이스를 구현한 오브젝트와만 상호작용하도록 처리할 수 있습니다.


🔄 GetComponent vs TryGetComponent

항목 GetComponent<T>() TryGetComponent<T>(out T)
반환값 컴포넌트 객체 또는 null true 또는 false
예외 처리 null 체크 필요 예외 없음
성능 약간 느릴 수 있음 약간 더 효율적
코드 가독성 길어질 수 있음 간결하고 안전함
 

🧠 마무리하며

TryGetComponent는 Unity 개발에서 보다 안전하고 깔끔한 코드를 작성하는 데 매우 유용한 도구입니다. 특히 충돌 처리, 상호작용, 조건부 로직 등에서 널 예외를 방지하고 코드를 단순하게 만들어 줍니다.

앞으로 GetComponent 대신 TryGetComponent를 기본으로 사용하는 습관을 들여보세요. 실수도 줄고, 유지보수도 쉬워질 것입니다!

반응형