TIL

2024 11 08 TIL

noc777 2024. 11. 8. 23:46

심화주차에 앞서 먼저 복습반에 들어간 이유는 내가 배운 내용에 대해 좀 더 정확히 이해하여 잘못된 정보를 걸러내고 

부족하였던 부분에서 좀 더 공부를 해보고 싶어 계기가 되었다.

 

개인 과제 수행에 앞서서 해설 영상을 보 이미 한번 배웠었지만 놓쳤던 부분에 대하여 적어보는 시간을 가진다.

또한 오늘 특강들(4시간 거의 논스톱..) 중 패턴(OOP) 에 대한 내용을 토대로 좀 정리해보고 싶다.

 

Animator의 패러미터를 조작할 때 string 형으로 사용하는 경우 문자열에서 Hash로 변환하는 과정을 

반복하기에 성능에 좋지 않다고 한다. (shader 나 material 관련 패러미터를 조작하는 경우도 동일)

animator.SetTrigger("Attacking");
//바로 SetTrigger에 문자열을 넣어준 모습

---------------------------------------------------------------------------

int attackHash = Animator.StringToHash("Attacking");
animator.SetTrigger(attackHash);
//문자열을 hash로 변환 후에 hash를 SetTrigger에 넣어준 모습

----------------------------------------------------------------------------

private readonly int attackHash = Animator.StringToHash("Attacking");
//readonly를 사용하여 멤버변수로 바로 초기화하여 여러군데에서 사용도 가능하다.
//readyOnly로 선언한경우 변수를 선언할 때와 생성자안에서 변수값을 바꿀 때만 값 변경 가능

private void Awake()
{
    animator.SetTrigger(attackHash);
}

 

Has Exit Time 옵션 같은 경우 오랜만에 애니메이터를 사용하게 되면 헷갈리는 경우가 있어 적는다.

켜져있는 경우 애니메이션을 한번 실행하고 나서 넘어가게 되고

꺼져있다면 실행되는 도중 조건에 부합하면 다음 애니메이션으로 넘어가는 것이다.

 

UI 작업 시엔 무조건  Scale With Screen Size 모드에 현재 해상도를 입력해주어야 UI 작업을 다하고나서

UI들이 다 깨지는 것을 막을 수 있다.

 

 

다음은 OOP에 관한 이야기다.  

 

지금까지 배운 패턴이라고 하면 솔직히 잘 모르고 사용한 경우가 많았던 것 같다. 

강의 내용 중에선 지금까지 패턴인지도 모르고 썻던 경우도 많았다. 

 

일단 가장 익숙한 싱글톤은 사용하는 이유에 대하여 정적인 참조수단을 만드는 것이라고 저는 생각한다.

유니티에서 컴포넌트 간의 연결은 매우 제한적이고 이 연결을 위해 코드가 늘어나는 것을 줄이기 위해 생겨났다고 

생각하고 있다.

 

싱글톤을 구성하는 핵심요소는

- 자기자신을 정적으로 설정하는 static 키워드.

- 그리고 오직 고유한 싱글톤 객체가 존재하여야하기에 이후 생성되는 같은 클래스의 다른 객체는 파괴되는 로직.

 

필수까진 아니지만

DontDestroyOnLoad키워드를 통해 씬이 넘어가는 상황에서도 해당 객체를 유지하는 로직도

상황에 따라 적절히 사용하면 좋을 것 같다.

private static CharacterManager instance; // 원래는 바로 public을 쓰지만 보안성 강화를 위해 프로퍼티로 값을 넣어줄 예정

public static CharacterManager Instance
{
	get
    {
   		if(instance == null)//조건 : instance의 현 상태가 초기화가 되지않은 null 상태라면
        {
        	instance = new GameObject(nameof(CharacterManager)).AddComponent<CharacterManager>();
        	//새로 게임오브젝트를 생성하면서 CharacterManager 컴포넌트를 추가해주고 그것은 instance에 할당
        }
        return instance;   //다른 곳에서 불러와질 때 instance를 제공
    }
};

void Awake()
{
	if(instance != null) //이 클래스의 인스턴스가 존재한다면
    {
    	Destroy(gameobject) // 중복된 인스턴스인 현재 오브젝트를 제거
    }
    else
    {	
    	instance = this; // 현재 오브젝트가 인스턴스로 사용됨
    	DontDestroyOnLoad(this.gameObject); // 현재 오브젝트를 씬 이동시에도 파괴되지않도록함
    }
}

 

 

Bridge 패턴은 기능적으로 여러 변형을 가진 클래스를 나누고 정돈할 때 쓰입니다.

클래스의 기능들이 세분화될수록 여러 기능들을 한 스크립트에 몰아넣는다면 알아보기가 쉽지 않고 

복잡해집니다.  이러한 기능들을 나누어 서로를 연결하는 다리처럼 만들어주는 것을

저는 브리지 패턴으로 이해하였습니다.

 

아래는 특강에서의 튜터님의 강의 코드 내용입니다. 또한 검색하여 찾아본 결과 같은 내용의 패턴 설명이 있어 링크를 첨부합니다.

https://refactoring.guru/ko/design-patterns/bridge

 

브리지 패턴

/ 디자인 패턴들 / 구조 패턴 브리지 패턴 다음 이름으로도 불립니다: Bridge 의도 브리지는 큰 클래스 또는 밀접하게 관련된 클래스들의 집합을 두 개의 개별 계층구조​(추상화 및 구현)​로 나

refactoring.guru

 

먼저 리팩토링하기 전의 코드입니다.

  switch (color) //선언된 Enum EColor를 매개변수로 활용
  {
      case EColor.Red:// 사물의 색을 조건으로 설정
          meshR.material.color = Color.red; //Enum에 따라 사물의 색 결정
          break;
      case EColor.Blue:
          meshR.material.color = Color.blue;
          break;
  }


switch (shape) //선언된 Enum Eshape 를 매개변수로 활용
{
    case EShape.Square: //사물의 모양을 조건으로 설정
        name += "SQUARE"; //Enum 에 따라 사물 형태를 기반으로 이름을 추가
        break;
    case EShape.Circle:
        name += "CIRCLE";
        break;
}

 

한 스크립트 내에 두개의 조건문이 있는 상태입니다. 현재 상태에서는 이렇게 사용하여도 큰 불편함은 없겠지만

 만약에 여기서 기능이 더 추가되거나 기능의 종류가 더 많아진다면 더더욱 코드가 복잡해지고 보수에 있어 불편함을 겪을 것입니다. (색을 더 추가한다거나 특정한 움직임을 넣는 기능을 추가하거나 한다면 코드가 길어짐은 피할수 없는 문제다)

 

브리지 패턴으로 리팩토링

public interface IColorable //색에 대한 설정을 인터페이스로 구현할 예정
{
    public void PaintColor();
    public string ColorString();
}

public class NewShape : MonoBehaviour
{
    protected string s_name;

    protected IColorable m_Colorable;

    private void Awake()
    {
        m_Colorable = GetComponent<IColorable>();  //오브젝트 내의 인터페이스를 받아옴
        Paint(); 
    }

    protected virtual void Paint() //받아온 구현 인터페이스의 메서드를 사용
    {
        m_Colorable.PaintColor();
    }
}

 

 

public class ColorBlue : MonoBehaviour, IColorable //색 인터페이스를 상속받음
{
    public void PaintColor() // 인터페이스의 내용 구현
    {
        GetComponent<MeshRenderer>().material.color = Color.blue;
    }
}

----------------------------------------------------------------------------

public class ColorRed : MonoBehaviour, IColorable //색 인터페이스를 상속받음
{
    public void PaintColor() // 인터페이스의 내용 구현
    {
        GetComponent<MeshRenderer>().material.color = Color.red;
    }
}

 

public class TCircle : NewShape //NewShape를 상속받아 위의 인터페이스 구현 내용들도 모두 적용됨
{
    void Start()
    {
        s_name = "Circle";
        gameObject.name = $"{s_name}";
    }
}

-------------------------------------------

public class TSquare : NewShape
{
    void Start()
    {
        s_name = "Square";
        gameObject.name = $"{s_name}";
    }
}

 

브리지 패턴으로 리팩토링한 코드를 보면 인터페이스로 따로 색의 정보를 받아오고 또한 상속으로 사물의 형태를 분류하였고  이것이 한 스크립트에 모든 조건을 부여하는 것에 비해 스크립트를 계속 추가하는 가능성을 열어두었다고 생각할 수도 있지만 우린 협업의 관점으로 보아야합니다.   

 

만약에 새로운 색을 추가하고 싶다고 할 때 리팩토링 전의 코드를 수정한다고하려 할때  해당 코드를 같은 시각 다른 사람이 수정하였고 그대로 깃에 병합된다면 무조건 충돌은 발생할 수 밖에 없습니다. 

 

리팩토링 후의 코드를 사용하여 수정을 한다면 기능별로 따로 스크립트를 나누어놓았기에 각자원하는 기능을 따로 만든다면 충돌이 발생할 위험은 낮아지게 됩니다. 

 

이처럼 기능별로 구현을 나누어두면 유지보수에 있어 상당한 이점이 생기게 됩니다.  이번에 협업을 할 때도 이러한 이점들을 활용할 상황이 있으면 활용해보는 것이 좋아보입니다.  

'TIL' 카테고리의 다른 글

2024 11 12 TIL  (0) 2024.11.12
2024 11 11 TIL  (2) 2024.11.11
2024 11 07 TIL  (0) 2024.11.07
2024 11 06 TIL  (1) 2024.11.06
2024 11 05 TIL  (1) 2024.11.05