[공부] 261026 좀비서바이벌2

강현구
∙∙조회 0

좀비서바이벌 2일차

  • 건 재장전 기능 일부
  • 좀비 기초

 

Mathf

csharp
      int ammoToFill = Mathf.Min(ammoRemain, gunData.magCapacity - magAmmo, ammoRemain);

Mathf는 숫자를 계산하거나 비교할 때 사용하는 Unity의 수학 도구 모음

  • Mathf.Min()은 전달받은 숫자들 중에서 가장 작은 값 하나를 반환하는 기능

자주 쓰는 Mathf 함수

함수역할예시
Mathf.Min(a, b)작은 값상한 제한
Mathf.Max(a, b)큰 값하한 제한 (예: Max(0, hp))
Mathf.Clamp(v, min, max)범위 안으로 제한Clamp(hp, 0, maxHp)
Mathf.Clamp01(v)0~1로 제한게이지 비율
Mathf.Abs(v)절댓값거리, 차이
Mathf.Lerp(a, b, t)선형 보간부드러운 값 변화
Mathf.Approximately(a, b)float 근사 비교== 대신 사용
Mathf.Sin, Mathf.Cos삼각함수 (라디안)흔들림, 원운동
Mathf.RoundToInt(v)반올림 후 intfloat를 int로

 

 


Event

csharp
    public UnityEvent OnDead;//죽었을때 호출할 함수 등록

델리게이트의 일종

  • 함수를 참조함
  • 이벤트는 델리게이트에 '캡슐화(보호)' 기능을 추가한 객체입니다.
  • "구독자 명단 관리"는 외부에서 할 수 있지만, "실행 버튼"은 오직 그 이벤트를 선언한 클래스 내부에서만 누를 수 있게 만든 특수한 델리게이트입니다.

특징

델리게이트와 90% 똑같지만, 딱 두 가지 제약 조건이 붙습니다.

  • 제약 1: 외부에서 = (덮어쓰기)를 못 합니다. 오직 +=, =만 가능합니다. (안전)
  • 제약 2: 외부에서 Invoke() (실행)를 못 합니다. (주권 보호)

 

사용법

csharp
public class Player : MonoBehaviour
{
    // 1. 선언: 접근제한자 + event + 델리게이트타입 + 이름
    public event Action OnDied;

    public void TakeDamage(int damage)
    {
        // ... 데미지 로직 ...
        if (HP <= 0)
        {
            // 2. 호출: 클래스 "내부"에서만 Invoke 가능
            OnDied?.Invoke();
        }
    }
}

public class UIManager : MonoBehaviour
{
    private void Start()
    {
        Player player = FindObjectOfType<Player>();

        // 3. 사용: 외부에서는 += 또는 -= 만 가능
        player.OnDied += ShowGameOverUI;

        // ❌ 에러 발생: 외부에서 Invoke하거나 = 로 초기화하면 빨간 줄 뜸
        // player.OnDied.Invoke(); 
        // player.OnDied = MyFunction; 
    }

    void ShowGameOverUI() { /* UI 띄우기 */ }
}

왜 쓰는가?

💡

  • 델리게이트(Action, Func)를 쓸 때:
    • 전략적인 대입이 필요할 때. (예: Func 계산기를 이 식에서 저 식으로 아예 갈아끼워야 할 때)
    • 콜백(Callback) 함수를 매개변수로 넘겨줄 때.
  • 이벤트(event Action)를 쓸 때 (현업 90%):
    • *"사건(Event)"**이 발생했음을 알릴 때. (예: 죽음, 아이템 획득, 레벨업, 버튼 클릭)
    • 여러 개의 시스템이 한꺼번에 그 소식을 들어야 할 때. (예: 죽었을 때 UI도 뜨고, 소리도 나고, 카메라도 흔들려야 함)
    • 협업할 때. 내가 만든 기능을 다른 사람이 실수로 실행시키지 못하게 방어막을 쳐야 할 때.

Enum 타입

csharp
    public enum Status
    {
        Idle,
        Trace,
        Attack,
        Die
    }
    
  • enum은 여러 상태에 이름을 붙여서 하나의 타입으로 관리하는 기능이야.
  • 작성한 코드를 예로 들면, 게임 캐릭터가 가질 수 있는 상태를 미리 정해 놓는 거야.

enum은 어떻게 사용할까?

  • enum은 선언만 한다고 캐릭터가 자동으로 움직이거나 공격하지는 않음
  • 상태를 저장하고, 그 상태에 따라 다른 행동을 실행하도록 코드를 작성해야함

① 상태 선언하기

arduino
public enum Status
{
    Idle,
    Trace,
    Attack,
    Die
}

② 현재 상태를 변수에 저장하기

ini
public Status currentStatus = Status.Idle;

여기서 중요한 부분을 살펴보자.

  • Status : 변수의 타입
  • currentStatus : 변수 이름
  • Status.Idle : 변수에 넣을 값
ini
int number = 10;

Status currentStatus = Status.Idle;

int 변수에는 정수를 저장하고, Status 변수에는 Idle, Trace, Attack, Die 중 하나를 저장하는 거야.

 

 


 

Unity에서 적 캐릭터가 플레이어를 쫓아오게 만들거나, 장애물을 피해 목적지까지 이동시키기

  • NavMesh: 캐릭터가 이동할 수 있는 길을 나타내는 데이터
  • NavMesh Surface: 이동할 수 있는 길을 분석해서 NavMesh를 생성하는 컴포넌트
  • NavMeshAgent: 생성된 길을 이용해 캐릭터를 이동시키는 컴포넌트

 

  • 1단계. NavMesh Surface
    • 바닥과 장애물 등을 분석해 캐릭터가 이동할 수 있는 영역을 생성해.
  • 2단계. NavMesh
    • 생성된 이동 가능 영역을 바탕으로 목적지까지 갈 수 있는 경로를 계산할 수 있어.
  • 3단계. NavMeshAgent
    • 적 캐릭터에 붙여서 계산된 경로를 따라 이동하게 해.

캐릭터가 이동할 수 있는 영역을 메시 형태로 표현한 데이터

  • 다만 NavMesh 자체가 캐릭터를 움직이는 것은 아니야. 이동 가능한 영역과 경로 탐색에 필요한 정보를 제공하는 역할

 

NavMesh Surface는 NavMesh를 생성하기 위한 컴포넌트야. Unity의 AI Navigation 패키지에서 제공

  • 쉽게 말해 맵을 분석해서 이동 가능한 영역을 만들어 주는 역할
설정역할
Agent Type어떤 종류의 캐릭터를 위한 NavMesh인지 지정
Collect Objects어떤 오브젝트를 수집 대상으로 삼아 베이크할지 지정
Include Layers어떤 레이어를 베이크에 포함할지 지정
Use Geometry렌더링 메시 또는 물리 충돌체 중 어떤 형상을 기준으로 분석할지 선택
Default Area생성된 영역의 기본 이동 영역 유형 지정

 

NavMeshAgent는 캐릭터가 NavMesh를 이용해 이동하도록 해 주는 컴포넌트

속성역할
Speed이동 속도
Angular Speed회전 속도
Acceleration가속도
Stopping Distance목적지에 도착했다고 판단할 거리
Radius에이전트의 반경
Height에이전트의 높이

 

 

 


OverlapSphere

 

특정 위치를 중심으로 구형 범위를 만들고, 그 범위 안에 들어온 콜라이더(Collider)들을 찾아주는 기능

  • 게임에서는 주변 적 탐색, 폭발 범위 공격, 아이템 감지 등에 자주 사용

 

기본 사용법

ini
Collider[] colliders = Physics.OverlapSphere(
    transform.position,
    5f
);

각 부분의 의미를 살펴보자.

코드의미
Physics.OverlapSphere()구형 범위 안의 Collider 검색
transform.position구의 중심 위치
5f구의 반경 5 Unity 단위
Collider[]검색 결과로 반환된 Collider 배열

예를 들어 검색 범위 안에 적 2명과 상자 1개가 있다면, 조건에 맞는 Collider 3개가 배열에 들어올 수 있어.

여기서 주의할 점은 OverlapSphere는 적만 찾아주는 기능이 아니라는 것이야. 범위 안의 Collider를 검색하므로 적만 찾으려면 레이어나 태그 등의 조건을 추가

 

실제 게임 코드

csharp
using UnityEngine;

public class PlayerDetector : MonoBehaviour
{
    public float detectRadius = 5f;

    private void Update()
    {
        Collider[] colliders = Physics.OverlapSphere(
            transform.position,
            detectRadius
        );

        foreach (Collider col in colliders)
        {
            Debug.Log(col.name);
        }
    }
}

 

자주 사용하는 옵션: LayerMask

기본 OverlapSphere()는 모든 레이어를 대상으로 검색해. 특정 레이어만 검색하고 싶다면 LayerMask를 사용할 수 있어.

arduino
public LayerMask targetLayer;

private void Detect()
{
    Collider[] colliders = Physics.OverlapSphere(
        transform.position,
        5f,
        targetLayer
    );
}

 

네가 앞에서 공부한 총기 코드의 Physics.Raycast()와 비교하면 이해하기 쉬워.

구분OverlapSphere()Raycast()
검색 형태구형 범위한 방향으로 뻗는 선
주요 목적주변 오브젝트 탐색조준 대상, 충돌 지점 탐색
결과겹치는 Collider 배열가장 먼저 맞은 대상 정보
활용 예시주변 적 탐색, 폭발 공격총알 명중 판정, 시야 확인

예를 들어 총을 쏠 때는 조준 방향의 대상을 찾기 위해 Raycast()를 사용할 수 있고, 수류탄이 폭발했을 때는 주변의 여러 적을 찾기 위해 OverlapSphere()를 사용할 수 있어.

 

응용: 광역 공격

ini
public float explosionRadius = 5f;
public int damage = 20;

private void Explode()
{
    Collider[] targets = Physics.OverlapSphere(
        transform.position,
        explosionRadius
    );

    foreach (Collider target in targets)
    {
        IDamageable damageable =
            target.GetComponent<IDamageable>();

        if (damageable != null)
        {
            damageable.OnDamage(
                damage,
                target.ClosestPoint(transform.position),
                Vector3.up
            );
        }
    }
}

 


 

 

 

Vector3.Distance

csharp
// UpdateTrace: 가까워지면 Attack으로 전환
if (target != null && Vector3.Distance(transform.position, target.position) < attackDistance)
{
    CurrentStatus = Status.Attack;
    return;
}

// UpdateAttack: 멀어지면 Trace로 복귀
if (target == null || Vector3.Distance(transform.position, target.position) > attackDistance)
{
    CurrentStatus = Status.Trace;
    return;
}

// UpdateAttack: 쿨타임이 지났으면 대상에게 바로 데미지
if (Time.time >= lastAttackTime + attackInterval)
{
    ...
    damageable.OnDamage(damage, target.position, hitNormal);
}

 

 

 


인게임 캔버스

 

 

 

 

 


동적 배열