Tower Defender: Waves of War


Overview

This project is a mobile tower defense game developed in a month as part of a coursework project in 2023. It features the core gameplay elements of the tower defense genre and was designed with a structured approach to key defense mechanics.

⚠️ Note: No design documents or assets were provided. I planned the game, built the system, and chose all assets myself.

View Repository
Watch Demo


Game Showcase

Defend the central tower across 10 waves by collecting resources, building towers, and deploying autonomous units. Adapt your strategy as monster patterns shift and resource timing becomes critical.


Core Features

⚠️ Note: All code snippets are simplified versions. Full implementation in the repository

1. Randomized Resource & Monster Spawner

Resources and monsters use separate randomized systems to serve distinct gameplay roles.

Randomized Resource Spawner

  • Resources are randomly placed at game start. Some require multiple taps to collect and can spawn follow-up objects before being removed.

FieldManager Class

void GenerateObjects(int amount)
{
	for (int i = 0; i < amount; i++)
	{
		int prefabIdx = Random.Range(0, pickUpPrefabList.Count);
		PickUp prefab = pickUpPrefabList[prefabIdx];
		CapsuleCollider2D collider = 
			prefab.GetComponent<CapsuleCollider2D>();
 
		for (int attempt = 0; attempt < 500; attempt++)
		{
			Vector2 spawnPos =
				new(Random.Range(p00.x, p11.x), Random.Range(p00.y, p11.y));
 
			Collider2D[] overlapList = Physics2D.OverlapBoxAll(spawnPos, collider.size, 0, blockLayer);
			if (overlapList.Length == 0)
			{
				PickUp clonedObj = Instantiate(prefab, spawnPos, Quaternion.identity, transform);
				pickUpSpawnedList.Add(clonedObj);
				clonedObj.Init();
 
				break;
			}
		}
	}
}
Spawns a given number of resource objects at random positions within the field bounds. Each object is placed only if the spawn area is free of overlaps, using its collider size for precise spacing. A maximum of 500 attempts per object ensures safe placement without infinite loops.


Randomized Monster Spawner

  • Monsters are spawned dynamically during gameplay at regular intervals in each wave. They appear from random edges of the map, and spawn rate increases as the game progresses.

EnemyManager Class

void SpawnEnemy()
{
	if (enemyPrefabList.Count > 0)
	{
		int prefabIdx = Random.Range(0, enemyPrefabList.Count);
 
		Enemy clonedObj = Instantiate(enemyPrefabList[prefabIdx], RandomSpawnPos(), Quaternion.identity, transform);
		enemySpawnedList.Add(clonedObj);
		clonedObj.Init();
	}
}
 
Vector3 RandomSpawnPos()
{
	Vector3 spawnPosition = Vector3.zero;
	int randomSide = Random.Range(0, 4);
	float interval = Random.Range(0f, 1f);
	switch (randomSide)
	{
		case 0: spawnPosition = Vector3.Lerp(p00, p10, interval); break;
		case 1: spawnPosition = Vector3.Lerp(p01, p11, interval); break;
		case 2: spawnPosition = Vector3.Lerp(p00, p01, interval); break;
		case 3: spawnPosition = Vector3.Lerp(p10, p11, interval); break;
	}
	return spawnPosition;
}
SpawnEnemy() is triggered at every m_NextSpawnTime to instantiate a random enemy prefab at a random edge of the map. The spawn position blends between predefined corner points, ensuring enemies enter from all directions. Each enemy is initialized and added to enemySpawnedList.


2. Simple AI Pattern for Units (FSM Structure)

All units follow simple finite state machine (FSM) to manage movement, targeting, and attack behaviors in real time. (see the 2D Platformer Game for a similar FSM implementation)

The yellow and red circles drawn via OnDrawGizmos() visually represent each unit’s searchRadius and attackRadius, respectively.

  • Guardians
    • State:
      • IDLE → CHASE → ATTACK
      • Transitions based on enemy proximity.
    • Targeting:
      • SearchEnemy() scans surrounding area with Physics2D.OverlapCircleAll() to locate the nearest Enemy.
    • Combat Logic:
      • Pursues target when within searchRadius, stops when close enough to attack. Returns to IDLE if no valid target remains.
  • Enemies
    • State:
      • MOVE → ATTACK
      • Continuously seeks targets and engages when in range.
    • Multi-Target Logic:
      • Prioritizes targets in this order:
        • Guardian → GuardianTower → CentralTower
    • Fallback Behavior:
      • If no Guardian is within searchRadius, enemies continue moving toward the central tower as their final objective.

3. Mobile-Friendly Camera Control

Supports drag and pinch gestures for camera movement and zoom. Keeps the camera view within map boundaries at all times.

CameraManager Class

void MobileInput()
{
	if (Input.touchCount == 0) 
		return;
	
	Touch t0 = Input.GetTouch(0);
 
	if (Input.touchCount == 1)
	{
		if (t0.phase == TouchPhase.Began)
		{
			m_TouchBeganPos = m_Camera.ScreenToWorldPoint(t0.position);
		}
		else if (t0.phase == TouchPhase.Moved)
		{
			Vector3 moved = m_TouchBeganPos - m_Camera.ScreenToWorldPoint(t0.position);
			m_CameraTrans.position += moved;
		}
	}
	else if (Input.touchCount == 2)
	{
		Touch t1 = Input.GetTouch(1);
 
		Vector2 prev0 = t0.position - t0.deltaPosition;
		Vector2 prev1 = t1.position - t1.deltaPosition;
 
		float prevMag = (prev0 - prev1).magnitude;
		float currMag = (t0.position - t1.position).magnitude;
		float delta = currMag - prevMag;
 
		Zoom(delta * m_ZoomSensitivity);
	}
	
	ClampCameraPosition();
}
Handles touch-based camera interaction for mobile gameplay.

  • void MobileInput():
    • One-finger drag pans the camera.
    • Two-finger pinch zooms in or out.
  • void ClampCameraPosition():
    • Ensures the camera view remains within the game world bounds.
  • void Zoom():
    • Adjusts orthographic size and recalculate the camera bounds to fit the current zoom level.

Conclusion

This project focused on building tower defense systems with dynamic spawning, autonomous unit behavior, and mobile-friendly controls. While compact in scope, it provided a solid foundation for designing scalable game logic and managing multiple systems in real-time.