본문 바로가기
디지털노마드

챗GPT 게임개발72: Unity에서 타이머와 애니메이션 제어하기

by 인텔리노마드라이프 2024. 12. 4.
728x90

안녕하세요. 인텔리원스튜디오(IntelliOneStudio)입니다.

 

이번 시간에는 Unity에서 타이머 기능을 구현하고, 시간이 흐름에 따라 UI를 업데이트하며, 타이머 종료 시 특정 이벤트를 발생시키는 방법을 알아보겠습니다. 특히 TimerManager 스크립트를 중심으로, 시간 표시, 일시 정지, 재개, 시간 추가, 그리고 애니메이션 제어 등을 구현하는 방법을 소개해 드릴게요.

 

이 스크립트를 통해 게임 내에서 시간 제한이 있는 레벨이나 미션을 효과적으로 관리할 수 있습니다. 단계별로 필요한 코드와 설명을 함께 제공하니, 프로젝트에서 바로 적용해 보세요.

 


신개념 메모리 게임의 탄생. 외워라! 창고.

2024.11.14 - [분류 전체보기] - [외워라!창고] 챗GPT를 활용해서 개발한 두 번째 게임을 여러분께 소개합니다!

 

[외워라!창고] 챗GPT를 활용해서 개발한 두 번째 게임을 여러분께 소개합니다!

안녕하세요, 인텔리원스튜디오(IntelliOneStudio)에서 흥미진진한 소식을 가지고 왔어요! 저희가 챗GPT를 활용해서 개발한 두 번째 게임을 여러분께 소개합니다! 이 게임은 챗GPT의 놀라운 AI 기능을

intellinomadlife.tistory.com

 

창고에 물건을 보관하면 더이상 보여주지 않고, 오로지 당신의 기억력으로 찾아야해요.

저희가 챗GPT를 활용해서 개발한 첫 번째 게임을 여러분께 소개합니다! 이 게임은 챗GPT의 놀라운 AI 기능을 활용해 개발했어요. 즐거움이 가득한 이번 게임은, 챗GPT를 활용해서 누구든지 게임을 재미있게 만들 수 있다는 것을 보여줄 거에요.

 

* APP Store 에서 다운로드 

https://apps.apple.com/kr/app/%EB%86%8D%EB%B6%80%EC%9D%98-%EA%BF%88/id6479183930

 

‎농부의 꿈

‎오래된 전설이 숨겨져 있는 [농부의 꿈]목장 한구석에서 ‘비밀의 문'과 '마법 책'이 발견되었어요. [변신하는 동물들] 병아리가 합쳐져 닭이 되고, 닭이 합쳐져 고슴도치로 변신하며, 점점 더

apps.apple.com

 

 

*Google Play 에서 다운로드

https://play.google.com/store/apps/details?id=com.intellione.farmersfirstdream&pcampaignid=web_share 

 

농부의 꿈 - Google Play 앱

평화로운 목장에서 신나는 동물 합치기 무료게임 (바람, 뚫어뻥, 밧줄, 무중력 등 매직 아이템 활용해 병아리를 젖소로 키워보세요.)

play.google.com

 


 

주요 기능 소개

타이머 구현 및 UI 업데이트

  • 남은 시간을 계산하여 UI 텍스트에 표시합니다.
  • 특정 시간대에 따라 텍스트 색상과 표시 형식을 변경합니다.

 

타이머 일시 정지 및 재개

  • 게임 이벤트에 따라 타이머를 일시 정지하거나 재개할 수 있습니다.
  • 애니메이션의 재생 속도를 조절하여 시각적인 일치감을 줍니다.

 

시간 추가 및 애니메이션 효과

  • DOTween을 사용하여 남은 시간을 부드럽게 증가시키는 애니메이션을 구현합니다.

 

씬 전환 시 타이머 리셋

  • 특정 씬이 로드될 때 타이머를 리셋하여 새로운 레벨이나 미션에 대비합니다.

 

아래와 같이 ChatGPT에게 질문했어요.

※ ChatGPT 답변 내용 중 제가 별도로 표시한 굵은 글씨를 기준으로 읽으시면 좀 더 이해가 쉽게 될 거예요.


 

[질문]

안녕, ChatGPT. Unity에서 타이머를 구현하고, 시간이 흐름에 따라 UI를 업데이트하며, 타이머 종료 시 게임 오버를 처리하고 싶어. TimerManager 스크립트를 작성했는데, 어떻게 동작하는지 자세히 설명해 줄 수 있어?


 

[답변]

안녕하세요! TimerManager 스크립트를 통해 Unity에서 타이머를 구현하고, 시간에 따른 UI 업데이트 및 게임 오버 처리를 하는 방법을 단계별로 설명해 드릴게요.

 

1. 싱글턴 패턴을 통한 인스턴스 관리

public static TimerManager Instance { get; private set; }

private void Awake()
{
    if (Instance == null)
    {
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }
    else
    {
        Destroy(gameObject);
    }
}

 

설명:

  • 싱글턴 패턴을 사용하여 TimerManager의 인스턴스를 전역에서 접근할 수 있도록 합니다.
  • 이미 인스턴스가 존재하면 Destroy(gameObject)로 중복 생성을 방지합니다.

2. 타이머 초기화 및 시작

private void Start()
{
    remainingTime = totalTime;
    isTimerRunning = true;
    UpdateTimerText();
    UpdateDayText(); // 추가: Day 텍스트 업데이트
}

 

설명:

  • remainingTime을 totalTime으로 초기화하고, 타이머를 실행 상태로 설정합니다.
  • UpdateTimerText()와 UpdateDayText()를 호출하여 UI를 초기화합니다.

3. Day 텍스트 업데이트

private void UpdateDayText()
{
    int selectedDay = PlayerPrefs.GetInt("SelectedDayIndex", 0);
    dayText.text = $"Day {selectedDay + 1}";
}

 

설명:

  • PlayerPrefs에서 선택된 Day 인덱스를 가져와 dayText에 표시합니다.

4. 타이머 업데이트 및 UI 반영

private void Update()
{
    if (isTimerRunning && !isTimerPaused)
    {
        remainingTime -= Time.deltaTime;

        if (remainingTime <= 0)
        {
            remainingTime = 0;
            isTimerRunning = false;
            TriggerGameOver();
        }

        UpdateTimerText();
    }
}

 

설명:

  • 매 프레임마다 남은 시간을 감소시키고, 시간이 0이 되면 게임 오버를 트리거합니다.
  • UpdateTimerText()를 호출하여 UI에 남은 시간을 업데이트합니다.

5. 남은 시간에 따른 UI 효과

private void UpdateTimerText()
{
    int minutes = Mathf.FloorToInt(remainingTime / 60);
    int seconds = Mathf.FloorToInt(remainingTime % 60);

    if (remainingTime <= 10f)
    {
        // 깜빡임 효과
        timerText.color = Mathf.FloorToInt(remainingTime * 5) % 2 == 0 ? Color.red : Color.white;
        timerText.text = $"{Mathf.FloorToInt(remainingTime)}";
    }
    else if (remainingTime <= 60f)
    {
        timerText.color = Color.yellow;
        timerText.text = $"{Mathf.FloorToInt(remainingTime)}";
    }
    else
    {
        timerText.color = Color.white;
        timerText.text = string.Format("{0}:{1:00}", minutes, seconds);
    }
}

 

설명:

  • 남은 시간이 10초 이하일 때는 텍스트 색상을 빨간색과 흰색으로 번갈아 표시하여 긴박감을 줍니다.
  • 남은 시간이 1분 이하일 때는 텍스트 색상을 노란색으로 표시합니다.
  • 그 외에는 기본 색상과 분:초 형식으로 시간을 표시합니다.

6. 게임 오버 처리 및 애니메이션 제어

private void TriggerGameOver()
{
    if (LifeManager.Instance != null)
    {
        LifeManager.Instance.TriggerGameOver();
    }
    else
    {
        // Debug.LogError("LifeManager instance not found. Cannot trigger game over.");
    }

    if (targetAnimator != null)
    {
        targetAnimator.SetTrigger("StopAnimation");
    }
}

 

설명:

  • 남은 시간이 0이 되었을 때 게임 오버 이벤트를 발생시킵니다.
  • targetAnimator에 StopAnimation 트리거를 설정하여 애니메이션을 멈춥니다.

7. 타이머 일시 정지 및 재개

public void PauseTimer()
{
    isTimerPaused = true;

    if (targetAnimator != null)
    {
        targetAnimator.speed = 0f;
    }
}

public void ResumeTimer()
{
    isTimerPaused = false;

    if (targetAnimator != null)
    {
        targetAnimator.speed = 1f;
    }
}

 

설명:

  • 게임 이벤트에 따라 타이머를 일시 정지하거나 재개할 수 있습니다.
  • 애니메이터의 속도를 조절하여 애니메이션도 함께 제어합니다.

8. 타이머 리셋 및 씬 전환 처리

private void OnEnable()
{
    SceneManager.sceneLoaded += OnSceneLoaded;
}

private void OnDisable()
{
    SceneManager.sceneLoaded -= OnSceneLoaded;
}

private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
    if (scene.name == "DaySceneName") // 데이 씬 이름을 여기에 기재
    {
        ResetTimer();
    }
}

public void ResetTimer()
{
    remainingTime = totalTime;
    isTimerRunning = false;
    isTimerPaused = false;
    UpdateTimerText();
}

 

설명:

  • 씬이 로드될 때 이벤트를 구독하여 특정 씬에서 타이머를 리셋합니다.
  • OnSceneLoaded에서 씬 이름을 확인하고, 타이머를 초기화합니다.

9. 시간 추가 및 애니메이션 효과

public void AddTime(float timeToAdd)
{
    float startValue = remainingTime;
    float endValue = remainingTime + timeToAdd;

    DOTween.To(() => remainingTime, x =>
    {
        remainingTime = x;
        UpdateTimerText();
    }, endValue, 0.5f)
    .SetEase(Ease.OutQuad);
}

 

설명:

  • DOTween을 사용하여 남은 시간을 부드럽게 증가시키는 애니메이션을 구현합니다.
  • 시간이 증가하는 동안 UI도 실시간으로 업데이트됩니다.

전체 코드 예제

using UnityEngine;
using TMPro;
using WarehouseDeliveryQuest.UI;
using UnityEngine.SceneManagement;
using DG.Tweening;

namespace WarehouseDeliveryQuest.Managers
{
    public class TimerManager : MonoBehaviour
    {
        public static TimerManager Instance { get; private set; }

        public float totalTime = 239f;
        public TextMeshProUGUI timerText;
        public TextMeshProUGUI dayText; // Day를 표시하는 텍스트
        public Animator targetAnimator; // 애니메이션 제어를 위한 Animator

        private float remainingTime;
        private bool isTimerRunning = false;
        private bool isTimerPaused = false;

        private void Awake()
        {
            if (Instance == null)
            {
                Instance = this;
                DontDestroyOnLoad(gameObject);
            }
            else
            {
                Destroy(gameObject);
            }
        }

        private void Start()
        {
            remainingTime = totalTime;
            isTimerRunning = true;
            UpdateTimerText();
            UpdateDayText();
        }

        private void OnEnable()
        {
            SceneManager.sceneLoaded += OnSceneLoaded;
        }

        private void OnDisable()
        {
            SceneManager.sceneLoaded -= OnSceneLoaded;
        }

        private void Update()
        {
            if (isTimerRunning && !isTimerPaused)
            {
                remainingTime -= Time.deltaTime;

                if (remainingTime <= 0)
                {
                    remainingTime = 0;
                    isTimerRunning = false;
                    TriggerGameOver();
                }

                UpdateTimerText();
            }
        }

        private void UpdateTimerText()
        {
            int minutes = Mathf.FloorToInt(remainingTime / 60);
            int seconds = Mathf.FloorToInt(remainingTime % 60);

            if (remainingTime <= 10f)
            {
                timerText.color = Mathf.FloorToInt(remainingTime * 5) % 2 == 0 ? Color.red : Color.white;
                timerText.text = $"{Mathf.FloorToInt(remainingTime)}";
            }
            else if (remainingTime <= 60f)
            {
                timerText.color = Color.yellow;
                timerText.text = $"{Mathf.FloorToInt(remainingTime)}";
            }
            else
            {
                timerText.color = Color.white;
                timerText.text = string.Format("{0}:{1:00}", minutes, seconds);
            }
        }

        private void UpdateDayText()
        {
            int selectedDay = PlayerPrefs.GetInt("SelectedDayIndex", 0);
            dayText.text = $"Day {selectedDay + 1}";
        }

        private void TriggerGameOver()
        {
            if (LifeManager.Instance != null)
            {
                LifeManager.Instance.TriggerGameOver();
            }
            else
            {
                Debug.LogError("LifeManager instance not found. Cannot trigger game over.");
            }

            if (targetAnimator != null)
            {
                targetAnimator.SetTrigger("StopAnimation");
            }
        }

        public void PauseTimer()
        {
            isTimerPaused = true;

            if (targetAnimator != null)
            {
                targetAnimator.speed = 0f;
            }
        }

        public void ResumeTimer()
        {
            isTimerPaused = false;

            if (targetAnimator != null)
            {
                targetAnimator.speed = 1f;
            }
        }

        public void ResetTimer()
        {
            remainingTime = totalTime;
            isTimerRunning = false;
            isTimerPaused = false;
            UpdateTimerText();
        }

        public float GetRemainingTime()
        {
            return remainingTime;
        }

        private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
        {
            if (scene.name == "DaySceneName") // 실제 씬 이름으로 변경하세요
            {
                ResetTimer();
            }
        }

        public void AddTime(float timeToAdd)
        {
            float startValue = remainingTime;
            float endValue = remainingTime + timeToAdd;

            DOTween.To(() => remainingTime, x =>
            {
                remainingTime = x;
                UpdateTimerText();
            }, endValue, 0.5f)
            .SetEase(Ease.OutQuad);
        }
    }
}

 


 

오늘은 TimerManager 스크립트를 활용하여 Unity에서 타이머를 구현하고, 시간에 따른 UI 업데이트와 게임 오버 처리, 그리고 애니메이션 제어까지 하는 방법을 알아봤어요.

 

이 시스템을 통해 게임 플레이에 긴박감을 주고, 플레이어에게 도전적인 요소를 제공할 수 있습니다.

 

지금까지 인텔리원스튜디오(IntelliOneStudio)였습니다!

감사합니다.

728x90