4 분 소요

게임시작과 종료

게임 시작 및 UI

  • 플레이어가 버튼을 누르면 게임이 시작되도록 UI를 만들어보겠습니다.
  • Canvas - Create Empty(Game Start) - Image(Title), Button(Button Start)을 추가합니다.
  • Image, Button Sprite는 Sprites 폴더에 따로 있으니 추가해줍니다.

image

  • Title과 Button의 위치와 속성등을 적절히 배치합니다.
  • 게임시작 UI는 게임시작버튼을 눌렀을 때 사라져야 되므로, Button의 OnClick 이벤트에서 처리합니다.
  • Button Start의 OnClick에 GameStart를 추가하고, SetActive를 추가합니다.
  • 그다음 게임시작을 누르기 전에는 게임 내 UI들(LevelUp Bar, Hp, Time, Kills… 등등)이 보이지 않았으면하므로, Canvas - Create Empty 하여 HUD라는 오브젝트를 만든 후, Exp, Level, Kill, Timer, Hp 오브젝트를 HUD의 자식으로 넣어줍니다.
  • 이번에는 Button Start의 OnClick에 HUD를 추가하고, SetActive를 추가합니다. 그리고 HUD는 비활성화 해줍니다.

image

  • 게임 실행을 해보면, 게임 시작 버튼을 눌러도 게임의 시간이 흐르지않는것을 볼 수 있습니다.
  • GameManager 스크립트를 조금 수정하여 버튼을 누르면 게임시간이 흐르도록 수정하겠습니다.

GameManager.cs

 public void GameStart()
    {
        health = maxHealth;

        // .. test
        uiLevelUp.Select(0);
       
        isLive = true;

    }
  • Start() 함수는 이제 public의 GameStart()가 되어 Button Start의 OnClick 이벤트에서 사용할 수 있도록 변경했습니다.
  • Button Start의 OnClick에 GameManager를 추가하고, GameStart를 연결합니다.

플레이어 피격 구현

  • 플레이어가 몬스터에 피격되면 Hp가 깎이도록 만들어봅니다.
  • 먼저, GameManager 스크립트에 health와 maxHealth 변수의 타입을 float으로 변경해줍니다.

Player.cs

 // .. 플레이어 피격
    void OnCollisionStay2D(Collision2D collision)
    {
        if (!GameManager.instance.isLive)
            return;

        GameManager.instance.health -= Time.deltaTime * 10;

        // .. 플레이어 사망 시
        if(GameManager.instance.health < 0)
        {
            // .. 플레이어의 자식 오브젝트 비활성화
            for(int index = 2;index<transform.childCount;index++)
            {
                transform.GetChild(index).gameObject.SetActive(false);
            }

            anim.SetTrigger("Dead");
        }    
    }
  • 플레이어 피격은 OnCollisionStay2D 함수에서 구현합니다.
  • 플레이어가 몬스터에 피격되면 Hp가 10 깎이는데, Time.deltaTime 을 곱해주어 급사하지않도록 해줍니다.
  • 플레이어가 죽으면(Hp가 0 아래) 플레이어가 가진 자식오브젝트들을 순회하면서 비활성화 시켜줍니다.
  • 사망하면 플레이어의 애니메이션은 Dead로 변경해줍니다.

게임 오버

  • GameStart 오브젝트를 재활용하여 게임오버를 만들어보겠습니다. GameStart 오브젝트를 Ctrl+D 하여 복사합니다.
  • 이름은 GameResult, Button Start는 Button Retry로 이름을 변경합니다.
  • Title의 이미지는 Sprites 폴더의 Title2로, Button의 Text는 돌아가기로 변경합니다.
  • Button의 OnClick 이벤트에 있던것들을 모두 제거해줍니다.

GameManager.cs

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

public class GameManager : MonoBehaviour
{
    // ... 매니저 인스턴스를 어디서든 접근 가능하게 함
    public static GameManager instance;

    [Header("# Game Object")]
    public bool isLive;
    public Player player;
    public PoolManager pool;
    public LevelUp uiLevelUp;
    public GameObject uiResult;

    [Header("# Game Control")]
    // ... 게임 시간과 최대게임시간 변수
    public float gameTime;
    public float maxGameTime = 2 * 10f;

    [Header("# Player Info")]
    // ... 체력, 레벨, 킬수, 경험치, 다음레벨에 필요한 경험치
    public float health;
    public float maxHealth = 100;
    public int level;
    public int kill;
    public int exp;
    public int[] nextExp = { 10, 30, 60, 100, 150, 210, 280, 360, 450, 600 };
    

    void Awake()
    {
        instance = this;
    }

     public void GameStart()
    {
        health = maxHealth;

        // .. test
        uiLevelUp.Select(0);
       
        isLive = true;

    }

    public void GameOver()
    {
        StartCoroutine(GameOverRoutine());
    }

    IEnumerator GameOverRoutine()
    {
        isLive = false;

        // .. Dead Animation을 위한 텀
        yield return new WaitForSeconds(0.5f);

        uiResult.SetActive(true);
        Stop();
    }

    
    public void GameRetry()
    {
        SceneManager.LoadScene(0);
    }

    void Update()
    {
        if (!isLive)
            return;

        gameTime += Time.deltaTime;

        if (gameTime > maxGameTime)
        {
            gameTime = maxGameTime;
        }
    }

    public void GetExp()
    {
        exp++;

        if(exp == nextExp[Mathf.Min(level, nextExp.Length-1)])
        {
            level++;
            exp = 0;
            uiLevelUp.Show();
        }
    }

    public void Stop()
    {
        isLive = false;
        // .. 유니티 시간 속도(배율) 조절(기본 1)
        Time.timeScale = 0;
    }

    public void Resume()
    {
        isLive = true;
        Time.timeScale = 1;
    }
}

  • GameRetry() 함수에서 처음 화면으로 돌아가도록 했습니다.
  • 플레이어가 사망하면 isLive = false로 바꾸어 제어를 못하도록 했고, 플레이어가 사망한 애니메이션을 볼 수 있도록 0.5초 텀을 둔 뒤, GameResult UI를 띄우도록 했습니다.

게임승리

  • 남은시간동안 생존하여 게임에 승리하는것을 구현하겠습니다.
  • 생존하여 게임에서 승리했을 때, 남아있는 몬스터들을 모두 제거하는 연출을 위해 Bullet을 하나 만들어보겠습니다.
  • Hierarchy뷰에서 Create Empty - enemyCleaner를 만들어줍니다.

image

  • 게임승리 UI를 만들기 위해 GameResult 오브젝트의 Title를 Title Over로 이름을 변경하고, 복사하여 Title Victory의 오브젝트를 만들어줍니다.
  • 게임에서 패배했을 때는 Title Over를, 승리했을 때는 Title Victory UI를 띄워야합니다. 그것을 위한 스크립트 Result를 만들어줍니다.

Result.cs

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

public class Result : MonoBehaviour
{
    public GameObject[] titles;

    public void Lose()
    {
        // .. 패배 Title
        titles[0].SetActive(true);
    }

    public void Win()
    {
        // .. 승리 Title
        titles[1].SetActive(true);
    }
}

  • 두가지 title을 담을 변수를두고, 단순히 승리, 패배했을 때 알맞는 UI를 띄우도록 했습니다.

GameManager.cs

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

public class GameManager : MonoBehaviour
{
    // ... 매니저 인스턴스를 어디서든 접근 가능하게 함
    public static GameManager instance;

    [Header("# Game Object")]
    public bool isLive;
    public Player player;
    public PoolManager pool;
    public LevelUp uiLevelUp;
    public Result uiResult;
    public GameObject enemyCleaner;     // .. 게임승리 시 남은 적 처리하는 Bullet

    [Header("# Game Control")]
    // ... 게임 시간과 최대게임시간 변수
    public float gameTime;
    public float maxGameTime = 2 * 10f;

    [Header("# Player Info")]
    // ... 체력, 레벨, 킬수, 경험치, 다음레벨에 필요한 경험치
    public float health;
    public float maxHealth = 100;
    public int level;
    public int kill;
    public int exp;
    public int[] nextExp = { 10, 30, 60, 100, 150, 210, 280, 360, 450, 600 };
    

    void Awake()
    {
        instance = this;
    }

     public void GameStart()
    {
        health = maxHealth;

        // .. test
        uiLevelUp.Select(0);
        Resume();       
    }

    public void GameOver()
    {
        StartCoroutine(GameOverRoutine());
    }

    IEnumerator GameOverRoutine()
    {
        isLive = false;

        // .. Dead Animation을 위한 텀
        yield return new WaitForSeconds(0.5f);

        uiResult.gameObject.SetActive(true);
        uiResult.Lose();
        Stop();
    }

    public void GameVictory()
    {
        StartCoroutine(GameVictoryRoutine());
    }

    IEnumerator GameVictoryRoutine()
    {
        isLive = false;
        enemyCleaner.SetActive(true);

        // .. Dead Animation을 위한 텀
        yield return new WaitForSeconds(0.5f);

        uiResult.gameObject.SetActive(true);
        uiResult.Win();
        Stop();
    }
    public void GameRetry()
    {
        SceneManager.LoadScene(0);
    }

    void Update()
    {
        if (!isLive)
            return;

        gameTime += Time.deltaTime;

        if (gameTime > maxGameTime)
        {
            gameTime = maxGameTime;
            GameVictory();
        }
    }

    public void GetExp()
    {
        // .. 게임종료시 레벨업 안하도록
        if (!isLive)
            return;

        exp++;

        if(exp == nextExp[Mathf.Min(level, nextExp.Length-1)])
        {
            level++;
            exp = 0;
            uiLevelUp.Show();
        }
    }

    public void Stop()
    {
        isLive = false;
        // .. 유니티 시간 속도(배율) 조절(기본 1)
        Time.timeScale = 0;
    }

    public void Resume()
    {
        isLive = true;
        Time.timeScale = 1;
    }
}

  • 주요 변경점입니다. 기존에 uiResult 변수 타입이었던 GameObject를 Result로 변경합니다.
  • GameOverRoutine()에서 Lose()를 호출합니다.
  • GameVictory(), GameVictoryRoutine()을 추가하여 게임승리시 로직을 추가했습니다.
  • 게임승리시에는 enemyCleaner를 활성화하여 남아있는 몬스터를 모두 처치하는 연출을 보여줍니다.
  • 게임이 끝났음에도 enemyCleaner를 통해 몬스터를 처치했을 때, 레벨업을 하지않도록 GetExp() 함수에 반환로직을 추가합니다.
  • 게임오버, 승리 후 돌아가기버튼을 누르면 Stop() 함수에서 timeScale을 0으로 했던것을 GameStart() 함수에서 Resume() 호출을 통해 다시 시간이 흐르도록 했습니다.

댓글남기기