본문 바로가기
개발/Unity 내일배움캠프 TIL

[내일배움단] Unity TIL 1. 빗방울 모으기 게임

by 석시 2023. 7. 25.



1. 배경 세팅하기

1-1) 메인 씬 이름 바꾸기

(Project 탭에서) → Scenes → SampleScene 우클릭 → Rename (또는 SampleScene 클릭 후 F2)

변경할거냐 확인 후 엔터

다음과 같이 Hierarchy에서도 바뀐 것을 알 수 있다.

1-2) 화면 비율 변경

Game 탭에서 Aspect 토글 박스를 통해 가능

Game 탭에서 Free Aspect 박스 클릭 → 더하기 버튼 (Add New Item) 클릭

다음과 같이 입력 Label : Phone Width & Height : 760x1280

1-3) 배경 색깔 입히기

(Hierarchy 탭에서) GameObject → 2D Object → Sprites → Square 클릭

Background로 이름 바꿔주고 색을 변경하자.

Hierarchy에서 Background를 클릭하면 Inspector에 관련 정보와 Component를 볼 수 있다!

(Background 클릭 후 Inspector 탭에서) Sprite Renderer Component → Color 클릭

그리고 나서 색을 255, 255, 220, 255로 맞추자.

(Background 클릭 후 Inspector 탭에서) Transform Component → Scale

X: 6, Y: 10으로 맞추면?

정확히 카메라에 꽉 차게 된다.

2. UI 박스 만들기

UI 박스는 두 가지 기능이 있다.

  • 점수 표시 기능
  • 빗물이 닿으면 없어지게 하는 기능

배경과 똑같이 만들되, 이름은 Ground로 만들자. 색은 50, 50, 50, 255, Scale은 X: 6, Y: 1.5로 만들자.

2-1) Transform Component - Position 조정

추가로 Position을 조정하자.

(Ground 클릭 후 Inspector 탭에서) Transform Component → Position

Poisition을 Y: -4.3으로 수정하면?

2-2) Sprite Renderer Component - Order in Layer 설정

(Background 클릭 후 Inspector 탭에서) Sprite Renderer Component → Order in Layer

Background와 Ground의 앞뒤 순서를 확실히 하기 위해 Order in Layer를 1로 수정하자.

3. 캐릭터 삽입하기

(너무 귀엽다)

3-1) Asset Folder 만들기

(Project 탭에서) Assets 우클릭 → Create → Folder

Image에 위의 두 이미지를 드래그하면?

이미지를 성공적으로 추가했다.

캐릭터를 만들기 위해 역시 MainScene에 Object를 추가하자.

(Hierarchy 탭에서) MainScene 우클릭 → GameObject → 2D Object → Sprites → Square

그 후 이름을 rtan으로 변경하자.

그 후 Object가 캐릭터 이미지를 가지도록 하자.

3-2) Sprite Renderer - Sprite 설정

(rtan 클릭 후 Inspector 활성화 상태에서) Project탭 → Images → 르탄이1을 Inspector탭 → Sprite Renderer → Sprite로 드래그

당연히 이런다고 르탄이는 보이지 않는다.

Sprite Renderer의 Order in Layer1로 변경하자.

바닥과 위치를 맞춰주기 위해 Position을 Y: -2.9로 바꿔주면……..

(너무 귀엽다)

4. 애니메이션 세팅

삽입한 캐릭터를 움직이게 만들어보자.

같은 방법으로 Assets - Animation 폴더를 만들자.

4-1) Animation 생성

(Project 탭에서) Assets - Animation 우클릭 → Create → Animation

그 후 이름을 rtan_run으로 바꿔주자.

4-2) Loop Time

(rtan_run의 Inspector 탭에서) Loop Time에 체크

Loop Time을 활성화시키지 않으면, 애니메이션이 단 한 번 재생되고 멈춘다.

애니메이션이 계속 나와야하니 Loop Time을 체크하는 것.

4-3) Animation Controller 생성

(Project 탭에서) rtan_run을 Hierarchy 탭의 rtan 오브젝트에 드래그

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를 Animation 편집창의 타임라인에 번갈아 배치

르탄이1 - 르탄이2 - 르탄이1의 순서대로 배치를 하자.

Animation의 재생버튼을 눌러 애니메이션이 잘 나오는지 확인할 수 있다.

5. 캐릭터 움직이기

일단 Visual Studio 세팅부터.

상단 탭 Edit → Preferences → External Tools → External Script Editor를 지정

간혹 Visual Studio Code 쓰면 안됨?이라고들 많이 물어보던데, 쓰면 많이 후회한다더라…..

코딩은 주로 Object의 라이프사이클을 다룰 때 많이들 사용하게 될 것이다.

라이프사이클이 뭐냐하면, 다음 문서를 한 번 읽어보는게 제일일 것이다.

이벤트 함수의 실행 순서 - Unity 매뉴얼
Unity 스크립트를 실행하면 사전에 지정한 순서대로 여러 개의 이벤트 함수가 실행됩니다. 이 페이지에서는 이러한 이벤트 함수를 소개하고 실행 시퀀스에 어떻게 포함되는지 설명합니다.
https://docs.unity3d.com/kr/2019.4/Manual/ExecutionOrder.html

읽어보고 이해가 안가는 것이 너무 많지만, 일단 주로 쓰는 StartUpdate만 알아놓자.

코드도 마찬가지로 Asset이다.

먼저 Assets 하단에 Scripts Folder를 먼저 만들자.

5-1) Script 만들기

(Project 탭에서) Assets - Scripts 우클릭 → Create → C# Script

다음과 같이 이름을 rtan으로 변경하자.

rtan을 더블클릭하면 Visual Studio가 실행된다.

코드가 어느정도 써있는 모습.

5-2) 캐릭터 움직임 코딩하기

우리가 할 것은 무엇인가?

  1. 마우스 클릭을 입력받아 클릭할 때마다 방향 전환
  1. 왼쪽이면 왼쪽으로 가고, 오른쪽이면 오른쪽으로 간다.
  1. 왼쪽이면 왼쪽을 보게하고, 오른쪽이면 오른쪽을 보게한다.

이정도면 되지 않을까?

일단은 다음 코드를 적용시켜보자.

void Update()
{
    // Move Character to Right
    transform.position += new Vector3(0.05f, 0, 0);
}

읽어봤을 때 알 수 있는 것은 각 컴포넌트의 요소들 역시 객체니까 transform.position처럼 코드로 접근이 가능하고, Position은 Vector3를 값으로 받을 수 있구나! 정도겠다.

코드를 저장하고, 스크립트를 캐릭터에 적용시켜보자.

(Project 탭에서) Assets - Scripts → rtan을 Hierarchy 탭의 Object rtan으로 드래그하자

실행을 시켜보면 르탄이가 쌩하고 튀어나가는 모습을 볼 수 있다.

(어디까지 가니)

이렇게 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);

찍힌 로그는 콘솔에서 확인할 수 있다.

Ctrl + Shift + C로 콘솔 창을 확인할 수 있다.

마우스 클릭은 어떻게 입력받나?

바로 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. 빗방울 만들기

빗방울에 요구되는 기능은 다음과 같다.

  1. 낙하하다가 땅과 부딪히면 사라짐
  1. 크기와 점수를 랜덤으로 결정
  1. 하늘의 랜덤한 위치에서 스폰
  1. 캐릭터와 부딪히면 점수가 올라감

6-1) 빗방울 세팅하기

간단한 빗방울을 먼저 만들어보자.

(Hierarchy 탭에서) 우클릭→ 2D Object → Sprites → Circle 클릭

색은 150, 150, 255, 255, Position은 Y: 4로 설정하자.

Scene에서 안보이면 역시 Order in Layer 설정으로 해결하자.

빗방울은 떨어지게 하려면 중력의 영향을 받게 해야하는데, 유니티에는 물체의 물리를 구현하게 해주는 방안이 존재한다.

Rigidbody 2D

Rigidbody 2D Component 는 2D 게임에서 오브젝트에게 질량을 부여하는 컴포넌트이다.

따라서 중력, 충돌 등의 물리적인 컨트롤이 가능해지는 것이다.

(rain의 Inspector 탭에서) Add Component → Rigidbody 2D 검색 후 추가

실행을 시켜보면 중력의 영향을 받아 떨어지는 것을 확인할 수 있다.

이제 땅에 닿으면 없어지는 것을 구현해보자.

Collider

(rain의 Inspector 탭에서) Add Component → Circle Collider 2D 검색 후 추가
(Ground의 Inspector 탭에서) Add Component → Box Collider 2D 검색 후 추가

충돌하려면 조건이 두 가지가 있다.

  1. 충돌하는 두 물체에 모두 Collider 존재
  1. 둘 중 하나는 Rigidbody 존재

바닥과 만나면 낙하를 멈춘다.

이 때 땅에 닿으면 없어지게 만들어보자.

Tag를 통한 충돌그룹 관리

땅에 닿았을 때는 사라져야하고, 캐릭터와 닿았을 때는 점수가 올라가면서 사라져야 한다.

충돌의 판정이 다른 것이다.

따라서 충돌 그룹을 나누어 관리할 필요가 있고, 이를 태그를 달아 관리하는 것이다.

(Ground의 Inspector 탭에서) Tag 토글 박스 클릭 → Add 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 폴더를 만들고 폰트를 다운 받아 넣어주자.

http://pop.baemin.com/fonts/hanna11yrs/BMHANNA_11yrs_ttf.ttf

7-1) Canvas 설정

(Hierarchy 탭에서) 우클릭 → UI → Canvas

Canvas가 생성된 것을 확인할 수 있다.

EventSystem까지 같이 생성된 것은 덤.

Text 생성

(Hierarchy 탭에서) Canvas 우클릭 → UI → Legacy → 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;
    }

이렇게 하면 싱글톤 화가 되어 두 가지 이점이 생긴다.

  1. 오브젝트 중복 생성 방지 (static)
  1. 어디서든 불러올 수 있다. (public)

7-3) 캐릭터 충돌 세팅

캐릭터에 tag와 collider 추가

(rtan Inspector에서) Tag 토글박스 클릭 → Tag 추가 → rtan 그 후 rtan의 Tag를 추가한 tag으로 설정
(rtan Inspector에서) Add Component → Circle Collider 2D

코드) 캐릭터와 충돌 시 점수 상승

  • 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

gameManagerScore TextScore 오브젝트를 드래그해주자.

닿을 때마다 점수가 올라가는 것을 확인할 수 있다.

8. 게임 끝내기

이제 마지막으로 할 작업은 세 개가 남았다.

  1. 줄어드는 타이머 설정
  1. 종료 시 결과 출력
  1. 다시시작 버튼

8-1) 다시시작 버튼

(Hierarchy 탭에서) Canvas에 우클릭 → UI → Canvas

그 후 이름을 Panel로 변경하자.

Panel의 하위에 Image 오브젝트를 만들자.

(Hierarchy 탭에서) Panel에 우클릭 → UI → Image

그 후 Image의 프로퍼티를 다음과 같이 변경하자.

Width: 400 Height: 250 Color: (232, 52, 78, 255)

Panel의 하위에 Text 오브젝트를 만들자.

(Hierarchy 탭에서) Panel에 우클릭 → UI → Legacy → 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이 되면, 타이머는 물론이고 빗방울까지 멈춘다.

캐릭터는 멈추지 않는데, 이에 대해 게시글로 따로 빼서 서술했으니 참고하자.

[내일배움단] Unity - Update() vs FixedUpdate()
이걸 글로 따로 정리하게 된 이유는 이해가 안갔던 상황이 너무 많았기 때문이다. 바로 Time.timeScale에 관한 부분이었다.강의에서 게임을 정지하기 위해 Time.timeScale = 0; 다음과 같이 timeScale을 0으로 만들어 줬는데, 어떤 건 정지하고, 어떤 건 정지하지 않고 여전히 움직이는 이상한 상황. 문제의 장면.빗방울과 타이머는 멈췄지만, 캐릭터는 멈추지 않았다.골때리는건, 내가 따라 만든 게임은 캐릭터도 멈춰있는 것이었다. 원인을 찾아보니 차이점이 있었다.강의에서 캐릭터의 움직임을 구현할 때는 Update() 함수를 사용하였고,내가 이것을 따라만들 때는 FixedUpdate() 함수를 사용하였던 것이다. 이걸 보고 바로 알 수 있는 점은, ”Time.timeScale은 Upda..
https://seoksii.tistory.com/12

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) 다시시작 버튼 기능 만들기

이제 버튼을 누르면 기능이 동작하도록 만들어줘야 한다.

(Panel의 Inspector 에서) Add Component → Button 추가

그 후 다시하기 함수를 만들어주자.

코드) 다시하기 함수

  • gameManager.cs 코드
    using UnityEngine.SceneManagement;
    
    public void retry()
    {
        SceneManager.LoadScene("MainScene");
    }

문제는 gameManager 스크립트를 패널에서 불러올 수가 없다는 점이다.

따라서 gameManager 스크립트를 받아올 수 있는 다른 스크립트를 만들어, 이를 panel과 연결해주도록 하겠다.

  • panel.cs 코드
    public void retry()
    {
        gameManager.I.retry();
    }

그 후 함수를 연결해주자.

(Panel의 Inspector 에서) Button → On Click () 함수 추가 → Object: Panel로 설정 후 retry() 함수 지정

이제 버튼을 누르면 씬이 다시 로딩되도록 수정을 했다.

씬을 다시 로딩해도 게임이 그대로일텐데, 문제는 씬이 로딩되어도 gameManager의 변수는 그대로 남아 있기 때문이다.

이를 초기화하는 변수를 따로 작성하자

코드) gameManager 변수 초기화

  • gameManager.cs 코드
    void initGame()
    {
        Time.timeScale = 1.0f;
        totalScore = 0;
        limit = 60.0f;
    }

이렇게 게임은 완성!

9. 숙제 - 빨간 빗방울 만들기

📄
맞으면 -5 점이 되는 빨간(rgb = 255,100,255) 빗방울을 만들어보세요!
  • 사이즈는 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