[SCC] TIL (5)

suhan0304·2024년 8월 21일

SCC - TIL

목록 보기
5/17
post-thumbnail

Date : 2024.08.20


KnifeHit

유니티도 복습할 겸 해서 간단한 캐주얼 게임을 모작해보자.

게임 영상

기능 구현

어떤 기능들을 구현할 것인지 대강만 적어보면 아래와 같다.

  1. 칼 던지기
  2. 칼 꽂히게 하기
  3. 칼끼리 충돌 이벤트 구현하기
  4. 목표 오브젝트 회전하기
  5. 목표 오브젝트 파괴되게 하기

이 정도만 적었는데 추후에 구현하면서 추가해볼 예정이다. Sprite나 image는 추후에 그냥 덮어씌우는 느낌으로 가고 일단은 간단하게만 구현해보는게 목적이다.

자세한 개발일지는 아래 개발 로그를 참고하자.

(1) : https://velog.io/@suhan0304/Unity-Knife-Hit-1


NIL (New I Learned)

OnTriggerEnter2D

Enter가 바로 작동하면서 바로 velocity를 Vector3.zero로 바꿔서 표면에 접촉하자마자 멈추는 효과를 기대했는데 생각보다 OnTriggerEnter2D가 실행되는 시간이 조금 걸렸다. 이게 객체가 매우 빠르게 움직이고 있으면 객체가 충돌 면을 지나쳐서 약간 들어간 상태에서 충돌 이벤트가 발생할 수 있다. (유니티는 기본적으로 Fixed Time Step으로 물리 연산 및 FixedUpdate() 이벤트가 수행되는 시기가 결정되기 때문이다.)

Rigidbody2DCollision DetectionContinous로 설정해주어 이를 보완하는 방법이 있다.

CCD

유니티는 자체적으로 CCD(Continous Collision Detection) 메서드를 제공한다. CCD 메서드는 예측성 알고리즘을 활용하셔 물리 타임스탬프 사이에 발생하는 충돌을 계산해준다. 이 모드는 더 정확하지만 일반적으로 (당연하게도) 더 많은 계산 리소스가 필요하다.

추측성 CCD

스위핑 기반 충돌 검사보다 저 적은 계산을 요구한다. 작동 방식은 아래 사진을 함께 보면 이해하기 쉽다.

t0에서 t1의 시간까지 오브젝트가 이동한다고 했을때 t0에서 물리 연산이 진행되고 t1에서 그 다음 물리 연산이 진행되면 오브젝트가 벽을 통과해버리는, 충돌이 누락되는 터널링 현상이 발생하게 되는데, 추측성 CCD의 경우 오브젝트의 리니어 및 각도 모션을 기반으로 정렬된 최소 경계 상자(AABB)를 늘려서 (붉은 점선 네모) 각각 n1, n2 노멀을 포함하는 2개의 컨택트, 충돌 요소를 감지하게 된다. 그런 다음 구체 오브젝트가 벽면을 통과하지 않도록 솔버에 이러한 컨택트를 유지하도록 요청하게 되고 우리가 원하는 t2 위치에 놓이게 된다.

물론 이러한 추측성 CCD로 타임 스탬프 사이에 발생하는 물리 연산을 100% 완벽하게 처리할 수는 없다. 부정확한 컨택트 노멀을 생성하게 되면 오히려 오브젝트가 이상한 방향으로 이동하게 되거나 AABB 외부에서 발생한 충돌을 누락시킬 수 있다는 문제점이 있다.

스위핑 기반 CCD

스위핑 기반 CCD는 TOI(충돌 시점) 알고리즘을 사용하여 잠재 충돌을 계산한다. 이를 위해 알고리즘은 오브젝트의 현재 속도로 앞쪽 궤도를 스위핑하거나 감지한다. (스위핑은 쉽게 말해 '쓸다'라는 뜻인데 오브젝트의 예상 이동 경로를 한번 미리 쓸어보면서 충돌할 만한 오브젝트가 있는지를 검사한다고 보면 된다.)

각 CCD 메소드별 자세한 구동 원리나 차이점은 공식 문서를 참고하자.

  • A는 시작 위치의 오브젝트이다.
  • B는 스위핑 기반 CCD 알고리즘이 충돌을 예측한 위치의 오브젝트이다.
  • C는 한 물리 타임스텝 이후의 원래 예측 위치이다.
  • D는 리니어 경로를 방해하는 콜라이더로, 스위핑 기반 CCD 알고리즘에 의해 감지된다.

스위핑 기반 CCD는 특히 성능에 영향을 많이 미칠 수 있다. 스위핑 기반 CCD를 사용하는 많은 수의 고속 오브젝트가 근접해 있는 경우, 물리 엔진이 더 많은 스위핑과 더 많은 CCD 하위 단계를 수행해야 하므로 CCD 오버헤드가 빠르게 증가한다.

스위핑 기반 CCD의 한계점은 리니어 스위핑(직선 방향)만 수행할 수 있고 각도 스위핑은 수행할 수 없어서 물리 바디가 회정할 때 발생할 수 있는 충돌을 예측할 수 없다.

Damage Flash

데미지를 받을 때 피격 이펙트로 반짝이는 이펙트를 주려고 했는데 간단하게 흰색 오브젝트를 덮어씌워서 alpha 값을 조절하는 것으로 매우 간단하게 구현했는데, 그것보다 아래의 방법을 알아두는게 좋을 것 같다.


Unity Quest

7. 행맨 게임

string secretWord = "hangman";
char[] guessWord = new char[secretWord.Length];
for (int i = 0; i < guessWord.Length; i++)
{
    guessWord[i] = '_';
}

int attempts = 6;
bool wordGuessed = false;

while (attempts > 0 && !wordGuessed)
{
    Console.WriteLine("\nGuess word: " + new string(guessWord));
    Console.WriteLine("You have " + attempts + " attempts left");
    Console.Write("Guess the alphabet (a-z) : ");
    char guessChar = Console.ReadKey().KeyChar;

    Console.WriteLine();

    bool flag = false;

    for (int i = 0; i< secretWord.Length; i++)
    {
        if (secretWord[i] == guessChar)
        {
            guessWord[i] = guessChar;
            flag = true;
        }
    }

    if (!flag)
    {
        attempts--;
        Console.WriteLine("Incorrect Guess!");
    }
    else
    {
        Console.WriteLine("Correct Guess");
    }

    if (new string(guessWord) == secretWord)
    {
        wordGuessed = true;
        Console.WriteLine("\nCongratulations! The word was \"" + secretWord + "\"");
    }
}
if (!wordGuessed)
{
    Console.WriteLine("\nYou've run out of attemps. The word was \"" + secretWord + "\"");
}

7. 숫자 야구 게임

Random random = new Random();
int[] targetNumber = new int[3];

for (int i = 0; i < 3; i++)
{
    int newNumber;
    do
    {
        newNumber = random.Next(0, 10); 
    } while (Array.Exists(targetNumber, number => number == newNumber)); 

    targetNumber[i] = newNumber; 
}

int[] userGuess = new int[3];
bool guessedCorrectly = false;

int attempts = 0;

while (!guessedCorrectly)
{
    Console.Write("Enter your guess(3 digits): ");
    string input = Console.ReadLine() ?? "000";
    attempts++;

    int strikes = 0;
    int balls = 0;

    for (int i = 0; i < 3; i++)
    {
        userGuess[i] = int.Parse(input[i].ToString());
        if (userGuess[i] == targetNumber[i])
        {
            strikes++;
        }
        else if (Array.Exists(targetNumber, number => number == userGuess[i]))
        {
            balls++;
        }
    }

    Console.WriteLine($"{strikes} Strike(s), {balls} Ball(s)");
    if(strikes == 3)
    {
        Console.WriteLine($"Congratulations! You've guessed the number in {attempts} attempts.");
        break;
    }
}
profile
Be Honest, Be Harder, Be Stronger

0개의 댓글