Mesh Combiner를 이용한 최적화

devKyoun·2024년 11월 1일
0

Unity

목록 보기
22/27

⚙️ 맵을 어떻게 관리하나

해당 부분은 인게임의 맵이다.
이 맵은 세개의 섹션으로 나누어져 있다.
도로 1, 도로 2, 도로 3 이다.
도로 1의 특정한 지점을 플레이어가 지나치면 도로 1이 도로 3의 끝으로 가게 되는 구조다.
그렇게 도로3은 도로1이 되는 것이고 도로 1은 도로 3이 되는 식의 시스템이다.

Environment Manager

public class EnvironmentManager : MonoBehaviour
{
    [SerializeField]
    private Transform vanTransform;

    [SerializeField]
    private Transform[] Sections;
    [SerializeField]
    private float moveOffSet = 60.0f;
    [SerializeField]
    private float standardOffSet = 10.0f;

    private int sectionIndex = 0;


    private bool moveTrigger = false;
    
    // Update is called once per frame
    void Update()
    {
        // 기준이 되는 Section
        if(vanTransform != null && vanTransform.position.z >= 
                                Sections[(sectionIndex + 1)%Sections.Length].position.z + standardOffSet)
        {

            moveTrigger = true;
            
        }


        if (moveTrigger)
        {
            // 이동하는 Section
            Sections[sectionIndex % Sections.Length].position += new Vector3(0, 0, moveOffSet);
            sectionIndex += 1;
            moveTrigger = false;
        }
    }

}

🧐 문제점

하지만 그랬을 경우 별 문제가 없을지 고민 했는데 한 가지 좀 걸리는 것이 있다.
어쨋거나 맵 자체를 Transform 을 이용해서 이동을 하는 것인데 이러한 방식은 생각해야 할 것이 있다.
뭐냐면 자식 오브젝트가 많을 수록 성능 저하가 일어난다는 것이다.

Unity Asset Store에서 구한 Asset인데 맵 자체가 자식 오브젝트가 굉장히 많았다.

근데 해당 맵은 애니메이터의 자식 오브젝트 생략하는 방식을 사용하지도 못한다.
어떻게 해야 할까?


💡Mesh Combiner

public class MeshCombiner : MonoBehaviour
{
    public GameObject Parent;
    public Material Material;
    public bool DeactivateParentAfterMerge = true;
    public bool DestroyParentAfterMerge = false;

    [ContextMenu("Merge")]
    public void MergeMeshes()
    {
        MeshFilter[] meshFilters = Parent.GetComponentsInChildren<MeshFilter>();
        List<CombineInstance> combineList = new List<CombineInstance>();

        for (int i = 0; i < meshFilters.Length; i++)
        {
            if (meshFilters[i].sharedMesh != null)
            {
                CombineInstance combineInstance = new CombineInstance
                {
                    mesh = meshFilters[i].sharedMesh,
                    transform = meshFilters[i].transform.localToWorldMatrix
                };
                combineList.Add(combineInstance);
            }
        }

        GameObject combinedObject = new GameObject("Combined Mesh");
        combinedObject.AddComponent<MeshFilter>();
        combinedObject.AddComponent<MeshRenderer>();
        combinedObject.GetComponent<MeshFilter>().sharedMesh = new Mesh();
        combinedObject.GetComponent<MeshFilter>().sharedMesh.CombineMeshes(combineList.ToArray());
        combinedObject.GetComponent<MeshRenderer>().material = Material;

        if (DeactivateParentAfterMerge)
        {
            Parent.SetActive(false);
        }

        if (DestroyParentAfterMerge)
        {
            Destroy(Parent);
        }

    }
}

출처 : 진우의 혼잣말하는 블로그

해당 과정을 ContextMenu를 통해 Merge를 실행하는 것 외엔 이해를 잘 하지 못해서 서칭을 하며 공부해봤다.

원리를 간단하게 두줄로 정리하자면 이렇다.

자식 오브젝트들의 메쉬와 변환 정보를 수집하여 CombineInstance 리스트에 저장하고,
CombineMeshes()를 사용해 메쉬를 하나로 결합한 후 새로운 GameObject에 적용합니다.

즉 컴포넌트에 부모 오브젝트를 설정해주면 알아서 자식 오브젝트들을 합쳐주고 Transform 이동에 과부하가 생기지 않는다는 점을 알고 있으면 된다.

Rails의 자식오브젝트가 많으며 그 자식오브젝트들이 다 같은 Material을 사용하고 있어야 된다.

해당 부분은 섹션 총 세개로 이루어진 맵이니까 한 섹션마다 같은 머터리얼끼리 묶어줘서 Merge 해줬다.

Rails가 한 개의 오브젝트 Rail로 Merge 된 모습이다
이로 인해서 Transform 시 자식이 많아서 생기는 성능 저하를 우려 할 필요가 없게 된다.

profile
Game Developer

0개의 댓글