[공부] 261026 좀비서바이벌2
좀비서바이벌 2일차
- 건 재장전 기능 일부
- 좀비 기초
Mathf
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) | 반올림 후 int | float를 int로 |
Event
public UnityEvent OnDead;//죽었을때 호출할 함수 등록
델리게이트의 일종
- 함수를 참조함
- 이벤트는 델리게이트에 '캡슐화(보호)' 기능을 추가한 객체입니다.
- "구독자 명단 관리"는 외부에서 할 수 있지만, "실행 버튼"은 오직 그 이벤트를 선언한 클래스 내부에서만 누를 수 있게 만든 특수한 델리게이트입니다.
특징
델리게이트와 90% 똑같지만, 딱 두 가지 제약 조건이 붙습니다.
- 제약 1: 외부에서
=(덮어쓰기)를 못 합니다. 오직+=,=만 가능합니다. (안전) - 제약 2: 외부에서
Invoke()(실행)를 못 합니다. (주권 보호)
사용법
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 타입
public enum Status
{
Idle,
Trace,
Attack,
Die
}
enum은 여러 상태에 이름을 붙여서 하나의 타입으로 관리하는 기능이야.- 작성한 코드를 예로 들면, 게임 캐릭터가 가질 수 있는 상태를 미리 정해 놓는 거야.
enum은 어떻게 사용할까?
enum은 선언만 한다고 캐릭터가 자동으로 움직이거나 공격하지는 않음- 상태를 저장하고, 그 상태에 따라 다른 행동을 실행하도록 코드를 작성해야함
① 상태 선언하기
public enum Status
{
Idle,
Trace,
Attack,
Die
}
② 현재 상태를 변수에 저장하기
public Status currentStatus = Status.Idle;
여기서 중요한 부분을 살펴보자.
- Status : 변수의 타입
- currentStatus : 변수 이름
- Status.Idle : 변수에 넣을 값
int number = 10;
Status currentStatus = Status.Idle;
int 변수에는 정수를 저장하고, Status 변수에는 Idle, Trace, Attack, Die 중 하나를 저장하는 거야.
NavMesh
Unity에서 적 캐릭터가 플레이어를 쫓아오게 만들거나, 장애물을 피해 목적지까지 이동시키기
- NavMesh: 캐릭터가 이동할 수 있는 길을 나타내는 데이터
- NavMesh Surface: 이동할 수 있는 길을 분석해서 NavMesh를 생성하는 컴포넌트
- NavMeshAgent: 생성된 길을 이용해 캐릭터를 이동시키는 컴포넌트
- 1단계. NavMesh Surface
- 바닥과 장애물 등을 분석해 캐릭터가 이동할 수 있는 영역을 생성해.
- 2단계. NavMesh
- 생성된 이동 가능 영역을 바탕으로 목적지까지 갈 수 있는 경로를 계산할 수 있어.
- 3단계. NavMeshAgent
- 적 캐릭터에 붙여서 계산된 경로를 따라 이동하게 해.
NavMesh
캐릭터가 이동할 수 있는 영역을 메시 형태로 표현한 데이터
- 다만 NavMesh 자체가 캐릭터를 움직이는 것은 아니야. 이동 가능한 영역과 경로 탐색에 필요한 정보를 제공하는 역할
NavMesh Surface
NavMesh Surface는 NavMesh를 생성하기 위한 컴포넌트야. Unity의 AI Navigation 패키지에서 제공
- 쉽게 말해 맵을 분석해서 이동 가능한 영역을 만들어 주는 역할
| 설정 | 역할 |
|---|---|
| Agent Type | 어떤 종류의 캐릭터를 위한 NavMesh인지 지정 |
| Collect Objects | 어떤 오브젝트를 수집 대상으로 삼아 베이크할지 지정 |
| Include Layers | 어떤 레이어를 베이크에 포함할지 지정 |
| Use Geometry | 렌더링 메시 또는 물리 충돌체 중 어떤 형상을 기준으로 분석할지 선택 |
| Default Area | 생성된 영역의 기본 이동 영역 유형 지정 |
NavMeshAgent
NavMeshAgent는 캐릭터가 NavMesh를 이용해 이동하도록 해 주는 컴포넌트
| 속성 | 역할 |
|---|---|
| Speed | 이동 속도 |
| Angular Speed | 회전 속도 |
| Acceleration | 가속도 |
| Stopping Distance | 목적지에 도착했다고 판단할 거리 |
| Radius | 에이전트의 반경 |
| Height | 에이전트의 높이 |
OverlapSphere
특정 위치를 중심으로 구형 범위를 만들고, 그 범위 안에 들어온 콜라이더(Collider)들을 찾아주는 기능
- 게임에서는 주변 적 탐색, 폭발 범위 공격, 아이템 감지 등에 자주 사용
기본 사용법
Collider[] colliders = Physics.OverlapSphere(
transform.position,
5f
);
각 부분의 의미를 살펴보자.
| 코드 | 의미 |
|---|---|
Physics.OverlapSphere() | 구형 범위 안의 Collider 검색 |
transform.position | 구의 중심 위치 |
5f | 구의 반경 5 Unity 단위 |
Collider[] | 검색 결과로 반환된 Collider 배열 |
예를 들어 검색 범위 안에 적 2명과 상자 1개가 있다면, 조건에 맞는 Collider 3개가 배열에 들어올 수 있어.
여기서 주의할 점은 OverlapSphere는 적만 찾아주는 기능이 아니라는 것이야. 범위 안의 Collider를 검색하므로 적만 찾으려면 레이어나 태그 등의 조건을 추가
실제 게임 코드
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를 사용할 수 있어.
public LayerMask targetLayer;
private void Detect()
{
Collider[] colliders = Physics.OverlapSphere(
transform.position,
5f,
targetLayer
);
}
네가 앞에서 공부한 총기 코드의 Physics.Raycast()와 비교하면 이해하기 쉬워.
| 구분 | OverlapSphere() | Raycast() |
|---|---|---|
| 검색 형태 | 구형 범위 | 한 방향으로 뻗는 선 |
| 주요 목적 | 주변 오브젝트 탐색 | 조준 대상, 충돌 지점 탐색 |
| 결과 | 겹치는 Collider 배열 | 가장 먼저 맞은 대상 정보 |
| 활용 예시 | 주변 적 탐색, 폭발 공격 | 총알 명중 판정, 시야 확인 |
예를 들어 총을 쏠 때는 조준 방향의 대상을 찾기 위해 Raycast()를 사용할 수 있고, 수류탄이 폭발했을 때는 주변의 여러 적을 찾기 위해 OverlapSphere()를 사용할 수 있어.
응용: 광역 공격
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
// 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);
}
인게임 캔버스
동적 배열



