[Unity][3D-Game] Tower Defense Game (11)

suhan0304·2023년 12월 15일
0
post-thumbnail

강의영상 (13)


개발

Lives 구현

플레이어의 목숨, Lives가 얼마나 남았는지를 PlayerStatus에 추가하고 원하는 값으로 게임 시작 시에 초기화 되도록 한다.

PlayerStatus.cs

public static int Lives; //현재 생명 값
public static int startLives = 20; //초기화 값
void Start()
{
    Money = startMoney; //게임 시작 시 Money를 시작 머니로 설정    
    Lives = startLives;
}

이제 생명을 표시하기 위해 기존에 만들어놨던 하단 UI를 그대로 복사 붙여넣기 한 후 맵의 윗부분에 위치시킨다.

  • 하단 캔버스 이름 : Bottom Canvas
  • 상단 캔버스 이름 : Top Canvas

상단 캔버스를 아래와 같이 설정한다.

  • scale을 0.1, 0.1, 0.1로 설정
  • 크기를 적절하게 조절
  • Money 자식 오브젝트를 삭제
  • Timer의 Text를 하단 가운데 정렬
  • Timer를 Lives로 변경 후 폰트 사이즈를 200으로 설정

Lives 연결

현제 체력에 맞게 Lives UI가 업데이트 되도록 LivesUI 스크립트를 다음과 같이 작성해주고 Lives의 컴포넌트로 추가해준다.

저번에도 언급했지만 이처럼 프레임 단위로 갱신하는 것은 비효율적이라 실제로는 수치가 변할 때만 UI가 업데이트 되도록 하면 된다. 하지만 현재 프로젝트는 규모가 작고 가볍기 때문에 단순한 방식으로 구현하기 위해 update 문 안에 나이브하게 코드를 작성한 것이다.

LivesUI.cs

public class LivesUI : MonoBehaviour
{
    public Text livesText;

    // Update is called once per frame
    void Update()
    {
        livesText.text = PlayerStats.Lives.ToString() + " LIVES";
    }
}

이제 Lives를 인스펙터를 이용해 초기화 해주고 실행시키면 정상적으로 업데이트가 수행될 것이다.

경로의 종료 지점 도달

Enemy가 경로의 끝에 도착하게 되면 lives가 감소하는 로직을 구현한다.

Enemy.cs

void GetNextWayPoint()
{
    if (wavepointIndex >= WayPoints.points.Length - 1) //만약 가지고 있는 모든 웨이포인트를 방문 > 도착 지점 도달
    {
        EndPath();
        return; //아래의 다음 웨이포인트 가져오는 것을 하지 않고 바로 종료
    }

    wavepointIndex++; //다음 웨이포인트 인덱스
    target = WayPoints.points[wavepointIndex]; //다음 인덱스의 웨이포인트 오브젝트를 받아온다.
}

void EndPath() //경로의 끝(목표)에 도달
{
    Destroy(gameObject); //도착지점 도착 시 오브젝트 파괴
    PlayerStats.Lives--; // lives를 1 감소
}

Lives 감소

live 감소를 PlayerStatus의 메소드 상에서 실행되도록 구현하는 것이 좋다.

게임이 커지고, 최적화가 필요해지면 기능들을 로컬에서 처리하는게 더 합리적이다. 따라서 Lives를 Enemy에서 마이너스해주는 것이 아니라 PlayerStatus에 1을 감소시키는 메소드를 만들고 호출한다.

종료 로직

게임의 상태를 전반적으로 관리할 GameManager 스크립트를 생성하고 lives를 확ㅇ니해 0보다 작거나 같아지면 게임이 종료되도록 EndGame을 구현한다.

PlayerStats.cs

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

public class GameManager : MonoBehaviour
{
    private bool gameEnded = false; 
    1
    void Update()
    {
        if (gameEnded)
            return;

        if(PlayerStats.Lives <= 0) //플레이어 체력이 0 이하
        {
            EndGame(); //게임 종료 함수
        }
    }


    void EndGame()
    {
        gameEnded = true;
        Debug.Log("Game Over!"); //게임 종료
    }
}

Enemy 데미지 로직

이제 포탑의 공격을 받으면 감소될 Enemy의 체력 로직을 구현해보자. Enemy 스크립트에 health를 만들어주고 TakeDamage 함수로 데미지를 입었을때 체력이 감소하도록하고 체력이 0보다 작아지면 에너미가 죽도록 구현한다.

Enemy.cs

public void TakeDamage(int amount)
{
    health -= amount; //amount 만큼 체력 감소

    if(health <= 0)
    {
        Die(); // 에너미 사망 메서드
    }
}

void Die()
{
    Destroy(gameObject);
}

이제 미리 구현해놨던 Bullet 스크립트의 Damage에서 TakeDamage를 호출할 수 있도록 다음과 같이 구현한다.

Bullet.cs

void Damage(Transform enemy) // 데미지 입력
{
    Enemy e = enemy.GetComponent<Enemy>();  //Enemy 오브젝트의 컴포넌트 enemy 스크립트를 e로 불러온 후에

    if (e != null) // e가 null이 아닐 때만 실행
    {
        e.TakeDamage(damage);                   //enemy 스크립트 내부 메서드인 TakeDamage에 damage 변수를 넘겨서 데미지 로직 실행
    }
}

매개변수로 넘어온 Enemy를 그대로 사용하지 않은 이유는 Enemy의 체력을 감소시키는 TakeDamage는 Enemy 스크립트의 메소드이기 때문에 Enemy 스크립트 컴포넌트를 가져와야 한다. 따라서 GetComponent로 Enemy 스크립트를 가져온 후에 TakeDamage를 실행해야 정상적으로 체력을 감소 시킬 수 있다.

Bullet의 damage는 50으로 설정해놨지만 각 Bullet의 프리팹의 가서 Bullet 스크립트의 damage를 수정하면서 총알의 종류에 따라 데미지를 다르게 지정할 수도 있다.

Bullet 스크립트 컴포넌트의 Damage를 원하는 값으로 설정 후 사용

Enemy 사망 이벤트

적 오브젝트를 죽였으면 이제 여러 가지 이벤트(돈 증가, 사망 이펙트 추가)들이 실행되도록 구현한다.

Money 이벤트

value라는 변수로 에너미 사망 시 플레이어에게 지급할 돈을 초기화 해준 후 Die 메소드 내부에서 플레이어에게 돈을 지급하돌고 구현한다.

Enemy.cs

void Die()
{
    PlayerStats.Money += value; //플레이어에게 돈 지급
    Destroy(gameObject);
}

Enemy 사망 이펙트

적 오브젝트 사망 시 실행 될 이펙트를 BulletImpactEffect를 그대로 복사해와서 새로 생성한다.

  • 이름은 EnemyDeathEffect로 설정
  • Renderer의 Material을 기존의 Enemy Material을 사용

기타 설정은 강의 영상이나 프로젝트 내부의 파티클 시스템 오브젝트를 참고하자.

이제 생성한 이펙트 오브젝트를 선언 및 초기화 해준후에 Die 함수 내부에서 Play하도록 구현한다.

Instantiate로 복사하면 알아서 처음에 실행된다. 실행되고 나서 5초 후에 이펙트 오브젝트가 삭제되도록 이펙트 Play를 구현한다.

Enemy.cs

void Die()
{
    PlayerStats.Money += value; //플레이어에게 돈 지급

    Instantiate(deathEffect, transform.position, Quaternion.identity);
    Destroy(deathEffect, 5f);

    Destroy(gameObject);
}


결과물

profile
Be Honest, Be Harder, Be Stronger

0개의 댓글