본문 바로가기

유니티/디자인 패턴

[Unity] 옵저버 패턴 (Observer Pattern)

반응형

옵저버 패턴이란?

옵저버 패턴(감시자 패턴)은 옵저버가 어떠한 오브젝트를 바라보고 있다가, 해당 오브젝트에 어떤 이벤트가 발생하면 observer(subscriber, listener라고도 한다)에게 상황을 알려주며 어떠한 행위를 하게 하는 디자인 패턴이다.

 

옵저버 패턴 구조


왜 사용하는 것일까?

만약 특정 이벤트가 발생 했는지에 대해 옵저버가 스스로 매 프레임마다 체크 한다면 해당 이벤트의 발생 유무에 대해 알 수 있을 것이다. 그러나 언제 발생할지도 모르는 이벤트를 위해 매번 체크하는 행위(polling)는 매우 큰 손실이다. 더구나 관측자가 여러 개라면 효율은 더욱더 낮아진다. 옵저버 패턴은 이벤트가 발생했을 때 한 번만 실행되어 콜백(call back)을 받도록 한다.


어떻게 사용할까?

유니티에서 게임오브젝트로 경찰들을 여러개 생성한 다음 도둑이 생성되면 모든 경찰들이 해당 방향으로 쳐다보는 방식으로 예제를 만들어 보았다. 다음 스크립트를 보도록 하자.

public interface IStation
{
	//경찰들에게 알림을 알릴 Station
    void Add(IPerson person);
    void Alert();
}

public interface IPerson
{
	//경찰들에게 어떠한 행동을 지시하도록 정보를 담을 인터페이스
    void Behave(IStation station);
}

C#코드의 다형성을 위해 클래스가 상속받을 인터페이스를 제작해 준다.

 

아래 스크립트는 IStation 인터페이스를 상속받은 PoliceStation에 대한 코드이다.

public class PoliceStation : MonoBehaviour, IStation
{
    public GameObject Thief
    {
        get { return thief; }
        set
        {
            thief = value;
            Alert();
        }
    }
    public List<IPerson> chaser = new List<IPerson>();
    private GameObject thief;

    public void Add(IPerson person)
    {
        chaser.Add(person);
    }

    public void Alert()
    {
        chaser.ForEach(
            x => x.Behave(this)
        );
    }
}

PoliceStation은 하나의 도둑 오브젝트에 대한 정보와 여러 개의 IPerson인터페이스의 정보를 갖는 chaser 리스트를 갖고 있다.

 

public class Police : MonoBehaviour, IPerson
{
    public void Behave(IStation station)
    {
        if (station is PoliceStation policeStation)
        {
            transform.LookAt(policeStation.Thief.transform);
        }
    }
}

Istation을 상속받는 PoliceStation으로 다운캐스팅이 가능하다면 policeStation에 접근해서 Thief오브젝트 방향으로 바라보도록 한다.

 

public class MainSystem : MonoBehaviour
{
    public Police policeModel;
    public GameObject thiefModel;
    public PoliceStation policeStation;

    IEnumerator Start()
    {
        for (int i = 0; i < 10; i++)
        {
            PoliceGenerater();
            yield return new WaitForSeconds(1f);
        }
        ThiefGenerater();
    }

    private void PoliceGenerater()
    {
        Police clone = Instantiate(policeModel, Random.insideUnitCircle * 10, Quaternion.identity);
        policeStation.Add(clone);
    }

    private void ThiefGenerater()
    {
        GameObject clone = Instantiate(thiefModel, Random.insideUnitCircle * 10, Quaternion.identity);
        policeStation.Thief = clone;
    }
}

이제 씬을 시작하면 Police를 10개 만들고 Thief를 1개 만들어 본다. 한 번에 모든 코드를 실행하면 변화를 알 수 없으니 Start함수를 코루틴으로 실행한다. 

 

실행 결과

씬 시작 시 오브젝트가 생성이 되고 10초 후에 모든 Police 오브젝트들이 Thief를 향해 바라본다.

반응형