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

게임개발 중급(50) - Monster Killer(12)

by jyppro 2023. 6. 11.

Monster Killer(12)

저번에는 데미지 텍스트를 구현하는 과정에 대해 다뤄보았습니다. 이번에는 플레이어에게 스테이지에 대한 정보를 알려주기 위한 UI를 만드는 과정을 다뤄보겠습니다.

 

스테이지 UI 생성

스테이지 UI는 다양한 방식을 통해 사람들에게 다음 스테이지를 안내해 줄 수 있습니다. 저는 매우 간단하게 체력바와 같은 슬라이더를 사용하여 UI를 만들겠습니다.

스테이지-UI
스테이지 UI

체력바 밑에 작게 슬라이더 하나가 생겼습니다. 근데 이 슬라이더는 점이 4개가 존재합니다. 이 스테이지바에서 점은 몬스터의 개수 및 단계를 시각적으로 표시해줍니다. 저는 이 UI에서 체력과 마찬가지로 빨간색으로 된 슬라이더의 value값을 사용하여 진행도를 표시해 주겠습니다.

 

Monster Controller

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

public class MonsterController : MonoBehaviour
{
    private Canvas canvas; // 캔버스
    private float maxHealth = 100f; // 최대 체력
    private float currentHealth = 100f; // 현재 체력
    public Slider healthSlider; // 체력바 슬라이더
    public Slider healthSliderCopy;
    public Slider StageBar; // 스테이지를 나타내는 UI
    public Slider StageBarCopy;
    public TextMeshProUGUI textHP; // 체력바 텍스트
    public TextMeshProUGUI textHPCopy;
    public GameObject damageTextPrefab; // 데미지 텍스트
    public GameObject damageTextPrefabCopy;
    private Animator animator; // 애니메이션
    public GameObject nextMonsterPrefab; // 다음 몬스터로 전환할 때 사용할 프리팹

    void Start()
    {
        canvas = FindObjectOfType<Canvas>(); // Canvas 찾기
        animator = GetComponent<Animator>(); // 애니메이터 연결
        healthSliderCopy = this.healthSlider;
        StageBarCopy = this.StageBar;
        textHPCopy = this.textHP;
        damageTextPrefabCopy = this.damageTextPrefab;
        UpdateHealthSlider();
    }

    public void TakeDamage(int damage) // 데미지 주는 함수
    {
        currentHealth -= damage;
        if (currentHealth <= 0f)
        {
            currentHealth = maxHealth;
            // 체력이 전부 소진되면 다른 몬스터로 전환하고 체력 증가
            animator.SetBool("Death", true);
            Invoke("SpawnNextMonster", 3f);
        }
        UpdateHealthSlider();
    }

    private void SpawnNextMonster()
    {
        if (nextMonsterPrefab != null)
        {
            GameObject nextMonster = Instantiate(nextMonsterPrefab, transform.position, transform.rotation);
            MonsterController nextMonsterController = nextMonster.GetComponent<MonsterController>();
            nextMonsterController.healthSlider = healthSliderCopy;
            nextMonsterController.StageBar = StageBarCopy;
            nextMonsterController.textHP = textHPCopy;
            nextMonsterController.damageTextPrefab = damageTextPrefabCopy;
            nextMonsterController.maxHealth = maxHealth;
            nextMonsterController.currentHealth = currentHealth;
            nextMonsterController.UpdateHealthSlider();
            nextMonsterController.IncreaseHealth();
        }
        StageBar.value += 0.33f;
        if(StageBarCopy.value >= 1f)
        {
            StageBarCopy.value = 0f;
        }
        Destroy(gameObject);
    }

    public void UpdateHealthSlider()
    {
        float decreaseHp = currentHealth / maxHealth;
        // 체력바 슬라이더의 값을 현재 체력 비율로 설정
        healthSlider.value = decreaseHp;
        textHP.text = $"{currentHealth} / {maxHealth}";
    }

    public void IncreaseHealth()
    {
        maxHealth = (maxHealth + 23f) * 2.2f;

        if (currentHealth != maxHealth)
        {
            currentHealth = maxHealth;
            UpdateHealthSlider();
        }
    }

    public void ShowDamageText(int damage, Vector3 position)
    {
        GameObject damageTextObject = Instantiate(damageTextPrefab, position, Quaternion.identity);
        TextMeshProUGUI textComponent = damageTextObject.GetComponent<TextMeshProUGUI>();
        textComponent.text = "-" + damage.ToString();

        // 텍스트의 부모를 Canvas로 설정
        damageTextObject.transform.SetParent(canvas.transform, false);

        // 애니메이션 적용 (예: 위로 올라가면서 사라짐)
        StartCoroutine(AnimateDamageText(textComponent));
    }

    private IEnumerator AnimateDamageText(TextMeshProUGUI textComponent)
    {
        float duration = 1.5f; // 애니메이션 지속 시간
        float elapsedTime = 0f;

        Vector3 startPosition = textComponent.transform.position;
        Vector3 endPosition = startPosition + Vector3.up * 50f; // 위로 올라갈 위치
        Color startColor = textComponent.color;
        Color endColor = new Color(startColor.r, startColor.g, startColor.b, 0f); // 투명도 조정

        while (elapsedTime < duration)
        {
            float progress = elapsedTime / duration;

            // 위치 이동
            textComponent.transform.position = Vector3.Lerp(startPosition, endPosition, progress);

            // 투명도 조정
            textComponent.color = Color.Lerp(startColor, endColor, progress);

            elapsedTime += Time.deltaTime;
            yield return null;
        }

        Destroy(textComponent.gameObject); // 애니메이션 종료 후 텍스트 오브젝트 제거
    }
}

크게 바뀐것은 없습니다. 기존의 UI를 다루던 방식대로, StageBar 슬라이더를 연결해주고, Copy합니다. 그리고 SpawnNextMonster() 가 실행될 때마다, value값을 3단계로 나눈 0.33만큼 깎아줍니다. value값이 1이 넘어가게 되면 다음 스테이지로 넘어가는 데 문제가 생길 수 있으니 0으로 초기화 시켜줍니다.

 

게임을 실행시켜 보겠습니다.

1스테이지-클리어
1스테이지 클리어

첫번째 몬스터를 사냥하면, 두번째 스테이지로 넘어가며 빨간색으로 진행도가 표시된 것을 확인할 수 있었습니다.

 

마지막-스테이지
마지막 스테이지

마지막 스테이지의 몬스터를 잡기 전 모습입니다. 진행도가 가득 차있는 모습을 볼 수 있습니다. 이 몬스터를 죽이면 현재까지는 다음 몬스터가 없는 상황입니다.

 

진행도-초기화
진행도 초기화

다음 몬스터가 없어서 현재는 진행이 불가하지만, 추후에 추가하면 됩니다. 스테이지의 마지막 몬스터를 잡아 진행도를 초기화 시켜준 모습입니다.

 

<NEXT>

오늘은 간단하게 스테이지 진행도를 UI로 보여주는 방법에 대해서 진행하였습니다. 다음에는 플레이어의 체력과 몬스터의 공격을 만들어 보도록 하겠습니다. 감사합니다.