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
no image
Unity - Unity의 메인 카메라(Main Camera) & Projection
🎥 Unity의 메인 카메라(Main Camera)란?✅ 1. 기본 정의Unity 씬(Scene)에서 **플레이어가 보는 시점(Viewport)**을 결정하는 렌더링 카메라기본적으로 Main Camera 태그를 가진 GameObject로 자동 생성됨Camera.main 으로 쉽게 접근할 수 있음 (GameObject.FindWithTag("MainCamera")의 성능 최적화 버전)✅ 2. Camera 주요 설정속성설명Clear Flags배경을 어떻게 처리할지 결정 (Skybox, Solid Color, Depth Only, Don't Clear)Culling Mask어떤 레이어의 오브젝트만 렌더링할지 선택Projection카메라 투영 방식 (Perspective: 원근, Orthographic: 평면..
2025.08.04
no image
Unity - Unity Instantiate() 오버로드 정리 & 자식으로 설정할 때의 차이
🧱 Unity Instantiate() 오버로드 정리 & 자식으로 설정할 때의 차이Unity에서 동적으로 오브젝트를 생성할 때 가장 많이 사용하는 함수는 Instantiate()다.하지만 부모를 지정하는 방식, 월드 좌표 유지 여부, 그리고 스케일/포지션의 차이까지 제대로 이해하지 않으면 예기치 못한 결과가 발생할 수 있다.이번 포스트에서는 Instantiate()의 다양한 오버로드 버전과, 다음과 같은 코드 두 개의 차이점을 중심으로 정리한다:🔍 예시private void FirstSpawn(){ Instantiate(obj, transform);}private void SecondSpawn(){ GameObject newObj = Instantiate(obj); newObj.tra..
2025.07.28
no image
Unity - Invoke() vs Coroutine 비교 정리 & InvokeRepeating()
⏰ Unity Invoke() vs Coroutine 비교 정리Unity에서 일정 시간 이후에 어떤 작업을 수행하려면 보통 Invoke()나 Coroutine을 사용한다.둘 다 **시간 지연(delay)**을 다룰 수 있지만, 구조와 활용도에는 큰 차이가 있다.이 글에서는 두 방법의 차이점, 장단점, 실제 사용 예시를 함께 정리한다.🔹 1. Invoke()란?Invoke()는 지정한 시간 후에 단 한 번 특정 함수를 호출하는 Unity 내장 함수다.✅ 사용법void Start(){ Invoke("DelayedFunc", 2f); // 2초 후 DelayedFunc 실행}void DelayedFunc(){ Debug.Log("2초 후 실행됨");}🔹 2. Coroutine이란?Coroutin..
2025.07.28
반응형

# 🧭 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 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. 자체 평가 의견

반응형
반응형

🎥 Unity의 메인 카메라(Main Camera)란?

✅ 1. 기본 정의

  • Unity 씬(Scene)에서 **플레이어가 보는 시점(Viewport)**을 결정하는 렌더링 카메라
  • 기본적으로 Main Camera 태그를 가진 GameObject로 자동 생성됨
  • Camera.main 으로 쉽게 접근할 수 있음 (GameObject.FindWithTag("MainCamera")의 성능 최적화 버전)

✅ 2. Camera 주요 설정

속성 설명
Clear Flags 배경을 어떻게 처리할지 결정 (Skybox, Solid Color, Depth Only, Don't Clear)
Culling Mask 어떤 레이어의 오브젝트만 렌더링할지 선택
Projection 카메라 투영 방식 (Perspective: 원근, Orthographic: 평면)
Field of View 시야각 (Perspective일 때만 적용)
Clipping Planes 카메라가 렌더링할 최소/최대 거리
Depth 여러 카메라가 있을 때 그리는 순서 (높을수록 위에 그려짐)
Target Texture RenderTexture에 출력하고 싶을 때 사용 (예: 포탈, CCTV 효과)

✅ 3. 카메라 제어 예시

🎮 플레이어 따라가기

public class FollowCamera : MonoBehaviour
{
    public Transform target;
    public Vector3 offset;

    void LateUpdate()
    {
        if (target != null)
            transform.position = target.position + offset;
    }
}

🎞 카메라 흔들기 (Shake)

public IEnumerator CameraShake(float duration, float magnitude)
{
    Vector3 originalPos = transform.localPosition;

    float elapsed = 0f;
    while (elapsed < duration)
    {
        float x = Random.Range(-1f, 1f) * magnitude;
        float y = Random.Range(-1f, 1f) * magnitude;

        transform.localPosition = new Vector3(x, y, originalPos.z);
        elapsed += Time.deltaTime;
        yield return null;
    }

    transform.localPosition = originalPos;
}

✅ 4. 메인 카메라 관련 실전 예시

예시 설명
2D 게임 Orthographic 투영 사용, 플레이어를 따라다니며 흔들림 없음
3D 게임 Perspective 투영 사용, 자유 시점 회전, 줌 인/아웃
미니맵 별도 카메라 사용 + RenderTexture
포탈/거울 카메라 + RenderTexture + RawImage 사용

✅ 5. 자주 하는 실수

  • 카메라가 씬에 두 개 이상 있을 때, Camera.main은 가장 먼저 찾은 MainCamera 태그만 사용함 → 명확한 참조 권장
  • 카메라 Depth 설정 안 해서 UI용 카메라가 게임 씬을 덮어버리는 경우 발생
  • 클리핑 거리 조정 실패 → 너무 멀거나 가까운 물체가 안 보이는 경우

🧠 마무리 정리

개념 기억 포인트
Main Camera 태그 Camera.main 접근 대상
Projection 설정 2D: Orthographic / 3D: Perspective
이동 LateUpdate로 자연스럽게 따라가기
다중 카메라 Depth, Culling Mask, RenderTexture 활용
최적화 너무 많은 카메라 활성화는 성능 저하 가능

 

 

🎯 3D에서 Orthographic 카메라를 쓰면?

✅ 핵심 효과

  • **원근감(Perspective)**이 없어진다
  • 카메라와의 거리에 상관없이 모든 오브젝트가 같은 크기로 보인다
  • 멀리 있는 물체도 작아지지 않음
  • 투시 왜곡 없음 → 마치 2D처럼 보이는 3D

👀 비교 예시

항목 Perspective (원근) Orthographic (직교)
가까운 물체 크게 보임 동일하게 보임
먼 물체 작게 보임 동일하게 보임
시야각 Field of View 설정 Size로 보기 범위 설정
대표 사용 일반 3D 게임, FPS, 3인칭 등 전략 시점, 건축 시뮬, 퍼즐, 등각 뷰 게임 등
 

🎮 시각적 차이 예시

  • Perspective
    캐릭터 앞에 있는 적은 큼직하게, 멀리 있는 적은 작게 보임 → 자연스러운 현실 느낌
    예: FPS, RPG
  • Orthographic
    앞에 있는 적이든, 멀리 있는 적이든 크기가 똑같이 보임 → 깊이 구분이 없음
    예: 전통적 RTS, 스타듀밸리 같은 탑뷰, 건축 프리뷰, 에디터 툴

🧪 Unity 실습: 전환 방법

Camera.main.orthographic = true;      // 직교 투영 모드로 전환
Camera.main.orthographicSize = 5f;    // 보이는 영역 범위 조정

🧠 언제 Orthographic을 쓰면 좋을까?

쓰면 좋은 경우 이유
등각뷰 게임 (ex: 시뮬레이션, 탑뷰 퍼즐) 모든 오브젝트를 동일한 크기로 정밀하게 보여주고 싶을 때
건축/에디터 툴 정확한 위치 비교와 거리감 없는 작업이 필요할 때
2.5D 게임 3D 모델을 사용하지만 카메라 뷰는 2D처럼 고정할 때

 


✅ 요약

  • Orthographic은 원근이 없는 평면적 시점
  • 3D 씬에서도 2D처럼 연출 가능
  • 객체가 카메라에서 멀어져도 크기 변화 없음
  • 2D UI 느낌이나 등각뷰 게임에 적합

 

사실 이걸 작성하는건 이번에 궁수의 전설 모티브로 2.5D 게임을 만들었어야 했는데 어떻게해도 잘 안됐었다..그러다보니 3D 메인 카메라에서 Orhographic을 쓰면 된다고 그러길래 해봤더니..완벽했다 허허.. 새로운걸 하나 배워간다.

왼쪽은 Projection - Perspective 오른쪽은 Projection - Orhograpihc

 

반응형
반응형

🧱 Unity Instantiate() 오버로드 정리 & 자식으로 설정할 때의 차이

Unity에서 동적으로 오브젝트를 생성할 때 가장 많이 사용하는 함수는 Instantiate()다.
하지만 부모를 지정하는 방식, 월드 좌표 유지 여부, 그리고 스케일/포지션의 차이까지 제대로 이해하지 않으면 예기치 못한 결과가 발생할 수 있다.

이번 포스트에서는 Instantiate()의 다양한 오버로드 버전과, 다음과 같은 코드 두 개의 차이점을 중심으로 정리한다:


🔍 예시

private void FirstSpawn()
{
    Instantiate(obj, transform);
}

private void SecondSpawn()
{
    GameObject newObj = Instantiate(obj);
    newObj.transform.SetParent(transform);
}

❓ 차이점은?

  • FirstSpawn()은 Instantiate 시점에 부모(transform)를 직접 지정한다.
  • SecondSpawn()은 Instantiate 후에 SetParent()로 부모를 설정한다.

⚠️ 결정적 차이: 위치와 스케일의 기준

항목 FirstSpawn SecondSpawn
부모 지정 시점 즉시 (Instantiate 내에서) 나중에 (SetParent로 나중에 지정)
로컬 위치/회전 유지 ✅ 유지됨 ❌ 월드 좌표 기준으로 설정됨
부모 스케일의 영향 ✅ 즉시 반영 ❌ SetParent 시점에 적용 방식 다름
 

즉, 동일한 결과를 원한다면 SecondSpawn 쪽에서 아래와 같이 써야 한다:

newObj.transform.SetParent(transform, false); // worldPositionStays = false

false를 전달하면 로컬 기준으로 배치되어 FirstSpawn과 동일한 결과가 됨.


🧪 Instantiate() 오버로드 총정리

Unity는 다양한 방식으로 Instantiate()를 오버로드하고 있다:

Instantiate(obj); // 기본 복제
Instantiate(obj, transform); // 부모 지정
Instantiate(obj, transform, true/false); // 부모 + 월드/로컬 기준 설정
Instantiate(obj, position, rotation); // 위치/회전 지정
Instantiate(obj, position, rotation, parent); // 위치/회전/부모 모두 지정

함수 설명
Instantiate(original) 원본 위치/회전 그대로 복제
Instantiate(original, parent) 부모 지정, 기본적으로 로컬 좌표 기준
Instantiate(original, parent, bool worldPositionStays) 부모 지정 + 월드 좌표 유지 여부 선택
Instantiate(original, pos, rot) 위치/회전 지정하여 복제
Instantiate(original, pos, rot, parent) 위치/회전/부모까지 모두 설정 가능
 

📌 worldPositionStays란?

Instantiate(obj, parent, true);  // 현재 위치 유지하며 부모만 설정 (월드 기준)
Instantiate(obj, parent, false); // 부모 기준으로 위치 재조정 (로컬 기준)

의미
true 월드 위치/회전 유지, 부모의 로컬 기준 무시
false 부모의 로컬 위치/회전 기준으로 재배치
 

🧠 정리 요약

구분 설명
Instantiate(obj, parent) 로컬 기준으로 부모 설정됨
SetParent(transform) 월드 기준 위치로 이동됨 (스케일 왜곡 주의)
SetParent(transform, false) 로컬 기준으로 부모 설정 (추천)
Instantiate(obj, pos, rot, parent) 원하는 위치/회전 + 부모까지 완벽 지정 가능
transform.localScale 영향 부모 지정 시 즉시 반영됨, 특히 SetParent() 시 의도치 않은 스케일 왜곡 주의
 

📚 참고 링크

반응형
반응형

⏰ Unity Invoke() vs Coroutine 비교 정리

Unity에서 일정 시간 이후에 어떤 작업을 수행하려면 보통 Invoke()나 Coroutine을 사용한다.
둘 다 **시간 지연(delay)**을 다룰 수 있지만, 구조와 활용도에는 큰 차이가 있다.

이 글에서는 두 방법의 차이점, 장단점, 실제 사용 예시를 함께 정리한다.


🔹 1. Invoke()란?

Invoke()는 지정한 시간 후에 단 한 번 특정 함수를 호출하는 Unity 내장 함수다.

✅ 사용법

void Start()
{
    Invoke("DelayedFunc", 2f); // 2초 후 DelayedFunc 실행
}

void DelayedFunc()
{
    Debug.Log("2초 후 실행됨");
}

🔹 2. Coroutine이란?

Coroutine은 시간의 흐름에 따라 동작을 일시정지하고 다시 재개할 수 있는 기능이다.
IEnumerator를 반환하고, yield return을 통해 흐름을 제어한다.

✅ 사용법

void Start()
{
    StartCoroutine(DelayedCoroutine());
}

IEnumerator DelayedCoroutine()
{
    yield return new WaitForSeconds(2f);
    Debug.Log("2초 후 실행됨 (코루틴)");
}

🔍 차이점 비교

항목 Invoke() Coroutine()
기본 개념 일정 시간 후 한 번 함수 호출 흐름을 일시정지하고 재개
지연 시간 제어 고정된 1회성 딜레이 반복/조건 기반 다양하게 제어 가능
정지 조건 CancelInvoke() 호출 시 StopCoroutine() 호출 시
복잡한 로직 처리 ❌ 불가능 ✅ 가능 (반복, 조건대기 등)
가독성 간단한 작업에 유리 반복 동작일 땐 더 명확함
타이밍 유연성 낮음 높음
Time.timeScale 영향 ✅ 받음 ✅ 받음 (WaitForSeconds)

🛑 Invoke 정지 예시

▶ 전체 코드

using UnityEngine;

public class InvokeStopExample : MonoBehaviour
{
    void Start()
    {
        // 5초 후에 GameOver()를 실행하려고 예약
        Invoke("GameOver", 5f);

        // 2초 뒤에 취소
        Invoke("CancelGameOver", 2f);
    }

    void GameOver()
    {
        Debug.Log("🟥 Game Over!");
    }

    void CancelGameOver()
    {
        Debug.Log("🟨 Game Over 예약 취소");
        CancelInvoke("GameOver");
    }
}

📝 요약

  • Invoke("GameOver", 5f)로 예약 
  • 2초 후 CancelInvoke("GameOver")로 중단
  • 결국 GameOver()는 실행되지 않음

🛑 Coroutine 정지 예시

▶ 전체 코드

using UnityEngine;
using System.Collections;

public class CoroutineStopExample : MonoBehaviour
{
    private Coroutine attackRoutine;

    void Start()
    {
        attackRoutine = StartCoroutine(DelayedAttack());
        Invoke("StopAttack", 2f); // 2초 후 코루틴 중단
    }

    IEnumerator DelayedAttack()
    {
        Debug.Log("🟢 공격 준비...");
        yield return new WaitForSeconds(5f); // 5초 대기
        Debug.Log("🔴 공격 실행!");
    }

    void StopAttack()
    {
        if (attackRoutine != null)
        {
            StopCoroutine(attackRoutine);
            Debug.Log("🟨 공격 중단됨");
        }
    }
}

 

📝 요약

  • 5초 후 공격을 하려는 코루틴 실행
  • 2초 후 StopCoroutine()으로 중단
  • 결국 🔴 공격 실행!은 출력되지 않음

✅ 핵심 요약

항목 정지 방법
Invoke CancelInvoke("함수이름")
Coroutine StopCoroutine(코루틴변수) 또는 StopAllCoroutines()
 
오.. 지금 처음 알게된게 코루틴도 변수처럼 쓸수있는걸 처음 알았다...

🧪 예시 비교

🎯 예: 3초 후 공격 애니메이션 시작

✅ Invoke 방식

void Start()
{
    Invoke("StartAttack", 3f);
}

void StartAttack()
{
    animator.SetTrigger("Attack");
}

✅ Coroutine 방식

IEnumerator StartAttackCoroutine()
{
    yield return new WaitForSeconds(3f);
    animator.SetTrigger("Attack");
}

void Start()
{
    StartCoroutine(StartAttackCoroutine());
}

✅ 언제 어떤 걸 써야 할까?

상황 추천방식
단순하게 일정 시간 후 함수 실행 Invoke()
반복/조건 기반 대기 필요 Coroutine
시간 흐름에 따라 여러 동작을 연결해야 할 때 Coroutine
가독성이 중요한 짧은 로직 Invoke()
 

🧠 Tip: InvokeRepeating()도 있다!

InvokeRepeating()을 사용하면 일정 시간 간격으로 반복 실행도 가능하다.

InvokeRepeating("SpawnEnemy", 1f, 5f); // 1초 후부터 5초 간격으로 반복 실행

이 역시 코루틴의 while(true) + WaitForSeconds()로 대체 가능하지만, 매우 간단할 때는 편리하다.


🏁 결론

한 줄 요약
간단한 타이머 → Invoke() / 유연한 시간 흐름 제어 → Coroutine
 

Invoke()는 빠르고 간단하지만 제어력이 낮고, Coroutine은 더 복잡하지만 강력하다.
상황에 따라 적절히 선택하면 효율적인 Unity 개발이 가능하다.

 

🔁 Unity InvokeRepeating() 간단 정리

✅ 개념

InvokeRepeating()은 지정한 시간 후부터 일정 간격으로 함수를 반복 실행하는 Unity 내장 함수다.
Update()나 Coroutine 없이도 정기적인 타이머 기반 로직을 구현할 수 있다.


🧪 사용 예시

using UnityEngine;

public class RepeatExample : MonoBehaviour
{
    void Start()
    {
        // 2초 후부터 시작해서, 1초마다 Attack() 실행
        InvokeRepeating("Attack", 2f, 1f);
    }

    void Attack()
    {
        Debug.Log("🔥 공격 실행!");
    }
}

 


🛑 반복 중단

 
void StopAttack()
{
    CancelInvoke("Attack");
}

📌 요약 표

항목 설명
사용 함수 InvokeRepeating("함수이름", 지연시간, 반복간격)
중지 방법 CancelInvoke("함수이름") 또는 CancelInvoke()
실행 조건 MonoBehaviour가 활성화된 상태여야 실행됨
장점 매우 간단한 반복 호출에 적합
단점 복잡한 흐름 제어나 조건 분기에는 불리
 

✅ 언제 사용하면 좋을까?

사용 상황 적합 여부
단순한 반복 로직 (ex. 1초마다 이펙트 생성) 👍 매우 적합
조건에 따라 반복 중단/재개 👍 사용 가능 (with CancelInvoke)
복잡한 흐름 제어가 필요한 경우 ❌ Coroutine이 더 적합
 

🏁 결론

InvokeRepeating()은 초간단 반복 타이머를 구현할 때 매우 유용한 도구다.
단, 조건 제어나 흐름 조절이 필요하면 Coroutine으로 전환하는 것이 좋다.

 

원래 3개는 다 알고있었지만 쓰기만하고 잘 알지는 못했었다. 이번 기회로 더 잘 사용하고 언제 사용할지에대해 알게된거같다!! 실제로 많이 써봐야지..그래야 실력이 늘지 ㅠㅜ

반응형