Vector Siege

een tower‑defense game waarin je golven vijanden tegenhoudt door torens te plaatsen. de core gameplay is: vijandengolven (waves), padvolgende AI, geldsysteem om torens te plaatsen, basis‑health en feedback (screen shake).

 

features

  • Waves van vijanden: spawner stuurt automatisch golven met oplopende moeilijkheid.
  • Padvolgende AI: vijanden volgen waypoints richting de basis.
  • Economy: verdien geld bij kills en geef het uit aan torens.
  • Base health: verliest leven als vijanden de basis bereiken; game over bij 0.
  • Feedback: subtiele screen shake bij schade voor meer impactgevoel.

Hoe het werkt

  • EnemySpawner start golven via een coroutine en spawnt vijanden met interval.
  • EnemyMovement verplaatst elke vijand naar het volgende waypoint en doet schade bij de basis.
  • MoneySystem beheert saldo (Add/Spend) en update de UI.
  • PlayerHealth verwerkt schade, triggert screen shake en laadt de game‑over scene.
  • ScreenShake schudt de camera kort met een coroutine.

Code highlights

 

  1. Geld veilig beheren (UI up-to-date)
  • Context: saldo verhogen/verlagen met UI‑update en guard op te weinig geld.
				
					public class MoneySystem : MonoBehaviour
{
    [SerializeField] private int money = 50;
    [SerializeField] private Text moneyText;

    public int Money => money;

    void Start() => UpdateMoneyUI();

    public void AddMoney(int amount)
    {
        money += amount;
        UpdateMoneyUI();
    }

    public void SpendMoney(int amount)
    {
        if (money >= amount)
        {
            money -= amount;
            UpdateMoneyUI();
        }
    }

    void UpdateMoneyUI()
    {
        moneyText.text = "FoolsGold: " + money;
    }
}
				
			
  1. Waves spawnen met oplopende moeilijkheid
  • Context: coroutine die golven start, enemies instantiëert, en snelheid/aantal aanpast.
				
					IEnumerator SpawnWaves()
{
    while (true)
    {
        if (waveInProgress == false)
        {
            // Wacht tussen golven afhankelijk van type
            switch (currentWave)
            {
                case WaveType.Initial:  yield return new WaitForSeconds(10f); break;
                case WaveType.Regular:  yield return new WaitForSeconds(timeBetweenWaves); break;
                case WaveType.Advanced: yield return new WaitForSeconds(2f); break;
            }
        }

        waveInProgress = true;
        currentWave = GetNextWaveType(currentWave);
        waveText.text = "Wave: " + currentWave;

        if (currentWave == WaveType.Advanced)
        {
            minEnemiesPerWave += 2;
            maxEnemiesPerWave += 2;
            spawnWaitTime = Mathf.Max(0.05f, spawnWaitTime - 0.05f);
        }
        else
        {
            currentEnemySpeed += speedIncrement;
        }

        for (int i = 0; i < enemiesInThisWave; i++)
        {
            var enemy = Instantiate(enemyPrefab, transform.position, Quaternion.identity);
            var move = enemy.GetComponent<EnemyMovement>();
            if (move != null)
            {
                move.Waypoints = waypoints;
                move.Speed = (int)currentEnemySpeed;
            }
            yield return new WaitForSeconds(spawnWaitTime);
        }

        yield return new WaitUntil(AllEnemiesDestroyed);
        waveInProgress = false;
        UpdateEnemySpeeds(currentEnemySpeed);
    }
}
				
			
  1. Waypoint‑navigatie en basis‑damage
  • Context: verplaatsen naar huidig waypoint; bij laatste waypoint schade doen en object vernietigen.
				
					void MoveTowardsWaypoint()
{
    if (waypoints == null || waypoints.Length == 0) return;

    Transform targetWaypoint = waypoints[waypointIndex];
    Vector3 direction = (targetWaypoint.position - transform.position).normalized;

    transform.position += direction * speed * Time.deltaTime;

    if (direction.x != 0)
    {
        var s = transform.localScale;
        s.x = Mathf.Sign(direction.x) * Mathf.Abs(s.x);
        transform.localScale = s;
    }

    if (Vector3.Distance(transform.position, targetWaypoint.position) < 0.2f)
    {
        waypointIndex++;
        if (waypointIndex >= waypoints.Length)
        {
            playerHealth?.TakeDamage(1);
            Destroy(gameObject);
        }
    }
}
				
			
  1. Schade afhandelen + screen shake + game over
  • Context: health verlagen, camera schudden en bij 0 health de game‑over scene laden.
				
					public void TakeDamage(int amount)
{
    currentHealth -= amount;
    UpdateHealthUI();

    if (screenShake != null)
        StartCoroutine(screenShake.Shake(0.2f, 0.1f));

    if (currentHealth <= 0)
    {
        Debug.Log("WASTED");
        LoadGameOverScene();
    }
}
				
			
  1. Eenvoudige, effectieve screen shake
  • Context: coroutine die de camera kort en subtiel laat schudden.
				
					public IEnumerator Shake(float duration, float magnitude)
{
    Vector3 originalPosition = transform.position;
    float elapsed = 0f;

    while (elapsed < duration)
    {
        float x = Random.Range(-1f, 1f) * magnitude;
        float y = Random.Range(-1f, 1f) * magnitude;
        transform.position = new Vector3(originalPosition.x + x, originalPosition.y + y, originalPosition.z);
        elapsed += Time.deltaTime;
        yield return null;
    }
    transform.position = originalPosition;
}