1. 배경 세팅하기
1-1) 메인 씬 이름 바꾸기
변경할거냐 확인 후 엔터
다음과 같이 Hierarchy에서도 바뀐 것을 알 수 있다.
1-2) 화면 비율 변경
Game 탭에서 Free Aspect 박스 클릭 → 더하기 버튼 (Add New Item) 클릭
다음과 같이 입력 Label : Phone Width & Height : 760x1280
1-3) 배경 색깔 입히기
Background로 이름 바꿔주고 색을 변경하자.
그리고 나서 색을 255, 255, 220, 255
로 맞추자.
X: 6, Y: 10
으로 맞추면?
정확히 카메라에 꽉 차게 된다.
2. UI 박스 만들기
UI 박스는 두 가지 기능이 있다.
- 점수 표시 기능
- 빗물이 닿으면 없어지게 하는 기능
배경과 똑같이 만들되, 이름은 Ground로 만들자.
색은 50, 50, 50, 255
,
Scale은 X: 6, Y: 1.5
로 만들자.
2-1) Transform Component - Position 조정
추가로 Position을 조정하자.
Poisition을 Y: -4.3
으로 수정하면?
2-2) Sprite Renderer Component - Order in Layer 설정
Background와 Ground의 앞뒤 순서를 확실히 하기 위해 Order in Layer를 1로 수정하자.
3. 캐릭터 삽입하기
(너무 귀엽다)
3-1) Asset Folder 만들기
Image에 위의 두 이미지를 드래그하면?
이미지를 성공적으로 추가했다.
캐릭터를 만들기 위해 역시 MainScene에 Object를 추가하자.
그 후 이름을 rtan으로 변경하자.
그 후 Object가 캐릭터 이미지를 가지도록 하자.
3-2) Sprite Renderer - Sprite 설정
당연히 이런다고 르탄이는 보이지 않는다.
Sprite Renderer의 Order in Layer
를 1
로 변경하자.
바닥과 위치를 맞춰주기 위해
Position을 Y: -2.9
로 바꿔주면……..
(너무 귀엽다)
4. 애니메이션 세팅
삽입한 캐릭터를 움직이게 만들어보자.
같은 방법으로 Assets - Animation
폴더를 만들자.
4-1) Animation 생성
그 후 이름을 rtan_run
으로 바꿔주자.
4-2) Loop Time
Loop Time을 활성화시키지 않으면, 애니메이션이 단 한 번 재생되고 멈춘다.
애니메이션이 계속 나와야하니 Loop Time을 체크하는 것.
4-3) Animation Controller 생성
rtan이라는 이름의 Animation Controller가 생긴 것을 확인할 수 있다.
rtan의 Inspector 보면 Animator라는 컴포넌트가 새로 추가된 것도 알 수 있다.
참고) Animation, Controller, Animator
뭔가 새로운 것이 세 가지가 있다. 정리해보자면
Animation
: 애니메이션 동작 파일
Animation Controller
: 이 애니메이션이 언제 실행되는지 특정 상태(평소, 걸을 때, 맞을 때 등)에 따라 결정해주는 파일
Animator
: Object의 애니메이션을 관리해주는 컴포넌트
Animation rtan_run
을 더블클릭해보자.
다음과 같이 창이 뜬것을 확인할 수 있다.
우리는 아까 추가했던 르탄이1
과 르탄이2
가 번갈아 동작하는 것을 원하기 때문에 이를 설정해주자.
4-4) Animation 설정하기
Animation 편집 창이 켜져있는 상태로
Object rtan
을 더블클릭 해주자.
부가적으로 귀여운 Object rtan
이 Scene에서 확대되어 보여지는 것을 볼 수 있다.
르탄이1 - 르탄이2 - 르탄이1의 순서대로 배치를 하자.
Animation의 재생버튼을 눌러 애니메이션이 잘 나오는지 확인할 수 있다.
5. 캐릭터 움직이기
일단 Visual Studio 세팅부터.
간혹 Visual Studio Code 쓰면 안됨?이라고들 많이 물어보던데, 쓰면 많이 후회한다더라…..
코딩은 주로 Object의 라이프사이클을 다룰 때 많이들 사용하게 될 것이다.
라이프사이클이 뭐냐하면, 다음 문서를 한 번 읽어보는게 제일일 것이다.
읽어보고 이해가 안가는 것이 너무 많지만,
일단 주로 쓰는 Start
와 Update
만 알아놓자.
코드도 마찬가지로 Asset이다.
먼저 Assets 하단에 Scripts
Folder를 먼저 만들자.
5-1) Script 만들기
다음과 같이 이름을 rtan
으로 변경하자.
rtan을 더블클릭하면 Visual Studio가 실행된다.
코드가 어느정도 써있는 모습.
5-2) 캐릭터 움직임 코딩하기
우리가 할 것은 무엇인가?
- 마우스 클릭을 입력받아 클릭할 때마다 방향 전환
- 왼쪽이면 왼쪽으로 가고, 오른쪽이면 오른쪽으로 간다.
- 왼쪽이면 왼쪽을 보게하고, 오른쪽이면 오른쪽을 보게한다.
이정도면 되지 않을까?
일단은 다음 코드를 적용시켜보자.
void Update()
{
// Move Character to Right
transform.position += new Vector3(0.05f, 0, 0);
}
읽어봤을 때 알 수 있는 것은
각 컴포넌트의 요소들 역시 객체니까
transform.position
처럼 코드로 접근이 가능하고,
Position은 Vector3
를 값으로 받을 수 있구나! 정도겠다.
코드를 저장하고, 스크립트를 캐릭터에 적용시켜보자.
실행을 시켜보면 르탄이가 쌩하고 튀어나가는 모습을 볼 수 있다.
(어디까지 가니)
이렇게 transform.position
을 코드를 통해 성공적으로 변경할 수 있었다.
Update()
는 매 프레임마다 실행되는 함수기 때문에
컴퓨터의 사양에 따라 반복해서 실행되는 속도가 달라질 수 있다.
따라서 나는 이를 게임 내의 고정된 시간마다 업데이트를 해주는 FixedUpdate()
를 사용하도록 하겠다.
코드) 벽에 닿으면 방향 전환하기
코드
float direction = 0.05f; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { if (transform.position.x > 2.8f) { direction = -0.05f; } else if (transform.position.x < -2.8f) { direction = 0.05f; } } void FixedUpdate() { // Move Character to Direction transform.position += new Vector3(direction, 0, 0); }
벽에 팅팅 튕기면서 좌우로 왔다갔다 하는 모습을 볼 수 있다.
바라보는 방향은 어떻게 바꿀까?
transform.localScale
의 수정을 통해 가능하다.
코드) 바라보는 방향까지 바꾸기
코드
float direction = 0.05f; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { if (transform.position.x > 2.8f) { direction = -0.05f; transform.localScale = new Vector3(-1, 1, 1); } else if (transform.position.x < -2.8f) { direction = 0.05f; transform.localScale = new Vector3(1, 1, 1); } } void FixedUpdate() { // Move Character to Direction transform.position += new Vector3(direction, 0, 0); }
방향까지 잘 바뀐다.
코드) log로 위치 확인하기
코드
Debug.Log(transform.position.x);
찍힌 로그는 콘솔에서 확인할 수 있다.
마우스 클릭은 어떻게 입력받나?
바로 Input.GetMouseButtonDown(0)
을 통해 가능하다.
유추할 수 있겠지만, 0번이 좌클릭이겠다.
코드) 마우스 입력받아 방향 변경하기
코드
float direction = 0.05f; float toward = 1.0f; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { if (Input.GetMouseButtonDown(0)) { toward *= -1; direction *= -1; } if (transform.position.x > 2.8f) { direction = -0.05f; toward = -1.0f; } else if (transform.position.x < -2.8f) { direction = 0.05f; toward = 1.0f; } transform.localScale = new Vector3(toward, 1, 1); } void FixedUpdate() { // Move Character to Direction transform.position += new Vector3(direction, 0, 0); // print log Debug.Log(transform.position.x); }
이제 클릭을 입력받을 때마다 방향을 바꾸는 아름다운 그림이 완성되었다.
6. 빗방울 만들기
빗방울에 요구되는 기능은 다음과 같다.
- 낙하하다가 땅과 부딪히면 사라짐
- 크기와 점수를 랜덤으로 결정
- 하늘의 랜덤한 위치에서 스폰
- 캐릭터와 부딪히면 점수가 올라감
6-1) 빗방울 세팅하기
간단한 빗방울을 먼저 만들어보자.
색은 150, 150, 255, 255
,
Position은 Y: 4
로 설정하자.
Scene에서 안보이면 역시 Order in Layer 설정으로 해결하자.
빗방울은 떨어지게 하려면 중력의 영향을 받게 해야하는데, 유니티에는 물체의 물리를 구현하게 해주는 방안이 존재한다.
Rigidbody 2D
Rigidbody 2D
Component 는 2D 게임에서
오브젝트에게 질량을 부여하는 컴포넌트이다.
따라서 중력, 충돌 등의 물리적인 컨트롤이 가능해지는 것이다.
실행을 시켜보면 중력의 영향을 받아 떨어지는 것을 확인할 수 있다.
이제 땅에 닿으면 없어지는 것을 구현해보자.
Collider
충돌하려면 조건이 두 가지가 있다.
- 충돌하는 두 물체에 모두
Collider
존재
- 둘 중 하나는
Rigidbody
존재
바닥과 만나면 낙하를 멈춘다.
이 때 땅에 닿으면 없어지게 만들어보자.
Tag를 통한 충돌그룹 관리
땅에 닿았을 때는 사라져야하고, 캐릭터와 닿았을 때는 점수가 올라가면서 사라져야 한다.
충돌의 판정이 다른 것이다.
따라서 충돌 그룹을 나누어 관리할 필요가 있고, 이를 태그를 달아 관리하는 것이다.
Tags의 더하기 버튼을 눌러 Ground라는 이름의 새로운 태그를 만들자.
그 후 Ground의 Inspector에서 Tag를 Ground
로 바꿔주자.
코드로 충돌을 확인해보자.
rain이라는 이름의 스크립트를 Assets - Scripts 폴더에 생성해주자.
코드에서는 충돌 이벤트를
OnCollisionEnter2D(Collision2D coll)
함수로 관리할 수 있고,
이 때 태그는 coll.gameObject.tag
로 접근할 수 있다.
코드) 땅과 충돌 확인 코드
코드
void OnCollisionEnter2D(Collision2D coll) { if (coll.gameObject.tag == "ground") { Debug.Log("땅이다!"); } }
오브젝트에 스크립트 적용시키는 거 잊지말고, 한 번 확인해보자.
이 때 로그 출력이 아닌, 비를 없어지게 만들어보자.
오브젝트 제거는 Destroy()
로 가능하다.
이 때 매개변수로 빗방울을 넣어줘야할텐데,
gameObject
라는 문은 해당 스크립트가 실행되는 오브젝트를 가리킨다.
따라서 Destroy(gameObject)
로 스크립트가 실행되고 있는 오브젝트를 없앨 수 있다.
코드) 오브젝트 제거하기
코드
void OnCollisionEnter2D(Collision2D coll) { if (coll.gameObject.tag == "Ground") { Destroy(gameObject); } }
6-2) 위치와 크기 랜덤하게 설정하기
코드) 랜덤 Position
코드
void Start() { float x = Random.Range(-2.7f, 2.7f); float y = Random.Range(3.0f, 5.0f); transform.position = new Vector3(x, y, 0); }
유니티에서의 랜덤 관리는 생각보다 편하게 되어 있는 것 같다.
따로 구현된 함수들이 좀 있나봄.
코드) 랜덤 사이즈, 컬러 추가
코드
float size; int score; public enum RAINTYPE { TYPE1 = 1, TYPE2 = 2, TYPE3 = 3 } // Start is called before the first frame update void Start() { // set Random Position float x = Random.Range(-2.7f, 2.7f); float y = Random.Range(3.0f, 5.0f); transform.position = new Vector3(x, y, 0); int count = System.Enum.GetValues(typeof(RAINTYPE)).Length; RAINTYPE type = (RAINTYPE)Random.Range(1, count + 1); switch (type) { case RAINTYPE.TYPE1: size = 1.2f; score = 3; GetComponent<SpriteRenderer>().color = new Color(100 / 255f, 100 / 255f, 255 / 255f, 255 / 255f); break; case RAINTYPE.TYPE2: size = 1.0f; score = 2; GetComponent<SpriteRenderer>().color = new Color(130 / 255f, 130 / 255f, 255 / 255f, 255 / 255f); break; case RAINTYPE.TYPE3: size = 0.8f; score = 1; GetComponent<SpriteRenderer>().color = new Color(150 / 255f, 150 / 255f, 255 / 255f, 255 / 255f); break; default: size = 0.0f; score = 0; break; } transform.localScale = new Vector3(size, size, 0); }
Type의 경우 enum
을 사용해서 짜보았다.
6-3) 빗방울 계속 생성하기
GameManager
빗방울을 계속 관리해야하는 오브젝트와 스크립트가 필요하다.
보통 게임 전체를 총괄하는 오브젝트를 GameManager라고 한다.
gameManager
라는 이름의 오브젝트,
gameManager
라는 이름의 스크립트를 각각 생성해주자.
신기하게도 gameManager엔 기본 설정 아이콘이 있다.
연결 설정도 까먹지 말고 하자.
Prefabs
지금은 빗방울을 오브젝트로 만들었는데, 빗방울을 계속 만들어주기 위해 이 오브젝트를 계속해서 복사하는 것이 아니라 오브젝트의 설계도를 통해 복사를 한다.
이 설계도를 Prefab이라 한다.
Prefab 역시 Asset이기 때문에 Assets 폴더에 Prefabs 폴더를 생성 후 rain 오브젝트를 드래그해서 갖다 놔주자.
그렇게 되면 설계도화가 완료되었기 때문에 기존 Object는 삭제해도 상관이 없다.
Property 설정
이렇게 만들어 준 Prefab을 gameManager가 인식할 수 있어야 한다.
gameManager 스크립트에 public GameObject rain;
을 추가시켜주자.
프로퍼티에 public
을 붙여주게 되면
이걸 유니티의 해당 Inspector에서 볼 수 있게 된다.
해당 Rain Property에 rain Prefab을 드래그 해놓으면 설정 완료.
InvokeRepeating
이제 반복을 위해 일정 주기로 함수를 호출해줄 필요가 있겠다.
비내리는 건 makeRain()
이라는 함수가 있다고 치고
InvokeRepeating
을 활용해 반복 함수 호출을 설정해줄 수 있다.
코드
void Start() { InvokeRepeating("makeRain", 0, 0.5f); }
Start()
에서 설정해줘야 함에 유의하자.
Instantiate
이제 만들어진 Prefab으로 Object를 생성하는 함수만 만들면 된다.
이런 기능은 Instantiate
라는 함수로 가능하다.
코드
void makeRain() { Instantiate(rain); }
이렇게 하면 이제 랜덤으로 생성되는 빗방울까지 완성.
7. 점수 UI 만들기
글을 표시하려면 폰트가 있어야할 것 아닌가?
폰트도 애셋이다. Assets 폴더에 Fonts 폴더를 만들고 폰트를 다운 받아 넣어주자.
7-1) Canvas 설정
Canvas가 생성된 것을 확인할 수 있다.
EventSystem까지 같이 생성된 것은 덤.
Text 생성
그 후 이름을 Label-Score
로 변경하자.
Rect Transform이라는 특이한 형태의 Transform Component를 사용 중이다.
이해는 안가지만 일단 오른쪽 위의 점 세개를 눌러 Reset을 한번 해주자.
그 후 Rect Transform의 프로퍼티를 다음과 같이 수정해주자.
Pos Y: -550
Width: 200
Height: 200
그 후 Text 컴포넌트의 프로퍼티를 다음과 같이 수정하자.
Text: 빗방울
Font: 다운 받은 폰트로 변경
Font Style: Bold
Alignment : 모두 가운데
Color: (255, 255, 255, 255)
Label-Score
의 세팅은 끝났다.
이를 세 번 복제한 다음
이름을 각각 Score
, Label-Time
, Time
으로 바꿔주자.
그 후 프로퍼티들을 적절히 변경해 다음과 같이 만들어주자.
7-2) 점수 세팅
점수를 세팅해야하는데, gameManager가 관리하는 것은 마찬가지일테지만, gameManager라는 것은 게임 내에 단 한 개만 존재해야하는 오브젝트이기 때문에, 이를 싱글톤으로 만들어야 한다.
C#에서 공부했던 static
이라고 생각하면 된다.
코드) 싱글톤 화 (static)
코드
public static gameManager I; void Awake() { I = this; }
이렇게 하면 싱글톤 화가 되어 두 가지 이점이 생긴다.
- 오브젝트 중복 생성 방지 (
static
)
- 어디서든 불러올 수 있다. (
public
)
7-3) 캐릭터 충돌 세팅
캐릭터에 tag와 collider 추가
코드) 캐릭터와 충돌 시 점수 상승
rain.cs
코드void OnCollisionEnter2D(Collision2D coll) { if (coll.gameObject.tag == "Ground") { Destroy(gameObject); } else if (coll.gameObject.tag == "rtan") { gameManager.I.addScore(score); Destroy(gameObject); } }
7-4) 점수 표기
UI의 Text가 매번 점수를 반영해줘야 하기 때문에, 이를 세팅해주자.
코드) UI에 점수 반영
gameManager.cs
코드맨 상단에
using UnityEngine.UI;
변수 선언
public Text scoreText;
addScore()
함수public void addScore(int score) { totalScore += score; scoreText.text = totalScore.ToString(); }
그 후 Score의 Property를 다음과 같이 수정하자.
Text: 0
gameManager
의 Score Text
는 Score
오브젝트를 드래그해주자.
닿을 때마다 점수가 올라가는 것을 확인할 수 있다.
8. 게임 끝내기
이제 마지막으로 할 작업은 세 개가 남았다.
- 줄어드는 타이머 설정
- 종료 시 결과 출력
- 다시시작 버튼
8-1) 다시시작 버튼
그 후 이름을 Panel
로 변경하자.
Panel의 하위에 Image 오브젝트를 만들자.
그 후 Image의 프로퍼티를 다음과 같이 변경하자.
Width: 400
Height: 250
Color: (232, 52, 78, 255)
Panel의 하위에 Text 오브젝트를 만들자.
그 후 Text의 프로퍼티는 다음과 같이 변경하자.
Width: 400
Height: 250
Text: 끝! 다시?
Font Style: Bold
Font Size: 80
Alignment: 모두 가운데
Color: (255, 255, 255, 255)
하지만, 게임 시작할 때 Retry UI가 활성화 되어 있으면 안되기 때문에,
Panel
의 상태를 Inactive
로 변경하자.
나중에 다시시작 패널이 튀어나와야할 때, 해당 Object를 active로만 만들어주면 되는 것이다.
8-2) 줄어드는 타이머 설정
게임에서 흐르는 시간은 Time.deltaTime
을 사용한다.
Time.deltaTime
은 한 프레임이 흐르는데 몇 초가 걸렸는지를 알려주기 때문에 이를 이용해 시간의 흐름을 표현할 수 있게 된다.
코드) 시간이 줄어들도록 세팅
gameManager.cs 코드
float limit = 60f; void Update() { limit -= Time.deltaTime; timeText.text = limit.ToString("N2"); }
참고로
ToString()
에 들어간“N2”
는 콤마표기 + 소수점 자리수 둘째자리까지 표기이다.
이제 시간이 잘 줄어드는 것을 확인할 수 있다.
하지만 아직 0이 되었을 때 타이머를 멈추고, 게임을 종료하고, 다시하기 버튼을 띄우기 위한 설정을 전혀 해주지 않았다.
코드) 타이머 멈추기
gameManager.cs 코드
void Update() { limit -= Time.deltaTime; if (limit < 0) { Time.timeScale = 0.0f; limit = 0.0f; } timeText.text = limit.ToString("N2"); }
Time.timeScale
은 시간의 흐름 속도 조절을 하는 기능이다. 기본 값은 1.0f로 되어있으며, 2.0f로 바꿀 경우 세상이 두 배로 빠르게 시간이 흐르게 된다.불릿 타임과 같이 시간을 느리게 만드는 연출을 할 수도 있고, 위 코드처럼 0을 넣어주면 시간이 아예 흐르지 않게 할 수 있다.
이 후 게임을 실행해보면 타이머가 0이 되면, 타이머는 물론이고 빗방울까지 멈춘다.
캐릭터는 멈추지 않는데, 이에 대해 게시글로 따로 빼서 서술했으니 참고하자.
8-3) 다시하기 패널 띄우기
코드) 패널 활성화
gameManager.cs 코드
public GameObject panel; void Update() { limit -= Time.deltaTime; if (limit < 0) { Time.timeScale = 0.0f; limit = 0.0f; panel.SetActive(true); } timeText.text = limit.ToString("N2"); }
역시 유니티에서 오브젝트 연결하는 것까지 잊지말자.
이렇게 설정해주면 다시하기 패널이 성공적으로 뜬다.
8-4) 다시시작 버튼 기능 만들기
이제 버튼을 누르면 기능이 동작하도록 만들어줘야 한다.
그 후 다시하기 함수를 만들어주자.
코드) 다시하기 함수
gameManager.cs 코드
using UnityEngine.SceneManagement; public void retry() { SceneManager.LoadScene("MainScene"); }
문제는 gameManager 스크립트를 패널에서 불러올 수가 없다는 점이다.
따라서 gameManager 스크립트를 받아올 수 있는 다른 스크립트를 만들어, 이를 panel과 연결해주도록 하겠다.
panel.cs 코드
public void retry() { gameManager.I.retry(); }
그 후 함수를 연결해주자.
이제 버튼을 누르면 씬이 다시 로딩되도록 수정을 했다.
씬을 다시 로딩해도 게임이 그대로일텐데, 문제는 씬이 로딩되어도 gameManager의 변수는 그대로 남아 있기 때문이다.
이를 초기화하는 변수를 따로 작성하자
코드) gameManager 변수 초기화
gameManager.cs 코드
void initGame() { Time.timeScale = 1.0f; totalScore = 0; limit = 60.0f; }
이렇게 게임은 완성!
9. 숙제 - 빨간 빗방울 만들기
- 사이즈는
0.8
로 해주세요!
- 색은
new Color(255 / 255.0f, 100.0f / 255.0f, 100.0f / 255.0f, 255.0f / 255.0f);
이렇게!
enum으로 미리 만들어서 그런가 추가가 매우 쉽다.
rain.cs만 수정해서 해당 기능을 구현하자.
코드) 빨간 빗방울 추가
rain.cs 코드
public enum RAINTYPE { TYPE1 = 1, TYPE2 = 2, TYPE3 = 3, TYPE4 = 4 } // Start is called before the first frame update void Start() { // set Random Position float x = Random.Range(-2.7f, 2.7f); float y = Random.Range(3.0f, 5.0f); transform.position = new Vector3(x, y, 0); int count = System.Enum.GetValues(typeof(RAINTYPE)).Length; Debug.Log($"{count}"); RAINTYPE type = (RAINTYPE)Random.Range(1, count + 1); switch (type) { case RAINTYPE.TYPE1: size = 1.2f; score = 3; GetComponent<SpriteRenderer>().color = new Color(100f / 255f, 100f / 255f, 255f / 255f, 255f / 255f); break; case RAINTYPE.TYPE2: size = 1.0f; score = 2; GetComponent<SpriteRenderer>().color = new Color(130f / 255f, 130f / 255f, 255f / 255f, 255f / 255f); break; case RAINTYPE.TYPE3: size = 0.8f; score = 1; GetComponent<SpriteRenderer>().color = new Color(150f / 255f, 150f / 255f, 255f / 255f, 255f / 255f); break; case RAINTYPE.TYPE4: size = 0.8f; score = -5; GetComponent<SpriteRenderer>().color = new Color(255f / 255f, 100f / 255f, 100f / 255f, 255f / 255f); break; default: size = 0.0f; score = 0; break; } transform.localScale = new Vector3(size, size, 0); }
빨간 빗방울을 성공적으로 추가했다.
Uploaded by N2T
'개발 > Unity 내일배움캠프 TIL' 카테고리의 다른 글
[내일배움단] Unity TIL 2. 풍선을 지켜라 (0) | 2023.08.01 |
---|---|
Unity - Update() vs FixedUpdate() (0) | 2023.07.28 |
[내일배움단] 사전캠프 C# TIL 8. 알아두면 좋을 것들 (0) | 2023.07.25 |
[내일배움단] 사전캠프 C# TIL 7. 클래스와 객체 (0) | 2023.07.21 |
[내일배움단] 사전캠프 C# TIL 6. 함수 (0) | 2023.07.21 |