내일배움캠프 Unity 73일차 TIL - 팀 9와 4분의 3 - 개발일지

Wooooo·2024년 2월 7일
0

내일배움캠프Unity

목록 보기
75/94

[오늘의 키워드]

  1. 자원 오브젝트 배치용 씬 추가
  2. NavMeshSourceObject 클래스 작성
  3. WorldNavMeshBuilder 클래스 구조 변경

[자원 오브젝트 배치용 씬 추가]

기존의 자원 오브젝트 배치 작업은 되게 험난했다...

이미 생성된 맵 위에 ray를 쏴서 ground 레이어에 닿아야 생성되므로, 메인씬을 실행해서 world를 생성하고, 몬스터들을 지우고, 비활성화된 청크를 키고,, 암튼 귀찮다.

그래서 자원 오브젝트 배치용 씬을 추가했다.
청크도 비활성화되지 않게 하고, 오브젝트도 비활성화되지 않아서 생성된 오브젝트도 한 눈에 볼 수 있도록..

public class ResourceObjectGenerateScene : MonoBehaviour
{
    private void Start()
    {
        // 1. 리소스 로드
        Managers.Resource.LoadAllAsync<UnityEngine.Object>("MainScene", (key, count, total) =>
        {
            if (count == total)
            {
                // 2. 맵 생성
                Managers.Game.GenerateWorldAsync(null,
                () =>
                {
                    // 맵 생성 완료 시 콜백
                    // 3. 객체 생성, 초기화
                    Managers.Game.World.enabled = false;
                    foreach (var chunk in Managers.Game.World.ChunkMap.Values)
                        chunk.IsActive = true;
                });
            }
        });
    }
}

씬을 하나 더 만드니까, 기존에 생성했던 생성기 컴포넌트들의 값도 유지돼서 좋은 듯 ?
메인 씬에는 얘네를 그냥 둘 수 없어서 생성 작업을 하고 나면 하이어라키에서 지웠었다.


[NavMeshSourceObject 작성]

섬과 섬을 잇는 다리 오브젝트가 있다.
이 오브젝트는 아직 WorldNavMeshBuilder 클래스의 수집 대상이 아니라서, (현재는 청크의 메시 정보만 수집 중) 청크 이외의 객체의 NavMeshBuildSource를 받아올 수 있는 컴포넌트를 작성했다.

public class NavMeshSourceObject : MonoBehaviour
{
    private NavMeshBuildSource[] _buildSources;

    [System.Serializable]
    private struct SeializableNavMeshBuildSource
    {
        public Object sourceObject;
        public NavMeshBuildSourceShape shape;
        public Transform transform;
        public Component component;
        public Vector3 size;
    }

    [SerializeField] private SeializableNavMeshBuildSource[] _sources;

    public void ConvertData()
    {
        _buildSources = new NavMeshBuildSource[_sources.Length];
        for (int i = 0; i < _sources.Length; i++)
        {
            _buildSources[i] = new()
            {
                shape = _sources[i].shape,
                sourceObject = _sources[i].sourceObject,
                transform = _sources[i].transform.localToWorldMatrix,
                component = _sources[i].component,
                size = _sources[i].size,
            };
        }
    }

    private void Start()
    {
        SendToBuilder();
    }

    private void SendToBuilder()
    {
        if (_buildSources == null)
            ConvertData();

        foreach (var source in _buildSources)
            Managers.Game.WorldNavMeshBuilder.AddSources(source);
    }
}

빌더가 사용하는 NavMeshBuildSource 구조체는 인스펙터에 나오지 않아서,
인스펙터에 띄우기 위한 Serializable 구조체를 하나 작성했다.

처음엔 필드의 자료형도 NavMeshBuildSource의 프로퍼티와 동일하게 했었는데,
인스펙터에서 끌어다 할당할 수 있는 자료형들로 바꿔서 빌더에게 전송하기 전에 데이터를 Convert해서 전송하도록 했다.

ex)
NavMeshBuildSource의 transform 자료형은 Matrix4x4이기 때문에, 이를 인스펙터에서 끌어다 놓을 수 있는 Transform자료형으로 변경

그런데 현재 다리의 메시가 중간 중간 틈이 있어서, 청크처럼 shape를 Mesh로 하면 구멍이 난다.

그래서, 박스콜라이더로 대신한 다음 shape를 box로 넣었더니, 아예 인식을 못한다.

공식문서를 찾아보니, 3D 프리미티브 도형으로 생성할 때는 size값을 사용하는 것이었다.

나는 여태 size를 (0, 0, 0)으로 줬었다... transform이 matrix4x4라서 여기서 사이즈를 받을 수 있다고 생각했었다.

이젠 잘 구워진다 !


[WorldNavMeshBuilder 구조 변경]

현재 WorldNavMeshBuilderWorld 클래스가 필드로 가지고 있다.

사실 둘은 굳이 강한 결합일 필요가 없고, 방금 만든 NavMeshSourceObject 컴포넌트가 빌더에게 자신의 source 정보를 전달하기도 상당히 난해해서, World에서 빌더를 분리하기로 했다.

NavMeshSourceObject가 빌더에게 정보를 전달하기 위해서, 다음과 같은 규칙이 필요했다.

  1. 빌더는 접근이 간편해야한다. (아직 없다면 생성되어야함.)
    a. 빌더에게 source를 전달할 때, null reference exception 발생하면 안됨.
    b. 빌더를 초기화하지 않았어도, source의 전달은 가능해야함. (초기화는 World 생성 이후에 진행)
  2. 빌더는 다른 객체가 요청하기 전에는 NavMesh를 빌드하지 않는다.

GameManager.cs

    public World World { get; private set; }
    public WorldNavMeshBuilder WorldNavMeshBuilder
    {
        get
        {
            if (_worldNavmeshBuilder == null)
                _worldNavmeshBuilder = new GameObject(nameof(WorldNavMeshBuilder)).AddComponent<WorldNavMeshBuilder>();
            return _worldNavmeshBuilder;
        }
    }

TODO

    public AsyncOperation GenerateNavMeshAsync(Action<AsyncOperation> callback = null)
    {
        Initialize();

        foreach (var chunk in _world.ChunkMap.Values)
            AddChunkSources(chunk);

        var buildSettings = NavMesh.GetSettingsByID(0);
        var bounds = CaculateWorldBounds();
        var op = NavMeshBuilder.UpdateNavMeshDataAsync(_data, buildSettings, _sources, bounds);
        op.completed += callback;
        return op;
    }

현재 GenerateNavMeshAsync 메서드에서 모든 청크를 순회하여 source를 추가 중인데, World 클래스와 결합도가 좀 높다...

청크에도 NavMeshSourceObject를 추가하면 결합도를 낮출 수 있을 것 같다.

profile
game developer

0개의 댓글