내일배움캠프 66일차 TIL : UI들 꾸미기, 트레저 스테이지, 화이트 몬스터 추가, 보스방 입장 트리거 변경, 면접 질문 준비 13 ~ 27

woollim·2024년 12월 26일
0

내일배움캠프TIL

목록 보기
59/65

■ 개요

○ 오늘 계획

  • 보물상자 모델링 찾아서 적용하기
  • 잡몹 다양화
  • 옵션 UI 꾸미기
  • 에셋 쉐이더 문제 해결하기(윈도우>렌더링>컨버터)
  • 보너스 방 만들기 -> 한가운데에 석상두기
  • 보너스 방 인터렉션 스크립트 만들기
  • 쉘터에서 보스방 입장 -> 영역 트리거로 변경
  • 쉘터에서 계단내려가기 → 마을 이동 트리거


■ UI들 꾸미기

  • 이쁘게 꾸민거 한번 자랑해보고 싶었다.

○ UIEnhancement



○ UIInheritance



○ UIOptionExtraction



○ UIInventory



○ UISettingPopup



○ UITutorialPanel




■ 트레저 스테이지

○ TreasureStage Scene

  • 이벤트 타입 스테이지 중 별다른 행동 없이 보상만 얻는 스테이지
  • 가운데 보상석상(프리펩)과 상호작용하면 해당 스테이지의 보상 즉시 지급!
  • 골드, 명성치, 무기를 지급함

○ RewardAndPortalStone 클래스

  • 트레저 방 한가운데에 있는 구조물 보상석상의 클래스
  • 인터렉션을 하면 보상관련 UI를 띄운다
public class RewardAndPortalStone : MonoBehaviour, IInteractable
{
    [SerializeField] protected int NPCCode;
    protected Outlinable outlinable;

    [Header("윤곽선 설정")]
    [SerializeField] protected Color outlineColor = new Color(1f, 0.7f, 0f, 1f);
    [SerializeField] protected float outlineWidth = 5f;

    private void Awake()
    {
        outlinable = GetComponent<Outlinable>();
        if (outlinable == null)
            outlinable = gameObject.AddComponent<Outlinable>();

        outlinable.RenderStyle = RenderStyle.Single;
        outlinable.OutlineParameters.Color = outlineColor;
        outlinable.OutlineParameters.DilateShift = outlineWidth;
        outlinable.OutlineParameters.BlurShift = 1f;

        outlinable.AddAllChildRenderersToRenderingList();

        ShowOutline(false);
    }

    public void Interact()
    {
        var existingUI = Managers.UI.IsOpened<UIRewardStone>();
        if (existingUI != null)
            Managers.UI.Hide<UIRewardStone>();
        else
            Managers.UI.Show<UIRewardStone>();
    }

    public void ShowOutline(bool show)
    {
        if (outlinable != null)
            outlinable.OutlineParameters.Enabled = show;
    }

    public Transform GetTransform()
    {
        return this.transform;
    }
}

○ UIRewardStone

  • 비석과 인터렉션해서 UI가 켜지는 동시에 보상이 지급된다
  • 마을가기 : 마을가기
  • 계속하기 : 탑을 계속 등반
using UnityEngine;
using EPOOutline;

public class RewardAndPortalStone : MonoBehaviour, IInteractable
{
    [SerializeField] protected int NPCCode;
    protected Outlinable outlinable;

    [Header("윤곽선 설정")]
    [SerializeField] protected Color outlineColor = new Color(1f, 0.7f, 0f, 1f);
    [SerializeField] protected float outlineWidth = 5f;

    private void Awake()
    {
        outlinable = GetComponent<Outlinable>();
        if (outlinable == null)
            outlinable = gameObject.AddComponent<Outlinable>();

        outlinable.RenderStyle = RenderStyle.Single;
        outlinable.OutlineParameters.Color = outlineColor;
        outlinable.OutlineParameters.DilateShift = outlineWidth;
        outlinable.OutlineParameters.BlurShift = 1f;

        outlinable.AddAllChildRenderersToRenderingList();

        ShowOutline(false);
    }

    public void Interact()
    {
        var existingUI = Managers.UI.IsOpened<UIRewardStone>();
        if (existingUI != null)
            Managers.UI.Hide<UIRewardStone>();
        else
            Managers.UI.Show<UIRewardStone>();
    }

    public void ShowOutline(bool show)
    {
        if (outlinable != null)
            outlinable.OutlineParameters.Enabled = show;
    }

    public Transform GetTransform()
    {
        return this.transform;
    }
}


■ 화이트 몬스터 추가

  • 근접 애니메이터만 새로 만들고, 스크립트는 기존 밀리 에너미를 썼다
  • 이런식으로 몬스터 바리에이션을 늘릴예정


■ 보스방 입장 트리거 변경

○ EnterBossStage 클래스

  • 기존 석상을 클릭해 방으로 바로 이동되던 방식에서, 계단 영역(박스트리거)에 입장시 보스방에 입장할 것인지 선택하는 UI가 뜨도록했다
using UnityEngine;
using UnityEngine.Timeline;
public class EnterBossStage : MonoBehaviour
{
    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject == Managers.Character.Player.gameObject)
        {
            var existingUI = Managers.UI.IsOpened<UIShelterBossEnter>();
            if (existingUI != null)
                Managers.UI.Hide<UIShelterBossEnter>();
            else
                Managers.UI.Show<UIShelterBossEnter>();
        }
    }
}

○ UIShelterBossEnter

using TMPro;
using UnityEngine;
using UnityEngine.UI;
using System;

public class UIShelterBossEnter : UIPopUp
{
    [SerializeField] private TextMeshProUGUI floorNumber;
    [SerializeField] private Button enterButton;

    private int seletedFloor;

    protected override void Awake()
    {
        base.Awake();

        enterButton.onClick.AddListener(OnEnterButton);
    }

    private void OnEnable()
    {
        floorNumber.text = Managers.Game.TopCurFloor.ToString();
    }

    public override void Hide()
    {
        base.Hide();
        Managers.UI.Hide<UIShelterBossEnter>();
    }

    public void OnEnterButton()
    {
        Managers.Sound.PlaySFX(SFX.NPC_OpenGate);
        Hide();
        Managers.Stage.CreateStage();
    }
}


■ 면접 질문 준비

13. 참조 형식과 값 형식

  • 참조 형식과 값 형식은 데이터 저장 방식과 메모리 사용에 차이가 있습니다.

  • 참조 형식

    • 변수가 데이터의 참조(주소)를 저장함
    • 메모리에서 힙(Heap)에 저장되며, 실제 데이터에 대한 참조(주소)를 저장함
    • 참조 형식의 변수는 동일한 객체를 참조하므로, 한 변수의 값을 변경하면 다른 변수에도 영향을 미침
    • 예: class, string, object, array
  • 값 형식

    • 변수가 값을 직접 저장하는 타입
    • 메모리에서 스택(Stack)에 저장되며, 변수 자체가 데이터를 포함
    • 값 형식의 변수는 개별 복사본을 가지므로, 한 변수의 값을 변경해도 다른 변수에 영향을 주지 않음
    • 예: int, float, struct

14. 메모리에서 스택과 힙의 차이점

  • 스택
    • 고정된 메모리 공간
    • 크기가 비교적 작으며, 컴파일 시 크기가 정해짐
    • 메서드 호출 시 자동으로 할당 및 해제
    • 빠른 접근 속도. 빠르게 할당 및 해제
    • 지역 변수와 값 형식 데이터 저장
    • LIFO (Last In, First Out) 방식으로 데이터가 관리
    • 동적 메모리 공간
    • 크기가 큼
    • 객체와 참조 형식 데이터 저장. 메모리의 주소를 통해 접근
    • 명시적 할당 및 해제(가비지 컬렉터에 의해 관리됨)
    • 스택보다 접근 속도가 느림

15. struct와 class의 차이점

  • 구조체는 가벼운 데이터 그룹을 다룰 때 적합하며, 클래스는 더 복잡한 데이터 및 동작을 다루는 데 적합
  • struct
    • 값 형식이며, 스택 메모리에 저장됨
    • 가볍고 단순한 데이터 구조에 적합함
    • 상속을 지원하지 않음
    • 기본 생성자가 제공되지 않으며, 생성 시 모든 필드는 기본값으로 초기화
    • 복사 시 값이 복제되므로 독립적인 사본을 가짐
  • class
    • 참조 형식이며, 힙에 저장됨
    • 복잡한 객체 지향 프로그래밍에 적합
    • 상속을 지원함
    • 기본 생성자가 자동으로 제공되며, 인스턴스를 생성할 때 명시적으로 초기화할 수 있음
    • 복사 시 주소만 복사되므로 원본과 복사본이 동일한 데이터를 참조

16. 얕은 복사와 깊은 복사의 차이점

  • 얕은 복사 (Shallow Copy)

    • 얕은 복사는 객체의 메모리 주소를 복사합니다.
    • 즉, 복사된 객체와 원본 객체가 같은 메모리의 데이터를 참조하게 됩니다.
    • 특징
      • 빠르게 수행됩니다.
      • 복사된 객체와 원본 객체가 같은 데이터를 공유합니다.
      • 참조형 데이터(배열, 리스트 등)가 포함된 객체의 경우 문제가 발생할 수 있습니다.
  • 깊은 복사 (Deep Copy)

    • 깊은 복사는 객체의 데이터까지 모두 복사하여 독립적인 메모리 공간을 만듭니다.
    • 특징
      • 복사된 객체는 원본과 완전히 독립적으로 동작합니다.
      • 원본이 변경되어도 복사본에 영향을 미치지 않습니다.
      • 복사가 느릴 수 있지만 안전하게 사용 가능합니다.
    • 예시: JSON 직렬화를 이용한 깊은 복사
      -C#에서는 Newtonsoft.Json 또는 System.Text.Json을 사용하여 JSON 형식으로 직렬화 후 역직렬화를 통해 깊은 복사를 구현할 수 있습니다.
using System;
using Newtonsoft.Json;

class Person {
    public string Name { get; set; }
    public int[] Scores { get; set; }
}

class Program {
    static T DeepCopy<T>(T obj) {
        string json = JsonConvert.SerializeObject(obj); // 객체를 JSON으로 직렬화
        return JsonConvert.DeserializeObject<T>(json); // JSON을 역직렬화하여 깊은 복사 생성
    }

    static void Main() {
        // 원본 객체 생성
        Person original = new Person { Name = "홍길동", Scores = new int[] { 90, 80, 70 } };

        // 깊은 복사 수행
        Person deepCopy = DeepCopy(original);

        // 복사본 수정
        deepCopy.Scores[0] = 100;

        // 결과 확인
        Console.WriteLine($"원본 Scores[0]: {original.Scores[0]}"); // 출력: 90
        Console.WriteLine($"복사본 Scores[0]: {deepCopy.Scores[0]}"); // 출력: 100
    }
}



17. 박싱과 언박싱

  • 박싱과 언박싱은 메모리 및 CPU 비용이 크기 때문에 성능 민감한 코드에서는 피하는 것이 좋습니다.
    • 필요한 경우
      • 값 형식 데이터를 System.Object 타입으로 저장
    • 불필요한 경우
      • 제네릭(Generic)을 사용하면 박싱/언박싱 없이도 값 형식을 처리할 수 있을때
  • 박싱 (Boxing)
    • 값 형식 데이터를 참조 형식 객체로 변환하는 과정입니다.
    • 스택에 존재하던 값 형식이 힙에 할당된 객체로 복사됩니다.
    • 참조 형식 객체는 System.Object로 저장되거나, 참조 형식이 필요한 곳에서 사용됩니다.
    • 박싱은 새로운 객체를 힙에 할당하므로 성능 비용이 있습니다.
      값 형식의 크기가 작아도 힙에 더 큰 메모리 공간이 할당됩니다.
int value = 10;          // 스택에 저장
object obj = value;      // 힙에 박싱된 값 저장
  • 언박싱 (Unboxing)
    • 박싱된 객체(참조 형식)를 다시 값 형식으로 변환하는 과정입니다.
    • 힙에 있는 데이터를 다시 스택으로 복사합니다.
    • 데이터의 정확한 값 형식으로 명시적 캐스팅이 필요합니다.
    • 잘못된 형식으로 캐스팅하면 InvalidCastException이 발생합니다.
object obj = 10;         // 힙에 박싱된 값
int value = (int)obj;    // 스택으로 언박싱
  • 메모리 관점 요약
    • 박싱: 값 → 힙에 객체로 복사 (메모리 할당 비용 발생)
    • 언박싱: 힙의 객체 → 값으로 복사 (추가 형변환 비용 발생)

18. 클래스를 다른 클래스로 상속하기 위한 방법

  • C#에서 상속은 콜론(:) 기호를 사용하여 구현합니다.
  • 상속은 기존 클래스(부모 클래스 또는 기본 클래스)의 기능을 다른 클래스(자식 클래스 또는 파생 클래스)가 물려받아 확장하거나 수정할 수 있도록 합니다.
class Parent { }
class Child : Parent { }

19. 다이아몬드 문제와 해결 방법

  • 문제
    • 다중 상속에서 동일한 조상 클래스를 여러 경로로 상속받는 경우, 상속 경로의 모호성 때문에 문제가 발생합니다.
    • D가 A의 멤버를 사용할 때, B에서 상속받은 것인지, C에서 상속받은 것인지 모호성이 발생합니다.
     A
    / \
   B   C
    \ /
     D
  • 해결 방법:
    • C#은 클래스의 다중 상속을 지원하지 않으므로 다이아몬드 문제가 발생하지 않지만, 대신, 인터페이스 다중 구현을 통해 유사한 기능을 제공합니다
    • 명시적 인터페이스 구현
      • 명시적 구현을 통해 모호성을 제거할 수 있습니다.
    • 명시적 호출을 통한 모호성 제거.

20. 인터페이스

  • 인터페이스란 클래스가 구현해야 하는 멤버들을 정의하는 것

  • 인터페이스는 클래스의 일종이 아니며, 클래스에 대한 제약 조건을 명시하는 것

  • 클래스가 인터페이스를 구현할 경우, 모든 인터페이스 멤버를 구현해야 함

  • 인터페이스는 다중 상속을 지원

  • 인터페이스에서 선언한 멤버(메서드, 프로퍼티, 이벤트 등)는 기본적으로 public으로 간주 됨. 또한 오직 public으로 선언해야 함

  • 구현 클래스는 상속받은 항목들을 public 아니먄 virtual로 구현해야 함

  • 사용하는 이유

    • 코드의 재사용성
      인터페이스를 사용하면 다른 클래스에서 해당 인터페이스를 구현하여 동일한 기능을 공유할 수 있음. 인터페이스를 통해 다양한 클래스가 동일한 동작을 수행할 수 있으므로 코드의 재사용성이 향상 됨

    • 다중 상속 제공
      C#에서는 클래스는 단일 상속만을 지원하지만, 인터페이스는 다중 상속을 지원함. 클래스가 여러 인터페이스를 구현함으로써 여러 개의 기능을 조합할 수 있음. 다중 상속을 통해 클래스는 더 다양한 동작을 수행할 수 있음

    • 유연한 설계
      인터페이스를 사용하면 클래스와 인터페이스 간에 느슨한 결합을 형성할 수 있음. 클래스는 인터페이스를 구현하기만 하면 되므로, 클래스의 내부 구현에 대한 변경 없이 인터페이스의 동작을 변경하거나 새로운 인터페이스를 추가할 수 있음. 이는 유연하고 확장 가능한 소프트웨어 설계를 가능하게 함


21. 인터페이스와 추상 클래스의 차이

  • 추상클래스

    • 추상 클래스는 추상 메서드(구현이 없는 메서드)와 구현된 메서드(일반 메서드)를 모두 포함할 수 있음. 즉, 일부 기능은 직접 구현하고, 일부는 자식 클래스에서 구현하도록 강제할 수 있음
    • 추상 클래스는 필드, 프로퍼티, 생성자 등을 가질 수 있음. 이로 인해 상태(데이터)를 관리할 수 있음
    • 추상 클래스의 메서드는 public, protected, internal 등 다양한 접근 제한자를 가질 수 있음. 즉, 접근 범위를 세밀하게 조정할 수 있음
    • 추상 클래스는 기본적인 기능을 일부 구현하고, 그 외의 추가적인 기능은 하위 클래스에서 확장하거나 구현하도록 설계 됨. 일반적으로 클래스 간에 공통된 동작을 공유하면서도, 일부는 자식 클래스가 구체적으로 정의해야 할 때 사용
  • 인터페이스

    • 인터페이스는 기본적으로 모든 메서드가 구현 없이 정의만 되어 있으며, 구현은 인터페이스를 구현하는 클래스에서 해야 함.
    • 인터페이스는 필드를 가질 수 없음. 오직 메서드, 프로퍼티, 이벤트 등을 정의할 수 있음
    • 인터페이스 내의 모든 멤버는 기본적으로 public. 인터페이스 내에서는 다른 접근 제한자를 사용할 수 없음
    • 인터페이스는 구체적인 구현 없이 동작을 정의하는 역할. 클래스 간에 공통적인 기능을 제공하면서도, 클래스가 반드시 특정 기능을 구현하도록 강제할 때 사용. 다중 상속이 필요하거나, 다양한 클래스가 동일한 계약을 따라야 할 때 유용

22. 가비지 컬렉터

  • 주요 원리
    가비지 컬렉터는 객체가 더 이상 참조되지 않는 상태로 식별되면 이를 메모리에서 해제합니다.
  • 세대 기반 수집
    낮은 세대(0세대)부터 수집을 시작하며, 이 과정은 더 작은 영역을 검사하므로 빠르고 효율적입니다. 필요한 경우 더 높은 세대까지 수집을 진행합니다.

23. 가비지 컬렉터의 장점과 단점

  • 장점
    • 메모리 사용을 최소화하면서 효율적인 관리 가능.
    • 대부분의 객체가 0세대에서 소멸되므로 성능이 최적화됨.
    • 2세대 객체의 수집은 드물게 발생하여 긴 생명 주기를 가진 객체의 관리 비용이 낮음.
  • 단점
    • GC 작업에 따른 지연
      가비지 컬렉터는 주기적으로 실행되며, 이 과정에서 CPU 및 메모리 리소스를 사용합니다. 특히 대규모 메모리 정리 작업이 발생하면 애플리케이션의 성능이 일시적으로 저하될 수 있습니다.
    • 객체가 더 이상 사용되지 않더라도 GC가 실행될 때까지 메모리가 해제되지 않습니다. 이는 메모리 사용량이 즉각적으로 줄어들지 않는 상황을 초래할 수 있습니다.
    • 세대 간 객체 이동은 추가적인 오버헤드를 발생시킬 수 있음.
    • 대규모 객체(LOH, Large Object Heap)는 별도로 관리되며, 수집 시 추가 비용이 들 수 있음.
    • 특정 상황에서는 GC 동작으로 인해 애플리케이션의 성능이 일시적으로 저하될 수 있음.

24. 가비지 컬렉터의 세대 개념

  • C#의 가비지 컬렉터(Garbage Collector, GC)는 객체의 생애 주기를 기반으로 세대(Generation)라는 개념을 사용하여 메모리를 효율적으로 관리
  • 이는 객체가 메모리에서 얼마나 오래 살아남았는지에 따라 분류되며, 각 세대는 서로 다른 관리 전략을 사용
  1. 세대의 정의
  • 0세대(Gen 0)
    새로 생성된 객체가 할당되는 공간. 대부분의 객체는 단기적으로 사용되고, 빠르게 소멸되므로 이 단계에서 수집됩니다.

  • 1세대(Gen 1)
    0세대에서 살아남은 객체가 이동되는 공간. 중기 생존 객체를 관리하며, 주로 0세대와 2세대 사이의 완충 역할을 합니다.

  • 2세대(Gen 2)
    1세대에서도 살아남아 장기적으로 존재하는 객체가 할당되는 공간. 노년 객체를 관리하며, 이 세대에서는 가장 덜 빈번하게 수집이 이루어집니다.

25. 박싱, 언박싱 주의점

  • 빈번한 박싱과 언박싱은 성능 저하를 유발하므로 최소화 필요.

27. 제네릭

  • 제너릭은 클래스나 메서드를 일반화시켜 다양한 자료형에 대응할 수 있는 기능
  • 제너릭을 사용하면 코드의 재사용성을 높일 수 있음
  • C#에서는 형태의 키워드를 이용하여 제너릭을 선언 함
  • 제너릭 클래스나 메서드에서 사용할 자료형은 선언 시점이 아닌 사용 시점에 결정
  • 제너릭 클래스나 메서드를 사용할 때는 대신 구체적인 자료형을 넣어 줌
  • class 클래스명 { 접근지정자 T 변수명; }
  • 클래스명<자료형> 인스턴스명 = new 클래스명<자료형>();

0개의 댓글