경일게임아카데미 멀티 디바이스 메타버스 플랫폼 개발자 양성과정 20220719 2022/04/04~2022/12/13

Jinho Lee·2022년 7월 19일
0

경일 메타버스 20220719 16주차 2일 수업내용. 옵저버 패턴, 유니티 - 유니런 : 오브젝트 풀링, 이벤트

옵저버 패턴(Observer Pattern)

  • 어떤 객체의 상태가 변할 때 그 객체에 의존성을 가진 다른 객체들이 그 변화를 통지 받아 자동으로 갱신될 수 있게 만드는 패턴

    • 변화가 드물 경우, 매번 확인하는 것(폴링)은 비효율적이기에 변화가 있을 시만 확인할 수 있도록 한다.
  • 폴링(Polling) :
    객체의 변화를 감지하고 싶을 때 ⇒ 주기적으로 객체에 변화가 이뤄졌는지 검사한다.

옵저버 패턴을 사용할 수 있는 상황

  1. 한 객체가 다른 객체에 종속적일 때

  2. 한 객체의 상태의 변경으로 다른 객체의 상태를 변경해야 하고, 프로그래머가 변경해야 하는 객체 수를 몰라도 될 때

  3. 어떤 객체가 다른 객체에 자신의 변화를 통지할 수 있는데, 그 변화에 관심 있어 하는(관련이 있는) 객체무엇인지에 대한 가정이 없어도 될 때

구조

  • Subject :
    변화가 이뤄지는 주체

  • Observer :
    변화를 관찰하는 관찰자 객체

  • Push Model :
    변화된 상태를 Subject가 보내주는 구조

  • Pull Model :
    Observer상태를 꺼내는 구조

  • 핵심은 변화가 이뤄질 때, Subject의 변화를 관찰하고 있는 대상들에게 통지 해주는 것

    • 함수형(Functional)을 이용하여 구현되기도 한다. Ex) C#의 이벤트(UnityEvent)

      • 함수형(Functional) :
        함수를 변수처럼 다룬다고 이해하자

옵저버 패턴을 사용함으로써 얻는 이익

  1. SubjectObserver강하게 결합되지 않는다.

    • 폴링을 이용하면 객체가 멤버로 Subject를 들고 있어야 해서 강하게 결합될 수 밖에 없다.
      ⇒ 옵저버 패턴은 이를 피할 수 있다.
  2. 브로드캐스트(Broadcast)가 가능하다.

    • Subject특정 타입의 객체에 한정되지 않고 누구든지 관찰자에게 변화를 통지할 수 있다.
  3. 예측하지 못한 데이터갱신할 수 있다.

    • 복잡한 시스템에서는 종속 관계가 복잡해질 수 있으며 추적하기가 무척 어렵다.
      이에 따라 반드시 해야 하는 갱신을 잊을 수 있다.
      ⇒ 옵저버 패턴은 이를 피할 수 있다.

단점

  1. 무효 참조(Dangling Reference)가 일어날 수 있다.

    1. Subject로부터 통지를 받으려면 등록을, 더 이상 받지 않으려면 해제를 해야 한다.

    2. 여기서 사라진 객체의 해제를 잊었을 때, 통지를 하면서 이미 사라져 무효한 객체에 대해 참조를 하는 무효 참조 이슈가 발생할 수 있다.

  2. 상태의 자체 일관성(Self-Consistency)이 깨질 수 있다.

    1. 서브 클래스슈퍼 클래스연산을 사용하는 과정에서 통지가 일어날 수 있다.|

    2. 즉, 통지가 두 번 이상 일어날 수 있고, 사용하는 데이터에 손상이 갈 수 있다.
      이에 대한 주의가 필요하다는 의미.

  3. 통지에 따른 갱신오래 걸릴 수 있다.

    1. 통지하는 시점이 병목이 될 수 있다.

    2. 이를 완화하기 위해 비동기 방식을 사용하거나,

      • 비동기 방식 (Asynchronous) :
        요청을 보낸 후 응답(=결과)와는 상관없이 다음 절차가 동작하는 방식
    3. 큐잉(Queueing)을 이용할 수 있다.

      • 큐잉(Queueing) :
        프로그램이 메시지를 처리할 준비가 될 때까지 메시지를 보유하는 방식
        메시지를 스토리지의 큐에 배치해 프로그램이 다른 속도와 시간에, 여러 위치에서, 서로 간에 논리적 연결 없이 독립적으로 실행될 수 있도록 한다.
  • 식 본문 (Expression Body)
    구문이 하나인 경우 아래와 같이 쓸 수 있다.
    int Add(int a, int b = 10) => a + b;

오브젝트 풀링 (Object Pooling)

  • 초기에 필요한 만큼 오브젝트를 미리 만들어 풀(Pool)에 쌓아두는 방식
    • 반복된 메모리의 할당과 해제자원을 많이 사용한다.

    • 이러한 할당과 해제의 반복을 피하고 최적화 하기 위해,
      사용하고자 하는 오브젝트를 미리 생성하고 디액티브 / 액티브하여 사용한다.

      // 1. 배열
      GameObject[] _objectPool = new GameObject[10];
      // 각 원소는 적절하게 초기화 되었다고 가정
      
      for (int i = 0; i < _objectPool.Length; ++i)
      {
      		if (_objectPool[i].active ==  false)
      		{
      				_objectPool[i].SetActive(true);
      		}
      		return;
      }
      
      // 2. Stack, Queue 등 자료구조 활용
      Stack<GameObject> _objectPool = new Stack<>();
      
      void Awake()
      {
      		for (int i = 0; i < 10; ++i)
      		{
      				_objectPool.Push(new GameObject("object" + i));
      		}
      }
      
      ...
    • 유니티 2020부터 이 기능을 지원한다.
      ObjectPool

이벤트 (유니티, C차이)

1. 선언
public UnityEvent<int> OnScoreChanged = new UnityEvent<int>();  // 객체 필요
public event UnityAction<int> OnScoreChanged2;

2. 통지
OnScoreChaned.Invoke(_currentScore);
OnScoreChaned2?.Invoke(_currentScore);  // Null 체크

3. 구독과 구독 해제
	GameManager.Instance.OnScoreChanged.AddListener(함수명);    // 구독
	GameManager.Instance.OnScoreChanged.RemoveListener(함수명); // 구독 해제
	
	GameManager.Instance.OnScoreChanged2 += 함수명;             // 구독
	GameManager.Instance.OnScoreChanged2 -= 함수명;             // 구독 해제

	// 단, 함수명 => 함수포인터
  • 위 : 유니티 제공 이벤트
  • 아래 : 유니티에서 쓸 수 있는 C# 이벤트
  1. 이벤트 선언
    유니티 제공 이벤트는 객체를 만들어야 하는 반면,
    C# 이벤트는 만들지 않아도 된다.

  2. .Invoke() 사용 시
    유니티 제공 이벤트는 null체크를 안 하지만,
    C# 이벤트는 해야 한다.
    null ⇒ 구독자가 있는가

  3. 구독과 구독 해제
    유니티 제공 이벤트는 메소드로,
    C# 이벤트는 연산자로 한다.

  4. 주의
    유니티 제공 이벤트는 외부에서 Invoke()를 호출할 수 있고
    C# 이벤트는 해당 변수를 선언한 클래스 내부에서만 Invoke()를 호출할 수 있다.

  5. 유니티 에디터
    유니티 제공 이벤트는 보이고
    C# 이벤트는 안 보인다.

0개의 댓글