TIL - 9

Chu_uhC·2023년 10월 2일
0

TIL

목록 보기
9/16
post-thumbnail

📄 23.10.2 ✍ 재사용하는 UI(뇌피셜)

✨ 클릭하면 나오는 UI, 유닛이나 몬스터에서도 재사용할 수 없을까?

🔨 List<Enum>UItype을 주면 버튼을 갱신해줄 함수

Image[] buttonList; // 하이라키에 있는 버튼 오브젝트들
Dictionary<UIType, List<Sprite>> ImageList; // UI타입에 맞는 스프라이트들
List<Sprite> defaultBtnSprites; //
List<Sprite> ResourceBtnSprites; // ImageList에 할당될 스프라이트

private void Awake()
{
    ImageList = new() { { UIType.Default, defaultBtnSprites },
    { UIType.Buy, ResourceBtnSprites }, { UIType.Sell, ResourceBtnSprites } };
}

// 버튼 오브젝트의 스프라이트를 바꿔줄 함수
// typeList는 버튼 이벤트 종류를 담은 enum값
// 이 enum은 인터페이스 배열의 함수이다.
void RefreshButton<T>(List<T> typeList, UIType uiType) where T : Enum
{
    // UIType에 맞는 스프라이트를 딕셔너리에서 빼온다.
    List<Sprite> selectSprite = ImageList[uiType];

    // 버튼 활성화 및 스프라이트 할당
    for (int i = 0; i < buttonList.Length; i++)
    {
        if (i >= typeList.Count + 1) // +1은 공통적으로 사용할 돌아가기 기능
        {
            buttonList[i].gameObject.SetActive(false);
            continue;
        }
        else if (buttonList[i].gameObject.activeSelf == false)
        {
            buttonList[i].gameObject.SetActive(true);
        }

        if (i == typeList.Count) // 돌아가기 추가
        {
            buttonList[i].sprite = defaultBtnSprites[0];
            continue;
        }
        int spriteNum = (int)(object)typeList[i]; 
        buttonList[i].sprite = selectSprite[spriteNum];
        // 가장 핵심적인 부분, typeList int값과 ImageList에서 선택한 
        // value값의 index에 맞춰서 불러온다.
        // 즉, Sprite[i] = T[i] & buttonList[i] = 어딘가의 인터페이스[i]
        // 어딘가의 인터페이스[i]의 EnumType = T[i]
    }

    SortOptionButton(typeList.Count + 1); // 버튼 정렬
}

📌 이걸 사용하기 위해서 인터페이스의 enum을 정해줘야 했고 그로인해 변수가 하나가
     더 추가되었다. 추가하지 않고 재사용할 방법이 있을까?

📌 제네릭을 처음 사용해보았는데 이론적인 부분을 시간이 있을 때 꼭 배우자
     아는 방법이 없어 어쩔수 없이 박싱언박싱을 해야했다는 게 가슴아프다.

📌 딕셔너리를 최근 자주 사용했었는데 가장 활용을 잘하지않았나 생각한다.
     새로운 스프라이트 타입이 추가된다면 Add만 나머진 알아서 해줄 듯

🎉 한 장 요약



📄 23.10.2 🌠 개천절



📄 23.10.4 ✍ 오버헤드란...

오버헤드 (Overhead) : 어떤 처리를 하기 위해서 부가적으로 들어간 성능이다.
if로 예외처리를 하고, using을 사용하거나 다른 클래스에 접근 하는 등
가장 단순한 처리에서 추가되는 부분들이 오버헤드이다.

🔨 가장 단순한 처리 방법
stopwatch.Start(); // 시간측정 시작
for (int i = 0; i < 1000000000; i++)
{
    num += num;
}
stopwatch.Stop();
// 실행시간 : 2250ms

🔨 함수를 호출하고 리턴값 받기
stopwatch.Start(); 
for (int i = 0; i < 1000000000; i++)
{
    num = Plus(num);
}
stopwatch.Stop();
// 실행시간 : 8845ms

int Plus(int value)
{
    return value += value;
}

// 같은 처리를 함수 안에서 하냐, 외부 함수를 사용하냐에 따라서
// 4배이상 차이가 나버렸다😱;
// 여기서 오버헤드는 6595ms만큼 발생하였다.

📌 가장 조심해야할 부분은 반복문이나 유니티의 Update()와 같이 계속 실행되는
     함수들이다.

📌 자주 참조하는 값들이 있으면 필드에 할당시키는 것이 좋은 방법이다.

📌 유니티에서 오버로드 함수나 같이 할당하는 함수를 잘 활용하는 것도 좋은 방법이다.

// 🔨 같은 클래스에 2번 접근
obj.transform.position = Vector3.zero;
obj.transform.rotation = Quaternion.identity;

// 🔨 같은 클래스에 1번 접근
obj.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);

// 🔨 생성만하기와 생성과 동시에 위치와 방향 설정
GameObject obj = Instantiate(gameObject);
GameObject obj = Instantiate(gameObject,Vector3.zero,Quaternion.identity);

🎉 한 장 요약



📄 23.10.5 ✍ 델리게이트를 인자로 주기

✨ 세상에.. 이때까지 델리게이트는 함수를 할당한 것만 전달 가능한 줄 알았는데 사실은 할당하지 않고 함수 자체로도 전달이 가능했었다..
이걸 몰랐기에 얼마나 비비고 꼬아서 함수를 실행하였던가...

// 팝업 UI
public class PopupUI : MonoBehaviour
{
    [SerializeField] Image image;
    public delegate void PopUpEvent();
    public PopUpEvent PopUp; // 클릭시 실행할 델리게이트
    
    // 팝업을 클릭하면 실행할 함수
    public void Click()
    {
        PopUp?.Invoke(); 
        image.color = Color.green; // 실행 완료시 초록색으로 변경!
    }
    
	// 스위치를 키면서 델리게이트에 함수를 등록
    public void On(PopUpEvent popupEvent)
    {
        gameObject.SetActive(true);
        PopUp += popupEvent;
    }
}

// UI와 모델을 분리해줄 컨트롤러
public class UIController : MonoBehaviour
{
    public static UIController Instance;
    public PopupUI popupUI;

    private void Awake() { Instance = this; }
	
    // UI로 전달해주는 함수
    public void PopUIOn(PopupUI.PopUpEvent action)
    {
        popupUI.On(action);
    }
}

// 팝업을 사용하여 함수를 실행시켜야할 모델
public class Item : MonoBehaviour
{
	// 모델 -> 컨트롤러 - > UI로 긴여정의 출발점
    public void SendPopUI()
    {
        UIController.Instance.PopUIOn(Eat);
    }
    
	// 팝업을 클릭시 실행되어야하는 함수(private)
    private void Eat()
    {
        Debug.Log("아이템을 먹다");
    }
}

📌 기존에는 이방법을 몰라서 모델 -> 컨트롤러 -> UI -> 컨트롤러 -> 모델로 돌아와서
     함수를 실행하였다...

📌 PopupUI의 델리게이트에 함수를 등록하여 사용하기때문에 재활용성, 모델성, 확장성,
     디커플링을 위배하지 않고 사용하는 것이 장점이다.

📌 직접 Delegate를 선언해도, Action을 써도 상관 없고 오히려 Action이나
     Func가 편한 느낌이었다.

🎉 한 장 요약



📄 23.10.6 ✍ 제네릭과 싱글톤

✨ 제네릭(Generic) : 일반화를 하여 데이터 형식을 미리 정하지 않아도 사용할 수 있게 만들어 외부에서 직접 타입을 지정하여 사용 할 수 있는 프래그래밍 방식
✨ 싱글톤 패턴(Singleton pattern) : 단 한개의 객체가 존재하게 유도하고 전역화 시켜 쉽게 접근할 수 있게 만드는 프래그래밍 패턴

이 두 가지를 조합하여 쉽게 싱글톤화 시키고 동적으로도 생성할 수 있다.

// 먼저 싱글톤 클래스는 싱글톤으로써의 기본적으로 코드들을 가지고있다.
// 기존에 사용했던 것과 다른 점은 제네릭을 사용하여 원하는 싱글톤을 동적으로
// 추가해줄 수 있다는 것이다.
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T instance;
    public static T Instance 
    { 
        get
        {
            if (instance == null)
            {
                instance = new GameObject(typeof(T).Name).AddComponent<T>();
                DontDestroyOnLoad(instance); // 파괴하고싶다면 없애도 된다.
            }
            
            return instance; 
        } 
    }
}

public class UIManager : Singleton<UIManager>{}
public class GameManager : Singleton<GameManager>{}
// 싱글톤을 사용할 클래스는 상속을 받아야한다.

이후로는 프로퍼티를 호출하면 자동적으로 생성하여 반환하거나 기존 것을 반환해준다

📌 제네릭의 타입을 지정해주지않으면 c#의 최상위 클래스 Object로 인식하고 작동한다
     불필요한 형변환으로 인한 성능 저하를 피하기 위해서는 반드시제약 조건으로 타입을
     명시해줘야한다.
     그것말고도 오류 방지, 예외처리, 가독성을 위해서도 지정해주는 것이 좋다.

📌 제네릭을 호출하거나 할땐 <>꺽쇠를 인자처럼 사용하면 된다.

📌 싱글톤을 사용하는 매니저는 최상위 클래스 느낌으로 사용하면 되고 씬이 넘어가도 유지
     가 되어야하는 데이터를 보관할 때 주로 사용된다.

📌 Void Method<A,B>() { }인자와 똑같이 여러개의 제네릭 타입을 지정해줄 수도 있다.

📌 자료 구조 또한 이러한 제네릭 형식을 사용한다.

🎉 한 장 요약



📔 주간 결산

1. 팀과제를 마치며..
연휴도 낑겨 있어서 어수선한 가운데 중간에 팀원이 사라지는 대참사가 일어났다.
5명이라고 생각하고 있던 역할 분담이 4명으로 줄어 들어서 오히려 4명으로 시작하는 것보다 더욱 힘이 들었지 않나 생각한다.
그럼에도 불구하고 결과가 잘나왔던 건 다들 포기하지않고 끝까지 최선을 다 하였기 때문이라고 확신한다.
좋은 프로젝트를 완성한 사람은 실력이 좋은 게 아니라 최선을 다 한 사람이 아닐까?

  1. 마지막 학습 과정, 3명의 최종 프로젝트 리더가 있는 팀의 리더가 되다.
    최종 프로젝트를 앞둔만큼 어수선하기도 하고 평소대로 팀 프로젝트에 모두가 하얗게 불태우는 것도 지금 과연 옳은 방법일까?
    리더란 팀과 문제에 대한 폭 넓은 시야로 볼 수 있어야하고, 비전을 어느 정도는 제시할 줄 알아야한다. 그러면 이 조건을 이번 과정에서 미리 준비할 수 없을까?
    가장 정답과 가까운 방법을 찾기 위해서는 팀원과 의견을 나누는 것이 가장 중요하지만
    먼저 나의 생각을 정리해보자면, 코드 컨벤션, 깃 규칙, 유니티 초기 설정(폴더, 스크립트 이름 규칙)등등 첫 단추만큼은 완벽에 가깝게 준비해나가는 것이 좋지 않나 생각한다.
profile
ChuNyan

0개의 댓글