[Unity] 다수의 적을 어떻게 움직일까(7) - 방향장 보간 처리
·
Unity,C#/Unity 정보
■ 기존 방향장의 문제점 방향장의 경계 근처에 있는 적이 다른 적들과의 겹침 해소 보정에 밀려 옆 셀로 넘어가면, 진행 방향이 불연속으로 꺾인다. 넘어간 셀의 방향이 원래 셀로 돌아오는 성분을 가지고 있다면 적은 곧바로 복귀하고, 복귀한 뒤에는 다시 밀려 경계를 넘어간다. 이 과정이 매 프레임 반복되며, 적이 밀집하는 병목 구간에서 경계에 걸친 적들은 심하게 떨린다.이러한 일이 일어나는 근본적인 원인은 방향장이 위치에 대한 계단 함수이기 때문이다.계단 함수 : 정의역 구간별로 나누어져 각 구간에 일정한 상수값을 반환하는 함수.UpdateFlowField는 _searchDir의 8방향 중 하나를 각 셀에 할당하므로 방향은 45° 단위로 양자화되고, 인접한 두 셀의 방향이 다르다면 그 차이는 최소 45°다...
[Unity] 다수의 적을 어떻게 움직일까(6) - 필드 몹
·
Unity,C#/Unity 정보
■ 필드 몹웨이브로 몰려오는 군중 티어(3티어) 외에, 게임이 시작될 때 이미 맵 위에 놓여 있는 적을 구현한다. 웨이브만 있으면 도시 탐험의 페널티가 없기 때문이다.필드 몹은 상태를 개체가 아니라 군집이 소유하는 티어인데, 특징은 다음과 같다.개별로 행동하지 않고, 10~30마리 안팎의 소규모 군집이 하나의 상태를 공유한다. 상태를 바꾸는 주체는 군집이고, 개체는 그 변화를 언제 반영할지만 정한다.정지 ↔ 이동 전이는 목적지 도달이 아니라 타이머로 이루어진다.추격 상태로의 전이는 시야가 아니라 플레이어가 내는 소음이 일으키며, 한 번 추격에 들어가면 되돌아오지 않는다. ■ EnemyGroup군집 하나의 상태와 이동 방향을 소유하는 클래스다. 개체는 이 값을 읽어 쓸 뿐 바꾸지 않는다.EnemyGroup..
[Unity] 다수의 적을 어떻게 움직일까(5) - 적 설계
·
Unity,C#/Unity 정보
■ 적 설계 구조모든 적 객체는 Enemy 추상 클래스를 상속받아 작성한다. 적은 모두 대미지를 받을 수 있으므로 IDamageable을 구현하고, 다른 객체를 밀어내는 쪽이므로 IRepulsionSource를 구현한다.public interface IDamageable{ public void TakeDamage(float dmg);}[IDamageable.cs]여기서 적들의 티어를 나눠, 군중 티어의 적들은 단순하지만 다량으로 몰려오도록 구현하고 정예 티어의 적들은 보다 다채롭게 행동하도록 구현한다. Tier3(군중)Tier2(필드)Tier1(정예)상태가지지 않음.개체가 아닌 군집이 소유.개체가 직접 소유.조향FlowField의 조향을 따름.Chase일 때만 FlowField, Move일 때는 정..
[Unity] 다수의 적을 어떻게 움직일까(4) - 겹침 해소 · 장애물 조향
·
Unity,C#/Unity 정보
■ 겹침 해소겹침 해소는 IRepulsionReceiver를 구현한 대상만 순회한다. 장애물은 이 인터페이스를 구현하지 않으므로 애초에 이 명부에 들어 있지 않다. 밀어내기는 해도 밀려나지는 않기 때문이다.public class EnemyRegister : MonoBehaviour{ private void Update() { foreach (var self in _repulsionsReceivers) { var selfPos = self.Position; _enemyHash.Query(selfPos, _enemyBuffer); _obstacleHash.Query(selfPos, _obstacleBuffer); ..
[Unity] 다수의 적을 어떻게 움직일까(3) - 적 관리 시스템
·
Unity,C#/Unity 정보
■ 명부 등록 및 관리 시스템 - EnemyRegister앞서 만든 Flow Field와 공간 해시 그리드는 수백~수천 마리의 적을 처리하기 위한 시스템이다. 그런데 이 적들이 각자 MonoBehaviour의 Update를 가지고 스스로 움직인다면 두 가지 문제가 생긴다.순서가 보장되지 않는다.공간 해시는 "비운다 → 전원을 등록한다 → 이웃을 조회한다"의 순서로 써야 하는데, 적들이 각자의 Update에서 이 일을 하면 어떤 적은 갱신되기 전의 해시를 읽고 어떤 적은 갱신된 뒤의 해시를 읽는다.호출 비용. 적이 1000 마리면 매 프레임 1000번의 Update 호출이 일어난다. 한 곳에서 리스트를 순회하면 이것이 1번으로 줄어든다.따라서 적들을 한곳에 모아두고, 한 시스템이 정해진 순서대로 전원을 구..
[Unity] 다수의 적을 어떻게 움직일까(2) - 공간 해시 그리드
·
Unity,C#/Unity 정보
■ 공간 해시 그리드(Spatial Hash) Flow Field의 방향을 따라 이동하는 적들은 동일한 방향으로 이동하기 때문에 일렬로 겹쳐서 오게 된다.따라서 분리 및 조향을 통해 서로 겹치지 않게 처리해주어야 한다.적 하나가 주변의 다른 적을 찾으려고 전체를 훑으면, 그것을 N마리가 각자 하므로 검사 횟수가 N²으로 늘어난다. 1000마리면 프레임당 100만 번이다. 물리 엔진의 OverlapSphere를 쓰면 이 훑는 일은 물리 엔진이 대신 해주지만, 대신 적마다 콜라이더를 유지해야 하고 매 프레임 물리 엔진을 왕복해야 한다.하지만 분리에 필요한 것은 "물리적인 충돌 정보"가 아니라 "근처에 무엇이 있나"라는 위치 정보뿐이다. 물리 엔진을 거칠 이유가 없다. ■ SpatialHash 구현공간 해싱 ..
[Unity] 다수의 적을 어떻게 움직일까(1) - Flowfield 길찾기
·
Unity,C#/Unity 정보
■ Flow FieldFlow Field는 길 찾기 알고리즘의 한 종류로, 객체마다 경로를 따로 구하는 A*와 달리 맵 전체에 방향장을 한 번 깔아 다수가 공유하는 방식이다.Flow Field는 맵의 모든 칸에 “여기 서 있으면 어디로 가야 하는지” 화살표를 하나씩 미리 꽂아둔 지도이다.출발지를 신경 쓰지 않고, 목적지에서 거꾸로 맵 전체에 화살표를 한 번 깐다. 이 한 번의 계산 비용을 모든 객체가 공유하므로 “길 찾기 비용이 객체 수와 무관해진다.”플레이어 위치로부터 탐색을 시작해서 플레이어로 가는 방향을 그 타일에 미리 기록해 둔다.1. 거리 맵 (Integration Field)플레이어 칸을 0으로 두고, 너비 우선 탐색(BFS) 혹은 각 셀마다 가중치(이동 비용)가 있다면 다익스트라 알고리즘으로..
4. 절차적 도심 생성 - 건물 배치
·
Unity,C#/절차적생성(PCG)
▼ 이전 글 3. 절차적 도심 생성 - Perlin Noise로 경계면 다듬기▼ 이전 글 2. 절차적 도심 생성 - 가중치 Voronoi를 통한 구역 분할▼ 이전 글 1. 절차적 도심 생성 - 도로 생성■ 도로 생성 실제 도심을 내려다보면 도로가 격자 형태로 나 있고, 그 사이에 생긴 블hate-errorlog.tistory.com ■ 개요앞서 나눈 구역 위에 실제 건물을 배치한다. 건물 프리팹은 1x1, 2x3처럼 저마다 크기가 다르므로, 칸을 하나씩 훑으면서 그 자리에 들어갈 수 있는 크기를 골라 빈 구역을 채운다. ■ 건물 프리팹 로드하기인스펙터에서 타입별·크기별로 프리팹을 일일이 링크할 수도 있지만, 항목이 늘어날수록 잘못 연결하는 실수가 생기기 쉽다.그래서 프리팹 네이밍 컨벤션을 정하고, 이름만..
3. 절차적 도심 생성 - Perlin Noise로 경계면 다듬기
·
Unity,C#/절차적생성(PCG)
▼ 이전 글 2. 절차적 도심 생성 - 가중치 Voronoi를 통한 구역 분할▼ 이전 글 1. 절차적 도심 생성 - 도로 생성■ 도로 생성 실제 도심을 내려다보면 도로가 격자 형태로 나 있고, 그 사이에 생긴 블록마다 건물이 들어선다. 도시 생성의 기준이 되는 이 도로망을hate-errorlog.tistory.com▼ 참고 자료 [C#, Unity, 절차적 생성] 절차적 지형 생성 - 1.PerlinNoise■ PerlinNoise PerlinNoise는 1983년 Ken Perlin이 개발한 그래디언트 기반의 노이즈(Gradient Noise)함수로, 지형의 절차적 생성, 텍스처 생성, 구름이나 연기 같은 자연 현상 표현 등 다양한 절차적 콘텐츠 생hate-errorlog.tistory.com■ Per..