TIL - 11

Chu_uhC·2023년 10월 17일
0

TIL

목록 보기
11/16
post-thumbnail

📄 23.10.16✍ String, Array 그리고 푸른 수염의 GC

악명 높은 String의 구조와 Array를 기반으로한 자료 구조 그리고 이것들이 왜 GC와 관련되어 문제가 되는 것인지 알아보자

🔨String은 초기화가 된 이후에 변경 할 수 없는 클래스(불변 클래스)이다.

string txt = "WOW";
txt += "XOX";
// string = char[], char는 2byte이다.
// 그러면 string에서 총 사용한 byte는 6? 12? 18?

📌 정답이 18인 이유는 "WOW"에서 6byte를 사용하고, "WOWXOX"라는
     12byte 크기의 string을 다시 만들어야한다.

📌 남겨진 데이터는 HEAP영역이있는 참조형 데이터이기에 언젠가는 GC에 의해 회수된다.

📌 문제를 조금이라도 방지하는 방법이 문자열 보간StringBuilder이다.

🔨 List,Queue, Stack도 결국은 Array이다.

// 실제 List 스크립트에서 들고온 코드
public class List<T> : IList<T>, IList, IReadOnlyList<T>
{
    internal T[] _items; // List가 자료를 보관하는 곳
    
	public List(int capacity)
	{
        _items = new T[capacity];
	}
}

📌 Add, Remove가 편한 이유는 이미 스스로 리사이즈하는 함수가 포함되어 있다.
     그 말인즉슨 사용하기 편할 뿐이지 삽입 삭제를 할 때 배열과 마찬가지로 O(n)
     실행되고 있었던 것이다. 💀🥛👍

📌 그럼 Listcapacity가 부족해지면 새로운 List가 만들어지고 남겨진 List
     GC에게 회수 당할 날만 남게 된다.

📌 이 문제를 방지하는 방법은 capacity를 생성시 알맞게 정해주는 것!

📌 LinkedListArray를 사용하지 않고 다음 노드(데이터)를 가르키는 형식이기에
     버려지는 데이터가 없지만 삽입과 제거가 O(1)인 대신 접근이 O(n)이다.
     그래서 잘 쓰이지는 않는다고 한다.

결국 GC에게 찍혀 회수당하면 가비지가 남고 성능에는 안 좋은 영향이 가는 것이다.
이런 문제들을 회피하는 방법은 Object Pooling,StringBuilder, etc. 느낌으로
껴쓰고 눠쓰고 꿔쓰고 시쓰는 방법으로 최대한 방지할 수 있다.

🎉 한 장 요약



📄 23.10.17✍ 확률을 만드는 방법 - 1

복권 당첨 확률은 되거나 안되거나 반반이다.
오지선다 문제를 찍고 채점할 때 맞거나 틀리거나 반반이 아닌 20% 확률로 맞추게 되고 복권처럼 1등과 2등의 확률이 다른 경우도 많다. 그런 다양한 확률을 구현해보도록 하자.

🔨 결과마다 동일한 확률

Case 1.
Random.Range(0,5);
// 0부터 4까지 동일한 확률로 등장

🔨 가중치를 보정한 확률

Case 2.
float[] randomTable = new float[] { 0.1f, 0.2f, 0.4f, 0.8f, 1.6f };
// 확률 테이블
float total = 0; 
float target = random.Length - 1; // Random.value가 1일 경우에 대한 예외 처리
    
foreach (float per in random)
{
	total += per; // 테이블의 총합
}
    
float randomPoint = Random.value * total;
// Random.value : 0~1f까지의 값, 0~100%
// randomPoint : 테이블 총합의 퍼센트값

// 0번부터 순차적으로 확인
for (int i = 0; i < random.Length; i++)
{
	if (randomPoint < random[i])
	{
		target = i;
		break;
	}
	else
	{
		randomPoint -= random[i];
	}
}

📌 Random.value1이 나왔을 때를 생각하며 어딘가에는 예외 처리를 해줘야한다.

📌 가중치이기때문에 절대값이 아닌 상대값이다. 0.1f = 10%가 아니다.
      반대로 1.0f를 맞춰서 사용한다면 스위치문 대신으로도 사용할 수 있다.


🔨 베지어 곡선에 의한 확률

Case 3.
public AnimationCurve randomCurve;
// 감사합니다. 정말 감사합니다...
// 다행히도 Unity에서는 인스펙터에서 직접 수정할 수 있고 프리셋 기능도 제공해준다!

randomCurve.Evaluate(Random.value);

📌 확률 분포 곡선이 아니라 역누적 확률 곡선에 더 가깝다라고 한다 🤔

📌 randomCurve.Evaluate(value)는 1. X값으로 접근하여 2. Y값을 리턴받는다.
     예를 들어, value = 0.5f 일경우 0.15f에 가깝게 리턴받는다.

🎉 한 장 요약



📄 23.10.18✍ 확률을 만드는 방법 - 2

간단한 응용 예시들을 만들어보자.

🔨 카드 섞기

void Shuffle<T>(List<T> deck)
{
    for (int i = 0; i < deck.Count; i++)
    {
        T temp = deck[i];
        int randomIndex = Random.Range(0, deck.Count);
        deck[i] = deck[randomIndex];
        deck[randomIndex] = temp;
    }
}

📌 무언가 특별한 알고리즘은 아니고 모든 원소에 기회를 주는 것 뿐이다.
     시각화하는 과정이 더 복잡했다는 게 깔깔 포인트 🤣

🔨 제비뽑기

// count : 원소, winner : 당첨
void Lots(int count, int winner)
{
    bool[] list = new bool[count]; // 원소 리스트의 선언과 초기화
    int winCount = winner; // 당첨 되었을 경우를 체크할 변수
	
    for (int i = 0; i < list.Length; i++)
    {
        float per = (float)winCount / (list.Length-i); // #1

        if (Random.value <= per)
        {
            winCount--;
            list[i] = true;

            if (winCount == 0)
                break;
        }
    }
}

📌 #1 : 남은 당첨 / 남은 원소 (ex. 1/5 : 20% 1/4 : 25% ... 1/1 : 100%)
     이렇게 하지 않으면 당첨자가 발생하지 않는 경우도 생길 수 있다.


🔨 원 위에서의 랜덤한 위치

public void circle()
{
	Vector2 pos = Random.insideUnitCircle;
    // 지름이 1인 원의 랜덤한 위치 값을 반환하는 함수
}

// insideUnitCircle의 내부를 볼 수 없기에 상상도
public static Vector2 insideUnitCircle()
{
    float slope = float.MaxValue;
    float x = 0;
    float y = 0;
    float radius = 2;
    float squareRadius = radius * radius;

    while (slope > squareRadius)
    {
        x = Random.Range(-radius, radius);
        y = Random.Range(-radius, radius);

        slope = (x * x) + (y * y);
    }

    return new Vector2(x, y);
}

📌 x2y2=radius2x^2 *y^2 = radius^2 : 원의 방정식
     x2y2<radius2x^2 *y^2 < radius^2 : 원 안에 있는 점,
     x2y2>radius2x^2 *y^2 > radius^2 : 원 밖에 있는 점

📌 Random에는 원 이외에도 다양한 도형에 관한 함수를 제공하고 있다.

임에서의 확률은 어떻게 사용하냐에 따라 불쾌함과 즐거움으로 나눠질 수 있다.
결과를 강요하기만 한다면 플레이어는 불쾌함을 느낄 것이며 결과에 관여할 수 있다면 같은 상황에서도 또 다른 즐거움을 얻을 수 있을 것이다.

🎉 한 장 요약



📄 23.10.19✍ 카메라와 컬링

카메라(Camere) : 유니티의 월드에서 장면을 캡처하여 플레이어에게 보여주는 장치이다.
탑뷰에서는 위에서 아래로, FPS의 경우 플레이어의 캐릭터에 설정하는 방식을 사용한다.
컬링(Culling) : 사전적 의미로는 고르기, 선택이다. 유니티에서는 카메라에 보이는 객체들을 렌더링 여부를 컬링할 수 있다.

니티의 오브젝트는 4가지 공간을 지나고 난 뒤 우리에게 보이게 된다.

   이름                설명             
1. Model Space모델의 로컬 좌표
2. World Space월드 공간에서의 모델 좌표 (씬뷰)
3. View Space카메라를 기준으로 3D 좌표, 이 글에서 주로 다루게 될 공간
4. Clip Space스크린으로 볼 수 있게 랜더링

이중에서 View Space에서 하는 역할에 대해서 알아 볼 것인데...
그중에서 그나마 쉬운 개념인 컬링에 대해서 알아보자!

🔨 Frustum Culling : 카메라의 시야 밖에 있는 오브젝트는 그리지 않는다.

📌 유니티에서 기본적으로 지원해주는 기능

📌 NearPlane, FarPlane 사이가 Frustum의 영역이다.

📌 FarPlane의 경계선에 겹치는 오브젝트를 부드럽게 처리해주기 위해서 Fog
     추가해주는 방법도 있다.

📌 OnBecameInvisible()를 사용하면 카메라에 렌더링 여부를 체크 해볼 수 있다!


🔨 Occlusion Culling : 카메라 기준 가려지는 오브젝트는 그리지 않는다.

📌 Occluder : 가림막, 정적인 오브젝트 Occludee : 가려진 오브젝트

📌 실외보다는 벽으로 가려진 오브젝트가 많은 시내에서 주로 사용한다.
     경우에 따라서는 오히려 퍼포먼스가 떨어질 수도 있다. (ex. 실내)

📌 Occlusion Portal 컴퍼넌트는 문과 비슷한 역할을 하는 곳에 사용한다.


가장 대표적인 컬링 두 가지를 알아보았다. 🤔
퍼포먼스를 개선하기 위해서는 컬링은 기본적인 기능으로도 추가될 만큼 중요한 기능이다.
모바일 환경에서는 컬링을 줄이기 위해서 카메라의 최대 거리를 최대한 줄여서 사용하기도 하고 탑다운 뷰로 쾌적하게 구성할 수도 있다.

이외에도 몇가지 카메라 최적화 팁이 있는데...

📌 멀티 카메라는 지양하고, 시네머신을 활용하라.
     꼭 사용해야한다면 서로의 영역이 겹치게 않게 관리!

📌 실내의 경우는 LOD?를 활용하라

📌 카메라는 Culing ->Layer 순으로 필터링 하기때문에 Layer만으로는 퍼포먼스 상향을
     기대하기 어렵다.

아무튼 자세하게 알아보고 실습하는 건 미래에 나에게 맡기자 😉

🎉 한 장 요약



profile
ChuNyan

0개의 댓글