개발/유니티

[유니티] 유니티 learn #4 (그려지는 순서,충돌처리)

kimchangmin02 2025. 8. 24. 11:21

방법 1: 선생님이 줄을 세워주는 법 (이게 Order in Layer 예요)

선생님이 반 친구들에게 번호표를 하나씩 줬어요.

  • 교실 뒤에 있는 칠판: 1번 번호표
  • 여러분 (캐릭터): 5번 번호표
  • 교실 중간에 있는 책상: 5번 번호표

선생님이 사진을 찍을 때 규칙이 있어요. "무조건 번호가 작은 건 뒤로 가고, 번호가 큰 건 앞으로 와!"

그래서 칠판(1번)은 항상 여러분(5번)보다 뒤에 있어요. 이건 괜찮죠?

문제점:
여러분이 책상(똑같은 5번) 뒤에 숨고 싶어도 숨을 수가 없어요. 선생님이 "너희 둘은 똑같은 5번이니까, 항상 똑같은 줄에 서!" 라고 정해버렸기 때문이에요. 한번 정해진 순서는 절대 바뀌지 않아요.

이게 바로 Order in Layer로 해결이 안 된다는 뜻이에요. 순서가 한번 정해지면 끝이에요!


방법 2: 똑똑한 카메라가 알아서 하는 법 (이게 'Y좌표 정렬'이에요)

이제 선생님이 번호표를 전부 뺏어갔어요. 대신 새로운 규칙을 만들었어요.

새로운 규칙: "줄 같은 거 없어! 그냥 카메라(우리 눈)에 더 가까이 있는 사람이 무조건 앞에 서는 거야!"

2D 게임에서는 누가 더 가까이 있는 걸까요?
바로 "발이 더 아래쪽에 있는 친구" 가 더 가까이 있는 거예요.

이제 마법이 일어나요!

  1. 여러분이 책상보다 더 아래쪽(카메라 쪽)으로 걸어오면?
    • 여러분의 발이 책상다리보다 더 아래에 있죠?
    • 똑똑한 카메라는 "아! 네가 더 앞에 있구나!" 하고 여러분을 책상 앞으로 그려줘요.
  2. 여러분이 책상보다 더 위쪽(칠판 쪽)으로 걸어가면?
    • 여러분의 발이 책상다리보다 더 위에 있죠?
    • 똑똑한 카메라는 "아! 이제 책상이 너보다 앞에 있네!" 하고 여러분을 책상 뒤로 그려줘요. 마치 책상 뒤에 숨은 것처럼요!

이게 바로 'Y좌표'로 순서를 정하는 방법이에요. 여러분이 어디에 서 있느냐에 따라, 카메라가 알아서 누가 앞인지, 누가 뒤인지를 계속 바꿔주는 거예요. 그래서 진짜 3D처럼 보이는 '가짜 입체감'이 생겨요.


한눈에 보는 정리

구분 선생님 마음대로 (Order in Layer) 똑똑한 카메라 마음대로 (Y-Sort)
규칙 정해진 줄 번호가 전부! 발이 더 아래 있는 사람이 이긴다!
장점 순서가 절대 안 헷갈려요. 저절로 앞에 갔다, 뒤에 갔다 해요.
단점 순서를 바꿀 수 없어요. -
언제 쓸까? 배경과 사람처럼 순서가 고정일 때 사람과 책상처럼 순서가 바뀔 때

결론: 캐릭터가 나무나 기둥 같은 물건의 앞뒤로 자유롭게 다니게 하려면, 선생님이 줄 세우는 법(Order in Layer) 말고, 똑똑한 카메라 법(Y좌표 정렬)을 써야 하는 거예요

 

 

 

 

뭔소린

 

상황 1: 플레이어가 나무 '앞으로' 왔을 때 (화면 아래쪽)

  • 플레이어의 발이 화면 아래쪽에 있습니다. -> Y좌표 숫자가 더 작습니다. (예: -2.5)
  • 나무의 밑동은 화면 위쪽에 있습니다. -> Y좌표 숫자가 더 큽니다. (예: -2.0)

컴퓨터의 생각 (황금 규칙 적용):
"음... 플레이어 Y좌표(-2.5)가 나무 Y좌표(-2.0)보다 숫자가 더 작네? 그럼 황금 규칙에 따라 나무를 먼저 그리고, 플레이어를 나중에 그려야겠다!"

우리 눈에 보이는 결과:
컴퓨터가 나무를 먼저 그린 다음, 그 위에 플레이어를 덧그립니다.
그래서 플레이어가 나무를 가리면서 앞에 있는 것처럼 보입니다. (성공!)


상황 2: 플레이어가 나무 '뒤로' 갔을 때 (화면 위쪽)

  • 플레이어의 발이 화면 위쪽에 있습니다. -> Y좌표 숫자가 더 큽니다. (예: -1.5)
  • 나무의 밑동은 화면 아래쪽에 있습니다. -> Y좌표 숫자가 더 작습니다. (예: -2.0)

컴퓨터의 생각 (황금 규칙 적용):
"어라? 이번엔 나무 Y좌표(-2.0)가 플레이어 Y좌표(-1.5)보다 숫자가 더 작네? 그럼 황금 규칙에 따라 플레이어를 먼저 그리고, 나무를 나중에 그려야겠다!"

우리 눈에 보이는 결과:
컴퓨터가 플레이어를 먼저 그린 다음, 그 위에 나무를 덧그립니다.
그래서 나무가 플레이어를 가리면서 앞에 있는 것처럼 보입니다. (성공!)

 

 

아 원근감

 


 

Unity는 게임의 모든 오브젝트를 대상으로 비용이 많이 드는 수학 연산을 적용하는 상황을 피하기 위해 Rigidbody 2D 컴포넌트를 보유한 게임 오브젝트에 대해서만 물리 연산을 수행합니다.

 

 

 

 

이제 게임 오브젝트Rigidbody로 인해 물리 시스템의 적용을 받고 있으니 물리 시스템에게 게임 오브젝트의 어떤 부분이 "솔리드(solid)"인지를 알려주어야 합니다. 이 경우 콜라이더(Colliders)를 이용하면 됩니다.
콜라이더는 사각형, 원 등의 단순한 모양으로 되어있으며, 물리 시스템이 게임 오브젝트의 대략적인 외형을 가늠하여 충돌 연산을 수행할 수 있도록 해줍니다.

 

 


Rigidbody 2D: "자동차의 엔진과 바퀴"

  • 한 줄 요약: 움직임을 담당하는 물리 부품입니다.
  • 설명:
    어떤 물체에 Rigidbody를 달아주는 것은, 그 물체에게 "너는 이제부터 물리 법칙의 지배를 받아라!" 라고 명령하는 것과 같습니다.
    • 중력(Gravity)의 영향을 받아 아래로 떨어집니다. (엔진이 꺼진 자동차가 비탈길에서 굴러 내려가듯이)
    • 힘(Force)을 가하면 그 방향으로 날아갑니다. (자동차 엑셀을 밟으면 앞으로 나아가듯이)
    • 무게(Mass), 저항(Drag) 같은 물리적인 속성을 갖게 됩니다.
  • 이 부품을 다는 순간, 그 물체는:
  • 결론: Rigidbody가 없으면, 그 물체는 물리적으로 절대 스스로 움직일 수 없습니다. 그냥 허공에 둥둥 떠 있는 그림일 뿐입니다. Rigidbody 움직임의 주체입니다.

Box Collider 2D: "자동차의 차체 (껍데기)"

  • 한 줄 요약: **충돌을 감지하는 모양(Shape)**입니다.
  • 설명:
    엔진과 바퀴만 있다고 자동차가 굴러갈 수는 없죠. 다른 차와 부딪혔을 때 "아! 부딪혔다!" 라고 알려줄 **단단한 껍데기(차체)**가 필요합니다. Collider가 바로 그 껍데기 역할을 합니다.
    • 물리적인 형태를 갖게 됩니다. 눈에는 보이지 않는 투명한 '영역'이 생기는 것입니다. (녹색 테두리가 바로 이것!)
    • 다른 Collider와 이 영역이 겹치면, "충돌 발생!" 이라는 신호를 물리 엔진에 보냅니다.
  • 이 부품을 다는 순간, 그 물체는:
  • 결론: Collider "내 모양은 이거야. 여기에 다른 게 닿으면 알려줘!" 라고 말하는 역할만 합니다. 스스로는 절대 움직이지 못하고, 충돌을 감지하기만 하는 수동적인 센서입니다.

둘의 관계 (매우 중요!)

Rigidbody를 상자에 추가하지 않았는데 이렇게 하는 것이 맞습니다.

이 문장이 바로 핵심입니다.

  • 루비 (플레이어):
    • Rigidbody (엔진) + Collider (차체) = 스스로 움직이는 자동차
    • 중력의 영향도 받고, 키보드 입력으로 움직일 수도 있으며, 다른 물체와 부딪힐 수도 있습니다.
  • MetalBox (금속 상자):
    • Collider (차체)만 있음 = 길가에 주차된 텅 빈 자동차
    • 엔진이 없으니 스스로 움직이거나 중력에 떨어지지 않습니다. 그냥 그 자리에 가만히 서 있습니다.
    • 하지만 껍데기(Collider)는 있기 때문에, 루비(움직이는 자동차)가 와서 들이받으면 부딪혔다는 것을 감지하고 막아낼 수 있습니다.

한눈에 보는 정리

구분 Rigidbody 2D Box Collider 2D
비유 엔진 & 바퀴 차체 (껍데기)
역할 움직임 담당 (물리 계산의 주체) 충돌 감지 담당 (모양)
하는 일 중력, 힘, 속도 등을 계산 "여기에 뭔가 닿았어!" 라고 알림
특징 이게 있어야 물리적으로 움직임 이것만 있으면 움직이지 못함
예시 플레이어, 몬스터, 날아가는 총알 바닥, 벽, 장애물

결론:
물리적으로 움직여야 하는 모든 것에는 Rigidbody Collider 둘 다 붙여주고,
가만히 서서 부딪히기만 하는 것에는 Collider 붙여주는 것이 유니티 물리 시스템의 기본 규칙입니다.

 

 

 

 

 

 

 


 

왜 떨리는가? (다시 한번, 가장 쉽게)

지금의 문제점: 여러분이 엄마 말을 전혀 듣지 않기 때문입니다.

  • '나' (지금 코드: transform.position = ...): 이건 '순간이동' 입니다. 엄마에게 말도 없이, 방 건너편으로 순간이동해서 벽에 몸을 반쯤 박아버리는 것과 같아요.
  • '엄마' (물리 엔진): 이 모습을 본 엄마는 기겁하면서 "규칙 위반이야!" 하고 달려와서 여러분을 벽 밖으로 빼냅니다.
  • 떨림: 여러분은 계속 벽으로 순간이동하고, 엄마는 계속 빼내는 '줄다리기' 가 1초에 60번씩 일어나서 부들부들 떠는 것처럼 보입니다.

결론: '순간이동'은 엄마의 규칙을 무시하는 아주 무례한 행동이라서 싸움이 나는 것입니다.


왜 이게 해결 방법인가?

이제 해결책은 '엄마에게 허락을 받고 움직이는 것' 입니다.

  • 새로운 코드 (rigidbody.MovePosition(...)): 이건 '순간이동'이 아닙니다. 이것은 엄마에게 가서 공손하게 부탁하는 것과 같아요.
  • "엄마, 저 벽 쪽으로 한 걸음만 가도 될까요?"
  • '엄마'의 반응 (물리 엔진): 엄마는 이 부탁을 듣고 미리 계산을 해봅니다.
    1. "음... 네가 지금 서 있는 곳에서 벽 쪽으로 한 걸음 간다고?"
    2. "어디 보자... 아! 그렇게 가면 네 발끝이 벽에 닿겠구나."
    3. "규칙상 벽을 뚫을 수는 없으니, 그 한 걸음은 허락해 줄 수 없겠구나."
    4. 결과적으로 엄마는 여러분을 한 걸음도 움직이지 못하게 그 자리에 세워 둡니다.

두 방식의 결정적인 차이

구분 지금 방식 (순간이동) transform.position 해결책 (허락받기) rigidbody.MovePosition
행동 순서 1. 내가 먼저 벽으로 뚫고 들어간다.<br>2. 엄마가 나중에 보고 빼낸다. 1. 내가 먼저 엄마에게 가도 되냐고 묻는다.<br>2. 엄마가 미리 계산하고 못 가게 막는다.
결과 벽에 들어갔다 나왔다를 반복 -> 떨림 애초에 벽으로 들어가지 못함 -> 가만히 서 있음
비유 무례한 행동 (선조치 후보고) 예의 바른 행동 (선보고 후조치)

"물리 시스템이 루비가 상자 안으로 들어간 후에 빠져나오는 게 아니라 상자 안으로 들어가기 전에 이동을 멈추게 할 수 있습니다."

이 문장의 뜻이 바로 이것입니다.

  • 순간이동 방식: [상자 안으로 들어감] -> [엄마가 빼냄]
  • 허락받는 방식: [상자 안으로 들어가려고 함] -> [엄마가 미리 막아서 못 들어감]

 

 

 

 

 

 

 

 

 


결론부터 말씀드리면, 이 코드는 리지드바디(Rigidbody)를 가진 게임 오브젝트가 100개, 1000개가 있어도 완벽하게 작동합니다.

그 이유는 GetComponent<Rigidbody2D>()라는 명령어가 작동하는 방식 때문입니다.


"나 자신"과 "나의 부품" (가장 쉬운 비유)

GetComponent<T>() 라는 명령어는 "세상 어딘가에 있는 T를 찾아라" 라는 뜻이 절대 아닙니다.

이 명령어의 진짜 의미는 이것입니다.

"지금 이 스크립트가 붙어있는 '나 자신(게임 오브젝트)'에게서 'T'라는 부품을 찾아라."

'나 자신(GameObject)'  '나의 부품(Component)' 에 비유해 볼게요.

  1. 게임 오브젝트 (GameObject): 이것은 '나' 라는 존재 그 자체입니다. 예를 들어 '루비' 캐릭터 한 명입니다.
  2. 컴포넌트 (Component): 이것은 '나'에게 붙어있는 '부품' 또는 '기능' 들입니다.
    • Transform: 나의 '위치, 회전, 크기' 정보
    • Sprite Renderer: 나의 '겉모습(그림)'
    • Rigidbody 2D: 나의 '물리 엔진'
    • Box Collider 2D: 나의 '몸체(충돌 영역)'
    • RubyController (Script): 나의 '뇌(생각하고 행동하는 부분)'

코드의 진짜 의미 (뇌의 생각)

이제 저 코드를 RubyController라는 '뇌'의 입장에서 다시 해석해 보겠습니다.

C#
// 나는 '루비 컨트롤러'라는 뇌입니다.
public class RubyController : MonoBehaviour
{
   // 나의 '물리 엔진' 부품을 담아둘 빈 상자를 하나 준비해 둡니다.
   Rigidbody2D rigidbody2d;
    
    // 게임이 시작되면 딱 한 번, 준비를 합니다.
    void Start()
    {
        // "나의 몸에서 '물리 엔진(Rigidbody2D)'이라는 부품을 찾아서,
        // 아까 준비해둔 빈 상자에 넣어둬야지!"
        rigidbody2d = GetComponent<Rigidbody2D>();
    }

    // 이제 매 순간마다 행동합니다.
    void Update()
    {
        // ... 생략 ...
        
        // "아까 상자에 넣어둔 나의 '물리 엔진'을 사용해서,
        // 이 위치로 나를 움직여줘!"
        rigidbody2d.MovePosition(position);
    }
}

만약에 두 개면 어떤 오브젝트인지 어떻게 알까요?

이제 '금속 상자(MetalBox)'라는 다른 게임 오브젝트가 있고, 여기에도 Rigidbody2D가 붙어있다고 상상해 봅시다.

  • 루비의 '뇌' (RubyController): GetComponent<Rigidbody2D>()를 실행하면, 오직 '루비' 자기 자신에게 붙어있는 리지드바디만 찾습니다. 절대 옆에 있는 '금속 상자'의 리지드바디를 쳐다보지도 않습니다.
  • 금속 상자의 '뇌' (만약 있다면): 그 스크립트가 GetComponent<Rigidbody2D>()를 실행하면, 오직 '금속 상자' 자기 자신에게 붙어있는 리지드바디만 찾습니다.

결론: GetComponent<T>()는 철저하게 '자기 몸' 안에서만 부품을 찾습니다.

그래서 씬에 리지드바디를 가진 오브젝트가 수백 개가 있어도, 이 코드는 자기가 붙어있는 바로 그 오브젝트의 리지드바디만 정확하게 찾아오기 때문에 아무런 문제가 없는 것입니다.

 

 

 

 

 

 

 

 

 

 

 

 


Update 함수에서는 Vector2 position = transform.position;

행을

Vector2 position = rigidbody2d.position;으로 교체했습니다.

 

이는 transform position 대신 Rigidbody를 사용한다는 의미합니다.

 

 

 

 

 

 

 

네, 정확히 맞습니다! "붙어있는 물 타일들의 충돌 처리를 하나로 통합한다" 는 뜻이 완벽한 요약입니다.

그 설명이 왜 필요하고, 정확히 무슨 의미인지 '레고(LEGO) 벽' 에 비유해서 아주 쉽게 설명해 드릴게요.


문제 상황: 개별 콜라이더 (레고 벽돌 하나하나)

여러분이 레고 블록 수백 개를 사용해서 거대한 벽을 만들었다고 상상해 보세요.

  • 레고 블록 하나하나: 이게 바로 '개별 타일 콜라이더' 입니다. 모든 타일이 자기만의 네모난 충돌 영역(콜라이더)을 가지고 있는 상태죠.

이 레고 벽에는 튜토리얼에서 말한 두 가지 큰 문제가 있습니다.

문제 1: 컴퓨터가 너무 힘들어요 (성능 저하)

  • 여러분이 이 벽에 공을 던지면, 컴퓨터는 이렇게 생각해야 합니다.
  • "공이 1번 레고에 부딪혔나? 아니네. 2번 레고는? 3번은? ... 99번은? 아! 100번 레고에 부딪혔구나!"
  • 벽이 커질수록 확인해야 할 레고 블록이 수백, 수천 개로 늘어납니다. 이건 컴퓨터에게 엄청나게 힘든 일이라 게임이 느려질 수 있습니다.

문제 2: 틈새에 걸려요 (부정확한 충돌)

  • 레고 블록들을 아무리 잘 붙여도, 블록과 블록 사이에는 아주 미세한 틈(경계선) 이 존재합니다.
  • 캐릭터가 이 레고 벽을 따라 미끄러져 갈 때, 이 틈새에 발이 순간적으로 걸려서 덜컹거리거나 튕겨 나가는 이상한 현상이 생길 수 있습니다. 이것이 "충돌 지점에서 드물게 오류가 발생"하는 이유입니다.

해결책: Composite Collider 2D (레고 벽을 녹여서 합치기)

Composite Collider는 이 문제를 해결하는 마법 같은 도구입니다.

  • Composite Collider의 역할:
    이것은 마치 모든 레고 블록을 거대한 용광로에 넣고 녹여서, 틈새 하나 없는 매끈한 '통짜 벽' 하나로 만들어버리는 것과 같습니다.

이제 이 '통짜 벽'은 어떤 장점을 가질까요?

장점 1: 컴퓨터가 편해져요 (성능 향상)

  • 이제 벽에 공을 던져도, 컴퓨터는 딱 한 번만 생각하면 됩니다.
  • "공이 '통짜 벽'에 부딪혔나? 응!"
  • 수백 번 하던 계산을 단 한 번에 끝내니 게임이 훨씬 빨라집니다.

장점 2: 아주 매끄러워요 (정확한 충돌)

  • '통짜 벽'에는 틈새나 경계선이 전혀 없습니다. 완벽하게 매끈한 표면이죠.
  • 캐릭터가 이 벽을 따라 미끄러져 갈 때, 더 이상 틈에 걸릴 일이 없으므로 아주 부드럽게 움직일 수 있습니다.

한눈에 보는 정리

구분 Before (개별 콜라이더) After (Composite Collider)
비유 LEGO 벽돌 하나하나 녹여서 합친 통짜 LEGO 벽
충돌 모양 수많은 작은 네모들 거대하고 단순한 하나의 모양
성능 느림 (모든 벽돌을 다 검사해야 함) 빠름 (통짜 벽 하나만 검사하면 됨)
움직임 틈에 걸려 덜컹거릴 수 있음 매끄럽게 미끄러짐