[Part 10] 타이머 설정 Timer & 카운트다운 CountDown: 유니티 이벤트 사용

이후 내가 맡은 역할인 타이머 구현 단계에 돌입하였다. 

 

<문제>

타이머가 시작된 시점이 Countdown이 완료된 후에 타이머가 시작되어야한다. 하지만 기존의 Countdown 코드에서는 게임의 시작을 알리는 코드가 없다. 

 

<해결>

보다 더 효율적으로 게임을 관리하기 위해 게임의 상태를 관리하는 상태관리Manager를 만들었다. 

 

1. PlayStateManager를 만들어서 게임 상태를 get하거나 set한다. 

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

// Play State
public enum Play_State
{
    Ready,
    Start,
    Playing,
    GameOver,
    End,
}
public class PlayStateManager
{
    private Play_State state;

    public void Init()
    {
        GameObject root = GameObject.Find("@PlayState");

        if (root == null)
        {
            root = new GameObject { name = "@PlayState" };
            Object.DontDestroyOnLoad(root);
            root.AddComponent<PlayState>();
        }
    }
    // 현재 상태 바꿈
    public void Set_State(Play_State state)
    {
        this.state = state;
    }

    public Play_State Get_State()
    {
        return this.state;
    }

    public void Clear_state()
    {
        this.state = Play_State.Ready;
    }

}

 

 

2. PlayState를 Scene에 놓아서 게임하는 동안에 게임 안의 플레이어의 상태를 관리한다. 

   아래와 같이 처음 게임을 시작할때 Counddown이 시작되기 때문에 Start함수에서 게임의 상태를 Ready 상태로 해준다. 

   이후 Update로 매 프레임마다 게임의 상태를 점검하여 게임의 상태가 Start가 되면 onPlayStart.Invoke()로 Start시 실행해야하는 함수 이벤트가 발생한다. 

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

public class PlayState: MonoBehaviour
{
    private Play_State current_state;
    public UnityEvent onPlayReady;
    public UnityEvent onPlayStart;
    public UnityEvent onPlaying;
    public UnityEvent onPlayEnd;

    private void End()
    {
        Debug.Log("Play Stop");

        onPlayEnd.Invoke();

    }
    void Start()
    {
        // 시작 할 때의  
        Managers.State.Set_State(Play_State.Ready);
    }

    void Update()
    {
        current_state = Managers.State.Get_State();

        Debug.Log($"현재 상태: { current_state }");

        switch (current_state)
        {
            case Play_State.Ready:
                // countdown 시작 
                // 자동차 멈춤
                // 자동차 감점 감지
                break;

            case Play_State.Start:
                Debug.Log("Play Start");
                onPlayStart.Invoke();
                Managers.State.Set_State(Play_State.Playing);
                break;

            case Play_State.Playing:
                
                break;

        }


    }
}

3. Timer에서는 타이머를 동작한다. 

   TimerisRunning은 타이머가 동작하는지에 대한 boolean값으로 게임 시작 전 Awake 함수에서 TimerisRunning을 false로 설정한다. 

   추가로 Awake에서는 타이머의 OnPlayStart 함수 이벤트를 PlayState의 OnPlayStart에 이벤트로 한다. 

    public void OnPlayStart()
    {
        TimerisRunning = true;
    }

    private void Awake()
    {
        TimerisRunning = false;
        // Text assignment
        ClockText = Util.FindChild<Text>(GameObject.Find("TimerUI"), "TimeText", true);
        GameObject.Find("@PlayState").GetComponent<PlayState>().onPlayStart.AddListener(OnPlayStart);
    }

4. Countdown 코드에서 카운트다운을 한 후 게임의 상태를 Start로 바꿔주는 코드를 추가한다. 

    IEnumerator CountStart()
    {
        Managers.Sound.Play("Car/startup", Define.Sound.PlayStart);
        Managers.Sound.Play("Car/idle", Define.Sound.Idle);

        yield return new WaitForSeconds(0.5f);
        countdown.GetComponent<Text>().text = "5";
        Managers.Sound.Play("Car/readyup", Define.Sound.Ready, 0.5f);
        countdown.SetActive(true);
        yield return new WaitForSeconds(1);
        countdown.SetActive(false);
        countdown.GetComponent<Text>().text = "4";
        Managers.Sound.Play("Car/readyup", Define.Sound.Ready, 0.5f);
        countdown.SetActive(true);
        yield return new WaitForSeconds(1);
        countdown.SetActive(false);
        countdown.GetComponent<Text>().text = "3";
        Managers.Sound.Play("Car/readyup", Define.Sound.Ready, 0.5f);
        countdown.SetActive(true);
        yield return new WaitForSeconds(1);
        countdown.SetActive(false);
        countdown.GetComponent<Text>().text = "2";
        Managers.Sound.Play("Car/readyup", Define.Sound.Ready, 0.5f);
        countdown.SetActive(true);
        yield return new WaitForSeconds(1);
        countdown.SetActive(false);
        countdown.GetComponent<Text>().text = "1";
        Managers.Sound.Play("Car/readyup", Define.Sound.Ready, 0.5f);
        countdown.SetActive(true);
        yield return new WaitForSeconds(1);
        countdown.SetActive(false);

        //LapTimer.SetActive(true);
        carController.wheelColliders[0].brakeTorque = 0;
        carController.wheelColliders[1].brakeTorque = 0;

        // Play State & Background music start
        Managers.State.Set_State(Play_State.Start);
        Managers.Sound.Play("Car/Zephyr", Define.Sound.Background); // Idle 하고 Background 음악 두 가지 동시에 수정 요망!

    }

5. 게임의 상태를 Start로 바꾸면 PlayState의 OnPlayStart.Invoke로 이벤트로 등록된 함수가 실행된다. 

   Timer의 OnPlayStart 함수가 실행되어 TimerisRunning값이 true로 바뀌고 타이머가 시작된다. 

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

public class Timer : MonoBehaviour
{
    public Text ClockText;
    float time;
    bool TimerisRunning = false;
    public float timeRemaining = 10;

    public void OnPlayStart()
    {
        TimerisRunning = true;
    }

    private void Awake()
    {
        TimerisRunning = false;
        // Text assignment
        ClockText = Util.FindChild<Text>(GameObject.Find("TimerUI"), "TimeText", true);
        GameObject.Find("@PlayState").GetComponent<PlayState>().onPlayStart.AddListener(OnPlayStart);
    }
    void Start()
    {
        // 한 Frame 기다리기
        StartCoroutine(WaitFrame());
    }

    IEnumerator WaitFrame()
    {
        yield return new WaitForSeconds(1.0f);
    }

    // Update is called once per frame
    void Update()
    {
        if (TimerisRunning)
        {
            if (timeRemaining > 0)
            {
                timeRemaining -= Time.deltaTime;
                DisPlayTime(timeRemaining);
            }
            else
            {
                Debug.Log("Time is over!!");
                Managers.Score.ScoreOut(Define.ScoreOut.TimerOut);
                TimerisRunning = false;
            }
        }
    }

    void DisPlayTime(float time)
    {
        time += 1;

        float minutes = Mathf.FloorToInt(time / 60);
        float seconds = Mathf.FloorToInt(time % 60);

        ClockText.text = string.Format("{0:00}:{1:00}", minutes, seconds);
    }
}

 

<결과>

위와 같이 유니티 이벤트를 사용하여 스파게티 코드를 줄여 효율적으로 코딩하였다. 다만, 게임 개발을 처음 해봐서 이러한 유니티 이벤트 기술을 게임의 전반에서 효율적으로 사용하지 못한 거 같아 아쉬움이 남는다.  

 

ref)

https://studio108.tistory.com/entry/Unity-3D-%ED%83%80%EC%9D%B4%EB%A8%B8-%EC%84%A4%EC%A0%95-%EB%B2%95

 

[Unity3D] 타이머를 설정하는 방법

유니티를 개발할때 일정 시간 이후 이벤트를 걸어야 할 경우가 많습니다! 이럴때 어떻게 타이머를 설정해야 할까요? 그 방법에 대해서 빠르게 알아보겠습니다. 오늘 설명할 방법은 3가지입니다.

studio108.tistory.com

https://daebalstudio.tistory.com/entry/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%99%84%EB%B2%BD%ED%95%98%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0?category=698562 

 

유니티 이벤트 완벽하게 이해하기 1

유니티 이벤트에 대해 정리해 보았습니다. Unity 이벤트 C#의 이벤트와 델리게이트를 유니티가 사용하기 편하도록 랩핑 해놓은 것입니다. 이벤트를 발동시키는 측도 그 이벤트에 기능을 등록시켜

daebalstudio.tistory.com

https://ansohxxn.github.io/unity%20lesson%201/chapter8-1/

 

Unity Chapter 8-1. C# 프로그래밍 [고급] : 유니티 이벤트

인프런에 있는 이제민님의 레트로의 유니티 C# 게임 프로그래밍 에센스 강의를 듣고 정리한 필기입니다. 😀 🌜 [레트로의 유니티 C# 게임 프로그래밍 에센스] 강의 들으러 가기!

ansohxxn.github.io

https://chameleonstudio.tistory.com/37

 

유니티 인보크 Invoke 사용법의 모든 것

해당 티스토리 페이지는 필자가 유니티 C# 개발을 하면서 학습한 내용들을 기록하고 공유하는 페이지입니다 ! - 틀린 부분이 있거나, 수정된 부분이 있다면 댓글로 알려주세요 ! - 해당 내용을 공

chameleonstudio.tistory.com