Unity/Design Pattern

[Unity] DirtyFlag Pattern

Patrick_ 2024. 10. 15. 15:44

DirtyFlag Pattern

DirtyFlag 패턴은 메모리를 효과적으로 관리하기 위해 실시간으로 필요한 객체는 활성화하고, 필요하지 않을 때 비활성화 하는 패턴이다. 예를 들어 오픈 월드일 때 캐릭터가 이동 시 캐릭터 주변에 있는 사물 및 환경만 로딩이 되고 캐릭터가 벗어나면 로딩이 내려가는 것으로 생각하면 된다. 오픈 월드를 한번에 로딩하면 메모리 과부하가 일어나므로, 청크 개념 단위로 나눠 월드를 쪼개어 캐릭터의 시야정도까지만 로딩해야 한다. 패턴이긴 하나, 최적화 방법에 조금 더 가깝다. DirtyFlag는 비주얼적인 면, 데이터적인 면 등 여러 방면에서 광범위하게 사용한다. 

구성이 복잡하고 비용이 많이드는 환경에 적합하며, 장면에서 많은 계산이나 업데이트가 발생할때 유용하다. 객체의 상태 변경 여부를 나타내는 단순한 boolean flag이다. 대표적인 예로는 Unity에서 하이어라키가 있다.

하위 transform은 상위 transform에 업데이트가 필요할 때까지 업데이트를 무시한다. 쉽게 설명하면 하위transform에서 변경 연산이 이루어져야 할 때, 바로 연산이 이루어지는 것이 아닌 변경 연산이 필요하다는 플래그를 활성화하게 된다. 그 이후에 모든 계층 구조에서 연산이 끝나고 나면(한 프레임 종료) 플래그를 수집하여 플래그가 활성화된 컴포넌트를 한번에 업데이트가 이루어진다. 이러한 업데이트는 엔진 내부에서 자동적으로 이루어진다.

DirtyFlag의 주 목적은 상태가 자주 변경되는 동적 개체에 대한 불필요한 계산을 최소화하는 것이다. 

예시

아래의 그림처럼 구역을 청크 단위로 나누고, 플레이어가 이동했을 때 가까운 거리에 있는 영역에서 로딩이 안된 부분은 DirtyFlag 활성화처리해준다.(2번) 플래그에 따라 load, unload를 해준다.(3번) 

이를 코드로 구현하면 아래와 같다. 

public class GameSectors : MonoBehaviour {
    public PlayerController m_Player; // player
    public Sector[] m_Sectors; // sector 정보
    
    private void Update() {
        foreach (Sector sector in m_Sectors) {
            bool isPlayerClose = sector.IsPlayerClose(m_Player.transform.position);
            // 거리 측정
            
            if (isPlayerClose != sector.IsLoaded) 
                sector.MarkDirty(); // 더티 플래그 세팅
            
            if (sector.IsDirty) { // 활성화
                if (isPlayerClose) 
                    sector.LoadContent();
                else
                    sector.UnloadContent();
                
                sector.Clean();
            }
        }
    }
}

참고 문서

[유니티 TIPS] 어디에서나 사용하는 DirtyFlag 패턴 | 프로그래밍 디자인패턴