[Unity] Project. Personal_5

Lingtea_luv·2025년 6월 1일

Project

목록 보기
21/38
post-thumbnail

Where Am I?


오늘은 기능 구현을 마무리하고 에셋을 적용하는 것을 목표로 프로젝트를 시작했다. 일단 아래 작업을 모두 마칠 수 있었고, 내일부터는 본격적으로 UI 작업에 들어가면 될 것 같다.

  1. 숨었을 때 시야각(POV) 변동
  2. 미로에 진입할 때 환경광, 메인 광원 설정
  3. 게임 클리어 조건 중 하나인 탈출 포탈 구현
  4. 플레이어, 몬스터 발소리 적용, 미로 BGM 적용
  5. 바닥, 벽 에셋 적용
  6. 랜턴 회전 적용
  7. Stuff 생성 위치 안전 구역과 가깝게 수정
  8. 날짜, 시간 기능, 표시 구현

그리고 이번 포스트에서 소개할 코드는 맵을 생성할 때 사용한 CombineInstance[] 이다. 맨 처음에 이를 적용한 이유는 플레이어의 이동이 누적되었을 때 가만히 있어도 떨리는 현상이 발생해서 이를 해결하기 위함이었다.

Vector3 단위 position에 따라 prefab을 생성할 경우 플레이어가 prefab 간 경계를 이동할 때
영향을 받을 것이라고 생각했고, 따라서 prefab 별로 동일한 종류끼리 합치기로 한 것이다.

(근데 지금와서 생각해보니 그냥 collider만 합쳐주면 되는거였나 생각이 들지만, 결론적으로 하나의 오브젝트로 합쳐준 것이다보니 성능상 이점이 있지 않을까 하고 납득하기로 했다... 아닌가?)

어쨋든 이렇게 구현할 경우 타일 별로 종류를 나누어 묶어줄 필요가 있었고, 따라서 꽤나 긴 작업이 되어 기억에 남는다.

CombineInstance[]

public class CombineTiles : MonoBehaviour
{
    private void Start()
    {
        Init();
    }

    private void Init()
    {
    	// 생성된 prefab들의 MeshFilter를 수집
        MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
        
        // 벽 프리팹의 MeshFilter을 담기 위한 List
        List<MeshFilter> wallMeshFilters = new List<MeshFilter>();
        // 바닥 프리팹의 MeshFilter을 담기 위한 List
        List<MeshFilter> planeMeshFilters = new List<MeshFilter>();
        // 안전 구역 프리팹의 MeshFilter을 담기 위한 List
        List<MeshFilter> safeZoneMeshFilters = new List<MeshFilter>();
        
        // 수집한 MeshFilter를 분류
        foreach (MeshFilter mesh in meshFilters)
        {
        	// 사용된 Mesh의 종류가 Cube일 경우 벽 프리팹으로 간주
            if (mesh.sharedMesh.name.StartsWith("Cube"))
            {
                wallMeshFilters.Add(mesh);
            }
            else
            {
            	// 레이어가 plane인 경우 바닥으로 간주
                if (mesh.gameObject.layer == 8)
                {
                    planeMeshFilters.Add(mesh);
                }
                // 둘 다 아니면 안전 구역으로 간주
                else
                {
                    safeZoneMeshFilters.Add(mesh);
                }
            }
        }
        
        // 수집한 MeshFilter를 바탕으로 CombineInstance[] 생성
        CombineInstance[] wallCombine = Initiate(wallMeshFilters);
        CombineInstance[] planeCombine = Initiate(planeMeshFilters);
        CombineInstance[] safeZoneCombine = Initiate(safeZoneMeshFilters);
		
        // 위에서 얻은 정보를 바탕으로 GameObject 생성
        GameObject wall = Combine(wallCombine, wallMeshFilters);
        GameObject floor = Combine(planeCombine, planeMeshFilters);
        GameObject safeZone = Combine(safeZoneCombine, safeZoneMeshFilters);
		
        // 생성된 오브젝트의 이름 설정
        floor.name = "CombinedFloor";
        wall.name = "CombinedWall";
        safeZone.name = "CombinedSafeZone";
        
        // 통로 오브젝트에는 추가로 NavMeshSurface 추가 및 Bake
        NavMeshSurface navSurface = floor.AddComponent<NavMeshSurface>();
        navSurface.collectObjects = CollectObjects.Children; 
        navSurface.BuildNavMesh(); 
    }

	// CombineInstance[]를 생성하는 메서드
    private CombineInstance[] Initiate(List<MeshFilter> meshFilters)
    {
        CombineInstance[] temp = new CombineInstance[meshFilters.Count];
        for (int i = 0; i < meshFilters.Count; i++)
        {
        	// CombineInstance[]의 Mesh는 수집한 종류의 Mesh로 설정
            temp[i].mesh = meshFilters[i].sharedMesh;
            // 위치 동기화
            temp[i].transform = meshFilters[i].transform.localToWorldMatrix;
            // 모두 활용된 prefab들은 비활성화
            meshFilters[i].gameObject.SetActive(false);
        }
        return temp;
    }

	// CombineInstance[] 정보를 바탕으로 하나의 GameObject를 생성하는 메서드
    private GameObject Combine(CombineInstance[] combine, List<MeshFilter> meshFilters)
    {
        GameObject temp = new GameObject();
        Mesh combinedMesh = new Mesh();
        
        // 프리팹의 개수가 많기 때문에 Format을 확장하여 IndexException error 해결
        combinedMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
        
        // CombineInstance[] 요소들의 Mesh들을 하나로 통합하는 메서드
        combinedMesh.CombineMeshes(combine);
        
        // 생성한 GameObject에 Component 추가
        temp.AddComponent<MeshFilter>().mesh = combinedMesh;
        
        // Material의 경우 수집한 prefab 종류에 추가된 Material로 설정
        temp.AddComponent<MeshRenderer>().sharedMaterial 
        = meshFilters[0].GetComponent<MeshRenderer>().sharedMaterial;
        
        // collider 추가
        temp.AddComponent<MeshCollider>();
		
        // 충돌 판정 처리를 위한 태그 추가
        temp.tag = "Wall";

        return temp;
    }
}
profile
뚠뚠뚠뚠

0개의 댓글