[SCC] TIL (2)

suhan0304·2024년 8월 14일

SCC - TIL

목록 보기
2/17
post-thumbnail

Date : 2024.08.14


ToString

num.ToString("N2");

ToString 자체를 새로 배워서 작성하기 보다는 뒤에 괄호 안에 N2를 주목해보자. N2는 쉽게 말하면 형식 문자열이다. 숫자를 다양한 형식으로 출력할 수 있으며 아래와 같은 다양한 숫자 서식이 있다.

형식 지정자종류값의 예다양한 출력의 예
N / n숫자
Number
int a = 12345;
double b = 2345.6789;
12345
12,345.00
12,345
2,345.6789
2,345.68
2,346
F / f고정 소수점
Fixed-point
int a = 12345;
double b = 2345.6789;
12345
12345.00
12345
2345.6789
2345.68
2346
C / c통화
Currency
int a = 1234;₩1,234
$1,234
D / d10진법
Decimal
int a = 1234;1234001234
E / e지수double a = 123.45;1.234500E+002
G / g일반
General
F 또는 E 형식 중에서 간단한 형식으로 출력
P / p백분율
Percentage
double a = 0.567;56.70%
56.7%
X / x16진법
Hexadecimal
int a=255;FF

따라서 N2의 경우 숫자를 소수 둘째자리까지 표기하겠다는 뜻이다.

ToStringString.Format으로 소숫 자리수를 표현하는 방식의 차이점은 뭘까?

str = num.ToString("N2");
str = string.Format("{0:f2}",num);

실제로 위 두 개의 코드 자체가 하는 일은 동일하다.

하지만 ToString의 경우 단일 값에만 사용 가능하다. 즉, num이라는 하나의 정수 변수만 문자열로 바꿀 수 있다. 두 개의 정수형 변수를 한 번에 바꿀 수는 없다.

string.Format은 아래와 같은 사용이 가능하다.

int hours = 5;
int minutes = 9;
string result = string.Format("{0:00}:{1:00}", hours, minutes);  // 결과: "05:09"

이처럼 ToString은 하나의 값에만 적용되는 반면 string.Format은 여러 값을 포맷팅하여 하나의 문자열로 결합할 수 있다.

따라서 특정 변수 하나만 문자열로 포맷팅 후에 사용하고 싶으면 ToString이, 여러 값을 복합적으로 포맷하고, 한 번에 다양한 형식을 적용할 필요가 있으면 string.Format이 더 좋을 것 같다.

예를 들어 점수나 시간 같은 값을 문자열로만 바꾸기 위해서는 ToString이, 어떠한 NPC와 플레이어 사이 간의 대화에 플레이어의 네임이나, 특정 값이 포함된 대화문을 만들 때는 string.Format이 편리할 것 같다.

성능 차이는 없을까? 리소스나, 메모리 사용 측면으로? 나중에 시간이 되면 포맷팅이 어떤 과정으로 이뤄지는지도 참고 삼아 보면 좋을 것 같다.


Animation with TimeScale

진행하면서 애니메이션을 실행할 때 Time.timeScale = 0.0f 때문에 애니메이션이 모두 실행되기도 전에 시간이 멈춰서 애니메이션이 정상적으로 실행되지 않는 문제를 Invoke문을 사용해서 수정해주었다.

이 timeScale과 Animation에 관한 내용도 전에 다뤘던 적이 있는것 같아 찾아보았다.

[Unity][3D-Game] Tower Defense Game (19)

위와 같이 컴포넌트를 Unscaled Time으로 해줘도 동일하게 timeScale0이어도 애니메이션 실행이 된다.

별도의 특정 작업을 지연 실행하는 목적이 아니라 단순히 시간의 영향을 받지 않고 애니메이션을 재생해야 한다면 TimeStop 함수를 따로 만들고 Invoke로 실행하는 것보다는 Unscaled Time이 더 효율적이긴 하다.

Invoke는 단순히 지연 실행에 있어서 Coroutine보단 간편할 지는 모르겠지만 매개변수 전달이 안 된다는 단점이 있다. 더 많은 차이점이 있지만 다 적는것보다는 정리해 놓은 좋은 문서가 있어서 해당 문서를 참고하면 좋을 것 같다.


Why "isPlay"?

void Update() {
    if (isPlay) {
        time += Time.deltaTime;
        timeText.text = time.ToString("N2");
    }
}

왜 isPlay를 써야할까? 대강이나마 충돌이 발생한 순간 GameOver가 실행되고 해당 시간을 그대로 사용하기 위해 시간의 변경을 막고자 isPlay를 사용한다고는 알고 있는데 자세히 알고 있는게 도움이 될 것 같아 추가적으로 조사해보았다.

자세한 절차는 아래와 같이 이뤄진다.

  1. Shield가 충돌을 감지하고 OnCollisionEnter2D를 호출하고 GameOver 메서드를 호출한다.
  2. GameManager의 GameOver 메서드가 실행된다.
  3. GameOver가 실행되는 동시에, GameManager의 Update 메서드가 time을 갱신하고 있다.

충돌 감지 메서드에서 GameOver가 호출된 후, 현재 프레임 내에서 Update 메서드가 여전히 호출되고 있을 수 있다. 이 상황에서 time 값이 갱신되고 있으면, 실제 GameOver 메서드에서 출력된 time 값이 실제 time 값과 다를 수 있다.

즉, GameOver 호출되고 난 이후에 Update로 인해 time 값의 갱신이 일어날 수 있다.

저번 TIL (1)에서 다뤘던 사진인데 OnCollision는 Update보다 먼저 처리된다.

이는 GameOver이 호출되고 난 이후에 Update문이 실행됨을 뜻한다. > GameOver 후에 time 값이 갱신됨

그렇기 때문에 EndPanel의 시간(nowScore)과 실제 상단의 시간이 다를 수 있음을 방지하기 위해 isPlay를 사용한다.

Panel이 점수를 가리게 하면 굳이 isPlay를 안 써도 되지 않을까?


Code Optimize

public void GameOver() {
    isPlay = false;
    anim.SetBool("isDie", true);
    Invoke("TimeStop", 0.5f);

    nowScore.text = time.ToString("N2");

    string key = "bestScore";
    
    if (PlayerPrefs.HasKey(key)) {
        float best = PlayerPrefs.GetFloat(key);
        if (best < time) {
            PlayerPrefs.SetFloat(key, time);
            bestScore.text = time.ToString("N2");
        }
        else {
            bestScore.text = best.ToString("N2");
        }
    }
    else {
        PlayerPrefs.SetFloat(key, time);
        bestScore.text = time.ToString("N2");
    }
    endPanel.SetActive(true);
}

실제로 이 코드 보다는 아래와 같은 코드로 정리해서 더 가독성을 향상시킬 수 있다.

public void GameOver() {
    isPlay = false;
    anim.SetBool("isDie", true);
    Invoke("TimeStop", 0.5f);

    nowScore.text = time.ToString("N2");

    string key = "bestScore";
    float best = PlayerPrefs.GetFloat(key, 0f); 

    if (time > best) {
        PlayerPrefs.SetFloat(key, time);
        best = time; 
    }

    bestScore.text = best.ToString("N2");
    endPanel.SetActive(true);
}

PlayerPrefs.GetFloat(key, 0f)를 사용해서 키가 없을 경우 기본 값을 0f로 가져오게 해서 HasKey를 굳이 확인하지 않고 진행되도록 수정해주었다.


Unity Quest

1. 홀수 출력하기

for (int i = 1; i <= 100; i++)
    if (i % 2 == 1) Console.WriteLine(i);

int j = 1;
while (j <= 100)
{
    if (j % 2 == 1) Console.WriteLine(j);
    j += 1;
}

int k = 1;
do
{
    if (k % 2 == 1) Console.WriteLine(k);
    k += 1;
} while (k <= 100);

2. 배열을 사용한 합께 및 평균 계산

int[] numbers = { 10, 20, 30, 40, 50 };

int sum = 0;
for (int i = 0; i < numbers.Length; i++)
{
    sum += numbers[i];
}

float average = sum / numbers.Length;

Console.WriteLine($"Sum: {sum}");
Console.WriteLine($"Average: {average}");

3. 팩토리얼 계산

Console.Write("Enter a number: ");
int number = int.Parse(Console.ReadLine());

int fact = 1;
for (int i = 1; i <= number; i++)
{
    fact *= i;
}
Console.WriteLine($"Factorial of {number} is {fact}");

4. 숫자 맞추기 게임

Random random = new Random();
int targetNumber = random.Next(1, 101);
int guess = 0;
do
{
    Console.Write("Enter your guess (1-100): ");
    guess = int.Parse(Console.ReadLine());
    if (targetNumber == guess)
    {
        Console.WriteLine("Congratulations! You guessed the number.");
        break;
    }
    else if (targetNumber > guess)
    {
        Console.WriteLine("Too low! Try again.");
    }
    else
    {
        Console.WriteLine("Too high! Try again.");
    }
} while (true);

5. 이중 반복문을 사용한 구구단 출력

for (int i = 1; i <= 9; i++)
{
    for (int j = 2; j <= 9; j++ )
    {
        Console.Write("{0} * {1} = {2}\t", j, i, j*i);
    }
    Console.WriteLine();
}

6. 배열 요소의 최대값과 최소값 찾기

int[] numbers = { 10, 20, 30, 40, 50 };
int max = numbers[0];
int min = numbers[0];

for (int i = 0; i < numbers.Length; i++)
{
    if (numbers[i] > max) max = numbers[i];
    if (numbers[i] < min) min = numbers[i];
}

Console.WriteLine($"Max: {max}");
Console.WriteLine($"Min: {min}");
profile
Be Honest, Be Harder, Be Stronger

0개의 댓글