- Published on
섹션 3: 스노우 보더
#섹션 3: 스노우 보더
Unity 2D에서 Sprite Shape을 활용한 스노우보드 게임 개발 가이드입니다. 지형 제작부터 물리 시스템, 카메라 제어, 파티클 효과까지 단계별로 학습합니다.
#목차
- 스프라이트 자료 다운로드 및 추가
- 스프라이트 쉐이프 기본 개념
- 스프라이트 쉐이프 구성 요소
- 지형 만들기
- 플레이어 설정
- 물리 시스템
- Cinemachine 카메라
- 파티클 시스템
- 사운드 시스템
- 게임 완성
#**스프라이트 자료 다운로드 및 추가**
강의 자료에서 Snow Boarder Sprite Assets를 다운로드한 후 압축을 풉니다.
이후, 유니티의 Assets 영역에 드래그하여 추가합니다.
스프라이트가 정상적으로 불러와지면, Sprites 폴더에서 확인할 수 있습니다.
#스프라이트 쉐이프란?
#기존 스프라이트 vs. 스프라이트 쉐이프
기존 스프라이트(Sprite): 단순한 이미지 파일(예: PNG)을 사용하여 게임 오브젝트를 구성하는 방식입니다.
예) 캐릭터, 배경, 아이템 등
스프라이트 쉐이프(Sprite Shape): 점(노드)과 곡선을 이용해 원하는 형태를 만들고, 텍스처를 자동으로 적용하는 기능입니다.
예) 언덕, 벽, 지형, 도로 등
👉 스프라이트 쉐이프는 기존 스프라이트보다 훨씬 더 자유로운 형태의 디자인이 가능하다는 점이 가장 큰 장점입니다!
#스프라이트 쉐이프의 구성 요소
#(1) 스프라이트 쉐이프 컨트롤러 (Sprite Shape Controller)
유니티에서 Sprite Shape을 사용하려면 반드시 필요한 컴포넌트입니다.
Inspector 창에서 확인할 수 있으며, Sprite Shape의 프로파일과 형태를 설정할 수 있습니다.
#(2) 스프라이트 쉐이프 프로파일 (Sprite Shape Profile)
스프라이트 쉐이프에 어떤 텍스처(이미지)를 사용할지 결정하는 데이터입니다.
쉽게 말해 **"이 스프라이트 쉐이프가 어떤 모양을 가질 것인지 정의하는 설계도"**라고 생각하면 됩니다.
새로 만들 수도 있고, 기존의 프로파일을 수정할 수도 있습니다.
👉 즉, Sprite Shape는 단순한 모양만 제공하고, 실제로 어떤 이미지가 적용될지는 Profile에서 결정됩니다!
#스프라이트 쉐이프 프로파일의 주요 설정
#(1) 가장자리 텍스처 (Edge Sprite)
지형의 가장자리에 적용될 스프라이트(이미지)를 설정합니다.
예) 눈 덮인 바닥, 잔디, 벽돌 패턴 등
#(2) 내부 채우기 텍스처 (Fill Texture)
닫힌 형태(Closed Shape)일 경우, 내부를 채울 텍스처를 설정합니다.
예) 땅 내부의 흙, 물, 파란 배경 등
👉 간단히 말해, 가장자리는 바깥을 꾸미고, 내부 채우기는 안쪽을 꾸미는 역할을 합니다.
#**스프라이트 모양 만들기**
Hierarchy 창에서 우클릭 후 2D Object → Sprite Shape → Closed Shape을 선택합니다.
새롭게 생성된 객체의 Inspector 창에서 Sprite Shape Controller를 확인합니다.
Sprite Shape Profile이 자동으로 설정됩니다.
#**새로운 스프라이트 프로파일 생성**
Assets → Sprites 폴더에서 우클릭 후 Create → 2D → Sprite Shape Profile을 선택합니다.
새롭게 생성된 프로파일의 이름을 Snow Profile로 변경합니다.
Closed Sprite Shape 객체의 Sprite Shape Controller에서 Snow Profile을 적용합니다.
Closed Sprite Shape의 이름을 Level Sprite Shape로 변경합니다.
#**스프라이트 설정 및 텍스처 적용**
Snow-tile-low-res 파일을 Sprite Shape Edge 부분에 드래그하여 배경을 설정합니다.
blue-fill 파일을 Sprite Shape Fill에 드래그하여 내부 색상을 설정합니다.
#**레벨 크기 조정 및 형태 디자인**
Edit Spline 버튼을 눌러 **점(Node)**을 확인합니다.
점을 드래그하여 레벨의 크기와 형태를 조정합니다.
필요하면 새로운 점을 추가하여 더욱 다양한 지형을 구성합니다.
노드를 조작하여 점프 가능 구간을 만듭니다.
곡선을 조정하여 플레이어가 부드럽게 이동할 수 있도록 합니다.
#**스프라이트 모양 활용의 장점**
플랫폼 게임, 횡스크롤 게임 등 다양한 장르에서 사용 가능.
레벨 디자인을 손쉽게 수정할 수 있어 빠른 테스트 및 개선 가능.
Cyclone Maniacs 같은 사이클링 게임에도 적용 가능.
#{ 스프라이트 모양과 물리 시스템 활용하기 }
이번 강의에서는 Unity의 **스프라이트 모양(Sprite Shape)**을 사용해 지형을 만들고,
**엣지 콜라이더(Edge Collider 2D)**를 추가하여 물리적으로 자연스럽게 동작하도록 설정했습니다.
또한, 공이 비탈길을 따라 굴러가도록 물리 시스템을 활용했습니다.
#엣지 콜라이더(Edge Collider 2D) 추가
Level Sprite Shape을 선택
Add Component 버튼 클릭 → Edge Collider 2D 추가
충돌 영역이 자동으로 생성됨 ( 원이나 네모가 아닌 내가 만든 모양으로 )
Edit Spline을 사용해 노드를 조정하면 충돌 영역도 함께 조정 가능
#Dynamic Sprite 사용하기
Physics → Dynamic Sprite 생성
이미 Rigidbody 2D와 Circle Collider 2D가 적용된 상태로 생성됨
"Physics → Dynamic Sprite 생성"
Unity 메뉴에서 이걸 선택하면,
자동으로 다음 구성의 오브젝트가 만들어짐:
Sprite Renderer
Rigidbody 2D (Body Type: Dynamic)
Circle Collider 2D
#지형과 공의 빈 공간 이슈
공이 바닥에서 떠 있는 경우 Collider의 Offset 값을 조정
#지형 조정 (눈을 더 크게 만들기)
눈 지형을 더 크게 만들어서 화면에 자연스럽게 표시하는 방법을 배웠습니다.
눈 지형을 키우면 공이 굴러가는 경사면을 더 길게 만들 수도 있고,
배경과의 조화를 맞출 수도 있습니다.
이를 위해 Sprite Shape의 Edit Spline 기능과 Height 값을 조정하는 방법을 사용합니다.
Edit Spline을 사용하여 높이 조정
Height 값을 조절해 눈 지형을 확대 또는 축소
#Edit Spline을 사용하여 지형 크기 조정
Sprite Shape은 **점(Node)들로 이루어진 선(Spline)**을 조정하여 원하는 지형을 만들 수 있습니다.
눈 지형을 더 크게 만들려면 Edit Spline 기능을 사용하여 점들을 이동하면 됩니다.
✔️ Edit Spline으로 지형 확대하는 방법
1. Sprite Shape을 선택
2. Inspector에서 "Edit Spline" 버튼 클릭
3. 점(Node)들을 선택하고 드래그하여 높이 조정
모든 점을 포함하려면 마우스로 드래그하여 전체 선택
개별 점을 조정하려면 각 점을 클릭 후 이동
4. 눈 지형의 높이를 키우기 위해 위쪽으로 이동
5. 조정이 끝나면 Edit Spline을 비활성화하고 변경 사항을 확인
#Height 값을 조정하여 눈 지형 확대
Sprite Shape에는 높이(Height)를 조정하는 기능이 있습니다.
Height 값을 조정하면 눈 지형이 전체적으로 더 크거나 작게 보이도록 조정 가능합니다.
✔️ Height 값 조정하는 방법
Sprite Shape을 선택
Inspector에서 Height 값을 확인
Height 값을 증가시키면 지형이 더 커짐
원하는 크기가 될 때까지 드래그하여 조정
Scene 뷰에서 변경 사항 확인
#Sprite Editor를 사용하여 스프라이트 모양 조정하기
스프라이트 모양을 사용하면 지형을 자유롭게 조정할 수 있지만, 특정 부분이 예상과 다르게 보일 수 있습니다.
특히 스프라이트 끝부분이 곡선으로 표현되지 않는 문제가 발생할 수 있습니다.
#스프라이트 끝부분을 곡선으로 보이게 하는 방법
Sprite Editor 열기
스프라이트 모양을 선택한 후, "Sprite Editor" 버튼 클릭
초록색 선(스프라이트 경계) 확인
Sprite Editor 내에서 초록색 선이 보이는데, 이 부분이 스프라이트 모양으로 사용할 영역을 나타냅니다.
현재 설정된 초록색 선이 곡선 부분을 포함하지 않으면, 게임 내에서 곡선이 보이지 않음.
초록색 선 조정하기
곡선이 보이도록 초록색 선을 조정하여 스프라이트의 범위를 확장함.
원하는 모양으로 잘 보이게 만든 후 적용(Apply) 버튼을 클릭
게임 화면에서 확인
Scene 뷰로 돌아가 보면, 스프라이트 모양이 조정된 것을 확인할 수 있음.
만약 원하는 형태가 아니라면 다시 Sprite Editor에서 수정 가능.
#Wrap Mode 설정하여 텍스처 올바르게 표시하기
스프라이트가 게임에서 이상하게 보이거나 텍스처가 끊어져 보이는 문제가 발생할 수 있습니다.
이 문제는 Wrap Mode 설정을 통해 해결할 수 있습니다.
프로젝트 창에서 텍스처 선택 (예: 눈 타일 이미지 선택)
Inspector 창에서 "Wrap Mode" 설정 확인
기본적으로 Repeat으로 설정되어 있어야 함
Repeat: 텍스처가 반복적으로 이어지면서 자연스럽게 보임 (✅ 올바른 설정)
Clamp: 텍스처 끝이 잘려서 이상한 모양이 나올 수 있음 (🚫 잘못된 설정)
설정을 변경한 후 Apply 버튼 클릭
#**{ 시네머신을 이용한 팔로우 카메라 설정 }**
#**1. 시네머신이란?**
시네머신(Cinemachine)은 유니티에서 제공하는 강력한 카메라 시스템 패키지로,
여러 대의 가상 카메라를 활용하여 다양한 카메라 연출을 쉽게 구현할 수 있도록 도와줍니다.
이를 통해 다음과 같은 기능을 수행할 수 있습니다:
1. 여러 개의 가상 카메라를 설정하여 특정 상황에서 자동 전환
2. 부드러운 카메라 이동 및 줌인/줌아웃 효과
3. 캐릭터나 오브젝트를 따라가는 팔로우(Follow) 카메라 구현
4. 영화 같은 연출이 가능한 컷신 시스템 지원
#**2. 시네머신 패키지 설치하기**
#**패키지 매니저 열기**
Window 메뉴에서 Package Manager를 클릭합니다.
#**시네머신 패키지 설치**
상단의 드롭다운에서 Packages: In Project를 Unity Registry로 변경합니다.
검색창에 Cinemachine을 입력하여 패키지를 찾습니다.
Install 버튼을 눌러 설치합니다.
설치가 완료되면 Remove 버튼이 표시됩니다.
#**3. 가상 카메라 추가 및 설정**
#**가상 카메라(Virtual Camera) 생성**
Hierarchy 창에서 마우스 오른쪽 버튼을 클릭합니다.
Cinemachine -> Virtual Camera를 선택하여 추가합니다.
Hierarchy 창에서 CM vcam1 이름을 VC_FollowCamera로 변경합니다.
#**메인 카메라 설정 확인**
Main Camera를 선택하면 Cinemachine Brain 컴포넌트가 자동 추가된 것을 확인할 수 있습니다.
#**4. 팔로우 카메라 설정**
#**가상 카메라 설정 변경**
VC_FollowCamera를 선택합니다.
Body 옵션을 Framing Transposer로 변경합니다.
#Body 옵션이란?
Cinemachine Virtual Camera가 **대상을 따라가는 방식(모션 알고리즘)**을 결정합니다.
예를 들어 "Framing Transposer"는 2D 게임에 자주 쓰이며,
카메라가 부드럽게 따라오면서도 대상이 화면 안에 잘 보이도록 위치를 잡아줍니다.
#주요 Body 옵션 종류
Do Nothing
아무 움직임도 없음. 카메라는 고정되어 있음. 디버그, 커스텀 제어
Hard Lock to Target
대상을 완전히 고정해서 따라감. 간단한 3D 따라가기
Transposer
타겟을 일정 거리로 따라가며 부드럽게 움직임. 3D TPS, 자유 카메라
Framing Transposer
타겟이 2D 프레임 안에서 유지되도록 따라감. 🎮 2D 게임(플랫폼, 액션 등)
Orbital Transposer
타겟 주변을 공전하면서 따라감. 3D 액션/어드벤처
Tracked Dolly
카메라가 경로(dolly track)를 따라 이동하며 타겟을 추적함. 컷신, 레일 카메라
3rd Person Follow
캐릭터의 뒷부분에 따라붙는 3인칭 시점 카메라 TPS, 3D RPG
Composer
(Body에선 안 쓰이고 Aim 쪽에서 사용됨) 프레임 안에서 타겟의 위치를 제어함 카메라 조준 연출
#**팔로우할 대상 지정**
Follow 필드에서 팔로우할 대상을 선택합니다.
(예: 공이 있다면 공을 선택)
#**카메라 중심 조정**
Screen X 값을 0.25 정도로 설정하여 카메라 중심을 화면의 왼쪽으로 이동시킵니다.
이를 통해 카메라가 공의 앞부분을 더 많이 보여줄 수 있습니다.
#{ 스노우보더 캐릭터 "배리" 만들기 }
#캐릭터의 신체 부위 추가 및 정렬
#(1) 하체 추가 및 정렬
하체를 배리(부모) 아래에 자식 관계로 추가
자식 오브젝트의 위치를 (X=0, Y=0)으로 설정 → 부모 위치를 기준으로 정렬
부모 오브젝트를 움직이면 자식도 함께 움직임
#(2) 상체 추가 및 정렬
상체도 배리 아래에 자식 관계로 추가
하체보다 위쪽으로 배치되도록 조정
중요 개념:
부모-자식 관계: 부모가 움직이면 자식도 함께 움직인다
자식 오브젝트의 위치는 부모를 기준으로 설정됨
#캐릭터의 시각적 정렬과 중심점(Pivot) 조정
상체와 하체를 함께 선택하고, 배리의 중심점(Pivot)이 몸의 중심에 오도록 위치 조정
이유: 회전할 때 중심점이 자연스러워야 하기 때문
#레이어 순서 조정 (캐릭터가 배경 뒤로 사라지는 문제 해결)
배리가 눈 아래로 떨어졌을 때 배경 뒤로 사라짐
해결 방법: 상체와 하체의 "Sorting Order"를 10으로 설정 → 항상 위에 보이도록 설정
#충돌 영역 (Collider) 설정 – 스노우보드 적용
콜라이더란? 충돌 감지를 위한 보이지 않는 영역
"Capsule Collider 2D" 추가 (배리의 몸과 스노우보드를 감싸도록 설정)
가로 방향(X)은 넓게, 세로 방향(Y)은 얇게 조정 → 스노우보드 형태와 비슷하게 설정
Offset을 조정하여 보드 위치와 일치시킴
#충돌 영역 추가 – 머리 부딪힘 감지용
"Circle Collider 2D" 추가하여 머리 부분에 배치
"Is Trigger" 활성화 → 나중에 이벤트를 감지할 수 있도록 설정
이유: 머리가 장애물에 부딪힐 때 특정 이벤트 (예: 게임 오버)를 발생시키기 위해
#{ Surface Effector 2D 사용하기 }
공식 문서
[https://docs.unity3d.com/Manual/2d-physics/effectors/surface-effector-2d-reference.html](https://docs.unity3d.com/Manual/2d-physics/effectors/surface-effector-2d-reference.html)
#개념부터 이해
#Surface Effector 2D란?
2D 충돌 영역(Collider)에 힘을 가해서 물체를 움직이게 하는 컴포넌트예요.
마치 컨베이어 벨트처럼, 이펙터가 달린 표면 위에 닿은 물체를 한 방향으로 밀어줍니다.
적용 대상: 해당 콜라이더 위에 닿은 리지드바디(Rigidbody2D)가 있는 오브젝트
#왜 필요할까?
물리 엔진은 기본적으로 중력만 적용돼요. 그래서 그냥 내리막길을 만들어도 자동으로 굴러가지 않아요.
캐릭터(배리)가 움직이게 하려면 추가적인 힘을 줘야 해요.
Surface Effector는 자동으로 힘을 주는 설정 도구입니다.
#어떻게 적용할까?
1. 배경 레벨(눈길) 오브젝트를 선택
이름: 보통 Level Sprite Shape 같은 오브젝트
2. 컴포넌트 추가
Add Component → Surface Effector 2D 추가
3. 엣지 콜라이더 설정
Edge Collider 2D 또는 Collider 2D 컴포넌트 안에 있는
✅ Used By Effector 체크
→ 이걸 체크해야 Surface Effector가 이 콜라이더에 힘을 줄 수 있음!
4. 세부 설정 변경
Speed: 밀어주는 속도 (예: 10~20)
Force Scale: 중력에 얼마나 저항할지 (1 = 그대로, 0.01 = 거의 영향 없음)
#배리가 갑자기 멈추거나 떨어질 때?
Rigidbody 2D → Collision Detection을 Discrete에서 Continuous로 바꾸기
→ 충돌 감지를 더 잘하게 해줌 (특히 빠르게 움직일 때)
🧍 배리 캐릭터의 중력이 너무 세면?
Gravity Scale 값 조절 가능 (기본은 1)
기그
👉 B 프레임에서는 이미 지나가 버렸기 때문이에요.
Continuous(컨티뉴어스)
🔸 이 옵션은 두 프레임 사이에 이동 경로 전체를 검사해서 충돌을 감지해요.
🔸 즉, "이동하는 동안 무언가와 부딪혔는지"를 확인하는 거죠.
✅ 빠르게 움직여도 충돌을 놓치지 않기 때문에
정확한 충돌 감지가 필요할 때 필수예요.
예시: 스노우보더가 빠른 속도로 움직이다가 경사나 벽에 제대로 부딪히도록 하고 싶을 때
#{ AddTorque를 활용한 캐릭터 회전 }
이번 강의에서는 AddTorque 메서드를 사용하여 캐릭터를 회전시키는 방법을 배웠습니다.
또한, 물리적인 움직임을 조절하는 Linear Drag(선형 마찰) 과 Angular Drag(각 마찰) 에 대해서도 알아보았습니다.
#1. AddTorque란?
AddTorque는 Torque(토크, 회전력) 를 적용하여 Rigidbody 2D 객체를 회전시키는 기능을 합니다.
Torque를 적용하면 게임 오브젝트가 물리 법칙을 따르며 부드럽게 회전합니다.
#왜 Transform.rotation을 사용하지 않을까?
Transform.rotation을 사용하여 회전각을 직접 변경할 수도 있지만, 물리 법칙을 무시한 인위적인 회전이 됩니다.
즉, 캐릭터가 땅에 닿아 있거나 충돌했을 때도 강제로 회전되므로 비정상적인 움직임이 발생할 수 있습니다.
반면, AddTorque는 Unity의 물리 엔진을 활용하므로 더 자연스러운 움직임을 만듭니다.
#2. Rigidbody 2D와 물리 엔진
#Rigidbody 2D란?
Unity에서 물리적인 계산을 적용하려면 Rigidbody 2D를 추가해야 합니다.
Rigidbody 2D가 적용된 게임 오브젝트는 중력, 마찰, 충돌 등의 물리 법칙을 따르게 됩니다.
#Rigidbody 2D를 가져오는 방법
Rigidbody2D rb2d;
rb2d = GetComponent<Rigidbody2D>();
이렇게 하면 해당 오브젝트의 Rigidbody 2D 컴포넌트에 접근할 수 있습니다.
#3. 키보드 입력을 받아 캐릭터 회전시키기
Unity에서는 Input.GetKey()를 사용하여 특정 키가 눌렸는지 감지할 수 있습니다.
이를 활용해 왼쪽 키(←)를 누르면 왼쪽 회전,
오른쪽 키(→)를 누르면 오른쪽 회전하도록 설정할 수 있습니다.
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow))
{
rb2d.AddTorque(torqueAmount); // 왼쪽 방향 회전
}
else if (Input.GetKey(KeyCode.RightArrow))
{
rb2d.AddTorque(-torqueAmount); // 오른쪽 방향 회전
}
}
AddTorque(torqueAmount) : 양수(+) 값을 적용하면 시계 반대 방향으로 회전
AddTorque(-torqueAmount) : 음수(-) 값을 적용하면 시계 방향으로 회전
#if문 대신 else if를 사용하는 이유
동시에 왼쪽(←)과 오른쪽(→) 키를 누를 경우 둘 다 작동하면 버그가 발생할 수 있습니다.
이를 방지하려면 else if를 사용하여 한 번에 하나의 방향만 적용되도록 합니다.
#4. 물리적인 마찰 조절: Linear Drag & Angular Drag
토크 값만 조절하면 키를 놓아도 캐릭터가 계속 회전할 수 있습니다.
이를 방지하기 위해 Angular Drag(각 마찰력) 을 사용합니다.
#각 마찰력 (Angular Drag)
회전하는 물체가 얼마나 빨리 멈추는지 결정하는 힘
Angular Drag는 회전하는 물체가 얼마나 빠르게 멈출지를 결정합니다.
값이 작으면 계속 회전하고, 값이 크면 빠르게 멈춥니다.
🔄 실생활 예시:
스케이트보드 위에서 회전하는 피겨 스케이터를 떠올려 보세요.
Angular Drag가 낮을 때:
→ 피겨 스케이터가 회전하다가 오랫동안 뱅글뱅글 돕니다.
→ 물리적으로 저항이 적기 때문에 회전이 계속되는 거죠.
Angular Drag가 높을 때:
→ 스케이터가 돌자마자 금방 멈춥니다.
→ 공기저항이나 마찰력이 강해서 빨리 회전이 멈추는 경우예요.
🎮 게임 예시:
스노우보드 게임에서 캐릭터가 공중에서 회전할 때,
Angular Drag가 낮으면 회전이 계속되고
Angular Drag가 높으면 금방 멈춰요.
#선형 마찰력 (Linear Drag)
앞으로 나아가는 물체가 얼마나 빨리 느려지는지 결정하는 힘
Linear Drag는 이동하는 물체가 얼마나 빠르게 감속할지 결정합니다.
값이 작으면 미끄러지듯 이동하고, 값이 크면 즉시 멈춥니다.
🏃 실생활 예시:
얼음 위에서 미끄러지는 펭귄을 떠올려 보세요.
Linear Drag가 낮을 때:
→ 펭귄이 쭉— 미끄러지며 멀리까지 갑니다.
→ 마찰이 거의 없어서 감속이 느린 상태죠.
Linear Drag가 높을 때:
→ 펭귄이 출발하자마자 거의 움직이지 않거나 금방 멈춥니다.
→ 바닥이 거칠거나 저항이 강해서 감속이 빠른 상태예요.
🎮 게임 예시:
플레이어가 경사길을 내려갈 때
Linear Drag가 낮으면 속도가 계속 붙고 멀리 나아가고
Linear Drag가 높으면 조금 가다가 멈춰버려요.
#5. 전체 코드 정리
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] float torqueAmount = 8f; // 회전력 조절
Rigidbody2D rb2d;
void Start()
{
rb2d = GetComponent<Rigidbody2D>(); // Rigidbody2D 가져오기
}
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow))
{
rb2d.AddTorque(torqueAmount); // 왼쪽 회전
}
else if (Input.GetKey(KeyCode.RightArrow))
{
rb2d.AddTorque(-torqueAmount); // 오른쪽 회전
}
}
}
#{ "성공 조건"과 "실패 조건"을 구현 }
✅ 성공 조건(Success Condition)이란?
게임에서 플레이어가 "성공했다"고 판단되는 기준이에요.
이 게임에서는 플레이어가 도착점(Finish Line)을 지나면 성공이에요.
성공했을 때는, 예를 들어 다음 레벨로 가거나 같은 레벨을 다시 시작할 수 있죠.
❌ 실패 조건(Fail Condition)이란?
플레이어가 "실패했다"고 판단되는 기준이에요.
이 게임에서는 플레이어가 보드를 타다 머리를 땅에 박으면 실패예요.
실패했을 때는 보통 다시 시작하거나, 게임 오버 화면으로 전환되죠.
#1. **성공 조건 만들기 – FinishLine 스크립트**
목표: 플레이어가 특정 지점을 통과했을 때 “성공”했다고 감지하기
#FinishLine.cs 코드
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Player"))
{
Debug.Log("Player has crossed the finish line!");
}
}
OnTriggerEnter2D : 어떤 객체가 트리거에 들어올 때 호출됨
CompareTag("Player") : 부딪힌 객체가 플레이어인지 확인
Debug.Log : 성공 메시지를 콘솔에 출력
#도착점 만드는 순서 요약
1. 빈 오브젝트 → Finish Line 생성
2. 자식으로 Post (기둥), Top (원)을 만들어 빨간색으로 표시
3. Finish Line 오브젝트에 BoxCollider2D 추가, Is Trigger 체크
4. FinishLine.cs 스크립트 부착
5. 플레이어 오브젝트에 "Player" 태그 부여
#2. 실패 조건 만들기 – CrashDetector 스크립트
목표: 플레이어가 머리로 땅에 닿았는지 감지하기
#CrashDetector.cs 코드
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Ground"))
{
Debug.Log("Player head crashed!");
}
}
#충돌 감지기 만드는 순서 요약
1. 새 스크립트 CrashDetector.cs 생성
2. 플레이어의 머리 부위(혹은 전체)에 CircleCollider2D 적용
3. CrashDetector 스크립트 부착
4. 땅에 "Ground" 태그 부여
5. OnTriggerEnter2D에서 CompareTag("Ground")로 감지
#{ 씬(Scene) 로딩과 네임스페이스 개념 }
캐릭터가 도착점에 도달하거나
머리를 부딪혀서 게임 오버 상태가 되면
👉 현재 씬을 다시 로딩해서 게임을 처음부터 시작하도록 만들기
#씬(Scene)의 개념
#씬이란?
씬(Scene) 은 유니티에서 게임의 한 장면을 의미해요.
예: 메인화면, 게임 시작, 게임 오버, 레벨 1, 보스전 등
#씬 안에는 무엇이 들어갈까?
캐릭터, 배경, 적, UI, 사운드, 카메라 등
👉 씬은 게임을 구성하는 모든 요소가 담긴 공간입니다.
#씬은 프로젝트에 어떻게 저장될까?
.unity 확장자의 파일로 저장됨
예: Level1.unity, MainMenu.unity
#새 씬 만들고 저장하기
#씬 저장 방법
File > Save As 클릭
Assets/Scenes 폴더 안에 Level1.unity로 저장
기존 SampleScene은 삭제해도 무방 (같은 내용을 복사한 것이므로)
#Build Settings에 씬 등록하기
#Build Settings란?
유니티는 어떤 씬들이 게임에 포함되는지 알 수 없기 때문에,
우리가 직접 등록해줘야 해요.
#설정 방법
메뉴바 → File > Build Settings
Add Open Scenes 버튼 클릭 → 현재 열려있는 씬이 추가됨
씬 이름 옆에 숫자 (Index)가 붙음 → 0, 1, 2…
#중요한 이유
SceneManager.LoadScene(0) 처럼 인덱스로 씬을 불러올 때,
Build Settings에 등록되지 않으면 불러오지 못함
#네임스페이스(Namespace)의 개념
#네임스페이스란?
코드끼리 충돌하지 않도록 그룹으로 묶는 기능
말 그대로 “이 코드들은 이 영역 안에 있어요!”라는 표시
🔸 충돌 방지란?
프로그래밍에서는 이름이 겹치면 문제가 생길 수 있어.
예를 들어, 너가 만든 함수 이름이 LoadScene()인데, Unity에도 똑같은 LoadScene()이 있으면,
컴퓨터가 "어떤 걸 써야 하지?" 하면서 헷갈려.
🔸 그래서 "묶는 것"이 필요해!
Unity에서는 기능들을 패키지별로 ‘이름 공간(Namespace)’에 묶어놨어.
이렇게 이름을 그룹으로 묶어놓으면 겹치지 않게 정리할 수 있어.
UnityEngine.SceneManagement.SceneManager.LoadScene("MainScene");
이렇게 쓰면 "SceneManager는 SceneManagement 안에 있는 거야" 라고 명확하게 알려주는 거지
using UnityEngine;
using UnityEngine.SceneManagement;
UnityEngine은 유니티 엔진의 기본 기능 (예: Debug.Log, GameObject)
UnityEngine.SceneManagement는 씬을 관리하는 기능들이 들어 있는 영역
#클래스(Class)의 개념
#클래스란?
기능(메서드)과 데이터(변수)를 담는 설계도/틀
유니티에서는 스크립트 파일 하나 = 클래스 하나인 경우가 많음
public class CrashDetector : MonoBehaviour
{
// 이 안에 기능들이 들어감
}
CrashDetector는 충돌을 감지하는 역할
MonoBehaviour를 상속해야 유니티에서 동작함
#SceneManager 클래스 소개
#SceneManager란?
유니티에서 씬을 불러오거나 바꿀 수 있게 해주는 관리자 역할 클래스
SceneManager.LoadScene(0); → 인덱스 0번 씬을 로딩
SceneManager.LoadScene("Level1"); → 이름으로도 가능
using UnityEngine.SceneManagement;
이 네임스페이스를 꼭 추가해야 SceneManager를 사용할 수 있어요
#충돌 감지 후 씬 로딩 구현하기
플레이어가 Ground에 닿으면 씬을 다시 로딩
#CrashDetector.cs
using UnityEngine;
using UnityEngine.SceneManagement;
public class CrashDetector : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Ground"))
{
SceneManager.LoadScene(0); // 현재 씬 다시 로드
}
}
}
OnTriggerEnter2D: 다른 오브젝트와 충돌했을 때 호출되는 함수
CompareTag("Ground"): 충돌한 오브젝트가 "Ground" 태그일 경우만 실행
SceneManager.LoadScene(0): 씬을 다시 불러옴 (0은 Build Settings에서 인덱스)
#도착점 통과 시 씬 다시 로드하기 (FinishLine.cs 예시)
플레이어가 성공적으로 도착점에 닿으면 씬을 다시 시작
using UnityEngine;
using UnityEngine.SceneManagement;
public class FinishLine : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
SceneManager.LoadScene(0); // 도착 시 씬 재시작
}
}
}
#{ Invoke 딜레이 주기 }
충돌이나 도착점 통과 시 바로 장면(Scene)을 다시 불러오는 것이 아니라 잠깐 기다린 후 장면을 리셋하게 만든다.
이 딜레이 시간 동안 입자 효과나 사운드 같은 연출을 넣을 수 있게 한다.
#딜레이를 주는 두 가지 방법
1.Invoke (이번 강의에서 사용)
간단하고 사용하기 쉬움
일정 시간 뒤에 특정 메소드를 호출할 수 있음
2. Coroutines (나중에 배울 예정)
더 유연한 딜레이 방식, 조건 추가도 가능함
#Invoke 사용법
Invoke("메소드이름", 딜레이시간);
"메소드이름": 큰따옴표로 묶어야 함 (자동완성 안 됨, 철자 주의!)
딜레이시간: 초 단위 (예: 2f는 2초)
#전체 코드 정리 (CrashDetector.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class CrashDetector : MonoBehaviour
{
[SerializeField] float loadDelay = 1f;
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Ground"))
{
Invoke("ReloadScene", loadDelay);
}
}
void ReloadScene()
{
Debug.Log("Player head crashed!");
SceneManager.LoadScene(0);
}
}
#FinishLine.cs
using System.Collections;
using System.Collections.Generic;
using UnityEditor.SearchService;
using UnityEngine;
using UnityEngine.SceneManagement;
public class FinishLine : MonoBehaviour
{
[SerializeField] float loadDelay = 1f; // Delay before loading the scene
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Player"))
{
Invoke("ReloadScene", loadDelay); // Call ReloadScene after 2 seconds
}
}
void ReloadScene() {
Debug.Log("Player has crossed the finish line!");
SceneManager.LoadScene(0);
}
}
#{ 입자 시스템 (Particle System) 사용하기 }
#1. **입자 시스템이란?**
작은 이미지들을 반복적으로 생성해 애니메이션처럼 보이게 하는 시각 효과 시스템
예시: 불꽃, 폭죽, 연기, 눈, 먼지, 물방울 등
게임에서 몰입감을 높이는 데 큰 역할
#2. **기본 입자 시스템 생성하기**
✅ 입자 시스템 생성
Hierarchy에서 우클릭 → Effects → Particle System 선택
입자가 기본적으로 분출되는 효과가 생성됨.
✅ 기즈모(Gizmos) 설명
Scene 창 상단의 Gizmos 설정에서 Selection Outline을 해제하면 테두리가 사라짐.
입자가 더 잘 보이게 할 수 있음.
✅ 이름 지정 & 부모-자식 관계 설정
입자 시스템 오브젝트의 이름을 Finish Effect로 변경.
Finish Line 오브젝트 아래에 드래그하여 자식으로 설정.
이렇게 하면 도착점과 함께 움직이게 됨.
✅ 위치 조정
Finish Effect의 위치를 (0, 0, 0) 으로 설정 → 도착점 정중앙에 위치.
#기본 설정 구성
Duration: 효과 전체 지속 시간
Looping: 반복 여부 (체크 해제 시 한 번만 재생)
Start Lifetime: 입자 하나의 생존 시간
Start Speed: 입자가 생성될 때의 초기 속도
Start Size: 입자의 크기
Start Rotation: 입자가 회전하면서 나가는 각도
Start Color: 입자의 기본 색상
Gravity Modifier: 중력 영향 설정
#3. **폭죽 효과 만들기**
Emission (입자 발생 관련)
Rate over Time: 시간당 몇 개 입자 생성할지
Bursts: 특정 타이밍에 대량으로 입자 생성
Shape (입자 나가는 방향)
기본값은 Cone (원뿔 모양)
Sphere, Hemisphere, Box, Circle, Edge 등으로 변경 가능
Random Direction으로 랜덤 발사 가능
#4. **Renderer 설정**
Material: 입자에 사용할 이미지 설정
Unity 기본 제공: Default-Particle 또는 Default-ParticleSystem
Render Mode: 입자 모양 방식 (Billboard, Mesh 등)
Sorting Layer: UI나 다른 오브젝트보다 앞에/뒤에 보이게 조정
#5. **Color over Lifetime**
시간이 지남에 따라 입자 색상이 변하도록 설정
그라디언트 컬러를 사용해 자연스러운 변화 가능
예: 빨강 → 노랑 → 투명
#6. **Noise 모듈 (자연스러운 흔들림 효과)**
입자의 움직임에 랜덤성을 부여
연기, 불꽃 등의 현실감 향상
Strength, Frequency 조절로 흔들림 강도 조절
#**{ 입자 효과(Particle System)** 트리거 }
*플레이어(배리)**가
도착점에 도달했을 때
장애물에 부딪혔을 때
입자 효과가 발생하도록 트리거를 만드는 것!
#개념 이해
1. 입자 시스템(Particle System)이란?
연기, 불꽃, 폭발, 반짝임 등의 시각적 효과를 만들 수 있는 Unity 기능.
게임 오브젝트처럼 배치하고, 스크립트로 조작할 수 있음.
2. 트리거(Trigger)란?
OnTriggerEnter 같은 메소드를 통해
오브젝트가 충돌하거나 겹쳤을 때 실행되는 이벤트.
3. 레퍼런스(Reference)란?
Unity에게 "이 입자 시스템을 나중에 실행해!" 하고 알려주는 것.
스크립트에서 입자 시스템을 **직렬화(SerializeField)**해서 인스펙터에서 드래그해서 연결 가능.
#구현 순서 정리
#입자 시스템 레퍼런스 만들기
먼저 FinishLine이나 CrashDetector 같은 스크립트에 다음과 같이 작성합니다:
이렇게 하면 인스펙터 창에서 입자 시스템을 드래그로 연결할 수 있어요.
[SerializeField] ParticleSystem finishEffect; // 도착점용
[SerializeField] ParticleSystem crashEffect; // 충돌용
#입자 시스템 실행 코드 작성
도착점에서는 플레이어가 도착했을 때:
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Player")) {
finishEffect.Play();
}
}
충돌 감지에서는 땅에 부딪힐 때:
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Ground")) {
crashEffect.Play();
Invoke("ReloadScene", loadDelay); // 1초 후 씬 재시작
}
}
#유니티 에디터 설정
#입자 시스템 오브젝트 선택
Play On Awake 체크 해제 (시작하자마자 실행되지 않도록)
Simulation Space를 World로 설정 (입자가 캐릭터를 따라오지 않고 자리에 남게)
#Z 위치 정리
배리, 도착점, 배경, 입자 효과 등 모든 오브젝트의 Z 좌표를 0으로 맞추기
입자 시스템이 카메라 뒤에 있으면 게임 화면에 안 나옴!
#Order in Layer 설정
입자 시스템의 Renderer > Order in Layer 값을 100 정도로 설정해서 다른 오브젝트보다 앞에 보이게
#인스펙터에서 입자 시스템 연결
FinishLine 오브젝트에는 finishEffect
배리에 붙은 CrashDetector 오브젝트에는 crashEffect
를 각각 드래그해서 연결
#{ 레벨 디자인을 시각적으로 발전 }
#왜 게임을 예쁘게 꾸며야 하나요?
동기부여: 그래픽이 좋으면 개발자 자신도 더 흥미를 느끼게 됩니다.
피드백 받기 용이: 게임이 어느 정도 완성되어 보여야 피드백 받기 좋고, 그걸 바탕으로 발전시킬 수 있어요.
아이디어 확장: 시각적으로 풍성하면, 새로운 기능 아이디어도 떠오르기 쉬워요.
최종에 몰아서 하지 말자: "끝나고 예쁘게 꾸미자"는 생각보다는, 계속 꾸며가며 진행하는 게 좋습니다.
#실습 내용 요약
1. 📷 카메라 조정
문제: 화면이 너무 가까워서 주변을 잘 못 봄.
해결: Main Camera의 Lens -> Ortho Size 값을 10으로 조정해서 더 넓게 보이게 함.
2. 🎨 배경 색깔 바꾸기
Main Camera 배경색을 어두운 파랑 ➜ 회색빛 하늘로 변경해서 더 부드러운 느낌 연출.
3. 🌳 나무 추가
스프라이트 조정: 나무 이미지의 Pixels Per Unit 값을 더 작게(예: 20) 해서 크게 보이게 설정.
반복 배치 시 팁:
X축 Scale을 -1로 주면 좌우 반전 → 똑같은 나무도 달라 보임.
나무 크기를 약간씩 다르게 만들어서 다양성 주기.
#오브젝트 정리하기
Empty Object를 만들어 이름을 Environment로 설정.
나무, 바위, 구름 등 전부 이 안에 정리해서 Hierarchy 깔끔하게 유지.
Transform → Reset으로 위치 초기화(0,0,0)해서 이상한 Z 값 문제 방지.
#부드러운 움직임 만들기
플레이어 오브젝트에서 Interpolate를 Interpolate로 설정하면:
움직임이 더 부드럽게,
떨림 현상 해결 가능.
#Interpolate 설정이란?
Unity에서 물리(Physics) 업데이트와 화면(화면 렌더링)이 시간 차이를 두고
돌아가기 때문에 생기는 작은 떨림이 있어요
Interpolate(보간)을 설정하면?
중간 값을 계산해서 화면에 부드럽게 보여주는 기능이에요.
#비유로 이해해볼까요?
예: 사진 찍는 로봇
당신의 캐릭터는 1초에 50번 움직여요 (물리 업데이트).
그런데 카메라는 1초에 60번 찍어요 (렌더링).
즉, 카메라는 캐릭터 움직임보다 더 자주 화면을 그리기 때문에,
중간중간 “위치 정보가 없어서” 살짝씩 움찔움찔하게 보여요.
💡 Interpolate를 켜면, Unity가 이렇게 말해요:
“음, 위치 정보가 없을 땐… 앞뒤 위치를 보고 적당히 중간 위치를 계산해서 보여줄게!”
그래서 더 부드럽게 보여지고, 떨림이 줄어드는 거죠!
#어떻게 설정하나요?
플레이어 오브젝트를 클릭
Rigidbody2D (혹은 Rigidbody) 컴포넌트 확인
Interpolate 항목에서
None → 떨림 있음
Interpolate → 부드럽게
Extrapolate → 예측해서 조금 더 미리 움직이게
대부분의 경우엔 그냥 Interpolate로 설정해주는 게 가장 안정적이에요.
#언제 꺼두는 게 좋을까?
수백 개의 Rigidbody가 동시에 움직일 때
물리 성능 최적화가 중요함
모바일에서 아주 가벼운 게임을 만들 때
프레임 최적화에 극도로 신경 쓸 때
주로 물리 계산보다 반응속도(FPS)가 더 중요할 때
(예: 빠른 멀티플레이어 슈팅)
이런 경우가 아니라면, 기본적으로 켜두는 걸 추천합니다!
일반적인 2D/3D 게임에서 Interpolate는
연산량이 아주 약간 증가할 뿐이고,
요즘 컴퓨터나 모바일에서는 티도 잘 안 나요.
특히 캐릭터가 몇 개 정도만 있다면
그냥 켜두는 게 이득이 더 큽니다
#요약
None 보간 없음 (기본값, 떨림 생길 수 있음)
Interpolate 현재와 이전 위치를 보고 중간 위치를 계산해서 부드럽게 보여줌
Extrapolate 다음 위치를 예측해서 보여줌 (실제보다 앞설 수 있어 약간 불안정)
#**{ 부스트 기능**을 추가 }
#1. 개념: GetComponent와 FindObjectOfType 차이점
#GetComponent()
동일한 게임 오브젝트 안에 있는 컴포넌트를 가져오는 방법이에요.
예: PlayerController 스크립트가 붙어 있는 오브젝트에 Rigidbody2D가 있다면, 이렇게 가져올 수 있어요:
rb2d = GetComponent<Rigidbody2D>();
#FindObjectOfType()
씬(Scene) 전체에서 특정 타입의 오브젝트 하나를 찾는 방법이에요.
여러 개가 있으면 첫 번째 것만 찾아요. 따라서 하나만 있을 때 써야 안전해요.
우리는 SurfaceEffector2D가 하나만 있다고 가정하므로 사용이 적절해요.
surfaceEffector2D = FindObjectOfType<SurfaceEffector2D>();
#2. SurfaceEffector2D가 뭐예요?
SurfaceEffector2D는 물체가 닿았을 때 힘을 주어 밀어주는 컴포넌트예요.
우리가 만든 게임에서는 플레이어가 이 위에 있을 때 앞으로 나아가는 힘을 주는 역할을 합니다.
여기엔 speed라는 속성값이 있어서, 이 값을 바꾸면 플레이어가 밀리는 속도를 조절할 수 있어요.
#3. 변수 선언하기
#부스트 기능을 위한 변수들
[SerializeField] float boostSpeed = 1000f; // 부스트 시 속도
[SerializeField] float baseSpeed = 20f; // 기본 속도
#4. Start() 함수에서 컴포넌트 참조 가져오기
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
surfaceEffector2D = FindObjectOfType<SurfaceEffector2D>();
}
Rigidbody2D는 물리 엔진과 상호작용하기 위해 필요하고요,
SurfaceEffector2D는 속도를 조절하기 위해 가져오는 겁니다.
#5. Update() 함수와 역할 분리
Update는 매 프레임마다 실행되기 때문에, 여기에 플레이어 회전과 부스트 처리를 넣어요.
가독성을 높이기 위해 기능별로 메서드를 나눴습니다. (이건 매우 좋은 습관이에요!)
void Update()
{
RotatePlayer();
RespondToBoost();
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] float torqueAmount = 1f;
[SerializeField] float boostSpeed = 1000f;
[SerializeField] float baseSpeed = 20f;
Rigidbody2D rb2d;
SurfaceEffector2D surfaceEffector2D;
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
surfaceEffector2D = FindObjectOfType<SurfaceEffector2D>();
}
// Update is called once per frame
void Update()
{
RotatePlayer();
RespondToBoost();
}
private void RespondToBoost()
{
if(Input.GetKey(KeyCode.UpArrow))
{
surfaceEffector2D.speed = boostSpeed;
}
else if (Input.GetKeyUp(KeyCode.UpArrow))
{
surfaceEffector2D.speed = baseSpeed;
}
}
void RotatePlayer()
{
if (Input.GetKey(KeyCode.LeftArrow))
{
rb2d.AddTorque(torqueAmount);
}
else if (Input.GetKey(KeyCode.RightArrow))
{
rb2d.AddTorque(-torqueAmount);
}
}
}
#{ 입자 효과 끄고 켜기 }
플레이어가 땅에 닿아있을 때만 먼지 입자 효과가 나오고,
공중에 있을 때는 꺼지게 만드는 것이 이번 강의의 핵심 목표입니다.
#1️⃣ 충돌(Collision)과 트리거(Trigger)의 개념 정리
#Collider(콜라이더)
충돌을 감지하기 위한 컴포넌트입니다.
Collider를 가진 두 오브젝트가 서로 맞닿으면 충돌 이벤트가 발생합니다.
#이벤트 종류
OnCollisionEnter2D
두 개의 콜라이더가 처음으로 접촉했을 때
OnCollisionExit2D
두 개의 콜라이더가 더 이상 접촉하지 않을 때
OnTriggerEnter2D
트리거로 설정된 콜라이더를 통과했을 때
OnTriggerExit2D
트리거에서 벗어났을 때
⚠️ 트리거는 Is Trigger 옵션이 체크된 콜라이더에서 사용됩니다. 이번에는 일반 콜라이더만 사용합니다.
#실습 순서
#Step 1: 입자 시스템(Particle System) 만들기
🧱 1. 입자 시스템 생성
Hierarchy 뷰에서
➡️ 우클릭 → Effects → Particle System
새로운 입자 시스템이 생깁니다.
이름을 Dust Particles로 변경하세요.
🔎 이 시스템은 먼지처럼 보이는 효과를 만들어주는 도구예요.
🧭 2. Transform 조정
입자 시스템 오브젝트를 플레이어의 뒤쪽으로 이동시킵니다.
🎯 입자가 플레이어의 뒤에서 흩어져야 자연스럽기 때문이에요.
⚙️ 3. 입자 설정 변경 (Inspector에서 수정)
▶ Duration: 0.1
입자 시스템의 한 사이클의 길이입니다.
0.1초마다 새로 시작되도록 설정하는 거예요.
보통 Looping을 켜면 의미가 크진 않지만, 짧게 설정하면 반복 속도가 빠릅니다.
🔁 Looping: 체크
입자 효과를 계속 반복할지 여부를 정합니다.
체크하면 멈출 때까지 계속 반복해서 먼지가 계속 나와요.
⏳ Start Lifetime: 0.5
한 개의 입자가 살아있는 시간입니다.
0.5초 후에 사라지는 거죠.
너무 길면 입자가 오래 떠 있고, 너무 짧으면 금방 사라져요.
🏃 Start Speed: 2
입자가 얼마나 빠르게 날아가는지를 정합니다.
2는 적당한 속도예요. 너무 빠르면 자연스럽지 않고, 느리면 뭉쳐 보여요.
📏 Start Size: 0.2
입자의 크기입니다.
먼지 입자는 작아야 자연스럽기 때문에 0.2로 작게 설정해요.
🌍 Simulation Space: World
입자가 생성된 후 어디를 기준으로 움직일지를 정해요.
Local 입자가 오브젝트(플레이어)에 붙어서 같이 움직임
World 입자가 세상에 고정되어 남음 (캐릭터는 지나감)
World로 설정해야, 플레이어가 지나간 자리에 먼지가 남아있는 것처럼 보입니다.
🔺 Shape: Cone (회전 Y: -90)
입자가 퍼지는 방향과 모양을 정합니다.
Cone 뿔 모양으로 퍼지게 함 (회전하면 방향 바뀜)
Rotation Y: -90을 하면 입자가 뒤쪽으로 퍼지게 됩니다.
🎨 Renderer → Material 설정
Material은 입자의 모양입니다.
**네모 모양(Material: Default-Particle 또는 Square 등)**을 선택합니다.
동그란 입자보다 네모가 먼지 느낌이 더 잘 나요.
⛔ Play on Awake: 해제
이 옵션을 끄면 게임 시작할 때 자동으로 입자가 나오지 않습니다.
우리는 땅에 닿을 때만 입자가 나와야 하므로 반드시 꺼야 해요.
#Step 2: C# 스크립트 만들기
DustTrail.cs라는 새 스크립트를 생성
다음 코드를 작성:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DustTrail : MonoBehaviour
{
[SerializeField] ParticleSystem dustParticle;
void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.CompareTag("Ground"))
{
dustParticle.Play();
}
}
void OnCollisionExit2D(Collision2D collision)
{
if(collision.gameObject.CompareTag("Ground"))
{
dustParticle.Stop();
}
}
}
#Step 3: 오브젝트에 스크립트 적용하기
플레이어 오브젝트 선택
DustTrail.cs 스크립트를 드래그해서 붙이기
dustParticle 슬롯에 아까 만든 Dust Particles를 드래그해서 연결
#Step 4: 태그(Tag) 설정
Ground 오브젝트 선택
Inspector에서 Tag를 Ground로 설정
(없으면 Add Tag → Ground 추가 후 선택)
#{ Unity에서 사운드 구현하기 }
#1. 사운드 시스템의 기본 개념
🎤 오디오 리스너 (Audio Listener)
소리를 듣는 역할.
보통 카메라에 자동으로 포함되어 있어요.
일종의 마이크처럼 생각하면 돼요.
게임에서 플레이어가 듣는 위치라고 생각하면 편합니다.
🔊 오디오 소스 (Audio Source)
소리를 내는 역할.
소리의 볼륨, 피치, 재생 방법 설정 가능.
오디오 클립(Audio Clip)을 연결해서 사용.
🎵 오디오 클립 (Audio Clip)
실제 소리 데이터.
예: .mp3, .wav, .ogg 파일.
Unity 프로젝트의 Assets 폴더에 넣어서 사용.
#2. 준비하기: 사운드 파일 불러오기
1. 사운드 효과(SFX) 파일 2개 준비하기
충돌 사운드
도착 지점 사운드
인터넷에서 무료 SFX 다운로드하거나 강의 제공 파일 사용
2. Assets 폴더에 'Audio' 폴더 만들기
Audio 폴더 안에 사운드 파일 정리
3. 파일 포맷 확인
.wav, .mp3, .ogg 가능
#3. 도착 지점에 사운드 추가하기 (Finish Line)
1. Finish 지점 오브젝트 선택
2. Audio Source 컴포넌트 추가
Add Component > Audio Source
3. 도착 효과음 연결
Audio Clip에 사운드 드래그하거나 선택
4. 설정 변경
Volume: 0.5로 조절
Play On Awake 체크 해제 ← 시작하자마자 소리 나면 안 되니까
5. 스크립트 수정 (FinishLine.cs)
if (collision.CompareTag("Player"))
{
finishEffect.Play(); // 파티클 재생
GetComponent<AudioSource>().Play(); // 사운드 재생
Invoke("ReloadScene", loadDelay); // 딜레이 후 씬 재시작
}
#4. 충돌 시 사운드 추가하기 (Crash)
1. Player 또는 충돌 감지 오브젝트 선택
2. Audio Source 컴포넌트 추가
충돌 시 재생하려면 필요함
3. 스크립트 수정 (CrashDetector.cs)
[SerializeField] AudioClip crashSFX;
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Ground"))
{
GetComponent<AudioSource>().PlayOneShot(crashSFX); // 효과음 재생
crashEffect.Play(); // 파티클 재생
Invoke("ReloadScene", loadDelay); // 씬 재시작
}
}
#PlayOneShot()
지정된 클립을 한 번만 재생
기존 재생 중인 소리와 겹쳐도 문제 없음
여러 효과음을 동시에 재생할 때 유용
#{ 퍼블릭 액세스 모디파이어 }
퍼블릭 액세스 모디파이어(Public Access Modifier)의 개념 이해하기
서로 다른 스크립트 간 소통 방법 배우기
실제로 플레이어의 움직임을 제한해보며 개념을 실습하기
#문제 상황 설명
머리에 부딪히면 충돌 이펙트가 나오고, 1초 후에 레벨이 다시 로딩됩니다.
하지만 그 1초 동안에도 플레이어가 계속 움직일 수 있고, 소리도 반복해서 납니다.
우리가 원하는 목표는: 머리에 부딪힌 순간, 플레이어가 더 이상 움직일 수 없도록 막는 것!
#개념 정리: 액세스 모디파이어(접근 제어자)
✔️ private (기본값)
기본값은 모두 private입니다.
같은 클래스 안에서만 변수나 메소드에 접근할 수 있습니다.
✔️ public
다른 클래스에서도 이 변수나 메소드에 접근할 수 있도록 공개합니다.
주의점: 실수로 외부에서 값을 바꾸는 일이 생길 수 있어 조심해서 써야 해요.
#목표를 이루기 위한 설계
✔️ 현재 상황
CrashDetector 스크립트는 충돌을 감지할 수 있습니다.
PlayerController 스크립트는 움직임을 제어합니다.
하지만 이 둘은 서로 연결되어 있지 않습니다.
#실습: 퍼블릭 메소드를 이용한 스크립트 연결
public void DisableControls()
{
canMove = false;
}
public 으로 선언해서 다른 클래스에서 호출이 가능해진다
public class PlayerController : MonoBehaviour
{
[SerializeField] float torqueAmount = 1f;
[SerializeField] float boostSpeed = 1000f;
[SerializeField] float baseSpeed = 20f;
Rigidbody2D rb2d;
SurfaceEffector2D surfaceEffector2D;
bool canMove = true;
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
surfaceEffector2D = FindObjectOfType<SurfaceEffector2D>();
}
// Update is called once per frame
void Update()
{
if (canMove) {
RotatePlayer();
RespondToBoost();
}
}
public void DisableControls()
{
canMove = false;
}
private void RespondToBoost()
{
if(Input.GetKey(KeyCode.UpArrow))
{
surfaceEffector2D.speed = boostSpeed;
}
else if (Input.GetKeyUp(KeyCode.UpArrow))
{
surfaceEffector2D.speed = baseSpeed;
}
}
void RotatePlayer()
{
if (Input.GetKey(KeyCode.LeftArrow))
{
rb2d.AddTorque(torqueAmount);
}
else if (Input.GetKey(KeyCode.RightArrow))
{
rb2d.AddTorque(-torqueAmount);
}
}
}
#`CrashDetector`에서 퍼블릭 메소드 호출하기
FindObjectOfType<PlayerController>().DisableControls();
이 한 줄로 PlayerController를 찾아서 DisableControls() 메소드를 호출할 수 있습니다.
이렇게 되면 충돌이 발생했을 때 즉시 움직임이 중지됩니다.
public class CrashDetector : MonoBehaviour
{
[SerializeField] float loadDelay = 1f;
[SerializeField] ParticleSystem crashEffect; // Reference to the particle system prefab
[SerializeField] AudioClip crashSFX;
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Ground"))
{
FindObjectOfType<PlayerController>().DisableControls(); // Disable player controls
GetComponent<AudioSource>().PlayOneShot(crashSFX); // Play the audio source
crashEffect.Play(); // Play the particle effect
Invoke("ReloadScene", loadDelay);
}
}
void ReloadScene()
{
Debug.Log("Player head crashed!");
SceneManager.LoadScene(0);
}
}
#{ 충돌이 **여러 번 감지 방지 }**
#문제
충돌이 여러 번 감지돼서 효과도 중복 재생됨
#해결 방법
bool hasCrashed = false; → 충돌 여부 저장
if (!hasCrashed) → 한 번만 실행되게 조건 걸기
효과 실행 후 hasCrashed = true; → 중복 방지
bool hasCrashed = false;
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Ground") && !hasCrashed)
{
hasCrashed = true;
// 컨트롤 끄기, 음향, 입자, 씬 리로드 실행
}
}
bool 하나로 중복 트리거 문제 해결!