no image
Unity - 3D Squad Swarm (팀 프로젝트)
3D_Squad_Swarm — 상태머신으로 쌓아 올린 라운드형 좀비 액션작은 맵에서 플레이어(좀비) vs NPC가 빠르게 부딪히는 3D 라운드 액션.상태머신(FSM) 아키텍처, NavMesh AI, Stage·UI·Audio 매니저 계층으로 구성.캐릭터 전부 FSM으로 설계(플레이어·NPC·좀비)제한시간/감염도 게이지가 핵심 규칙StageConfig(SO)로 난이도·스폰 제어, UI는 상태 전환 파이프라인NavMesh + Obstacle로 길막을 네비 데이터에서 보장▶️ 플레이영상🕹️ 플레이방법실행하면 Intro → StageSelect → Game 순으로 진입제한시간 안에 적을 처치하여 감염도를 채우면 GameClear시간 초과는 TimeUP, 플레이어 사망은 GameOver일시정지는 Pause(예:..
2025.09.05
no image
Unity - TopDown 레이싱 방치형 RPG
🚗 Unity Car Survival Prototype — 데이터·이벤트 기반 차량 생존 슈팅 만들기자동차에 무기를 장착해 몰려오는 적을 처치하며 스테이지를 버티는 탑다운 프로토타입입니다.싱글톤 매니저, 무기/업그레이드, 스테이지/스폰, 리스폰, UI·사운드까지 확장 가능한 구조로 구현했어요.🧾 개요요약: ScriptableObject(WeaponData) + 이벤트 기반(UI/상태) + 제네릭 싱글톤 매니저로 빠르게 기능을 붙여 나갈 수 있는 구조.핵심 요소: 자동 주행(CarAI), 자동 사격(BaseWeapon), 업그레이드(WeaponSlotsManager), Respawn, 스테이지 타이머/스폰.대상 독자: 서바이벌라이크/탑다운 프로토타입을 빨리 만들고 싶은 분, 아키텍처 참고가 필요한 분...
2025.08.29
no image
Unity - Project_Up
🎮 Project_Up – Unity 3D 서바이벌 시스템 제작기안녕하세요! 오늘은 제가 진행한 Unity 프로젝트 Project_Up을 소개하려고 합니다.이 프로젝트는 3D 환경에서 플레이어가 이동하고, 오브젝트와 상호작용하며, 자원을 채집하고 아이템을 사용하는 기본적인 서바이벌형 시스템을 구현한 예제입니다.최근에는 장비(Equip) 시스템을 완전히 제거하고, 소비형 아이템 중심의 단순한 인벤토리 구조로 리팩토링을 진행했어요.덕분에 전체 구조가 훨씬 가볍고 직관적으로 변했습니다.📌 프로젝트 개요장르 : 3D 서바이벌 / 인터랙션엔진 : Unity 2021.3 LTS개발 언어 : C#주요 목표캐릭터 이동, 점프, 달리기, 카메라 제어상호작용 가능한 오브젝트와의 간단한 인터페이스소비형 아이템 중심의 인..
2025.08.13
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
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
no image
Unity - Input System - InputAction
🎮 Unity Input System - InputAction 완전 정복1. 🧭 InputAction이란?InputAction은 Unity의 새 Input System에서 사용자의 입력을 추상화하여 "행동" 단위로 정의하는 구성 요소입니다.예를 들어 Jump, Move, Shoot 같은 게임 내 행동(행위) 을 정의하고, 여기에 어떤 키보드/마우스/게임패드 입력이 연결될지를 설정합니다.📦 InputAction 구성 구조InputActionAsset └─ Action Map (예: Player) └─ InputAction (예: Move, Jump) └─ Binding (예: WASD, Spacebar, Gamepad A버튼)InputActionAsset: 모든 입력 행동을 담는..
2025.08.06
no image
프로젝트 소개 - 「현대세계 최후의 궁수」
🎮 1. 게임 소개이번 프로젝트는 '궁수의 전설(Archero)'에서 영감을 받아 제작한 탑다운 슈팅 기반의 액션 게임입니다.현대 세계를 배경으로, 마지막 남은 궁수가 되어 적들을 물리치고 스테이지를 클리어하는 것이 목표입니다.게임의 제목은 **「현대세계 최후의 궁수」**입니다.유저는 다양한 무기와 스킬을 활용하여 몬스터와 보스를 처치하고, 방을 클리어하며 점점 더 강해지는 전투를 경험할 수 있습니다.🕹️ 2. 플레이 방법이동 조작 : W, A, S, D 키로 캐릭터를 상하좌우로 움직입니다.공격 조작 : 마우스 좌클릭으로 공격합니다. 자동 발사 방식이 아니며, 직접 눌러야 발사됩니다.스킬 선택 : 방을 클리어할 때마다 3개의 스킬 중 하나를 무작위로 선택하여 강화할 수 있습니다.스테이지 진행 : 각 ..
2025.08.05
반응형

3D_Squad_Swarm — 상태머신으로 쌓아 올린 라운드형 좀비 액션

작은 맵에서 플레이어(좀비) vs NPC가 빠르게 부딪히는 3D 라운드 액션.
상태머신(FSM) 아키텍처, NavMesh AI, Stage·UI·Audio 매니저 계층으로 구성.

  • 캐릭터 전부 FSM으로 설계(플레이어·NPC·좀비)
  • 제한시간/감염도 게이지가 핵심 규칙
  • StageConfig(SO)로 난이도·스폰 제어, UI는 상태 전환 파이프라인
  • NavMesh + Obstacle로 길막을 네비 데이터에서 보장

▶️ 플레이영상

🕹️ 플레이방법

  1. 실행하면 IntroStageSelectGame 순으로 진입
  2. 제한시간 안에 적을 처치하여 감염도를 채우면 GameClear
  3. 시간 초과는 TimeUP, 플레이어 사망은 GameOver
  4. 일시정지는 Pause(예: ESC)

입력 바인딩은 Unity Input System의 PlayerInput 에셋에서 관리.


✨ Features

  • 상태머신 기반 캐릭터 로직
    • 공통 FSM: StateMachine.cs
    • Player: PlayerStateMachine + PlayerIdle/Walk/Ground/Attack/Death
    • NPC: NPCBaseState, NPCAttackState (Idle/Chase/Flee/Death 포함)
    • Zombie(Follower/Charging): ZombieStateMachine + ZombieIdle/Follow/Chasing/Charge/Attack/Death/Rise
  • Stage 시스템 & 스폰
    • StageConfig(ScriptableObject) → StageManager + StageSpawner
    • 씬·스폰·타이머·네비 설정을 SO로 일괄 관리
  • UI 파이프라인
    • UIManager가 Intro/StageSelect/Game/Pause/GameOver/GameClear/TimeUP 전환
    • 타이머는 코루틴 핸들 보관으로 중복 실행 방지
  • 전투/인터랙션
    • IDamageable, Bullet, GunAimer, ForceReciever
    • 애니메이션 이벤트: AnimationEventForwarder, NPCAnimationEventForwarder
  • 오디오/카메라
    • AudioManager, BGMVolumeController, SoundEffect
    • CameraManager + CameraDeathEffect, CameraOcclusion

🧩 설계 & 디자인패턴

  • State Pattern(FSM): 각 도메인(플레이어/NPC/좀비)에 독립 FSM. Enter/Exit/Update 수명주기.
  • Singleton<T>: GameManager / StageManager / UIManager / AudioManager 등 전역 매니저 계층.
  • ScriptableObject 주입: StageConfig, ScriptableStats → 런타임에 StageManager/StatHandler가 적용.
  • Event-Driven: 스테이지 시작/종료/컷신 신호를 이벤트로 브로드캐스트.
  • NavMesh 중심 설계: 경로/차단을 네비 데이터로 일관되게 처리(Obstacle/Carving 포함).

🛠️ 사용기술 & 시스템

  • Unity(LTS), C#
  • NavMesh: NavMeshAgent, NavMeshObstacle(Carving), NavMesh 샘플링 스폰
  • Input System: PlayerInput 액션 맵
  • UGUI: Canvas/UI 화면 전환·게이지
  • Cinemachine(선택): 가상 카메라/우선도
  • Coroutines: 타이머·페이드·스폰 루프
  • ScriptableObject: 스테이지/스탯 파라미터화

📂 폴더 구조(요약, 실제 프로젝트 기준)

 
02. Scripts
├─ AnimationForwarder
│  ├─ AnimationEventForwarder.cs
│  └─ NPCAnimationEventForwarder.cs
│
├─ Base
│  ├─ StateMachine
│  │  └─ StateMachine.cs
│  ├─ Ui
│  │  └─ BaseUI.cs
│  └─ Zombie
│     └─ BaseZombie.cs
│
├─ Camera
│  ├─ CameraDeathEffect.cs
│  └─ CameraOcclusion.cs
│
├─ ForceReciever
│  └─ ForceReciever.cs
│
├─ Interface
│  └─ Attack
│     └─ IDamageable.cs
│
├─ Manager
│  ├─ Player
│  │  └─ PlayerManager.cs
│  ├─ Stage
│  │  └─ StageManager.cs
│  ├─ UI
│  │  └─ UIManager.cs
│  ├─ Util
│  │  ├─ AudioManager.cs
│  │  └─ CameraManager.cs
│  └─ Zombie
│     └─ ZombieManager.cs
│
├─ NPC
│  ├─ Gun
│  │  ├─ Bullet.cs
│  │  └─ GunAimer.cs
│  └─ StateMachin
│     ├─ NPCAttackState.cs
│     ├─ NPCBaseState.cs
│     ├─ NPCChaseState.cs
│     ├─ NPCDeathState.cs
│     ├─ NPCFleeState.cs
│     ├─ NPCGroundState.cs
│     ├─ NPCIdleState.cs
│     ├─ NPCStateMachine.cs
│     └─ NPCTest.cs
│
├─ Player
│  ├─ Anim
│  │  └─ PlayerAnimationData.cs
│  ├─ StateMachine
│  │  ├─ PlayerAttackState.cs
│  │  ├─ PlayerBaseState.cs
│  │  ├─ PlayerDeathState.cs
│  │  ├─ PlayerGroundState.cs
│  │  ├─ PlayerIdleState.cs
│  │  ├─ PlayerStateMachine.cs
│  │  └─ PlayerWalkState.cs
│  ├─ EnemyPointer.cs
│  ├─ Player.cs
│  └─ PlayerController.cs
│
├─ ScriptableObjects
│  └─ Stage
│     └─ StageConfig.cs
│
├─ Singleton
│  ├─ BGMVolumeController.cs
│  ├─ Singleton.cs
│  └─ SoundEffect.cs
│
├─ Spawner
│  ├─ Player
│  │  └─ PlayerSpawner.cs
│  └─ Stage
│     └─ StageSpawner.cs
│
├─ Stats
│  ├─ ScriptableStats.cs
│  └─ StatHandler.cs
│
├─ UI
│  ├─ Billboard
│  │  └─ Billboard.cs
│  ├─ Effect
│  │  └─ HoverEffect.cs
│  ├─ Game
│  │  ├─ GameClearUI.cs
│  │  ├─ GameOverSkipButton.cs
│  │  ├─ GameOverTimeline.cs
│  │  ├─ GameOverUI.cs
│  │  ├─ GameUI.cs
│  │  ├─ MoveArrow.cs
│  │  └─ MoveArrow1.cs
│  ├─ Handle
│  │  └─ HandleAnimationController.cs
│  ├─ Intro
│  │  ├─ IntroSkipButton.cs
│  │  ├─ IntroTimeline.cs
│  │  └─ IntroUI.cs
│  ├─ Stage
│  │  └─ StageSelectUI.cs
│  └─ Util
│     ├─ OptionUI.cs
│     ├─ PauseUI.cs
│     └─ TimeUPUI.cs
│
└─ Zombie
   ├─ Charging
   │  └─ ChargingZombie.cs
   └─ FollowerZombie
      ├─ FollowerZombie.cs
      └─ StateMachine
         ├─ ZombieAttackState.cs
         ├─ ZombieBaseState.cs
         ├─ ZombieChargeState.cs
         ├─ ZombieChasingState.cs
         ├─ ZombieDeathState.cs
         ├─ ZombieFollowState.cs
         ├─ ZombieIdleState.cs
         ├─ ZombieRiseState.cs
         └─ ZombieStateMachine.cs

🧯 트러블슈팅

 

🎮 개발자, 기획자

개발자 : 이재은, 박정현, 박민제, 임성준

기획자 :김노아, 황의영

🎮 개발기간 

개발기간 :(2025.09.01~09.05)

게임 시작 Scene
스테이지 Scene

나머지는 영상에 나와있습니다!

 

🧯 Troubleshooting

 

 

반응형
반응형

🚗 Unity Car Survival Prototype — 데이터·이벤트 기반 차량 생존 슈팅 만들기

자동차에 무기를 장착해 몰려오는 적을 처치하며 스테이지를 버티는 탑다운 프로토타입입니다.
싱글톤 매니저, 무기/업그레이드, 스테이지/스폰, 리스폰, UI·사운드까지 확장 가능한 구조로 구현했어요.


🧾 개요

  • 요약: ScriptableObject(WeaponData) + 이벤트 기반(UI/상태) + 제네릭 싱글톤 매니저로 빠르게 기능을 붙여 나갈 수 있는 구조.
  • 핵심 요소: 자동 주행(CarAI), 자동 사격(BaseWeapon), 업그레이드(WeaponSlotsManager), Respawn, 스테이지 타이머/스폰.
  • 대상 독자: 서바이벌라이크/탑다운 프로토타입을 빨리 만들고 싶은 분, 아키텍처 참고가 필요한 분.

🎮 게임 소개

차량을 조작(AI 주행)하며 시간이 지날수록 강해지는 적을 상대하는 탑다운 생존 슈팅입니다.
처치 보상으로 골드/경험치를 모아 무기를 업그레이드하고, 위기 상황에서는 Respawn으로 트랙 위 최근접 노드에 복귀해 흐름을 이어갑니다.

  • 목표: 가능한 오래 생존하며 Stage를 올리고 더 강한 웨이브를 버티기
  • 루프: 처치 보상 → 골드/EXP → 업그레이드 패널에서 레벨업/티어업

🖼️ 플레이 영상 & GIF

 

✨ Features

  • Generic Singleton: ClassName.Instance로 전역 접근(중복 생성 방지/선 생성 불필요)
  • 무기 시스템(SO): WeaponData로 피해/사거리/발사 속도/쿨타임/티어 정의 → BaseWeapon 공통 파이프라인 + CannonWeapon 파생
  • 업그레이드 & 슬롯: WeaponSlotsManager가 슬롯 mount에 무기를 스폰/교체하고 레벨·티어 업 로직 처리, UpgradePanelController로 UI 연동
  • 스테이지 & 스폰: StageManager 타이머로 Stage 상승, EnemyManager가 NavMesh 영역에 적 스폰(최대 생존 수 유지)
  • Respawn 버튼: “가장 가까운 트랙 노드”로 즉시 복귀(속도·웨이포인트 리셋)
  • UI/HUD: 체력/경험치/레벨, 골드, 속도(km/h), 적 머리 위 HP 빌보드
  • 사운드: BGM + 풀링 기반 SFX + 슬라이더로 볼륨 제어
  • 센서 & 이동 제어: 전방 센서로 정지/재가속, 런타임 파라미터 튜닝 샘플 제공

📁 프로젝트 구조(요약)

 
02. Scripts
├─ Base
│  ├─ BaseCondition.cs
│  ├─ BaseWeapon.cs
│  └─ Singleton.cs
├─ Camera
│  └─ FollowCamera.cs
├─ Controller
│  └─ UpgradePanelController.cs
├─ Enemy
│  ├─ AI
│  │  └─ EnemyAI.cs
│  ├─ Attack
│  │  ├─ EnemyAttack.cs
│  │  └─ EnemyCondition.cs
│  └─ Interface
│     └─ IDamageable.cs
├─ Manager
│  ├─ Enemy
│  │  └─ EnemyManager.cs
│  ├─ Player
│  │  ├─ PlayerManager.cs
│  │  └─ RespawnManager.cs
│  ├─ SkillManager.cs
│  ├─ CurrencyManager.cs
│  ├─ Weapon
│  │  └─ WeaponSlotsManager.cs
│  ├─ GameManager.cs
│  ├─ SoundManager.cs
│  └─ StageManager.cs
├─ Player
│  ├─ Skill
│  │  ├─ AttackController.cs (partial)
│  │  ├─ AttackController.Heal.cs (partial)
│  │  └─ AttackController.SpinAttack.cs (partial)
│  ├─ CarAI.cs
│  └─ PlayerCondition.cs
├─ Scriptable
│  └─ WeaponData.cs
├─ UI
│  ├─ Enemy
│  │  └─ EnemyHealthUI.cs
│  ├─ Player
│  │  ├─ CarSpeedUI.cs
│  │  └─ GoldUI.cs
│  └─ UIConditionBinder.cs
├─ Util
│  ├─ controllingCarAI.cs
│  └─ SensorManager.cs
└─ Weapon
   ├─ Projectile
   │  └─ SimpleProjectile.cs
   └─ CannonWeapon.cs

🧱 설계/디자인 패턴

  • Singleton(제네릭): 매니저 전역 접근/중복 생성 방지
  • Observer(이벤트 기반): PlayerCondition 이벤트 → UI가 구독해 즉시 갱신
  • Strategy(무기 확장): BaseWeapon 공통 파이프라인 + 파생 무기
  • Interface(결합도↓): IDamageable로 피해 처리를 통일
  • Partial Class: AttackController를 Spin/Heal로 분리해 관심사 정리
  • Data-Driven & Composition: ScriptableObject·컴포넌트 조합 중심 설계

🧰 사용 기술 & 시스템

  • NavMesh: CarAI 웨이포인트 스냅, EnemyManager 스폰 샘플링
  • WheelCollider + Rigidbody: GetWorldPose로 메시 동기화, 모터/브레이크/조향 제어
  • Catmull-Rom Spline: 트랙 노드 사이 부드러운 경로 생성(루프/샘플 수 옵션)
  • Physics Overlap/Trigger: Spin(OverlapSphere), 투사체/근접 충돌 → IDamageable
  • TMP + UGUI: HUD/패널 구성

🚗 핵심 동작 요약

  • CarAI: trackNodes → 스플라인 웨이포인트 생성 → 바퀴/조향/가속 갱신, carFront 기준 도달 판정, CurrentSpeedKmh 제공
  • Respawn: 현재 위치(가능하면 carFront) 기준 최근접 노드 탐색 → CarAI.TeleportToNode() → 속도·각속도 초기화 + 웨이포인트 리셋
  • 무기/업그레이드: 슬롯 mount에 프리팹 스폰, WeaponData로 레벨업/티어업, UI 반영
  • 스테이지/스폰: 타이머로 Stage 상승, NavMesh 영역 내 최대 생존 수 유지
  • UI: 플레이어 HUD + 적 HP 빌보드

🕹 플레이 방법

  1. 시작: 플레이하면 StageManager가 타이머를 돌리며 Stage가 주기적으로 상승
  2. 이동: CarAI가 trackNodes를 따라 주행(루프/랜덤/커스텀 경로). 전방 센서 접촉 시 일시 정지 후 재출발
  3. 전투: WeaponSlotsManager가 슬롯 mount에 무기 스폰 → BaseWeapon 파이프라인으로 자동 조준·발사
  4. 성장: 처치 보상으로 골드/EXP 획득 → 업그레이드 패널에서 레벨업/티어업
  5. 복구: 길 이탈 시 Respawn으로 최근접 트랙 노드 복귀

기본 흐름은 AI 주행 + 자동 사격. 입력형 조작을 추가하고 싶으면 별도 컨트롤러로 쉽게 확장 가능합니다.


🧪 씬 세팅 체크리스트

  • 싱글톤 매니저 배치: Game/Stage/Enemy/Sound/Currency/Skill/WeaponSlots/PlayerManager
  • 차량: CarAI + 바퀴(콜라이더/메시) + carFront 지정, trackNodes 등록
  • 무기: WeaponData 생성 → 슬롯 mount에 연결(시작 시 자동 스폰)
  • UI: 슬라이더/텍스트를 인스펙터로 바인딩
  • Respawn 버튼: RespawnManager.RespawnToNearestTrackNode() OnClick
  • NavMesh: 스폰 영역/트랙 주변 베이크

🧯 Troubleshooting (Deep Dive)

발생했는가 → 어떻게 고쳤는가 → 재발 방지까지 실제로 겪었던 이슈 중심으로 정리.

1) 생성자/필드 초기화에서 FindObjectsOfType/Singleton.Instance 호출

현상
UIConditionBinder가 생성자/필드 초기화 타이밍에 Singleton.Instance를 참조하면서 UnityException 발생.

원인(Why)
MonoBehaviour는 생성자/필드 초기화 시점에 씬 오브젝트가 준비되지 않음. 이 타이밍의 FindObjectsOfType/Instance 접근은 금기.

해결(How)

  • 참조 획득은 Awake/Start, 이벤트 구독은 OnEnable, 해제는 OnDisable.
  • 가능하면 [SerializeField] 후 인스펙터에서 수동 바인딩.

재발 방지(Prevent)

  • 팀 규칙: “필드 초기화/생성자에서 Singleton.Instance 금지”
  • 코드리뷰 체크 항목에 추가.

2) PlayerManager.carTransform 미할당으로 Respawn 버튼 NullReference/UnassignedReference

현상
버튼 클릭 시 RespawnManager가 carTransform 접근하다 예외.

원인

  • PlayerManager.Awake 실행 순서 지연, 또는 인스펙터 미바인딩.
  • 씬 활성/비활성 순서에 따른 타이밍 이슈.

해결

  • RespawnManager.ResolveRefs(): PlayerManager → 씬 검색 → car.transform 순으로 자체 복구 로직 추가.
  • Script Execution Order로 PlayerManager.Awake 선행.

재발 방지

  • 핵심 참조는 인스펙터 명시 바인딩 우선.
  • 런타임에서도 한 번 더 널 가드.

3) UI OnEnable에서 즉시 SFX → SoundManager NRE

현상
UpgradePanelController.OnEnable() 내 PlaySfx() 호출 시 NRE.

원인
사운드 풀/오디오소스가 아직 초기화되지 않은 타이밍에 호출.

해결

 
// UI 측 if (SoundManager.Instance && SoundManager.Instance.IsReady) SoundManager.Instance.PlaySfx(SoundManager.SfxId.Upgrade); // 또는 Start / WaitForEndOfFrame로 지연
  • SoundManager.Awake()에서 풀 확실히 초기화.

재발 방지

  • 가이드: “UI의 OnEnable에서 즉시 오디오 재생 금지”
  • 씬 부팅 순서 문서화.

4) BaseCondition 보호된 필드 접근(CS0122)

현상
EnemyHealthUI에서 BaseCondition.health/maxHealth 직접 접근 시 접근 제한자 에러.

원인
캡슐화 위반: 외부 UI가 내부 상태를 직접 읽으려 함.

해결

  • Public 프로퍼티/이벤트로 노출(Health, Health01, 변경 이벤트).
  • UI는 이벤트를 구독해 슬라이더만 업데이트.

재발 방지

  • 규칙: “UI는 이벤트/프로퍼티만 사용”.

5) 빌보드 HP UI 떨림/뒤집힘

현상
카메라 회전 시 HP 슬라이더가 순간적으로 뒤집히거나 흔들림.

원인
Update 순서/축 처리 미흡, LookAt이 Z축까지 뒤집음.

해결

void LateUpdate() {
    var cam = Camera.main.transform;
    Vector3 dir = cam.position - transform.position; dir.y = 0f; // 수평만
    if (dir.sqrMagnitude > 0.0001f)
        transform.rotation = Quaternion.LookRotation(dir);
}

재발 방지

  • 월드 스페이스 UI는 LateUpdate에서 위치/방향 갱신 고정.

6) 투사체 임팩트 후 TrailRenderer/파티클 누수

현상
충돌 직후 본체 파괴 시 Trail/VFX가 같이 사라지거나, 반대로 고아 오브젝트로 남음.

원인
Trail이 본체에 붙은 채 파괴되거나, 별도 라이프사이클 미관리.

해결

  • 충돌 시 Trail을 detach → 별도 타이머로 제거.
  • Impact VFX는 풀링 또는 Destroy(go, duration)로 정리.

재발 방지

  • 본체/트레일/임팩트 라이프사이클 분리 원칙.

7) NavMesh 스폰 지점이 벽/경계 밖으로 샘플링

현상
적이 벽 내부/경계 밖에서 등장하거나 추락.

원인
NavMesh.SamplePosition 반경 과소, areaMask 미설정.

해결

  • 후보 지점에서 SamplePosition(point, out hit, **radius**, **areaMask**)로 여유 반경 제공.
  • allowedAreaNames로 마스크 명시.

재발 방지

  • 스폰 영역 Gizmo/BoxCollider 시각화, 반경/마스크 프리셋 유지.

8) WheelCollider 시각/물리 불일치(튐/박힘)

현상
속도가 오르거나 경사면에서 바퀴 메시가 뜨거나 섀시가 튄다.

원인
메시 동기화가 Update에서 이뤄짐, 물리 타임스텝/보간 불일치.

해결

  • FixedUpdate에서 GetWorldPose(out pos, out rot)로 메시 동기화.
  • Rigidbody Interpolate 사용, Fixed Timestep(권장 0.02s) 점검.

재발 방지

  • 차량 비주얼 동기화는 항상 FixedUpdate.

9) 이벤트 중복 구독 → UI 두 배 갱신

현상
씬 재로드/패널 토글 후 HP/골드 텍스트가 중복 업데이트.

원인
OnEnable에서 AddListener만 하고 OnDisable 해제 누락.

해결

  • 모든 구독은 OnEnable ↔ OnDisable 페어 관리.
  • 필요 시 구독 전 RemoveListener로 1차 청소.

재발 방지

  • 리뷰 체크리스트: “이벤트 구독 해제했나?”

10) Stage 타이머 드리프트/폭주

현상
시간이 지날수록 Stage 속도가 빨라지거나 UI가 밀림.

원인
Time.time 기반 반복이 정지/프레임 드롭에 영향, 코루틴 중복 실행.

해결

  • 단일 코루틴 가드(isRunning) + WaitForSeconds.
  • 일시정지 대응 필요 시 unscaledDeltaTime 누적.

재발 방지

  • 모든 루프/타이머에 중복 실행 가드 + 정지/재개 시나리오 점검.
반응형

Unity - Project_Up

Dev_Jen
|2025. 8. 13. 10:31
반응형

🎮 Project_Up – Unity 3D 서바이벌 시스템 제작기

안녕하세요! 오늘은 제가 진행한 Unity 프로젝트 Project_Up을 소개하려고 합니다.
이 프로젝트는 3D 환경에서 플레이어가 이동하고, 오브젝트와 상호작용하며, 자원을 채집하고 아이템을 사용하는 기본적인 서바이벌형 시스템을 구현한 예제입니다.

최근에는 장비(Equip) 시스템을 완전히 제거하고, 소비형 아이템 중심의 단순한 인벤토리 구조로 리팩토링을 진행했어요.
덕분에 전체 구조가 훨씬 가볍고 직관적으로 변했습니다.


📌 프로젝트 개요

  • 장르 : 3D 서바이벌 / 인터랙션
  • 엔진 : Unity 2021.3 LTS
  • 개발 언어 : C#
  • 주요 목표
    1. 캐릭터 이동, 점프, 달리기, 카메라 제어
    2. 상호작용 가능한 오브젝트와의 간단한 인터페이스
    3. 소비형 아이템 중심의 인벤토리 시스템
    4. 환경 오브젝트(점프패드, 이동하는 차량) 구현

🛠 주요 기능

1. 플레이어 시스템

  • 이동/점프/달리기
    기본 WASD 이동과 점프, Shift를 통한 달리기 기능을 구현했습니다.
    카메라는 3인칭 시점이며, 벽이나 오브젝트에 부딪히면 자동으로 충돌 보정이 됩니다.
  • 스태미나 관리
    달릴 때 스태미나가 소모되며, 스태미나가 0이 되면 더 이상 달릴 수 없습니다.
  • 상태(Condition) 관리
    체력, 스태미나, 속도 증가(버프) 상태를 관리하고, UI와 실시간 연동됩니다.
  • 애니메이션 연동
    이동 속도, 점프 여부, 낙하 여부에 따라 애니메이션 파라미터를 업데이트합니다.

2. 아이템 & 인벤토리

  • 아이템 타입
    • Consumable : 체력 회복, 스태미나 회복, 속도 증가 등 사용 시 효과 적용
    • Environment : 환경 오브젝트(자원, 오브젝트 배치용)
  • 인벤토리 UI
    • 슬롯 기반 UI로 구성
    • 같은 종류의 아이템은 스택 가능
    • 아이템 사용 시 해당 효과 즉시 적용
    • 인벤토리가 가득 차면 아이템을 버림
  • 아이템 상호작용
    월드에 떨어진 ItemObject와 상호작용하면 인벤토리에 추가됩니다.

3. 상호작용 시스템

  • 화면 중앙에서 Raycast를 발사해 상호작용 가능한 오브젝트를 탐지합니다.
  • 감지 시 상호작용 안내 UI(TextMeshPro) 표시.
  • E 키 입력 시 IInteractable 인터페이스의 OnInteract() 호출.

4. 환경 오브젝트

  • JumpPad
    플레이어가 올라서면 위로 점프시키고, 애니메이션이 재생됩니다.
  • Move_SportCar
    DoTween을 사용해 차량이 앞뒤로 왕복하거나 원형 궤도를 도는 움직임을 구현했습니다.
    이동 방향에 따라 휠이 회전하는 디테일까지 넣었습니다.
  • Resource
    채집 시 아이템 드롭 프리팹을 생성하며, 정해진 수량만큼만 채집 가능합니다.

5. UI & 시각 효과

  • UICondition : 체력, 스태미나, 버프 상태 표시.
  • DamageIndicator : 피해를 받으면 화면이 붉게 플래시됩니다.
  • FootSteps : 이동 속도에 따라 발자국 소리가 재생됩니다.

🆕 최근 리팩토링 포인트

이번 업데이트에서 특히 큰 변화는 장비 시스템 제거입니다.

  • 불필요하게 복잡했던 장착/해제 로직과 Equip 클래스 삭제
  • 인벤토리에서 장착/해제 버튼 제거
  • ItemType에서 Equipable 제거
  • 아이템은 Consumable 또는 Environment만 존재
  • Player 클래스에서 equip 필드 삭제

이렇게 하니 코드가 훨씬 단순해지고, 아이템 관리와 UI 동작이 명확해졌습니다.


📂 폴더 구조

Assets/
├── 02.Scripts/
│   ├── Character/
│   │   ├── CharacterManager.cs
│   │   ├── Player.cs
│   │   ├── PlayerController.cs
│   │   ├── PlayerCondition.cs
│   │   ├── PlayerAnimation.cs
│   │
│   ├── Interaction/
│   │   ├── Interaction.cs
│   │   ├── ItemObject.cs
│   │   ├── Resource.cs
│   │
│   ├── Inventory/
│   │   ├── ItemData.cs
│   │   ├── ItemSlot.cs
│   │   ├── UIInventory.cs
│   │
│   ├── UI/
│   │   ├── Condition.cs
│   │   ├── UICondition.cs
│   │   ├── DamageIndicator.cs
│   │
│   ├── Environment/
│   │   ├── JumpPad.cs
│   │   ├── Move_SportCar.cs
│   │
│   └── Sound/
│       └── FootSteps.cs

🎥 플레이 화면 (GIF)


💡 마무리

이번 프로젝트는 기본적인 서바이벌형 시스템의 뼈대를 구현하는 것이 목표였습니다.
장비 시스템을 제거하고 나니 인벤토리와 아이템 구조가 훨씬 명확해지고 유지보수가 쉬워졌어요.
다음에 추가하고싶은건 전투 시스템퀘스트 시스템을 간단하게 붙여서,
더 풍부한 플레이 경험을 줄 수 있도록 확장하고싶습니다!

반응형
반응형

# 🧭 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    → 피격 시 화면 이펙트
'''












반응형

'Unity' 카테고리의 다른 글

Unity - TopDown 레이싱 방치형 RPG  (2) 2025.08.29
Unity - Project_Up  (3) 2025.08.13
Unity - Inventory 인벤토리 시스템 구현 정리  (3) 2025.08.07
Unity - Unity의 TryGetComponent  (1) 2025.08.07
Unity - Input System - InputAction  (6) 2025.08.06
반응형

🎒 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를 기본으로 사용하는 습관을 들여보세요. 실수도 줄고, 유지보수도 쉬워질 것입니다!

반응형
반응형

🎮 Unity Input System - InputAction 완전 정복

1. 🧭 InputAction이란?

InputAction은 Unity의 새 Input System에서 사용자의 입력을 추상화하여 "행동" 단위로 정의하는 구성 요소입니다.

예를 들어 Jump, Move, Shoot 같은 게임 내 행동(행위) 을 정의하고, 여기에 어떤 키보드/마우스/게임패드 입력이 연결될지를 설정합니다.


📦 InputAction 구성 구조

InputActionAsset
 └─ Action Map (예: Player)
     └─ InputAction (예: Move, Jump)
         └─ Binding (예: WASD, Spacebar, Gamepad A버튼)
  • InputActionAsset: 모든 입력 행동을 담는 설정 파일 (보통 .inputactions 확장자)
  • Action Map: 논리적인 입력 그룹 (예: Player, UI, Vehicle 등)
  • InputAction: 실제 행동 정의 (Move, Jump, Fire 등)
  • Binding: 특정 입력 장치와의 연결 (키보드, 마우스, 게임패드 등)

⚙️ 기존 방식 vs InputAction

기존 방식 InputAction
Input.GetKeyDown(KeyCode.Space) InputAction("Jump").performed += ...
코드 내에 직접 키 입력 체크 행동 단위로 추상화된 구조
입력마다 조건문 필요 입력 행동별로 분리 가능
디바이스 변경 시 코드 수정 하나의 Action에 여러 디바이스 바인딩 가능

💬 한 마디로 정리하면?

"InputAction은 '어떤 키를 눌렀는가'가 아니라, '어떤 행동을 실행할 것인가'에 집중하는 시스템입니다."

이 방식은 복잡한 입력 처리, 다양한 디바이스 대응, 깔끔한 코드 구조 등을 모두 만족시킬 수 있어 중대형 프로젝트에서 특히 강력한 입력 처리 구조입니다.


2. 🎯 InputAction 타입 종류

InputAction을 만들 때 선택할 수 있는 Action Type은 세 가지가 있습니다:

① Button (버튼 입력)

  • 가장 기본적인 입력 방식입니다.
  • 키보드나 게임패드의 특정 버튼처럼 눌렀다 뗐다의 이벤트를 감지합니다.
  • 예: 점프, 공격, 상호작용 등
InputAction jumpAction = new InputAction(type: InputActionType.Button);
jumpAction.performed += ctx => Jump();
  • 관련 phase:
    • started: 버튼이 눌리는 순간
    • performed: 동작이 수행된 순간
    • canceled: 버튼에서 손을 뗀 순간

② Value (값 입력)

  • 방향키, 스틱, 마우스 이동 등 연속적인 값을 처리할 때 사용합니다.
  • Vector2, float, int 등 다양한 타입의 값을 받아 처리할 수 있습니다.
  • 예: 이동, 마우스 위치, 줌 레벨 등
InputAction moveAction = new InputAction(type: InputActionType.Value);
Vector2 moveDir = moveAction.ReadValue<Vector2>();

③ Pass-through (즉시 전달형)

  • Unity 이벤트나 다른 처리 로직을 통하지 않고 바로 값 전달이 필요한 경우 사용합니다.
  • performed만 호출되며 started, canceled은 호출되지 않습니다.
  • 보통 처리 과정을 생략하고 입력 값을 그대로 넘겨야 할 때 사용됩니다.
InputAction lookAction = new InputAction(type: InputActionType.PassThrough);
lookAction.performed += ctx => Look(ctx.ReadValue<Vector2>());

✅ 타입 선택 기준 요약

타입 사용 예시 특징
Button 점프, 공격 상태 변화가 있는 단순 입력 처리
Value 이동, 카메라, 줌 값의 변화량을 지속적으로 감지
Pass-through 마우스 이동 빠르고 직접적인 값 전달, 필터 없음

 

3. 🧠 InputAction 이벤트 처리 완전 이해

InputAction의 입력 이벤트는 CallbackContext를 통해 다양한 정보를 제공합니다.

📌 CallbackContext란?

InputAction.CallbackContext는 입력 이벤트가 발생했을 때 전달되는 구조체입니다. 이 안에는 어떤 값이 눌렸는지, 어떤 상태인지 등 중요한 정보가 담겨 있습니다.


📍 주요 속성 설명

속성 설명
ctx.phase 입력의 현재 상태 (Started, Performed, Canceled)
ctx.ReadValue<T>() 입력값을 원하는 타입으로 받아옴 (예: Vector2, float, bool)
ctx.control.name 어떤 입력 컨트롤이 눌렸는지 확인 가능

✅ phase란?

  • Started: 키가 눌리기 시작했을 때 호출
  • Performed: 실제 동작이 완료되었을 때 호출 (ex. 스페이스바 눌러서 점프)
  • Canceled: 입력이 해제됐을 때 호출 (ex. 키에서 손 뗌)
public void OnJumpInput(InputAction.CallbackContext context)
{
    if (context.phase == InputActionPhase.Started)
    {
        Debug.Log("점프 시작!");
    }
    else if (context.phase == InputActionPhase.Performed)
    {
        Debug.Log("점프 실행!");
    }
    else if (context.phase == InputActionPhase.Canceled)
    {
        Debug.Log("점프 해제!");
    }
}

✅ ReadValue()란?

입력으로부터 값을 받을 때 사용되는 함수입니다.

  • ReadValue<Vector2>() → 방향키 입력
  • ReadValue<float>() → 마우스 휠, 트리거 압력
  • ReadValue<bool>() → 단순 버튼 on/off
Vector2 movement = context.ReadValue<Vector2>();

※ 제네릭 타입을 잘못 입력하면 런타임 에러가 발생할 수 있으니 주의해야 합니다.

 

 

 

4. 🧩 PlayerInput 컴포넌트와 InputAction 연동

InputAction을 사용하려면 Unity 오브젝트에 실제 입력을 받을 수단이 필요합니다. 바로 그것이 PlayerInput 컴포넌트입니다.

PlayerInput은 InputActionAsset과 연동되어 입력을 받아 동작을 실행하도록 해주는 중계자 역할을 합니다.


✅ PlayerInput의 구성 요소

항목 설명
Actions 사용할 InputActionAsset 파일 지정
Default Map 시작 시 활성화할 Action Map 선택 (예: "Player")
Behavior 이벤트 처리 방식 선택 (SendMessage, UnityEvents, Invoke C# Events 등)
Camera Look 입력을 카메라 기준으로 보정할 경우 연결

⚙️ Behavior 방식 비교

① SendMessage 방식

  • Unity의 기본 메시지 시스템 활용
  • 액션 이름과 같은 이름의 함수를 MonoBehaviour에서 자동으로 찾아 호출
void OnJump(InputValue value) { ... }
  • ✅ 빠른 구현 가능 / ❌ 퍼포먼스 이슈 가능, 함수명 일치 필수

② UnityEvents 방식

  • 인스펙터에서 액션별로 함수 연결 가능
  • 비개발자도 설정 가능하여 협업에 유리
  • PlayerInputEvents 탭에서 직접 함수 연결
// PlayerController.cs
public void OnMove(InputAction.CallbackContext context) { ... }

③ C# Events 방식

  • 가장 유연하고 퍼포먼스가 좋은 방식
  • 코드에서 직접 InputAction에 접근하고 .performed += 으로 연결
playerInput.actions["Jump"].performed += ctx => Jump();
  • ✅ 코드 기반으로 철저히 제어 가능 / ❌ 초보자에겐 진입장벽 있음

🎮 실습 예시: UnityEvents 방식 연결

  1. Player 오브젝트에 PlayerInput 컴포넌트 추가
  2. Actions에 .inputactions 파일 연결
  3. Behavior를 Invoke Unity Events로 설정
  4. PlayerController 스크립트에 이벤트 함수 작성
  5. 인스펙터에서 해당 액션에 함수 연결
public void OnMove(InputAction.CallbackContext context)
{
    Vector2 input = context.ReadValue<Vector2>();
    // 이동 처리
}

🔍 팁: PlayerInput 자동 생성 방지하기

PlayerInput 컴포넌트를 수동으로 붙여 쓰는 경우, 코드나 다른 매니저에서 InputAction을 생성하면 중복 생성될 수 있습니다. 항상 하나의 PlayerInput만 사용하도록 주의.

 

반응형
반응형

🎮 1. 게임 소개

이번 프로젝트는 '궁수의 전설(Archero)'에서 영감을 받아 제작한 탑다운 슈팅 기반의 액션 게임입니다.
현대 세계를 배경으로, 마지막 남은 궁수가 되어 적들을 물리치고 스테이지를 클리어하는 것이 목표입니다.
게임의 제목은 **「현대세계 최후의 궁수」**입니다.
유저는 다양한 무기와 스킬을 활용하여 몬스터와 보스를 처치하고, 방을 클리어하며 점점 더 강해지는 전투를 경험할 수 있습니다.


🕹️ 2. 플레이 방법

  • 이동 조작 : W, A, S, D 키로 캐릭터를 상하좌우로 움직입니다.
  • 공격 조작 : 마우스 좌클릭으로 공격합니다. 자동 발사 방식이 아니며, 직접 눌러야 발사됩니다.
  • 스킬 선택 : 방을 클리어할 때마다 3개의 스킬 중 하나를 무작위로 선택하여 강화할 수 있습니다.
  • 스테이지 진행 : 각 방을 클리어하면 자동으로 다음 방으로 이동되며, 최종 방에서는 보스가 등장합니다.

🛠 3. 사용 기술

본 프로젝트는 Unity 엔진을 기반으로 개발되었으며, 다음과 같은 기술들을 활용하였습니다:

  • Unity 2022.3.4f1 : 프로젝트 전체 구성 및 게임 구현
  • C# : 게임 로직 전반 구현
  • GitHub : 협업을 위한 형상 관리 및 버전 관리
  • ScriptableObject : 스킬 데이터 관리 및 확장성 있는 구조 구성
  • Singleton + Manager Pattern : GameManager, SoundManager 등 핵심 매니저 구조 설계
  • FSM 기반 보스 AI : 상태 전이에 따라 공격, 이동, 스킬 등을 제어
  • 랜덤 방 생성 알고리즘 : 던전 구조를 무작위로 생성하여 반복 플레이 요소 강화
  • 2D Sprite Animation : 플레이어 및 적 애니메이션 구현
  • Sound System : BGM 및 효과음 관리

✨ 4. 주요 구현 기능

  • 무작위 스킬 선택 시스템
    방을 클리어할 때마다 3개의 스킬 중 1개를 선택해 능력을 강화할 수 있으며, ScriptableObject 기반으로 유연하게 확장 가능합니다.
  • 스테이지 클리어 흐름 및 UI 연결
    플레이어가 모든 몬스터를 처치하면 스테이지가 클리어되고, 이후 보상 및 다음 스테이지로 자연스럽게 넘어가는 구조를 구현하였습니다.
  • 보스 패턴 구현
    FSM 구조를 기반으로 한 보스의 다양한 패턴 공격과 애니메이션 전환 로직을 구현하였습니다.
  • 사운드 매니저 및 효과음 시스템
    배경음과 효과음을 상황에 따라 다르게 출력하고, 중복 재생 방지 및 볼륨 설정 기능을 추가하였습니다.

🖼 5. 게임 화면

아래는 실제 게임 실행 화면 일부입니다. 플레이어 조작, 스킬 선택, 보스전, UI 구성이 포함된 장면으로, 다양한 게임 요소들이 조화를 이루도록 구현하였습니다.

  • 기본 전투 화면: 플레이어가 적을 향해 자동으로 공격하며, 일정 시간마다 보조 스킬을 사용할 수 있도록 구성하였습니다.
  • 스킬 선택 UI: 스테이지 클리어 시 3개의 스킬 중 1개를 선택하여 성장하는 방식이며, 시각적 피드백을 강화한 UI 디자인으로 몰입감을 높였습니다.
  • 보스 전투 화면: 보스의 다양한 공격 패턴이 적용되며, 체력 바 및 연출이 강화된 별도 스테이지에서 진행됩니다.
  • 게임 클리어 화면: 최종 보스를 처치하면 승리 연출과 함께 엔딩 씬으로 전환됩니다.
  • 타이틀 화면, 튜토리얼 화면 등등..

 

설명 추가 되어있는 영상

 

짧은 설명 X 영상

 


📂 6. 프로젝트 폴더 구조

📦 02. Scripts/
├── 📂Boss/ # 보스 관련 제어 스크립트
│ ├── Axe.cs # 보스 무기 충돌 감지
│ ├── BossController.cs # 보스 상태 전이 및 FSM
│ └── BossHpBar.cs # 보스 체력 UI
├── 📂Camera/
│ └── CanvasCameraSetter.cs # UI 캔버스에 메인 카메라 자동 연결
├── 📂Manager/ # 전역 시스템 관리자 클래스들
│ ├── 📂Camera/
│ │ └── CameraManager.cs
│ ├── 📂Monster/
│ │ └── MonsterSpawnManager.cs
│ ├── 📂PlayerSpawn/
│ │ └── PlayerSpawnManager.cs
│ ├── 📂Skill/
│ │ └── SkillManager.cs
│ ├── 📂Sound/
│ │ ├── PlayerSound.cs
│ │ └── SoundManager.cs
│ ├── 📂Stage/
│ │ ├── NextStageParticle.cs
│ │ └── StageManager.cs
│ ├── 📂Tutorial/
│ │ └── SkillTutorialUIManager.cs
│ ├── 📂UI/
│ │ ├── 📂Player/
│ │ │ ├── PlayerHpBar.cs
│ │ │ └── PlayerStatUI.cs
│ │ ├── 📂SettingUI/
│ │ │ └── SoundUI.cs
│ │ ├── 📂Skill/
│ │ │ └── SkillUIManager.cs
│ │ ├── 📂Tutorial/
│ │ │ └── TutorialSlotUI.cs
│ │ └── UIManager.cs
│ └── GameManager.cs # 게임 전반 흐름 관리
├── 📂Monster/ # 일반 몬스터 AI 및 전투 관련 스크립트
│ ├── Melee_Damage.cs
│ ├── MonsterFSM.cs
│ ├── MonsterHpBar.cs
│ ├── MonsterParticleControl.cs
│ ├── MonsterProjectile.cs
│ └── MonsterStat.cs
├── 📂Player/ # 플레이어 조작 및 상태 관리
│ ├── Bullet.cs
│ ├── PlayerMovement.cs
│ ├── PlayerShooting.cs
│ └── PlayerStat.cs
├── 📂ScriptableObject/ # 스킬 데이터 저장용 ScriptableObject
│ └── SkillData_ScriptableObject.cs
├── 📂Singleton/ # 제너릭 싱글톤 베이스 클래스
│ └── Singleton.cs
├── 📂Sound/ # 사운드 재생 소스
│ └── SoundSource.cs
├── 📂Stage/ # 스테이지 전환 로직
│ └── StageChanger.cs
├── 📂Test/ # 테스트 및 디버깅용
│ ├── End.cs
│ └── TimeController.cs
├── 📂UI/ # UI 관련 스크립트
│ ├── 📂Animation/
│ │ └── UIAnimationHandler.cs
│ ├── 📂Production/
│ │ └── Siren.cs
│ ├── 📂Skill/
│ │ └── SkillSlotUI.cs
│ └── 📂Title/
│ ├── StageChangeButton.cs
│ └── StateChangeButton.cs
├── 📂Utils/ # 유틸리티 스크립트
└── └── EnemyUtil.cs

 


👤 7. 개발자

이름 역할 담당 업무 요약
이재은 팀장 스킬 및 업그레이드 시스템 구현, UI 시스템 제작, 전체 프로젝트 구조 설계 및 총괄
김용민 팀원 랜덤 방 생성 로직 구현, 배경 및 사운드 전반 설정
정세윤 팀원 보스전 로직 설계 및 구현 (AI, 애니메이션 전환, 보스 연출 등)
김유경 팀원 적 AI 설계, 공격 패턴 작성 및 난이도 조정
김노아 팀원 플레이어 이동 및 공격 시스템 구현, QA 테스트 및 전반적 기능 보완

🛠 8. 트러블슈팅 (문제 해결 기록)

 

 

👤 9. 자체 평가 의견

반응형