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

Wooooo·2024년 1월 29일
0

내일배움캠프Unity

목록 보기
68/94

[오늘의 키워드]

현재 플레이어의 일정 거리 밖의 청크들은 비활성화되도록 해놓은 상태다.
청크만 비활성화 되는게 아니라, 몬스터, 상호작용 오브젝트 등등 다른 객체들도 비활성화 돼야한다.


[비활성화할 오브젝트인지 판단하기]

비활성화된 청크 내부에 있는 오브젝트를 비활성화해야한다.

현재 객체의 위치값을 이용해서 간단하게 현재 어느 청크에 위치해있는지 알 수 있다.

World.cs

    public ChunkCoord ConvertChunkCoord(Vector3 pos)
    {
        ChunkCoord res = pos;
        res.x /= VoxelData.ChunkSizeX;
        res.z /= VoxelData.ChunkSizeZ;
        return res;
    }

[무엇을 비활성화 할 것인지 설정하기]

오브젝트마다 비활성화할 요소가 다를 수 있다.

예를 들어, GameObject 자체를 꺼버린다던지, 렌더러만 끈다던지, 콜라이더만 끈다던지, 직접 작성한 Component (Behaviour)를 끈다던지.

각 객체마다 로직을 작성할 수도 있겠지만, 하나의 컴포넌트로 모듈화 할 수 있을 것 같았다.

ManagementedObject.cs

public class ManagementedObject : MonoBehaviour
{
    private ChunkCoord _coord;
    private World _world;
    private Transform _tranform;
    private ObjectManager _manager;

    public List<ObjectManagementProtocol> managedTargets = new();
    
    private void Start()
    {
        _world = Managers.Game.World;
        _manager = Managers.Game.ObjectManager;
        _tranform = transform;

        _coord = _world.ConvertChunkCoord(_tranform.position);
        _world.OnWorldUpdated += SwitchEnabled;
        SwitchEnabled();
    }

    public void SwitchEnabled()
    {
        _coord = _world.ConvertChunkCoord(_tranform.position);
        if (_world.ChunkMap.TryGetValue(_coord, out var chunk))
        {
            bool enabled = chunk.IsActive;
            foreach (var target in managedTargets)
                _manager.ManageObject(target, enabled);
        }
    }
}

[어떻게 비활성화할 것인지 설정하기]

객체의 타입마다 활성화/비활성화 하는 법이 다르다.

GameObject는 SetActive() 메서드를 이용해 활성화/비활성화 할 수 있다.

Collider, Renderer, Behaviour 모두 Component를 상속받지만, Component에는 enabled 프로퍼티가 없고 파생 클래스에서 enabled 프로퍼티를 제공하고 있기 때문에 Collider, Renderer, Behaviour 각각 따로따로 enabled를 set하도록 해야한다.

그 결과, 각 타입을 Key로, 비활성화 여부 설정 방법을 수행하는 Action을 Value로 갖는 딕셔너리를 만들어서 사용해보기로 했다.

ObjectManager.cs

public class ObjectManager
{
    private Dictionary<Type, Action<object, bool>> _process = new();

    public ObjectManager()
    {
        _process.Add(typeof(Renderer[]), (x, enabled) =>
        {
            foreach (var e in x as Renderer[]) 
                e.enabled = enabled; 
        });

        _process.Add(typeof(Renderer), (x, enabled) => 
        {
            (x as Renderer).enabled = enabled;
        });

        _process.Add(typeof(GameObject[]), (x, enabled) =>
        {
            foreach (var e in x as GameObject[])
                e.SetActive(enabled);
        });

        _process.Add(typeof(GameObject), (x, enabled) =>
        {
            (x as GameObject).SetActive(enabled); 
        });

        _process.Add(typeof(Behaviour[]), (x, enabled) => 
        {
            foreach (var e in x as Behaviour[]) 
                e.enabled = enabled; 
        });

        _process.Add(typeof(Behaviour), (x, enabled) => 
        {
            (x as Behaviour).enabled = enabled; 
        });

        _process.Add(typeof(Collider[]), (x, enabled) =>
        {
            foreach (var e in x as Collider[])
                e.enabled = enabled;
        });

        _process.Add(typeof(Collider), (x, enabled) =>
        {
            (x as Collider).enabled = enabled;
        });
    }

    public void ManageObject(ObjectManagementProtocol protocol, bool chunkEnabled)
    {
        _process[protocol.key]?.Invoke(protocol.target, chunkEnabled);
    }
}

[ManagementedObject와 ObjectManager 통신]

ManagementedObject에는 어떤 객체를 관리할 것인지가 작성돼있고, ObjectManager에는 관리될 객체의 타입 별 관리 방법이 작성돼있다.

이제 ManagementedObjectObjectManager에게서 관리 방법을 꺼내와 객체가 관리되도록 해야한다.

두 클래스가 통신할 수 있도록 Protocol 클래스를 만들어봤다.

ObjectManagementProtocol.cs

public class ObjectManagementProtocol
{
    public object target;
    public Type key;

    public ObjectManagementProtocol(object target, Type key)
    {
        this.target = target;
        this.key = key;
    }
}

[몬스터 클래스에 적용]

몬스터는 비활성화 된 청크에 있다면 ...

  1. 그려지지 않게 하기 위해 메시렌더러를 꺼야한다.
  2. 불필요한 충돌 검사를 줄이기 위해 콜라이더를 꺼야한다.
  3. FSM 동작을 멈추게 하기 위해 FSM 동작을 관리하는 Monster 컴포넌트를 비활성화해야한다.

Monster.cs

    private void Start()
    {
    	...
        
        SetManagementedObject();
    }

    public void SetManagementedObject()
    {
        var managedObject = gameObject.AddComponent<ManagementedObject>();
        managedObject.managedTargets.Add(new(this, typeof(Behaviour)));
        managedObject.managedTargets.Add(new(GetComponentsInChildren<Renderer>(), typeof(Renderer[])));
        managedObject.managedTargets.Add(new(GetComponentsInChildren<Collider>(), typeof(Collider[])));
    }

Monster 클래스 내부에서 자기 자신 (Monster 컴포넌트)를 관리하도록 this를 넘겨줬다.
직접 작성한 MonoBehaviour 컴포넌트는 프로토콜 클래스 생성 시, 타입(Key)를 Behaviour 타입으로 정해주면 된다.

Collider와 Renderer는 오브젝트에 달려있는 모든 Collider와 Renderer를 관리하기 위해 GetComponenetsInChildren을 이용해 넘겨줬다.


[구현 모습]

청크 경계면에서 왔다갔다 하면서 테스트해봤다.

Monster 컴포넌트, 콜라이더, 렌더러 모두 잘 활성화/비활성화된다.

이제 자원 오브젝트의 렌더러, 청크의 렌더러(현재는 청크 자체에서 직접 렌더러를 끄는 중)의 관리도 ManagementedObject에게 맡길 수 있게 됐다.

profile
game developer

0개의 댓글