본문 바로가기
게임 프로그래밍/게임개발 중급

게임개발 중급(30) - 3D 게임 만들기(10)

by jyppro 2023. 5. 21.

3D 게임 만들기(10)

이전에는 스테이지를 만들어 아이템과 게이지를 연동하여 스테이지가 넘어가는 것을 판단할 수 있도록 만들어 주었습니다. 이번에는 4스테이지에 도달하면 게임을 재시작 할 수 있는 버튼을 만들어 보겠습니다.

 

재시작 버튼 UI

버튼 UI를 만들어 가운데 배치해줍니다.이 버튼은 스테이지 4에 도달했을 때, 나타나서 누를 수 있어야 하므로 SetActive를 통해 활성화/비활성화를 컨트롤 해주어야 합니다.

재시작-버튼
재시작 버튼 UI

저는 이전에 에셋스토어에서 다운받은 버튼 UI 이미지를 사용해서 만들었습니다. 이제 스크립트를 작성해 보겠습니다.

 

Restart

using UnityEngine;
using UnityEngine.SceneManagement;

public class Restart : MonoBehaviour
{
    public void OnRestartButtonClick()
    {
        GameObject director = GameObject.Find("GameDirector");
        director.GetComponent<GameDirector>().ResetBtn();
    }
}

Restart 버튼에 들어갈 스크립트를 작성하였습니다. 코드를 보시면 GameDirector에서 함수를 실행하는 것을 볼 수 있습니다. 이 스크립트에 작성된 OnRestartButtonClick()은 버튼의 OnClick()을 통해 연결시켜주는 용도로 사용됩니다.

 

스크립트를 작성하는 과정 중에, score는 PlayerController에 있고, 다른 UI는 GameDirector에 있는 것을 발견하였습니다.

score도 UI이기 때문에 따로 컨트롤 해줄 필요가 없으니 GameDirector에 옮겨서 다시 작성해줍니다.

 

변경된 PlayerController 와 GameDirector 스크립트를 보여드리겠습니다.

PlayerController

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

public class PlayerController : MonoBehaviour
{
    private bool isMovingForward = false;
    private bool isMovingBackward = false;
    private bool isMovingLeft = false;
    private bool isMovingRight = false;
    Rigidbody rigid;
    float jumpForce = 5.0f;
    float walkForce = 5.0f;
    float forwardForce = 1.0f;
    int key = 0;
    public AudioSource AS;
    public ParticleSystem PS;

    void Start()
    {
        this.rigid = GetComponent<Rigidbody>();
        AS = GetComponent<AudioSource>();
        PS = GetComponent<ParticleSystem>();
    }

    void Update()
    {
        //점프
        if(Input.GetKeyDown(KeyCode.Space) && this.rigid.velocity.y == 0f){
            rigid.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        }

        //F키, B키로 움직이기
        if(Input.GetKey(KeyCode.F)) // 앞
        {
            key = 1;
            transform.Translate(0f,0f,forwardForce*key*Time.deltaTime);
        }
        else if(Input.GetKey(KeyCode.B)) // 뒤
        {
            key = -1;
            transform.Translate(0f,0f,forwardForce*key*Time.deltaTime);
        }

        //방향키로 움직이기
        if(Input.GetKey(KeyCode.RightArrow)){ // 오른쪽
            key = 1;
            this.rigid.AddForce(transform.right * key * this.walkForce);
        }
        if(Input.GetKey(KeyCode.LeftArrow)){ // 왼쪽
            key = -1;
            this.rigid.AddForce(transform.right * key * this.walkForce);
        }
        if(Input.GetKey(KeyCode.UpArrow)){ // 앞
            key = 1;
            this.rigid.AddForce(transform.forward * key * this.walkForce);
        }
        if(Input.GetKey(KeyCode.DownArrow)){ // 뒤
            key = -1;
            this.rigid.AddForce(transform.forward * key * this.walkForce);
        }

        //4방향 UI로 움직이기
        if (isMovingForward)
        {
            MoveForward();
        }
        else if (isMovingBackward)
        {
            MoveBackward();
        }
        else if (isMovingLeft)
        {
            MoveLeft();
        }
        else if (isMovingRight)
        {
            MoveRight();
        }
    }

    //아이템 먹으면 점수 올라가는 기능
    void OnTriggerEnter(Collider other)
    {
        if(other.CompareTag("Item"))
        {
        AS.Play();
        PS.Play();
        // 감독 스크립트에 플레이어와 아이템이 충돌했다고 전달한다 
        GameObject director = GameObject.Find("GameDirector");
        director.GetComponent<GameDirector>().DecreaseHp();
        director.GetComponent<GameDirector>().StageChange();
        director.GetComponent<GameDirector>().ScoreUp();
        other.gameObject.SetActive(false);
        GetComponent<ItemManager>().RespawnItem(other.gameObject);
        }
    }

    public void ForwardButtonDown()
    {
        isMovingForward = true;
    }

    public void ForwardButtonUp()
    {
        isMovingForward = false;
    }

    public void BackwardButtonDown()
    {
        isMovingBackward = true;
    }

    public void BackwardButtonUp()
    {
        isMovingBackward = false;
    }

    public void LeftButtonDown()
    {
        isMovingLeft = true;
    }

    public void LeftButtonUp()
    {
        isMovingLeft = false;
    }

    public void RightButtonDown()
    {
        isMovingRight = true;
    }

    public void RightButtonUp()
    {
        isMovingRight = false;
    }

    private void MoveForward()
    {
        key = 1;
        transform.Translate(0f,0f,walkForce*key*Time.deltaTime);
    }

    private void MoveBackward()
    {
        key = -1;
        transform.Translate(0f,0f,walkForce*key*Time.deltaTime);
    }

    private void MoveLeft()
    {
        key = -1;
        transform.Translate(walkForce*key*Time.deltaTime,0f,0f);
    }

    private void MoveRight()
    {
        key = 1;
        transform.Translate(walkForce*key*Time.deltaTime,0f,0f);
    }
}

 

GameDirector

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // UI를 사용하므로 잊지 않고 추가한다

public class GameDirector : MonoBehaviour
{
    GameObject GaugeUI;
    GameObject StageUI;
    GameObject ScoreBoardUI;
    GameObject RestartBtnUI;
    int stageLevel = 1;
    int score = 0;

    void Start()
    {
        this.GaugeUI = GameObject.Find("Gauge");
        this.StageUI = GameObject.Find("Stage");
        this.ScoreBoardUI = GameObject.Find("Score");
        this.RestartBtnUI = GameObject.Find("RestartButton");
        this.RestartBtnUI.SetActive(false);
    }

    public void DecreaseHp()
    {
        if(this.stageLevel == 1)
        {
            this.GaugeUI.GetComponent<Image>().fillAmount -= 0.1f; // 10개먹으면 다음 스테이지
        }
        else if(this.stageLevel == 2)
        {
            this.GaugeUI.GetComponent<Image>().fillAmount -= 0.1f; // 20개 먹으면 다음 스테이지
        }
        else
        {
            this.GaugeUI.GetComponent<Image>().fillAmount -= 0.1f; // 40개 먹으면 게임 클리어
        }
        
    }

    public void StageChange()
    {
        if(this.GaugeUI.GetComponent<Image>().fillAmount == 0f)
        {
            if(this.stageLevel >= 1)
            {
                this.stageLevel++;
                this.GaugeUI.GetComponent<Image>().fillAmount = 1f;
                this.StageUI.GetComponent<Text>().text = "Stage : " + stageLevel.ToString();
            }
            
            if (this.stageLevel == 4)
            {
                this.RestartBtnUI.SetActive(true);
                this.StageUI.GetComponent<Text>().text = "Game Clear!";
                this.StageUI.GetComponent<Text>().color = Color.green;
                Time.timeScale = 0f;
            }
        }
    }

    public void ScoreUp()
    {
        score += 10;
        if(score >= 0)
        {
            this.ScoreBoardUI.GetComponent<Text>().text = "Score: " + score.ToString();
        }
    }

    public void ResetBtn()
    {
        this.stageLevel = 1;
        this.score = 0;
        this.StageUI.GetComponent<Text>().text = "Stage : " + stageLevel.ToString();
        this.StageUI.GetComponent<Text>().color = Color.black;
        this.ScoreBoardUI.GetComponent<Text>().text = "Score: " + score.ToString();
        this.GaugeUI.GetComponent<Image>().fillAmount = 1f;
        this.RestartBtnUI.SetActive(false);
        Time.timeScale = 1f;
    }
}

점수를 처리하는 기능을 옮겨준 뒤에, ResetBtn()을 작성해 줍니다. 해당 버튼은 누르면 플레이어가 게임을 진행하면서 획득한 점수, 올라간 스테이지, 깎은 게이지를 모두 초기화 시켜주고 멈춰있던 게임을 다시 시작하는 역할을 합니다.

 

역시나 해당 버튼은 보이지 않다가 스테이지 4에 도달하면 보여야 하므로, Start()에서 SetActive()를 false로 해준 뒤, 4스테이지가 되면 true로 바꿔줍니다. 해당 버튼을 누르면 다시 false로 바꾸어 사라지게 해줍니다.

 

게임-클리어
4스테이지가 되어 버튼이 나타난 모습

저는 테스트를 위해 스테이지별 점수획득을 100점씩으로 두고 게임을 실행하였습니다.4스테이지에 도달하여 버튼이 나타나고 게임이 멈춘 상태가 되었습니다.

 

버튼-클릭
버튼을 클릭하여 재시작

버튼을 클릭하면 게임을 처음에 시작하는 상태로 되돌려 다시 할 수 있도록 해줍니다.

 

using UnityEngine;
using UnityEngine.SceneManagement;

public class RestartButton : MonoBehaviour
{
    public void OnRestartButtonClick()
    {
        // 현재 씬을 다시 로드하여 게임을 처음부터 재시작합니다.
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }
}

이 코드는 다른 방식으로 게임을 재시작할 수 있는 방법입니다. 버튼을 누르면 씬을 다시 로드하여 재시작하는 방식인데, 이 방식은 게임의 오브젝트나 모든 요소들을 다시 가져오기 때문에 상당한 로딩이 걸릴 수 있습니다.

그래서 저는 필요한 정보들을 초기화 시켜주는 방법으로 작성하였습니다.

 

<NEXT>

이제 예제 게임으로서 필요한 기본기능에 대한 설명은 모두 마친것 같습니다. 다음에는 총정리하는 시간을 가지고, 본격적으로 3D 게임을 만들어 보겠습니다. 감사합니다.