상세 컨텐츠

본문 제목

0823 TIL - 빗물 받는 르탄 제작 _2

스파르타 코딩캠프/'24 Today I Learned

by lucar 2024. 8. 23. 18:04

본문

어제에 이어서 빗물 받는 르탄을 이어서 제작해보도록 하겠다.

 

어제는 Rtan.cs파일을 만드는 것 까지 진행했고 오늘은 본격적으로 이동 및 점수 구현과 완료까지 목표로 진행해보겠다.

 

    // Update is called once per frame
    void Update()
    {
        transform.position += Vector3.right;
    }
}

 

void update() 함수에 오른쪽으로 이동하는 문구를 넣으면 매 프레임마다 오른쪽으로 이동하게 될 것이다.

 

실행시켜보면?

뭐가 지나갔나?

눈에 보이지 않을 정도로 엄청난 속도로 이동한다...

속도에 제한을 걸어보자

 

    void Start()
    {
        Application.targetFrameRate = 60;
    }

void Start() 함수에 프레임을 60으로 제한 시켜놨다.

이대로 실행하면?

 

느려졌나...?

아직도 순간이동하는 르탄이에게 족쇄를 달아보자

 

transform.position += Vector3.right * 0.05f;

 

오른쪽으로 움직이는 값에 0.05를 플로트 값으로 곱해주었다.

 

실행하면?

드디어 눈에 보이는 속도로 이동

이제 벽을 찍으면 다시 돌아오게 만들어보자.

 

르탄이가 X가 2.6일때 벽에 도달하니 X값이 2.6이 되면 방향을 바꿔보자.

public class Rtan : MonoBehaviour
{

    float direction = 0.05f; //방향 및 속도 변수 선언

    void Start()
    {
        Application.targetFrameRate = 60;
    }

    void Update()
    {
        transform.position += Vector3.right * direction; //direction 변수 값 방향으로 움직임
        if (transform.position.x > 2.6f) //X값의 위치가 2.6 보다 크다면
        {
            direction = -0.05f; //왼쪽으로 이동
        }
        if (transform.position.x < -2.6f) //X값의 위치가 -2.6 보다 작다면
        {
            direction = 0.05f; //오른쪽으로 이동
        }
    }
}

내친 김에 반대 쪽도 도달하면 돌아오게 하자

실행해보면?

 

잘 작동한다.

벽을 찍고나서 반대로 돌아보게 만들어보자.

Hierarchy에 Flip X를 체크하면 좌우 반전이 된다.

그러면 스크립트 파일에서 Flip X를 체크해보자.

우리가 위치를 변경할 때 Transform에 있는 포지션 X 값을 건드렸으니

 

이번엔 SpriteRenderer에 있는 Flip 값의 체크를 활성화 해주면 될 것이다.

Flip이.. 없어...?

하지만 플립은 존재하지 않는다.

알고보니 다른 정보들은 모두  변수를 선언하고 정보를 Get으로

불러와야한다고 한다.

 

public class Rtan : MonoBehaviour
{
    SpriteRenderer renderer; //Sprite Rendere를 renderer 변수로 선언
    float direction = 0.05f; //방향 및 속도 변수 선언
    

    void Start()
    {
        Application.targetFrameRate = 60;
        renderer = GetComponent<SpriteRenderer>(); //renderer에 SpriteRenderer 설정 값을 불러옴
    }

    void Update()
    {

        transform.position += Vector3.right * direction;
        if (transform.position.x > 2.6f) //X값의 위치가 2.6 보다 크다면
        {
            direction = -0.05f; //왼쪽으로 이동
            renderer.flipX = true; //오른쪽 벽에 도달하면 왼쪽을 바라보게 뒤집음
        }
        if (transform.position.x < -2.6f) //X값의 위치가 -2.6 보다 작다면
        {
            direction = 0.05f; //오른쪽으로 이동
            renderer.flipX = false; // 왼쪽 벽에 도달하면 오른쪽을 바라보게 뒤집음
        }
    }
}

 

코드를 작성 후 실행하면?

잘 작동한다.

 

이제 클릭하면 방향을 바꾸게 추가해보자.

if (Input.GetMouseButtonDown(0)) //마우스의 0번키가 입력되었을때 (왼쪽 마우스버튼)
{
    direction *= -1; //-1을 곱해서 음수를 양수로, 양수는 음수로 바꿔준다.
    renderer.flipX = !renderer.flipX; //!로 Not을 입력하여 참,거짓 값을 바꿔줌
}

void Update() 함수에 조건문을 추가해준다.

마우스가 입력되었을 때 방향과 플립 값을 반대로 변경하는 구문

실행해보면?

정상적으로 잘 작동한다.

 

이제 하늘에서 떨어지는 빗방울을 구현해보자.

 

2D스프라이트 중 Circle을 추가하고 위치와 색상을 설정했다.

컴포넌트를 추가해서 바닥에 떨어질 수 있도록 중력을 구현해준다.

 

이대로 실행하면 원이 바닥을 뚫고 내핵까지 내려가니 바닥에 부딪히게 Circle Collider를 추가해주자

마찬가지로 땅에도 콜라이더를 추가해준다.

이대로 실행해보자.

이제 Rain에 스크립트를 붙여서 땅에 붙으면 사라지게 만들어보자

 

public class Rain : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {

    }

    private void OnCollisionEnter2D(Collision2D collision) //충돌 메커니즘 불러오기
    {
        if (collision.gameObject.name == "Ground") //Ground 객체와 부딪힌다면
        {
            Destroy(collision.gameObject); //충돌한 물체를 파괴시켜라
        }
    }

실행해보면?

 

이 친구는 내핵까지 닿는 모든것을 파괴할꺼에요

땅이 부서져버렸다.

코드를 수정하자.

private void OnCollisionEnter2D(Collision2D collision) //충돌 메커니즘 불러오기
{
    if (collision.gameObject.name == "Ground") //Ground 객체와 부딪힌다면
    {
        Destroy(this.gameObject); //이걸 파괴시킨다
    }
}

 

 

드디어 정상 작동되는 것을 확인 할 수 있다.

 

추후 코딩할 때 다른 땅 지역에도 모두 Ground라는 이름을 붙일 수 없으니 태그로 정리해보자.

 Tag 버튼을 눌러 Add Tag를 누르고 Ground 태그를 추가해주었다.

 

이제 Ground 태그를 가진 물체와 충돌한다면 으로 코드를 변경해보자.

private void OnCollisionEnter2D(Collision2D collision) //충돌 메커니즘 불러오기
{
    if (collision.gameObject.CompareTag("Ground")) //Ground 태그를 가진 객체와 부딪힌다면
    {
        Destroy(this.gameObject); //이걸 파괴시킨다
    }
}

 

이제 빗방울이 랜덤한 위치에서 랜덤한 크기로 떨어질 수 있게 변경해보자.

void Start()
{
    float x = UnityEngine.Random.Range(-2.4f, 2.4f);
    float y = UnityEngine.Random.Range(3.0f, 5.0f);

    transform.position = new Vector3(x, y, 0);
}

 

void Start함수에 랜덤한 포지션을 입력 받아 position에 입력해주자.

이제 X값은 -2.4~2.4 Y값은 3~5 범위 내의 무작위 장소에서 물방울이 생성될 것이다.

 

크기랑 색깔도 3가지 타입으로 나눠주었다.

if(type == 1)
{
    float size = 0.8f;
    int score = 1;
    renderer.color = new Color(100 / 255f, 100 / 255f, 1f, 1f);
    
}

else if(type == 2)
{
    float size = 1.0f;
    int score = 2;
    renderer.color = new Color(130 / 255f, 130 / 255f, 1f, 1f);
}

else if(type == 3)
{
    float size = 1.0f;
    int score = 3;
    renderer.color = new Color(150 / 255f, 150 / 255f, 1f, 1f);
}
transform.localScale = new Vector3(size, size, 0);

 

이제 실행 할 때마다 랜덤한 위치에서 랜덤한 크기와 점수를 가진 물방울이 떨어지겠지만 아직은 물방울이 하나 밖에 내려오지 않는다.

 

 

우선 GameManager라는 객체와 스크립트를 작성해 연동하고 Prefabs 폴더를 제작해 Rain 객체를 폴더 안에 넣어주었다.

 

public class GameManager : MonoBehaviour
{
    public GameObject rain; //게임매니저 스크립트에 새로운 변수 생성

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

 

게임오브젝트라는 자료형으로 rain을 생성하면 Unity 화면에

해당 컴포넌트가 업데이트 되는데 이곳에 Prefabs안의 rain을 드래그 앤 드롭 해주자

 

그 후 게임매니저 스크립트에서 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public GameObject rain;

    // Start is called before the first frame update
    void Start()
    {
        Application.targetFrameRate = 60; //60 프레임으로 고정
    }

    // Update is called once per frame
    void Update()
    {
        MakeRain(); //함수 실행
    }

    void MakeRain() //함수를 만들면 함수명만 불러오는 것으로 내용을 실행 가능
    {
        Instantiate(rain); //rain 오브젝트를 생성함
    }
}

작성 후 실행해보자

 

지구멸망 3초 전

이번엔 비가 너무 많이 내린다. 1초가 60프레임이니 빗방울이 초당 60개가 쏟아지기 시작했다.

방법을 바꿔서

 

void Start()
{
    InvokeRepeating("MakeRain", 0f, 0.5f); //MakeRain함수를 0초부터 0.5초마다 생성
}

void Start 함수에 InvokeRepeating함수를 넣어서 MakeRain이라는 이름을 가진 함수를 0.5초마다 실행하게 만들어보자

 

실행하면?

이제 좀 게임 같아졌다.

이제 점수와 시간 제한을 걸고 게임을 완성시켜보자

 

UI Text 객체를 추가해주고

 

각각의 위치에 배치 후 글자나 숫자를 입력해준다.

 

다시 게임매니저를 켜고

public static GameManager instance;

private void Awake()
{
    instance = this;
}

클래스 내부에 싱글톤 스크립트를 작성해 다른 곳에서도 참조할 수 있게 만들어준다.

 

public void AddScore(int score) //매개변수로 숫자를 받아옴
{
    totalScore += score; //score값을 totalScore에 더한다.
    Debug.Log(totalScore);
}

그리고 게임매니저 안에 AddScore 함수를 추가해줬다.

르탄이가 물방울에 닿으면 점수를 얻을 수 있게 충돌박스와 태그를 추가해준다.

 

Rain 스크립트에 플레이어 태그를 가진 물체와 부딪히면 점수를 주는 스크립트를 추가한다.

private void OnCollisionEnter2D(Collision2D collision) //충돌 메커니즘 불러오기
{
    if (collision.gameObject.CompareTag("Ground")) //Ground 객체와 부딪힌다면
    {
        Destroy(this.gameObject); //이걸 파괴시킨다
    }
    if(collision.gameObject.CompareTag("Player")) //Player 객체와 부딪힌다면
    {
        Destroy(this.gameObject); //이걸 파괴시킨다.
        GameManager.instance.AddScore(score); //GameManager.cs 파일 안의
                                              //AddScore 함수를 불러와서
                                              //score를 매개변수로 함수 안에 넣어준다.
    }
}

 

이제 얻은 점수를 UI에 반영해보자

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI; //유니티 엔진의 UI에 접근

public class GameManager : MonoBehaviour
{
    public GameObject rain;
    public static GameManager instance;
    public Text totalScoreTxt; //게임매니저에 Text 컴포넌트를 추가
public void AddScore(int score) //매개변수로 숫자를 받아옴
{
    totalScore += score;
    totalScoreTxt.text = totalScore.ToString(); //숫자 자료형을 ToString 함수를 통해 문자열 자료형으로 변경
}

아래에 있는 함수 부분에 토탈스코어 값을 UI에 표시할 수 있도록 코드를 작성해준다.

 

이제 게임매니저 컴포넌트 안에 Score UI를 드래그 앤 드롭해주고 실행해보면?

 

UI가 정상적으로 추가되는걸 확인 할 수 있다.

 

이제 제한 시간만 추가해주면 게임은 완성될 것 같다.

 

그 전에 종료 UI를 만들어보자

이미지와 텍스트 UI를 추가해 주고 제일 우측 상단의 체크박스를 해제하면 보이지 않게된다.

 

public class GameManager : MonoBehaviour
{
    public GameObject rain;
    public static GameManager instance;
    public Text totalScoreTxt;
    public Text timeTxt; //시간 UI 컴포넌트 추가

    int totalScore = 0;

    float totalTime = 30.0f; //시간 제한은 30초


    private void Awake()
    {
        instance = this;
    }

    // Start is called before the first frame update
    void Start()
    {
        Application.targetFrameRate = 60;
        InvokeRepeating("MakeRain", 0f, 0.5f);
    }

    // Update is called once per frame
    void Update()
    {
        totalTime -= Time.deltaTime; //deltaTime은 기기마다 프레임이 다르지만 모두 같은 시간값을 가지게 해준다.
        timeTxt.text = totalTime.ToString("N2"); //N2를 입력하여 소숫점 2자리까지만 입력
    }

    void MakeRain()
    {
        Instantiate(rain);
    }

    public void AddScore(int score)
    {
        totalScore += score;
        totalScoreTxt.text = totalScore.ToString();
    }
}

작성 후 실행해보면 시간이 0 아래로 계속 내려가는 걸 확인 할 수 있다.

조금만 더 수정해보자

 

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
    public GameObject rain;
    public GameObject endPanel; //엔드 패널 UI 컴포넌트 추가

    public static GameManager instance;

    public Text totalScoreTxt;
    public Text timeTxt;


    int totalScore = 0;

    float totalTime = 30.0f;


    private void Awake()
    {
        instance = this;
    }

    // Start is called before the first frame update
    void Start()
    {
        Application.targetFrameRate = 60;
        InvokeRepeating("MakeRain", 0f, 0.5f);
    }

    // Update is called once per frame
    void Update()
    {
        if(totalTime > 0) //시간이 0보다 크다면 실행
        {
            totalTime -= Time.deltaTime;
        }
        else
        {
            totalTime = 0; //토탈타임 변수 값을 0으로 초기화
            endPanel.SetActive(true); // 엔드패널을 활성화
            Time.timeScale = 0f; //시간을 정지함
        }

        timeTxt.text = totalTime.ToString("N2");

    }

    void MakeRain()
    {
        Instantiate(rain);
    }

    public void AddScore(int score)
    {
        totalScore += score;
        totalScoreTxt.text = totalScore.ToString();
    }
}

 

실행 해보면 시간이 0초일 때 게임이 정지되고 엔드패널이 뜨는 것까지 확인했다.

이제 엔드 패널을 클릭함으로써 다시 시작할 수 있게 해보자

 

엔드 패널에 버튼 컴포넌트를 추가하고 Retry 스크립트를 추가해준다.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement; //씬매니저를 호출할 수 있게 됨

public class RetryButton : MonoBehaviour
{
    public void Retry()
    {
        SceneManager.LoadScene("MainScene"); //메인 씬을 로드
    }
}

스크립트를 작성해주고

온 클릭부분에 엔드패널을 추가해 준 후 리트라이 함수를 불러오게 설정한다.

그리고 버튼을 눌러보면?

다시 시작은 됐으나 빗방울이 떨어지지 않고 시간도 내려가지 않는다..

타임 스케일이 0이 되어서 시간이 완전 정지되었다.

private void Awake()
{
    instance = this;
    Time.timeScale = 1.0f; //다시 시간을 1.0의 속도로 흐르게 설정
}

게임매니저 어웨이크 부분에 타임스케일 1.0f를 추가해주고 재실행하면 정상적으로 실행되는 것을 확인할 수 있다.

 

자 이렇게 빗물 받는 르탄 프로젝트가 종료되었다.

 

관련글 더보기