MOODUL 03b · TASE 1 — ALGAJA

📐 Mängu Matemaatika Põhialused

Vektorid, trigonomeetria ja lineaarne interpolatsioon — matemaatika mis mänge ellu äratab. Ilma selleta ei saa ükski mäng hakkama!

⏱️ ~3-4 tundi 📝 8 harjutust 🟢 Algaja

📋 Eeldused

ℹ️

Peaksid olema läbinud Moodul 03. Pead tundma Canvas API-t ja game loop'i.

🎯 Mida Sa Õpid

  • ✅ 2D vektorite matemaatika (liitmine, lahutamine, pikkus, normaliseerimine)
  • ✅ Trigonomeetria mängudes (sin, cos, atan2, ringliikumine)
  • ✅ Kauguse ja nurga arvutamine kahe punkti vahel
  • ✅ Lineaarne interpolatsioon (lerp) sujuvaks liikumiseks
  • ✅ Partiklisüsteemi ehitamine
  • ✅ Skalaarkorrutis (dot product) ja selle kasutused

📦 Samm 1 — Vector2D Klass

Vektor on matemaatiline objekt millel on suund ja pikkus. Mängudes kasutame vektoreid positsiooni, kiiruse ja jõudude esitamiseks.

vector2d.js
class Vector2D {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }

  // Liida teine vektor
  add(v) {
    return new Vector2D(this.x + v.x, this.y + v.y);
  }

  // Lahuta teine vektor
  subtract(v) {
    return new Vector2D(this.x - v.x, this.y - v.y);
  }

  // Korruta skalariga
  multiply(scalar) {
    return new Vector2D(this.x * scalar, this.y * scalar);
  }

  // Vektori pikkus (magnitude)
  get magnitude() {
    return Math.sqrt(this.x * this.x + this.y * this.y);
  }

  // Normaliseeri (pikkus = 1, suund säilib)
  normalize() {
    const mag = this.magnitude;
    if (mag === 0) return new Vector2D(0, 0);
    return new Vector2D(this.x / mag, this.y / mag);
  }

  // Skalaarkorrutis (dot product)
  dot(v) {
    return this.x * v.x + this.y * v.y;
  }

  // Nurk radiaanides
  get angle() {
    return Math.atan2(this.y, this.x);
  }

  // Kaugus teise vektorini
  distanceTo(v) {
    return this.subtract(v).magnitude;
  }

  // Lineaarne interpolatsioon
  static lerp(a, b, t) {
    return new Vector2D(
      a.x + (b.x - a.x) * t,
      a.y + (b.y - a.y) * t
    );
  }

  // Loo vektorist nurgast ja pikkusest
  static fromAngle(angle, length = 1) {
    return new Vector2D(
      Math.cos(angle) * length,
      Math.sin(angle) * length
    );
  }
}
💡

Miks vektorid? Ilma vektoriteta peaksid x ja y koordinaate alati eraldi käsitlema. Vektoritega saad kirjutada position = position.add(velocity) — puhas ja loetav!

🔄 Samm 2 — Trigonomeetria Mängudes

sin() ja cos() on sinu parimad sõbrad ringikujuliste ja laineliste liikumiste jaoks.

main.js — Ringliikumine
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const center = { x: canvas.width / 2, y: canvas.height / 2 };

let angle = 0;
const radius = 150;
const orbitSpeed = 0.02;

// Mitu orbiidil olevat objekti
const orbiters = [
  { offset: 0, color: '#22c55e', size: 12 },
  { offset: Math.PI * 0.66, color: '#3b82f6', size: 10 },
  { offset: Math.PI * 1.33, color: '#ef4444', size: 8 },
];

function update() {
  angle += orbitSpeed;
}

function draw() {
  ctx.fillStyle = 'rgba(15, 15, 35, 0.1)';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  // Keskpunkt
  ctx.beginPath();
  ctx.arc(center.x, center.y, 8, 0, Math.PI * 2);
  ctx.fillStyle = '#eab308';
  ctx.fill();

  // Orbiteerivad objektid
  orbiters.forEach(orb => {
    const a = angle + orb.offset;
    const x = center.x + Math.cos(a) * radius;
    const y = center.y + Math.sin(a) * radius;

    ctx.beginPath();
    ctx.arc(x, y, orb.size, 0, Math.PI * 2);
    ctx.fillStyle = orb.color;
    ctx.fill();
  });
}

function gameLoop() {
  update();
  draw();
  requestAnimationFrame(gameLoop);
}
gameLoop();

🎯 Samm 3 — Objekti Suunamine Hiire Poole

Kasutame Math.atan2() et leida nurk kahe punkti vahel ja liigutame objekti selles suunas.

main.js — Suunamine vektoritega
const player = { pos: new Vector2D(400, 300), speed: 3 };
const mouse = new Vector2D(400, 300);

canvas.addEventListener('mousemove', (e) => {
  const rect = canvas.getBoundingClientRect();
  mouse.x = e.clientX - rect.left;
  mouse.y = e.clientY - rect.top;
});

function update() {
  // Suund mängijast hiireni
  const direction = mouse.subtract(player.pos).normalize();

  // Kaugus hiireni
  const distance = player.pos.distanceTo(mouse);

  // Liiguta ainult kui piisavalt kaugel
  if (distance > 5) {
    player.pos = player.pos.add(direction.multiply(player.speed));
  }
}

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // Mängija
  ctx.beginPath();
  ctx.arc(player.pos.x, player.pos.y, 15, 0, Math.PI * 2);
  ctx.fillStyle = '#22c55e';
  ctx.fill();

  // Joon hiireni (suuna näitamine)
  ctx.beginPath();
  ctx.moveTo(player.pos.x, player.pos.y);
  ctx.lineTo(mouse.x, mouse.y);
  ctx.strokeStyle = 'rgba(34, 197, 94, 0.3)';
  ctx.lineWidth = 1;
  ctx.stroke();
}

💥 Samm 4 — Partiklisüsteem

Partiklid on väikesed objektid mis tekivad, liiguvad ja kaovad. Kasutatakse plahvatuste, suitsu, tule ja muu jaoks.

particles.js
class Particle {
  constructor(x, y) {
    this.pos = new Vector2D(x, y);
    // Juhuslik suund ja kiirus
    const angle = Math.random() * Math.PI * 2;
    const speed = 1 + Math.random() * 4;
    this.vel = Vector2D.fromAngle(angle, speed);
    this.life = 1.0;          // 1.0 = täis, 0.0 = surnud
    this.decay = 0.01 + Math.random() * 0.03;
    this.size = 2 + Math.random() * 4;
    this.color = `hsl(${Math.random() * 60 + 10}, 100%, 60%)`;
  }

  update() {
    this.pos = this.pos.add(this.vel);
    this.vel = this.vel.multiply(0.98); // Hõõrdumine
    this.life -= this.decay;
  }

  draw(ctx) {
    ctx.globalAlpha = this.life;
    ctx.beginPath();
    ctx.arc(this.pos.x, this.pos.y, this.size * this.life, 0, Math.PI * 2);
    ctx.fillStyle = this.color;
    ctx.fill();
    ctx.globalAlpha = 1;
  }

  get isDead() {
    return this.life <= 0;
  }
}

// Partiklite haldur
const particles = [];

canvas.addEventListener('click', (e) => {
  const rect = canvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;

  // Loo 80 partiklit plahvatusena
  for (let i = 0; i < 80; i++) {
    particles.push(new Particle(x, y));
  }
});

function updateParticles() {
  for (let i = particles.length - 1; i >= 0; i--) {
    particles[i].update();
    if (particles[i].isDead) {
      particles.splice(i, 1);
    }
  }
}

function drawParticles() {
  particles.forEach(p => p.draw(ctx));
}

📏 Samm 5 — Lerp ja Sujuv Kaamera

camera.js — Sujuv kaamera
class Camera {
  constructor(x, y) {
    this.pos = new Vector2D(x, y);
    this.target = new Vector2D(x, y);
    this.smoothing = 0.08; // 0.01 = väga aeglane, 0.5 = kiire
  }

  follow(targetPos) {
    this.target = targetPos;
  }

  update() {
    // Lerp kaamera positsiooni sihtmärgi poole
    this.pos = Vector2D.lerp(this.pos, this.target, this.smoothing);
  }

  apply(ctx) {
    // Nihuta kogu Canvas et kaamera oleks keskmes
    ctx.translate(
      -this.pos.x + canvas.width / 2,
      -this.pos.y + canvas.height / 2
    );
  }

  restore(ctx) {
    ctx.setTransform(1, 0, 0, 1, 0, 0);
  }
}

// Kasutamine:
// const camera = new Camera(player.pos.x, player.pos.y);
// camera.follow(player.pos);
// camera.update();
// camera.apply(ctx);
// ... joonista mängumaailm ...
// camera.restore(ctx);

📝 Harjutused

  • Implementeeri Vector2D klass

    Kirjuta Vector2D klass kõigi meetoditega (add, subtract, multiply, normalize, magnitude, dot, angle, distanceTo, lerp, fromAngle). Testi iga meetodit konsoolis.

  • Hiire poole liikuv objekt

    Loo ring mis liigub pidevalt hiire suunas kasutades vektoreid. Kuva ekraanil: kaugus hiireni, suunavektor, kiirus.

  • Orbiteerivad planeedid

    Loo "päikesesüsteem" — keskne objekt + 5 erineva kiirusega orbiteerivat objekti kasutades sin() ja cos(). Lisa igale objektile erinev orbiidi raadius.

  • Laineline liikumine

    Loo rida 20 ringi mis liiguvad laineliselt kasutades sin(). Iga ring on veidi nihkes — tekib laineefekt. Katse amplituudi ja sageduse muutmist.

  • Partiklisüsteemi plahvatus

    Ehita partiklisüsteem — hiire klikkimisel tekib 100 osakest juhuslike suundade ja kiirustega. Lisa gravitatsioon (osakesed kukuvad alla) ja hõõrdumine.

  • Lerp funktsioon ja sujuv liikumine

    Implementeeri lerp ja loo 3 objekti mis jälgivad hiirt erinevate sujuvusfaktoritega (0.02, 0.1, 0.3). Visualiseeri erinevus.

  • Sujuv kaamera jälgimine

    Loo suur mängumaailm (2000x2000) ja väike Canvas. Mängija liigub WASD-ga, kaamera jälgib lerp-iga. Lisa taustale grid et liikumist näha.

  • Dot product — vaateväli

    Loo vaenlane kellel on vaatesuund. Kasuta dot product'i et kontrollida kas mängija on vaenlase vaateväljas (90° koonus). Visualiseeri vaateväli.

← Moodul 03 Moodul 04: Git Süvitsi →