옵저버 패턴
옵저버 패턴은 한 오브젝트가 주체역할이고 다른 오브젝트(들)이 관찰자 역할을 하는 일대다 관계를
형성 하는 패턴이다.
이벤트 버스 패턴은 다대다 관계라면 여기서는 약간 다르다. 햇갈릴수도있는데
두 패턴이 비슷하긴 하지만 약간 차이가 있어서 옵저버 패턴을 보고난뒤 이벤트 패턴도 한번 보는걸 추천한다.
일단 구조를 보면 한 객체 안에서 Subject 를 만들어주고
거기에 받아볼객체를 등록해준다.
그러면 받아볼 Observer는 notify()라는 공통으로 사용될 Event 함수를 하나 만들어두고
안에서 구현 해주면 된다.
예시로 캐릭터에 체력을 표시해주는 ui와 실제 플레이어 데이터로 구분해서 체력에 데미지 입었을때
관찰하고있다가 받아서 처리하는 코드를 짜보자
먼저 구독할 Observer과 주체가될 Subject를 작성한다.
using UnityEngine;
public abstract class Observer : MonoBehaviour
{
public abstract void Notify(Subject subject);
}
using System.Collections;
using UnityEngine;
public class Subject : MonoBehaviour
{
private readonly ArrayList _observers = new ArrayList();
public void Attach(Observer observer)
{
_observers.Add(observer);
}
public void Detach(Observer observer)
{
_observers.Remove(observer);
}
public void NotifyObservers()
{
foreach(Observer observer in _observers)
{
observer.Notify(this);
}
}
}
그리고 이제 플레이어와 그걸 표현해줄 UI를 각각 만들어준다.
using UnityEngine;
public class PlayerController : Subject
{
private UIController _uiController;
public float CurrentHealth { get { return health; } }
[SerializeField]
private float health = 100.0f;
private void Awake()
{
_uiController = gameObject.GetComponent<UIController>();
}
private void Start()
{
NotifyObservers();
}
private void OnEnable()
{
if (_uiController)
Attach(_uiController);
}
private void OnDisable()
{
if (_uiController)
Detach(_uiController);
}
public void TakeDamage(float amount)
{
health -= amount;
NotifyObservers();
if (health < 0)
Destroy(gameObject);
}
}
코드 보면 Awake에 정보 가져오고 OnEnable에 등록하고 OnDisable에 취소하고
Start에 한번 갱신하라고 신호를 보내는 코드이다.
데미지 입었을경우에도 한번 실행해주는코드이다.
using UnityEngine;
public class UIController : Observer
{
private float _currentHealth;
PlayerController playerController;
public override void Notify(Subject subject)
{
if (!playerController)
{
playerController = subject.GetComponent<PlayerController>();
}
if (playerController)
{
_currentHealth = playerController.CurrentHealth;
}
}
private void OnGUI()
{
GUILayout.BeginArea(new Rect(50, 50, 100, 200));
GUILayout.BeginHorizontal("box");
GUILayout.Label($"Health {_currentHealth}");
GUILayout.EndHorizontal();
if (_currentHealth <= 50.0f)
{
GUILayout.BeginHorizontal("box");
GUILayout.Label("w");
GUILayout.EndHorizontal();
}
GUILayout.EndArea();
}
}
여기서 Notify만 보면 된다. playerController등록후 체력을 갱신 해주는 코드이다.
그러면 이걸 실행해주는 모습이 잘 이해가 안갈텐데 Subject.cs에 NotifyObservers() 를보면
등록한 Observer에 Notify를 실행해주는걸 확인할수있다.
그러면 테스트 용으로 TakeDamage 실행해주는 코드를 만들자.
using UnityEngine;
public class ObserverClient : MonoBehaviour
{
PlayerController2 playerController2;
void Start()
{
playerController2 = (PlayerController2)FindObjectOfType(typeof(PlayerController2));
}
private void OnGUI()
{
if (GUILayout.Button("Damage"))
if (playerController2)
playerController2.TakeDamage(15.0f);
}
}
유니티에 오브젝트 하나 둔후
PlayerController와
UIController 를 넣고
새로 오브젝트 하나만든후 ObserverClient를 넣고 실행해서 버튼을 눌러보자
데미지 입을 떄마다 실행 하는걸 확인할수있다. 그리고 객체가 사라지면 자동으로 취소해서 버튼 눌러도 반응이
없는것도 확인 할수가 있다.
참조