반응형
옵저버 패턴 정의
한 객체의 주체(Subject)의 상태가 변경되었을 때, 이를 의존하는 다른 옵저버(Observers)가 자동으로 통보를 받고 업데이트되도록 하는 디자인 패턴.
장점
- 느슨한 결합: Subject와 Observer 간 의존성이 낮아, 객체 간 독립성이 높아집니다.
- 자동 업데이트: Subject 상태 변경 시 Observer가 자동으로 반응.
- 확장성: 새로운 Observer를 추가해도 Subject의 코드에 변경이 필요 없습니다.
단점
- 복잡성 증가: 너무 많은 옵저버가 존재하면 관리가 어려워질 수 있습니다.
- 예측 어려움: 의도치 않은 동작으로 디버깅이 까다로울 수 있음.
간단하게 이해하기
- 예시로 A라는 버튼이 있다고 가정하자 이 버튼을 클릭했을 때 B 오브젝트에서 소리가 나야 하고 C오브젝트의 애니메이션이 재생 되어야 한다면 옵저버 패턴을 사용하면 된다
- A 오브젝트는 Action 이벤트를 만들고 B,C 오브젝트에서는 Awake시점에 A에 접근 후 이벤트에 콜백함수를 등록만 해두면 된다. 아래 코드를 통해 확인해 보자
코드를 통해 이해하기
[Subject 이벤트 핸들러 생성]
주체는 아래와 같이 public event Action Clicked 처럼 이벤트 핸들러를 생성해놓기만 하면됩니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
namespace DesignPatterns.Observer
{
[RequireComponent(typeof(Collider))]
public class ButtonSubject: MonoBehaviour
{
public event Action Clicked;
private Collider m_Collider;
void Start()
{
m_Collider = GetComponent< Collider>();
}
public void ClickButton()
{
Clicked?.Invoke();
}
void Update()
{
CheckCollider();
}
private void CheckCollider()
{
// Check if the mouse left button is pressed over the collider
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo, 100f))
{
if (hitInfo.collider == this.m_Collider)
{
ClickButton();
}
}
}
}
}
}
[Observer 구독하기]
옵저버(Observer)는 위에서 서브젝트가 생성한 이벤트 핸들러를 구독합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace DesignPatterns.Observer
{
[RequireComponent(typeof(AudioSource))]
public class AudioObserver : MonoBehaviour
{
// dependency to observe
[SerializeField] ButtonSubject subjectToObserve;
[SerializeField] float delay = 0f;
private AudioSource source;
private void Awake()
{
source = GetComponent< AudioSource>();
if (subjectToObserve != null)
{
subjectToObserve.Clicked += OnThingHappened;
}
}
public void OnThingHappened()
{
StartCoroutine(PlayWithDelay());
}
IEnumerator PlayWithDelay()
{
yield return new WaitForSeconds(delay);
source.Stop();
source.Play();
}
private void OnDestroy()
{
// unsubscribe/deregister from the event if we destroy the object
if (subjectToObserve != null)
{
subjectToObserve.Clicked -= OnThingHappened;
}
}
}
}
[Observer 상황]
마찬가지로 옵저버 이다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace DesignPatterns.Observer
{
public class AnimObserver : MonoBehaviour
{
[SerializeField] Animation animClip;
[SerializeField] ButtonSubject subjectToObserve;
void Start()
{
if (subjectToObserve != null)
{
subjectToObserve.Clicked += OnThingHappened;
}
}
private void OnThingHappened()
{
if (animClip != null)
{
animClip.Stop();
animClip.Play();
}
}
}
}
결론
- 최종적으로 A 코드에서 Update -> CheckColider -> ClickButton 순서에 따라 Clicked?.Invoke 코드가
`인보크` 되면 Subject를 구독했던 Observer인 B의 소리와 C의 애니메이션이 실행된다. - 1:N 관계인 만큼 상태 변화에 따라 동작하는 객체 수 가 많은 경우 자주 사용 될 것 같다.
반응형
'유니티 > 구현내용정리' 카테고리의 다른 글
| 유니티 방치형 프로젝트 - 몬스터를 지정 반경 내에 랜덤하게 스폰 시키기(Random.insideUnitSphere) (0) | 2025.03.11 |
|---|---|
| 유니티 Robots 프로젝트 - 매치메이킹 구현 #1 (2) | 2025.03.04 |
| 어드레서블 - AWS 서버에서 다운로드 해야 할 리소스 파일 확인 하는 방법 (0) | 2025.02.16 |
| 유니티 데이터 드리븐 구조를 사용해서 게임 로직과 데이터를 분리하기 (0) | 2025.01.20 |
| 유니티 MVC 패턴 특징 정리 (디자인 패턴) (0) | 2025.01.19 |
