Boid(+그리드분할기법) 성능 개선 01
boid 알고리즘와 그리드 분할 기법을 사용하면서 60프레임을 유지하는 객체수가
350-> 600개로 늘어났지만 아직 충분하지않다. 성능 개선을 위해 유니티에서 자체 제공하는 기능인
Profiler 사용해 보기로 했다.
유니티 프로파일러에서는 실행중인 상황을 녹화하여, 일정 순간에 어느정도 프레임이 나오는지,
CPU사용률을 비롯한 각종 컴퓨터 자원을 얼마나 사용하는지를 나타내 준다.
프레임이 가장 떨어지는 순간을 분석한 결과 예상대로 Boid 알고리즘을 사용하는 Enemy 클래스가 가장 많은 작업을 진행하고 있었다. 그중 Enemy.CalculateSeparation() 메서드는 이웃간에 거리를 유지하려는 힘을 계산하는 메서드이다.
해당 메서드에서 가장 많은 부담을 차지하는건 Transform.get_position()으로 241개의 객체가 총 12696번의 연산을 하고있다고 해석할수있다. 해당 기능은 객체의 transform 컴포넌트에서 자신의 좌표를 불러온다.
성능 향상을 위해 해당 기능을 호출하는 빈도를 줄여야한다고 생각했다.
public Vector3 pos
private void Update()
{
timer += Time.deltaTime;
if (timer > updateRate)
{
pos = transform.position;
// ... 중략
}
}
// Separation: 가까운 이웃들과 거리를 유지하려는 힘
private Vector2 CalculateSeparation(List<Enemy> neighbors)
{
Vector2 force = Vector2.zero;
foreach (Enemy neigh in neighbors)
{
float distance = Vector2.Distance(pos, neigh.pos);
if (distance < separationDistance)
{
if(neigh != this)
{
Vector2 direction = (Vector2)pos - (Vector2)neigh.pos;
force += direction.normalized / distance;
}
}
}
return force;
}
그래서 Enemy 클래스에 position 정보를 저장할 pos 벡터를 만들고, boid알고리즘을 실행할때마다 transform.position을 호출하여 변수를 갱신하고, 해당 변수를 재활용하는 방향으로 코드를 변경하였다.
결과 72번의 호출에서 12696번 사용하던 기능이
198번의 호출에서 198번 사용하도록 확연히 줄어들었다. 한번의 업데이트에서 위치정보를 단 한번만 바꾸기 때문에, 한번의 호출에 한번의 갱신만으로 작동할수있었기에 가능한것같다.
다음으로 많은 리소스를 차지하는건 그리드 분할 기법의 Array.indexOf() 기능이다.
ClearEnemy() 메서드에서 indexOf()를 통해 그리드 상의 객체를 탐색하는 기능이다.
자신이 속한 그리드를 찾고 그 그리드 내에서 자신을 탐색하는 기능임으로, 결국 그리드를 순회한다는 것이
많은 리소스 사용의 원인인것이다.
그래서 일일히 자신을 찾지않고, 모든 Enemy의 움직임이 끝나면 하나하나 삭제하는것이 아닌 그냥
그리드 자체를 비워버리는 방식으로 변경했다.
private void Update()
{
timer += Time.deltaTime;
if (timer > updateRate)
{
//spatialGrid.ClearEnemy(this); 기능 제거
//중략 ...
}
}
private void LateUpdate()
{
spriter.flipX = target.position.x < pos.x;
spatialGrid.clearGrid();
}
이 방식이면 굳이 자신의 객체를 찾을 필요가 없어지기때문에, 훨씬 적은 연산을 사용할 것이다.
결과 380번의 호출에서 15.9%의 리소스를 차지했던 indexOf의 영향이
360의 호출에서 거의 사라진것을 확인할수있었다.
...이어서 작성중