눈팅하는 게임개발자 블로그
개발 기록 - Enemy 본문
웨이브가 시작되면 하나씩 나타나서 주어진 경로로 움직이는 Enemy.
이 게임에서는 웨이브 당 1종류, 총 5종류의 Enemy를 사용하였다.
체력이 높거나,
스피드가 빠르거나,
방어력이 높거나,
2개로 분열하거나,
터렛을 마비시키는 스킬을 쓰거나
의 특성을 가진 5종류이다.
게임 한판 한판이 어떤 특성을 가진 Enemy가 어떤 wave에서 나오는지가 무작위이기 때문에
플레이어는 Enemy가 나오는 웨이브를 파악해서 터렛을 어디에 배치할 지 잘 생각해야 한다.
관련 코드 Enemy, Enemy_Speed
github.com/Palamore/TATD_Codes/blob/master/Enemy.cs
github.com/Palamore/TATD_Codes/blob/master/Enemy_Speed.cs
기본적으로 Enemy가 해야할 동작은
경로 탐색,
이동,
공격을 받기?(Take Damage)에
까지가 Enemy의 기본 동작이고
각 종류의 Enemy가 고유한 스킬을 사용할 수 있도록 했다.
using UnityEngine;
using UnityEngine.UI;
public class Enemy : MonoBehaviour {
WaveSpawner WS;
//Way Point
private Waypoints2 WayPoints;
protected Transform target;
protected int waypointIndex = 0;
public Transform TargetToLook;
//Stats
protected float baseSpeed;
protected float speed;
public GameObject HPBarPrefab;
public float health;
public float MAXhealth;
protected float defenseRate;
public float mana;
public float MAXmana;
public GameObject deathEffect;
public Material[] lvMaterial = new Material[6];
public GameObject lvBelt;
private float debuffTimer;
private float manaTimer;
void Start()
{
WS = WaveSpawner.Instance();
mana = 0.0f;
MAXmana = 100.0f;
manaTimer = 25.0f;
speed = WS.speed;
baseSpeed = speed;
MAXhealth = WS.Enemy_health_stack_now;
MAXhealth = MAXhealth * WS.healthRate;
health = MAXhealth;
defenseRate = WS.defenseRate;
Instantiate(HPBarPrefab, transform.position + new Vector3(0.0f, 5.0f, 0.0f), Quaternion.identity);
GameObject WP = GameObject.FindGameObjectWithTag("WaypointHolder");
WayPoints = WP.GetComponent<Waypoints2>();
target = WayPoints.points[0];
WS.Enemy_Counter++;
debuffTimer = 0.0f;
lvBelt.GetComponent<Renderer>().material = lvMaterial[(WS.stageLevel - 1)];
}
public void TakeDamage (float amount)
{
health -= amount * defenseRate; // 방어력수치적용.
if(health <= 0.0f)
{
Die();
}
}
public void TakeTrueDamage(float amount)
{
health -= amount;
if(health <= 0.0f)
{
Die();
}
}
public void SetTargetToLook(Transform tr)
{
TargetToLook = tr;
}
void Die()
{
GameObject effect = (GameObject)Instantiate(deathEffect, transform.position, Quaternion.identity);
Destroy(effect, 5f);
Destroy(gameObject);
}
void Update()
{
Vector3 dir = target.position - transform.position;
transform.Translate(dir.normalized * speed * Time.deltaTime, Space.World);
transform.LookAt(target);
if (Vector3.Distance(transform.position, target.position) <= 0.7f)
{
GetNextWaypoint();
}
if (mana >= 100.0f)
{
SkillCast();
mana = -0.01f;
}
else
{
if (WS.Game_difficulty >= 2)
mana += Time.deltaTime * manaTimer;
}
if (debuffTimer >= 0.0f)
{
debuffTimer -= (Time.deltaTime * 0.9f);
}
else
{
speed = baseSpeed;
}
}
public void Debuff(float spd)
{
debuffTimer += 1.0f / 60.0f;
if (baseSpeed * spd >= speed)
return;
speed = baseSpeed * spd;
}
void GetNextWaypoint()
{
if (waypointIndex >= WayPoints.points.Length - 1)
{
ReachToPath();
return;
}
waypointIndex++;
target = WayPoints.points[waypointIndex];
}
void ReachToPath()
{
WS.Enemy_Stacked++;
if(WS.Enemy_Stacked == 20)
{
GameManager.Instance().GameOver();
}
Die();
}
protected virtual void SkillCast()
{
}
}
모든 Enemy의 기본 뼈대가 되는 Enemy 클래스.
public void TakeDamage (float amount)
{
health -= amount * defenseRate; // 방어력수치적용.
if(health <= 0.0f)
{
Die();
}
}
public void TakeTrueDamage(float amount)
{
health -= amount;
if(health <= 0.0f)
{
Die();
}
}
void Die()
{
GameObject effect = (GameObject)Instantiate(deathEffect, transform.position, Quaternion.identity);
Destroy(effect, 5f);
Destroy(gameObject);
}
데미지를 받는 부분의 함수들, 별거 없다. HP가 0이 되면 이펙트를 생성하고 사망.
void Update()
{
Vector3 dir = target.position - transform.position;
transform.Translate(dir.normalized * speed * Time.deltaTime, Space.World);
transform.LookAt(target);
if (Vector3.Distance(transform.position, target.position) <= 0.7f)
{
GetNextWaypoint();
}
if (mana >= 100.0f)
{
SkillCast();
mana = -0.01f;
}
else
{
if (WS.Game_difficulty >= 2)
mana += Time.deltaTime * manaTimer;
}
if (debuffTimer >= 0.0f)
{
debuffTimer -= (Time.deltaTime * 0.9f);
}
else
{
speed = baseSpeed;
}
}
void GetNextWaypoint()
{
if (waypointIndex >= WayPoints.points.Length - 1)
{
ReachToPath();
return;
}
waypointIndex++;
target = WayPoints.points[waypointIndex];
}
void ReachToPath()
{
WS.Enemy_Stacked++;
if(WS.Enemy_Stacked == 20)
{
GameManager.Instance().GameOver();
}
Die();
}
Enemy의 target은 경로상에 꺾이는 부분마다 설치되어 있는 waypoint가 된다.
target쪽으로 Translate로 움직이다가 target(waypoint)과의 Distance가 0.7 이하가 되면
GetNextWaypoint()로 다음 waypoint을 찾는다.
마지막 waypoint에 도달했다면 ReachToPath()함수 실행.
20개의 Enemy가 마지막 waypoint에 도달하면 게임 오버.
그 아래는 마나가 100이 되면 Skill을 쓰는 부분과
마나가 자동으로 채워지는 부분(hard 난이도 이상에서)
laser turret에게 공격을 당해서 speed가 느려지는 부분이 있다.
public void Debuff(float spd)
{
debuffTimer += 1.0f / 60.0f;
if (baseSpeed * spd >= speed)
return;
speed = baseSpeed * spd;
}
laser turret에게 공격을 받으면 실행되는 Debuff 함수.
이 Enemy 클래스를 상위 클래스로 자식 클래스들을 만들어주고
각 자식 클래스에서 SkillCast() 함수만 구현.
스킬은 각각의 특성에 따라 구현하였다.
속도 타입은 속도가 빨라지고, 방어력 타입은 방어력이 높아지고, 분열하는 타입은 또 분열하는 식의 내용이다.
'Project > Tactical Architect Tower Defense' 카테고리의 다른 글
개발 기록 - 시너지 (0) | 2020.09.18 |
---|---|
개발 기록 - 상점 시스템 (0) | 2020.09.18 |
개발 기록 - Turret (0) | 2020.09.14 |
개발 기록 - Tutorial Scene (0) | 2020.09.13 |
개발 기록 - Title Scene (0) | 2020.09.13 |