Unity - 1주차 떨어지는 사과 먹기

Amberjack·2023년 11월 30일
0

Unity

목록 보기
2/44
post-custom-banner

🍎 떨어지는 사과 먹기

👑 기본 룰 :
1. 제한 시간 안에 떨어지는 사과 먹기
2. 사과는 4개의 타입이 있다.
👉 타입 1, 2, 3: 크기 별로 나뉘며 클 수록 점수가 높다.
😱 타입 4: 병든 사과로 먹을 시 점수가 차감된다.

🤔 생각해보기

❓ 구현해야 할 것 :

  1. 사과
    👉 랜덤한 위치에서 랜덤한 종류의 사과 떨어트리기
    👉 캐릭터에 부딪히면 점수 업데이트 및 사라지기
    👉 땅에 부딪히면 사라지기
  2. 사과를 받아먹을 캐릭터
  3. 캐릭터가 돌아다닐 땅
  4. 제한 시간
  5. 제한 시간 초과 시 종료 화면
  6. 재시작 기능
  7. 점수

❗작업 순서 :

  1. 땅을 만들기
  2. 캐릭터 만들기
  3. 사과 만들기
  4. 사과의 충돌 효과 구현하기
  5. 사과 랜덤한 위치, 랜덤한 크기로 생성하기
  6. 사과를 계속 생성하기
  7. 점수 구현하기
  8. 제한시간 구현하기
  9. 게임 종료 및 재시작 구현하기

📌 유니티 기본 화면 설정하기

▪️ Window → Layouts → 2 by 3
▪️ Project 메뉴 → One Column Layout
▪️ Game 메뉴 → Free Aspect를 760 * 1280짜리로 변경
▪️ SampleScene → MainScene으로 이름 변경

▪️ MainScene 밑에 Square 생성 → 이름 background로 바꾸고 사이즈 6 * 10, 색상 #99CCFF

🌏 땅을 만들기

▪️ 땅 구현하기

  1. MainScene 밑에 Square를 생성, ground로 이름 변경
  2. 사이즈 : 6 * 1.5, PosY : -4.5, 색상 : #381407
  3. Order in Layer : 1로 설정

🐧 캐릭터 만들기

▪️ 캐릭터 에셋 추가

  1. Assets 밑에 Sprites 폴더 추가
  2. Sprites 밑에 캐릭터 에셋 추가(드래그 앤 드롭)
  3. 캐릭터 만들기
    1. MainScene에 Square 생성, 이름 변경하기
    2. 생성된 Square의 Inspector에서 Sprite Renderer → Sprite에 캐릭터 에셋을 드래그 앤 드롭
    3. Order in Layer : 1로 설정
    4. 캐릭터가 지면에 닿아 보이게끔 PosY : -1.5로 조절

▪️ 캐릭터 애니메이션 추가하기

  1. Assets 밑에 Animations 폴더 생성
  2. 생성된 Animations 폴더 밑에 Animation 생성, 이름 변경 및 Loop Time 체크하기
  3. 생성된 Animation을 캐릭터 스프라이트에 드래그 앤 드롭하여 Controller 생성

캐릭터의 Inspector에 Animator가 적용된 모습 ▼

  1. Animation을 더블 클릭하여 편집기를 연 후, 캐릭터 스프라이트 클릭
  1. Sprites 파일에 있는 캐릭터 스프라이트를 원하는 위치에 드래그 앤 드롭!
  1. Play해보기

▪️ 캐릭터 움직이기

생각해보기
캐릭터를 움직인다? 👉 캐릭터의 좌표값을 조절한다.
캐릭터의 좌표값? transform.position!

  1. Assets 밑에 Scripts 폴더 생성
  2. 캐릭터 이름의 C# Script 생성(ex. penguin.cs)
  1. 생긴 C# Script를 캐릭터에게 드래그 앤 드롭
    penguin의 Inspector에 penguin.cs가 등록되어 있는 것을 확인! ▼
  1. 스크립트 열어서 코드 작성

    void Update()
    {
    	// 캐릭터의 X좌표값을 0.5만큼 계속 더해준다.
    	transform.position += new vector3(0.05f, 0, 0);
    }

    실행 화면 ▼

  2. 코드 수정하기

    1. 위 코드의 0.05f를 변수로 선언하기
    float distance = 0.05f;
    
    void Update()
    {
    	transform.position += new Vector3(distance, 0, 0);
    }

    2. 캐릭터의 움직임이 너무 빠르다!
    👉 이는 Update()가 프레임 당 호출되기 때문이다. → 게임의 시간을 기준으로 호출하기?
    ✨ FixedUpdate()를 사용하면 게임 시간을 기준으로 호출되어 속도를 줄일 수 있다.

    void FixedUpdate()
    {
    	transform.position += new vector3(distance, 0, 0);
    }

    실행 화면 ▼

▪️ 캐릭터의 방향 전환

현재는 펭귄이 게임의 벽을 뚫고 나가버리는 것을 확인할 수 있다!
👉 벽에 부딪히면 방향 전환을 해 반대 방향으로 가게 하자!

🤔 생각해보기
1. 펭귄의 X좌표가 background의 범위를 벗어나면 distance를 -0.05로 전환하기
👉 대략 -2.2, 2.2정도면 벽에 닿는 듯 하다.

if(transform.position.x > 2.2f)	distance = -0.05f;
if(transform.position.x < -2.2f)	distance = 0.05f;
  1. 펭귄의 스프라이트 뒤집기? transform.localScale()!
  1. penguin.cs로 돌아와 코드 수정!

    void Update()
    {
    	if(transform.position.x > 2.2f)
    	{
    		distance = -0.05f;
    	}
    	if(transform.position.x < -2.2f)
    	{
    		distance = 0.05f;
    	}
    }

    실행 화면 ▼

  2. 스프라이트 뒤집기?


    penguin의 Inspector의 Transform에서 Scale의 X를 -1로 변경해보면 좌우가 반전된다.

👉 즉, Transform의 Scale의 X값을 -1과 1로 변경하면 좌우 반전을 할 수 있다!
이를 코드로 구현하면 아래와 같다.

'유니티 2D 좌우반전하기' 구글링 : transform.localScale = new vector3(-1, 1, 1)

  1. penguin.cs에 스프라이트 뒤집기 구현하기
    void Update()
    {
    	if(transform.position.x > 2.2f)
    	{
    		distance = -0.05f;
    		transform.localScale = new Vector3(-1, 1, 1);
    	}
    	if(transform.position.x < -2.2f)
    	{
    		distance = 0.05f;
    		transform.localScale = new Vector3(1, 1, 1);
    	}
    }

    실행 화면 ▼

  1. Scale의 X값을 변수로 선언하고 코드를 수정하자.
    float direction = 1.0f;
    
    void Update()
    {
    	if(transform.position.x > 2.2f)
    	{
    		distance = -0.05f;
    		direction = -1.0f;
    	}
    	if(transform.position.x < -2.2f)
    	{
    		distance = 0.05f;
    		direction = 1.0f;
    	}
    	transform.localScale = new vector3(direction, 1, 1);
    }

▪️ 마우스 클릭 시 캐릭터 방향 전환하기

🍎 떨어지는 사과를 먹기 위해 캐릭터의 방향을 임의로 전환할 필요가 있다!

🖱️마우스 클릭? getMouseButtonDown!

❗ 구글링을 통해 GetMouseButtonDown을 통해 값이 true면 방향전환을 시켜주면 됨을 알 수 있었다!

if (Input.GetMouseButtonDown(0))
{
    distance *= -1;
    direction *= -1;
}

▪️ 코드에 적용 시키기

float distance = 0.05f;
float direction = 1.0f;
// Start is called before the first frame update
void Start()
{
   
}

// Update is called once per frame
void Update()
{
	// 마우스 입력을 받으면
	if (Input.GetMouseButtonDown(0))
    {
       	distance *= -1;
        direction *= -1;
    }

    if (transform.position.x > 2.2f)
    {
        distance = -0.05f;
        direction = -1.0f;
    }
    if (transform.position.x < -2.2f)
    {
        distance = 0.05f;
        direction = 1.0f;
    }
    transform.localScale = new Vector3(direction, 1, 1);
}

private void FixedUpdate()
{
	transform.position += new Vector3(distance, 0, 0);
}

실행 화면 ▼

🍎 사과 만들기

▪️ 사과 만들기

  1. 사과모양 에셋을 Sprites 폴더에 드래드 앤 드롭
  1. MainScene에 Circle 만들어서 Inspector의 Sprite에 사과모양 에셋을 드래그 앤 드롭
  2. 이름 변경 및 사이즈 : 0.2 * 0.2, PosY : 4, Order in Layer : 1로 설정

▪️ 사과 떨어트리기

🤔 생각해보기
👉 사과가 떨어지려면? 중력 효과!
✨ 중력 효과? Rigidbody 2D를 사용하여 구현!

  1. 만든 apple의 Inspector에서 Add Component → Rigidbody 2D 적용
  1. 확인해보기

▪️ 사과 충돌시키기

🤔 생각해보기
👉 사과를 충돌시킬려면? 충돌 효과
✨ 충돌 효과? Collider!

Collider를 적용하기 위해선? : Rigidbody, Collider를 가진 물체가 다른 Collider를 가진 물체와 부딪히면 발생한다.
👉 지금은 사과와 펭귄, 땅이 Collider를 가져야 한다!

  1. apple의 Inspector에서 Add Component → Circle Collider 2D를 적용.
  1. penguin과 ground는 Box Collider 2D를 적용한다.

    ❗Collider 적용 시 적용 범위와 위치를 확인하여 조정하도록 하자!

  2. 확인한다.

▪️ 사과가 충돌하면 사라지게 하기

🤔 생각해보기
👉 사과가 무언가와 충돌했을 시? onCollisionEnter2D()!
👉 사과가 부딪히는 대상은 어떻게 식별하지? 태그 달기!

  1. ground에 'ground'라는 태그를 만들어 달기.
  1. ground에 충돌하면 사라지게 하기
    1. apple.cs를 만들어 apple과 연결.
    2. apple.cs를 열어 onCollisionEnter2D()를 작성
    private void OnCollisionEnter2D(Collision2D collision)
    {
        // 부딪히는 게임 오브젝트(collision.gameObject)의 태그가 ground일 경우 제거
    	if (collision.gameObject.tag == "ground") Destroy(gameObject);
    }

실행 화면 ▼

🍎 사과 랜덤하게 나타나게 하기

▪️ 사과 랜덤한 위치에서 나타나게 하기

🤔 생각해보기
랜덤하게 생성? apple.cs의 start()에서 랜덤한 X, Y좌표 및 사이즈를 가지게 만들기!
랜덤하게? Random.Range()

  1. 생각해보기를 바탕으로 apple.cs의 Start()를 작성하자!
    void Start()
    {
        float x = Random.Range(-2.5f, 2.5f);
        float y = Random.Range(2.5f, 4.5f);
    
        transform.position = new Vector3(x, y, 0);
    }
  2. 확인해보기.

▪️ 사과 랜덤한 크기로 나타나게 하기

나타야할 사과의 4 종류 :
사과는 3개의 크기로 나뉘어지며, 크기가 클 수록 점수가 높다(4번 사과 제외).

1번 사과 : 제일 작은 사과, 사이즈 0.2 0.2, RGB : 186, 186, 186, 점수 : 1점
2번 사과 : 중간 사이즈 사과, 사이즈 0.3
0.3, RGB : 210, 210, 210, 점수 : 2점
3번 사과 : 제일 큰 사과, 사이즈 0.4 0.4, RGB : 255, 255, 255, 점수 : 3점
4번 사과 : 썩은 사과, 사이즈 0.3
0.3, RGB : 0, 0, 0, 점수 : -5점

🤔 생각해보기
👉 랜덤한 크기? Random.Range()를 통해 랜덤한 크기값 생성!
생성된 크기에 따라 switch문 제어

  1. 변수 size, score, type을 선언한다(apple.cs).

  2. Start()에 type을 Random.Range()를 통해 랜덤한 값을 집어넣는다.

    int type;
    int score;
    float size;
    
    void Start()
    {
    	type = Random.Range(1, 5);	// 5는 범위에 포함되지 않는다.
    
        switch(type)
        {
        	case 1:
            	size = 0.2f;
                score = 1;
              
            case 2:
            	size = 0.3f;
                score = 2;
               
            case 3:
            	size = 0.4f;
                score = 3;
               
            case 4:
            	size = 0.3f;
                score = -5;
               
        }
       
        transform.localScale = new Vector3(size, size, 0);
    }
  3. getComponent<SpriteRender>()를 이용해 색상과 스프라이트를 교체한다.

코드 ▼(getComponent에 관한 함수를 velog에 작성하면 velog의 상태가 이상해져 캡쳐로 대체...)

실행 화면 ▼

🍎 사과 계속 떨어지게 하기

✨ 사과를 계속 생성하려면? gameManager 오브젝트를 만들고 → 사과를 Prefabs로 틀을 만들고 → Instantiate 복제를 한다!

▪️ gameManager 만들기

gameManager? 게임 전체를 조율하는 오브젝트.

  1. MainScene 아래에 Create Empty → gameManager로 이름 변경.
  2. gameManager.cs도 만들고 gamemanager와 연결해준다.

▪️ Prefabs 생성

Prefab? 오브젝트가 붕어빵이라고 한다면, prefab은 붕어빵틀! prefab을 통해 오브젝트를 복제할 수 있다.

  1. Assets 밑에 Prefabs 폴더를 생성한다.
  2. 생성된 폴더에 apple 오브젝트를 드래그 앤 드롭, 기존의 apple은 삭제!

▪️ Instantiate 복제

  1. gameManager.cs에 public gameObject apple;을 선언한다.

  2. 유니티로 돌아와 gameManager의 Inspector에 apple을 드래그 앤 드롭한다.

  3. InvokeRepeating()함수를 통해 반복 실행하도록 코드 작성!

    InvokeRepeating(string methodName, float time, float repeatRate);
    👉 methodName이란 함수를 time부터 repeatRate 마다 반복 실행하는 코드이다!

    public GameObject apple;
    void Start()
    {
        InvokeRepeating("makeApple", 0, 0.5f);	// makeApple()가 05초마다 실행된다.
    }
    
    void makeApple()
    {
        Instantiate(apple);		// apple을 반복해서 생성한다.
    }

실행 화면 ▼

👑 점수 올라가게 하기

▪️ 점수 보드 만들기

그 전에 먼저! 폰트 변경하기
1. Assets 밑에 Fonts 폴더 생성
2. Fonts 폴더 밑에 넣고 싶은 폰트 파일 드래그 앤 드롭.

  1. MainScene 밑에 UI → Canvas 생성하기

  2. Canvas 밑에 text 생성

    이름 : label_score
    사이즈 : 200 * 200, PosX : -275, PosY : -575
    폰트 사이즈 : 50, 폰트 스타일 : Bold, 색상 : 255, 255, 255
    내용 : 점수

  3. 생성된 label_score를 3번 복사(Ctrl + d), 아래와 같이 수정

    이름 : score, PosX : -150, 내용 : 0, 폰트 사이즈 60
    이름 : label_time, PosX : 25, 내용 : 남은 시간
    이름 : time, PosX : 250, 내용 : 30.00, 폰트 사이즈 60

결과 화면 ▼

▪️ gameManager 싱글톤화 시키기

싱글톤? 프로젝트 전체에 하나만 존재하도록 설정하는 것!

gameManager.cs에 아래의 코드 작성

public static gameManager I;	// default는 "public static gameManager instance;"

void Awake()
{
    I = this;	// 프로젝트 전체에서 gameManager.I를 부르면 이 gameManager가 불려진다.
}

▪️ 점수가 올라가는 함수 만들기

  1. gameManager.cs에 totalScore라는 변수를 생성
  2. addScore라는 함수를 만들어 totalScore에 더한다.
    int totalScore;
    
    public void addScore(int score)
    {
    	totalScore += score;
    }

▪️ 사과가 캐릭터에 닿으면 점수 올리기

  1. 펭귄에게 penguin이라는 태그를 만들기.
  1. apple.cs에 OnCollisionEnter2D()를 이용해 펭귄과 부딪힐 시 점수를 더하자.

    void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "ground")
        {
            Destroy(gameObject);
        }
    
    	// 펭귄이라는 태그를 지닌 물체와 부딪힐 시
        if (collision.gameObject.tag == "penguin")
        {
            gameManager.I.addScore(score);		// gameManager의 addScore에게 score를 넘김
            Destroy(gameObject);
        }
    }
  2. gameManager.cs에 score 텍스트 가져오기

    using UnityEngine.UI;
    
    public Text scoreText;
  3. 유니티도 돌아와서 gameManager에게 score 텍스트 드래그 앤 드롭

  1. addScore()에서 점수가 더해질 때마다 score 텍스트 업데이트 시켜주기
    public void addScore(int score)
    {
    	totalScore += score;
    	scoreText.text = totalScore.ToString();
    }

실행 화면 ▼

  1. 만약 totalScore가 5보다 작은 상태에서 -5점이 된다면 0점으로 표기하기
    public void addScore(int score)
    {
    	totalScore += score;
    	if (totalScore < 0) totalScore = 0;
    
    	scoreText.text = totalScore.ToString();
    }

실행 화면 ▼

⏱️ 제한시간 만들기

gameManager.cs에서 시간이 가도록 만들기!

🤔 생각해보기
👉 시간을 흐르게 한다? 유니티의 Time 클래스!
👉 Time.deltaTime : 마지막 프레임이 완료된 후 경과한 시간을 반환.
if(정해진 시간 - 경과 시간 < 0) 종료!

  1. 변수 선언!

    정해진 시간을 변수 limit으로 선언하자.
    또한 유니티의 time을 텍스트로 받아오기 위해 timeText라는 Text 변수를 생성하자.

    float limit = 30.0f;
    public Text timeText;

    timeText를 선언했으면 유니티로 돌아와 gameManager에게 timeText를 지정해주자.

  2. gameManager.cs의 Update()에 시간을 흐르게 하는 코드를 넣기.

void Update()
{
	limit -= Time.DeltaTime;
    timeText.text = limit.ToString("N2");	// N2 : 소수점 둘째 자리까지 출력!
}
  1. 남은 시간이 0보다 작아지면 게임 멈추기!

    🤔 게임을 멈추기? Time.timeScale = 0.0f로 변경하면 게임 내 시간이 멈춘다!
    선언한 변수 limit이 0보다 작아지면 Time.timeScale을 0으로 변경하자!

    void Update()
    {
        limit -= Time.deltaTime;
    
    	// 남은 시간이 0보다 작아지면
        if (limit < 0)
        {
            limit = 0.0f;	// 출력되는 timeText의 text값을 0.0으로 변경시켜주기
            Time.timeScale = 0.0f;		// 게임 시간 멈추기
        }
    
        timeText.text = limit.ToString("N2");
    }

실행 화면 ▼

🎮 게임 종료화면 만들기

🤔 생각해보기
종료 화면은 게임 내에 존재하나 게임이 끝날 때까지 나타나지 않다가 게임 종료 시 나타난다.
Inactivate를 시켰다가, 게임이 종료되는 시점에 Activate 시키자!

▪️ 종료 화면 만들기

  1. Canvas를 하나 더 만들어 이름을 panel로 변경!

  2. 그 밑에 Image, Text를 만든다.

    1. 이미지 :
      크기 : 400 * 250, PosY : 100
      색상 : 255, 255, 255
    2. Text : Text 생성 시 반드시! Text의 Inspector에서 Rect Transform을 Reset 시키자! 안그러면 텍스트가 화면에 나타나지 않는다...
      크기 400 * 250, PosY : 100
      색상 : 0, 0, 0

  3. panel을 Inactivate 시킨다(체크박스 체크 해제).

▪️ 게임 종료 시 Panel 띄우기

이제 만들어 놓은 종료 화면을 게임이 종료될 때 띄우자.
이미 gameManager.cs에 게임을 종료시키는 코드가 있었다. 그 코드를 활용해보자!

  1. gameManager.cs에 panel을 변수로 선언하기 및 유니티의 gameManager에게 panel 드래그 앤 드랍.
    public GameObject panel;

  1. gameManager.cs의 Update()의 게임 종료 코드에 panel을 activate 시키는 코드를 작성.
    void Update()
    {
        limit -= Time.deltaTime;
    
        if (limit < 0)
        {
            limit = 0.0f;
            // panel을 active 시키기
            panel.SetActive(true);		
            Time.timeScale = 0.0f;
        }
    
        timeText.text = limit.ToString("N2");
    }

실행 화면 ▼

🎮 게임 재시작하기

▪️ 재시작 버튼 만들기

우리가 만든 panel을 누르면 게임이 재시작하게 만들기!
게임 재시작? MainScene을 새로 고침하기!

  1. panel의 Inspector에서 Add Component → Button 추가!
  2. gameManager.cs로 돌아가서 MainScene을 다시 불러오는 코드를 작성하기.
    1. Scene에 대한 기능을 사용하기 위해서는 using UnityEngine.SceneManagement; 를 Import해야 한다.
    2. 특정 씬을 불러오는 코드 : SceneManager.LoadScene("Scene_name");

따라서 우리가 gameManager.cs에 추가해야 할 코드는 다음과 같다.

using UnityEngine.SceneManagement;

public void retry()
{
	SceneManager.LoadScene("MainScene");
}
  1. 클릭하면 작동할 함수 만들기

    ❗ panel을 클릭하면 → panel의 retry()함수가 gameManager의 retry()를 호출하는 방식!
    ❓ 이유? MainScene을 다시 부르는 정도의 중요한 코드는 gameManager가 실행을 하도록 만드는 편이 안전!

  1. panel.cs를 만들어 panel에 연결해준다.

  2. panel.cs에 retry()함수 추가.

public void retry()
{
	gameManager.I.retry()
}
  1. panel의 Button에서 OnClick()함수에 retry()를 연결한다.

실행 화면

😱 게임이 다시 시작하지 않는다!
이유? 아직 Time.timeScale이 0이기 때문이다!
해결 방안? Time.timeScale을 1로 초기화 시켜주는 함수를 만들자!

▪️ 초기화 함수 만들기

MainScene을 로드하면 initGame()이란 함수가 실행되도록 만들자.
🤔 초기화 되어야 할 요소? timeScale, 점수, 남은 시간

void Start()
{
    InvokeRepeating("makeApple", 0, 0.5f);
    // MainScene이 로드되면 실행된다
    initGame();
}
void initGame()
{
    Time.timeScale = 1.0f;
    totalScore = 0;
	limit = 30.0f;
}

실행 화면 ▼(잘 동작하는지 확인하기 위해 시간은 임시로 5초로 변경...)

✨ 마치며...

게임 개발 부트 캠프에서 배운 1주차 내용들을 복습하는 겸사겸사 TIL를 적어보았다. 아니면 WIL일려나? 아무튼...
처음 유니티를 시작하며 모르는 것도 많고 헷갈리는 것도 너무 많아 머리가 어지럽지만, 확실히 복습을 해보니 좋은 것 같다.
다만, 다음부터는 TIL를 하기 위해서는 시행 과정 전부를 적기보단 헷갈리거나 꼭 외워야겠다 싶은 것들 위주로 작성하는 것이 맞는 것 같다. 아직 익혀야 할 것들은 산더미처럼 많은데 이러한 방식은 너무 오래 걸린다. 간결하게 기능별로 적도록 하자!

post-custom-banner

0개의 댓글