20241217 / Unity_6차 15주차 화요일
현재 프로젝트의 NPC AI는 영역 좌표를 기반으로 동작한다.
부랑자는 캠프 영역 내를 배회하고, 궁수는 최외곽 벽을 기준으로 일정 거리 내의 영역을 순찰하며,
아군 NPC들의 이동 범위를 제한할 기지 영역은 벽이 지어지거나 파괴되면 크기가 바뀔 수 있다.
그렇기 때문에 맵의 모든 영역을 관리하는 컨트롤러가 따로 필요하다.
영역 컨트롤러 안에는 위에서 설명한 여러 영역에 대한 정보가 들어가야 하므로
각 영역을 나타낼 클래스를 내부에 따로 정의해야 한다.
이런 영역에 대한 편집은 무조건 컨트롤러 안에서만 가능해야 하는데,
영역 클래스 인스턴스의 필드를 읽는 것은 컨트롤러 바깥에서도 가능해야 한다.
쉽게 말해 컨트롤러 클래스는 다음과 같이 구현해야 한다.
public class Controller
{
// 내부 클래스
public class Boundary // private으로 제한하면 인스턴스를 밖에서 읽을 수 없음
{
private float left;
private float right;
public void SetLeft(float _left) // Controller 클래스 내에서는 편집이 가능해야 함
{
left = _left;
}
public void SetRight(float _right)
{
right = _right;
}
}
// 내부 클래스 인스턴스
public Boundary allyBase { get; private set; }
public void Initialize()
{
allyBase = new Boundary();
allyBase.SetLeft(-10);
allyBase.SetRight(10);
}
}
그런데 내부에서 정의된 클래스의 인스턴스를 외부에서 읽을 수 있게 만드는 순간
외부에서 내부 클래스 인스턴스의 필드를 편집하는 것까지 가능해 진다는 문제가 생긴다.
Controller controller = new Controller();
controller.allyBase.SetLeft(10);
이것을 해결하기 위해서는 읽기 전용 인터페이스가 필요하다.
내부 클래스는 private으로 제한하고 내부 클래스의 필드를 읽는 것만 가능한 인터페이스를 만들어
인터페이스를 외부에 노출시키는 것이다.
그렇게 만들어진 MapBoundaryController 클래스는 다음과 같다.
public class MapBoundaryController
{
// 내부 클래스의 읽기 전용 인터페이스
public interface IReadOnlyBoundaryData
{
float Left { get; }
float Right { get; }
}
// 내부 클래스
private class BoundaryData : IReadOnlyBoundaryData // private으로 제한 가능
{
private float left;
private float right;
// 읽기 전용 프로퍼티 구현
public float Left => left;
public float Right => right;
public BoundaryData()
{
left = 0;
right = 0;
}
public BoundaryData(float _left, float _right)
{
left = _left;
right = _right;
}
public void SetLeft(float _left)
{
left = _left;
}
public void SetRight(float _right)
{
right = _right;
}
}
// 내부 클래스 인스턴스
private readonly BoundaryData allyBase = new();
private readonly List<BoundaryData> vagrantCamp = new();
// 외부에서 읽기 전용 BoundaryData를 가져오기 위한 프로퍼티
public IReadOnlyBoundaryData AllyBase => allyBase;
public IReadOnlyList<IReadOnlyBoundaryData> VagrantCamp => vagrantCamp;
public void Initialize()
{
allyBase.SetLeft(-10);
allyBase.SetRight(10);
vagrantCamp.Add(new BoundaryData(20, 25));
vagrantCamp[0].SetLeft(22);
}
}
읽기 전용 인터페이스를 만들어 대신 노출시키면 외부에서는 내부 클래스의 존재조차 알 수 없지만
내부 클래스 인스턴스의 필드까지 읽는 것만은 가능해 진다.
MapBoundaryController mapBoundaryController = new MapBoundaryController();
mapBoundaryController.Initialize();
float allyBaseLeft = mapBoundaryController.AllyBase.Left;
float vagrantCampLeft = mapBoundaryController.VagrantCamp[0].Left;
참고로 IReadOnlyList는 닷넷에서 제공되는 인터페이스로, 리스트의 요소를 읽는 것만 가능하다.
(이것도 같은 읽기 전용 인터페이스다)