Tower Defence Game
일반적인 디펜스 게임으로 스테이지가 존재하고 각 몬스터는 길을 따라 이동을 한다.
라이프가 존재하고 돈을 가지고 타워를 설치가 가능하다. 타워를 설치(타일 클릭)를 하게 되면 돈이 차감되고 타워가 설치되고 공격 범위에 몬스터가 존재하면 공격을 시도한다.
총 3가지의 타워가 존재하고 가격은 각기 다르다.
프로젝트 링크 주소 : [링크]
<플레이 영상>
------------------------------------
몬스터들은 WayPoint를 기준으로 이동을 한다. 다음 순서의 WayPoint를 기준으로 타겟 이동을 진행한다.
<EnemyMovement>
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(Enemy))]
public class EnemyMovement : MonoBehaviour {
private Transform target;
private int wavepointIndex = 0;
private Enemy enemy;
void Start()
{
enemy = GetComponent<Enemy>();
target = Waypoints.points[0];
}
void Update()
{
Vector3 dir = target.position - transform.position;
transform.Translate(dir.normalized * enemy.speed * Time.deltaTime, Space.World);
if (Vector3.Distance(transform.position, target.position) <= 0.4f)
{
GetNextWaypoint();
}
enemy.speed = enemy.startSpeed;
}
void GetNextWaypoint()
{
if (wavepointIndex >= Waypoints.points.Length - 1)
{
EndPath();
return;
}
wavepointIndex++;
target = Waypoints.points[wavepointIndex];
}
void EndPath()
{
PlayerStats.Lives--;
WaveSpawner.EnemiesAlive--;
Destroy(gameObject);
}
}
<WaveSpawner>
public class WaveSpawner : MonoBehaviour {
public static int EnemiesAlive = 0;
public Wave[] waves;
public Transform spawnPoint;
public float timeBetweenWaves = 5f;
private float countdown = 2f;
public Text waveCountdownText;
public GameManager gameManager;
private int waveIndex = 0;
void Update ()
{
if (EnemiesAlive > 0)
{
return;
}
if (waveIndex == waves.Length)
{
gameManager.WinLevel();
this.enabled = false;
}
if (countdown <= 0f)
{
StartCoroutine(SpawnWave());
countdown = timeBetweenWaves;
return;
}
countdown -= Time.deltaTime;
countdown = Mathf.Clamp(countdown, 0f, Mathf.Infinity);
waveCountdownText.text = string.Format("{0:00.00}", countdown);
}
IEnumerator SpawnWave ()
{
PlayerStats.Rounds++;
Wave wave = waves[waveIndex];
EnemiesAlive = wave.count;
for (int i = 0; i < wave.count; i++)
{
SpawnEnemy(wave.enemy);
yield return new WaitForSeconds(1f / wave.rate);
}
waveIndex++;
}
void SpawnEnemy (GameObject enemy)
{
Instantiate(enemy, spawnPoint.position, spawnPoint.rotation);
}
}
터렛의 능력치 및 범위 설정
<Turret.cs>
using UnityEngine;
using System.Collections;
public class Turret : MonoBehaviour {
private Transform target;
private Enemy targetEnemy;
[Header("General")]
public float range = 15f;
[Header("Use Bullets (default)")]
public GameObject bulletPrefab;
public float fireRate = 1f;
private float fireCountdown = 0f;
[Header("Use Laser")]
public bool useLaser = false;
public int damageOverTime = 30;
public float slowAmount = .5f;
public LineRenderer lineRenderer;
public ParticleSystem impactEffect;
public Light impactLight;
[Header("Unity Setup Fields")]
public string enemyTag = "Enemy";
public Transform partToRotate;
public float turnSpeed = 10f;
public Transform firePoint;
// Use this for initialization
void Start () {
InvokeRepeating("UpdateTarget", 0f, 0.5f);
}
void UpdateTarget ()
{
GameObject[] enemies = GameObject.FindGameObjectsWithTag(enemyTag);
float shortestDistance = Mathf.Infinity;
GameObject nearestEnemy = null;
foreach (GameObject enemy in enemies)
{
float distanceToEnemy = Vector3.Distance(transform.position, enemy.transform.position);
if (distanceToEnemy < shortestDistance)
{
shortestDistance = distanceToEnemy;
nearestEnemy = enemy;
}
}
if (nearestEnemy != null && shortestDistance <= range)
{
target = nearestEnemy.transform;
targetEnemy = nearestEnemy.GetComponent<Enemy>();
} else
{
target = null;
}
}
// Update is called once per frame
void Update () {
if (target == null)
{
if (useLaser)
{
if (lineRenderer.enabled)
{
lineRenderer.enabled = false;
impactEffect.Stop();
impactLight.enabled = false;
}
}
return;
}
LockOnTarget();
if (useLaser)
{
Laser();
} else
{
if (fireCountdown <= 0f)
{
Shoot();
fireCountdown = 1f / fireRate;
}
fireCountdown -= Time.deltaTime;
}
}
void LockOnTarget ()
{
Vector3 dir = target.position - transform.position;
Quaternion lookRotation = Quaternion.LookRotation(dir);
Vector3 rotation = Quaternion.Lerp(partToRotate.rotation, lookRotation, Time.deltaTime * turnSpeed).eulerAngles;
partToRotate.rotation = Quaternion.Euler(0f, rotation.y, 0f);
}
void Laser ()
{
targetEnemy.TakeDamage(damageOverTime * Time.deltaTime);
targetEnemy.Slow(slowAmount);
if (!lineRenderer.enabled)
{
lineRenderer.enabled = true;
impactEffect.Play();
impactLight.enabled = true;
}
lineRenderer.SetPosition(0, firePoint.position);
lineRenderer.SetPosition(1, target.position);
Vector3 dir = firePoint.position - target.position;
impactEffect.transform.position = target.position + dir.normalized;
impactEffect.transform.rotation = Quaternion.LookRotation(dir);
}
void Shoot ()
{
GameObject bulletGO = (GameObject)Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
Bullet bullet = bulletGO.GetComponent<Bullet>();
if (bullet != null)
bullet.Seek(target);
}
void OnDrawGizmosSelected ()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, range);
}
}
-----------------------------------
<총평>
기본적인 디펜스 게임의 기본을 분석해봤다.
몬스터가 특정 위치에서 스폰이되고 Waypoint를 통해서 길 찾기를 처리한다.
타워가 설치가 가능한 위치들에 타워가 설치가 되고 타워는 기본적으로 범위와 공격에 대한 설정이 처리가 있다.
총 28부작으로 되어있는 Tutorial의 영상이다. 하나씩 차근차근 따라 해도 좋은 것 같다.
Unity Tutorial : [How to make a Tower Defense Game]
★★★★☆
반응형
'개발 > Unity) 코드분석' 카테고리의 다른 글
코드분석) MegaMan-Unity-8Bit(메가멘 8비트) (0) | 2021.08.16 |
---|---|
코드분석) 유전 알고리즘(카트) (0) | 2021.05.09 |
코드분석) Spy Game (0) | 2021.05.02 |
Unity)코드분석) Red Runner (0) | 2021.02.13 |
Unity)코드분석) 유니티 로얄 - Unity Royal (2) | 2021.01.05 |
댓글