📐 Mängu Matemaatika Põhialused
Vektorid, trigonomeetria ja lineaarne interpolatsioon — matemaatika mis mänge ellu äratab. Ilma selleta ei saa ükski mäng hakkama!
📋 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.
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.
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.
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.
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
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()jacos(). 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.