본문 바로가기
Project/<teamECHO> 4-1

[PJ/디자인패턴] Observer 패턴과 간단한 CharacterCustom

by 왹져박사 2024. 6. 2.
728x90

심플한 캐릭터 커스터마이징을 이번에는 Observer 패턴으로 구현해 보기로 하였다. 

Observer패턴은 주체가 되는 Subject와 그를 관찰하는 Observer로 이루어져 있다. 

보통 예시들을 보면 주체에 변화가 있을 때, 이를 UI로 전달하는 것들이 많다. 

하지만, 캐릭터 커스터마이징은 UI로 선택한 것을 캐릭터가 전달받아 보여주기 때문에, 

Subject를 Character, Observer를 UI로 설정하게 되었다. 

 

이미 다른 기능을 하던 객체에 해당 기능을 추가했기 때문에 interface로 상속하였다. 

 

Observer - CustomPlayer

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace NHR
{
    public interface ICharacterObserver
    {
        //옵저버 업데이트 캐릭터번호, 텍스쳐 이름
        void ObserverUpdate(int characterNum, string textureName);
    }

    public class CustomPlayer : MonoBehaviour, ICharacterObserver
    {
        //현재 캐릭터번호, 텍스쳐 이름
        public int nowCharacterNum;
        public string nowTextureName;

        public Character[] characters;

        private void Awake()
        {
            this.characters = this.GetComponentsInChildren<Character>();
            foreach (var character in this.characters)
            {
                character.gameObject.SetActive(false);
            }
            this.characters[this.nowCharacterNum].gameObject.SetActive(true);
        }
        public void ObserverUpdate(int characterNum, string textureName)
        {
            Debug.LogFormat("in CustomPlayer num : {0}, texture : {1}", characterNum, textureName);
            //전달된 정보 업데이트
            //이전 선택과 다를 경우
            if (this.nowCharacterNum != characterNum)
            {
                //캐릭터 변신
                this.characters[this.nowCharacterNum].gameObject.SetActive(false);
                this.nowCharacterNum = characterNum;
                this.characters[this.nowCharacterNum].gameObject.SetActive(true);
            }
            //텍스쳐 바꿈
            var mat = this.characters[this.nowCharacterNum].material;
            Debug.LogFormat("<color=yellow>material : {0}</color>", mat.name);
            //Debug.LogFormat("<color=yellow>main texture : {0}</color>", mat.mainTexture.name);
            mat.mainTexture = Resources.Load<Texture>("ClothesTexture/" + this.nowCharacterNum + textureName);

            this.nowTextureName = textureName;
        }
    }
}

 

 

Subject - UI

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace NHR
{
    public interface ICharacterUISubject
    {
        //옵저버 등록하기
        void ResisterObserver(ICharacterObserver observer);
        //옵저버 제거하기
        void RemoveObserver(ICharacterObserver observer);
        //모든 옵저버 업데이트하기
        void UpdateObservers();
    }

    public class UICharacterCustomManager : MonoBehaviour, ICharacterUISubject
    {
        //선택된 캐릭터, 컬러
        public UICharacterSlot selectedCharacter;
        public UIClothesColor selectedClothesColor;

        //옵저버 관리열
        private List<ICharacterObserver> observers = new List<ICharacterObserver>();
        public ICharacterObserver observer;

        //캐릭터 슬롯들
        public UICharacterSlot[] slots;
        //옷 컬러 슬롯들
        public UIClothesColor[] colors;

        private void Awake()
        {
            //캐릭터 슬롯들 받아오기
            this.slots = this.GetComponentsInChildren<UICharacterSlot>();

            for (int i = 0; i < this.slots.Length; i++)
            {
                this.slots[i].characterNum = i;
            }
        }
        private void Start()
        {
            Debug.Log("UICharacterCustomManager start");
        
            //디폴트로 선택된 캐릭터/색 활성화
            this.selectedCharacter.imageCharacter.color = Color.white;
            this.selectedClothesColor.selectedGo.SetActive(true);

            //버튼들 관리, 클릭 시 OnBtnClick 함수 호출
            foreach (UICharacterSlot slot in this.slots)
            {
                slot.buttonCharacterSlot.onClick.AddListener(() => OnBtnClick(slot));
            }
            foreach(UIClothesColor color in this.colors)
            {
                color.btnColor.onClick.AddListener(() => OnBtnColorClick(color));
            }
        }

        private void OnBtnClick(UICharacterSlot slot)
        {
            //버튼 클릭 시 옵저버들 업데이트, 캐릭터
            Debug.LogFormat("{0} clicked", slot);
            //이전 선택된 캐릭터 컬러 회색으로, 새로 선택한 캐릭터 컬러 흰색으로
            //이전 선택한 것과 같지 않다면
            if (this.selectedCharacter != slot)
            {
                this.selectedCharacter.imageCharacter.color = new Color(0.3f, 0.3f, 0.3f, 0.5f);
                this.selectedCharacter = slot;
                this.selectedCharacter.imageCharacter.color = Color.white;
                this.UpdateObservers();
            }
        }
        private void OnBtnColorClick(UIClothesColor color)
        {
            //버튼 클릭 시 옵저버들 업데이트, 옷 컬러
            Debug.LogFormat("{0} clicked", color);
            //이전 선택한 것과 같지 않다면
            if (this.selectedClothesColor != color)
            {
                this.selectedClothesColor.selectedGo.SetActive(false);
                this.selectedClothesColor = color;
                this.selectedClothesColor.selectedGo.SetActive(true);
                this.UpdateObservers();
            }
        }
        public void UpdateObservers()
        {
            Debug.Log("Update Observers");
            Debug.LogFormat("num : {0}, color name : {1}", this.selectedCharacter, this.selectedClothesColor);
            //모든 옵저버 업데이트하기
            foreach (ICharacterObserver observer in observers)
            {
                observer.ObserverUpdate(this.selectedCharacter.characterNum, this.selectedClothesColor.textureName);
            }
        }

        public void RemoveObserver(ICharacterObserver observer)
        {
            //옵저버 제거하기
            this.observers.Remove(observer);
        }

        public void ResisterObserver(ICharacterObserver observer)
        {
            //옵저버 등록하기
            this.observers.Add(observer);
        }
    }

}

 

 

 

728x90