Guía de juegos JS

Grunderna

Steg 1: Game Loop

Hjärtat i varje spel — en loop som kör update → draw 60 gånger per sekund med requestAnimationFrame.

Bouncing ball — game loop i aktion ▶ Kör automatiskt
// Grundläggande game loop function gameLoop(timestamp) { const dt = (timestamp - lastTime) / 1000; lastTime = timestamp; update(dt); // Uppdatera logik draw(); // Rita allt requestAnimationFrame(gameLoop); } requestAnimationFrame(gameLoop);
💡 Delta Time (dt): Multiplicera all rörelse med dt så att spelet körs lika snabbt oavsett skärmens bildfrekvens.
Kontroller

Steg 2: Tangentbordsinput

Spåra vilka tangenter som är nedtryckta med en Set eller objekt. Reagera i update-loopen, inte i event-hanteraren.

Styr fyrkanten med WASD / piltangenter 🎮 Spelbart
WASD
// Input-system med Set const keys = new Set(); window.addEventListener('keydown', e => keys.add(e.key)); window.addEventListener('keyup', e => keys.delete(e.key)); function update(dt) { const speed = 200; // px/s if (keys.has('ArrowLeft') || keys.has('a')) player.x -= speed * dt; if (keys.has('ArrowRight') || keys.has('d')) player.x += speed * dt; }
Fysik

Steg 3: Kollisionsdetektering

AABB (rektangel vs rektangel) är den vanligaste — enkel, snabb och räcker för de flesta 2D-spel.

Dra den gröna rutan — den blir röd vid kollision 🎮 Interaktiv
// AABB kollision (rektangel vs rektangel) function aabb(a, b) { return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y; } // Cirkel vs cirkel function circleCollision(a, b) { const dx = a.x - b.x; const dy = a.y - b.y; const dist = Math.sqrt(dx*dx + dy*dy); return dist < a.r + b.r; }
Speltyp

Steg 4: Plattformsspel

Gravitation + hopp + plattformar. Grunden för Mario, Celeste, Hollow Knight och tusentals andra spel.

Spelbart plattformsspel — samla mynten! 🎮 ←→ Flytta, ↑/Space Hoppa
Mynt: 0
JUMP
// Gravitation + hopp const GRAVITY = 800; // px/s² const JUMP_FORCE = -350; // px/s function update(dt) { // Applicera gravitation player.vy += GRAVITY * dt; player.y += player.vy * dt; // Hopp (bara om på marken) if (keys.has(' ') && player.grounded) { player.vy = JUMP_FORCE; player.grounded = false; } // Kolla plattformar for (const p of platforms) { if (aabb(player, p) && player.vy >= 0) { player.y = p.y - player.h; player.vy = 0; player.grounded = true; } } }
Speltyp

Steg 5: Top-Down

Ovanifrånperspektiv — perfekt för RPG, äventyr och Zelda-liknande spel. Fri rörelse i alla riktningar.

Utforska kartan och samla ädelstenar 🎮 WASD / Piltangenter
Ädelstenar: 0
MOVE
// Top-down rörelse med väggkollision function update(dt) { let dx = 0, dy = 0; if (keys.has('ArrowUp')) dy = -1; if (keys.has('ArrowDown')) dy = 1; if (keys.has('ArrowLeft')) dx = -1; if (keys.has('ArrowRight')) dx = 1; // Normalisera diagonal (annars 41% snabbare!) if (dx && dy) { dx *= 0.707; dy *= 0.707; } // Prova X och Y separat (slide mot väggar) player.x += dx * speed * dt; if (hitsWall(player)) player.x -= dx * speed * dt; player.y += dy * speed * dt; if (hitsWall(player)) player.y -= dy * speed * dt; }
Speltyp

Steg 6: Space Shooter

Skjut fiender, undvik kulor, få poäng. Grunden för shoot 'em ups och twin-stick shooters.

Spelbar space shooter! 🎮 ←→ Flytta, Space Skjut
Poäng: 0 | Liv: 3
🔫 SKJUT
// Kulor (object pool-mönster) const bullets = []; function shoot() { bullets.push({ x: player.x + player.w / 2 - 2, y: player.y, w: 4, h: 10, vy: -400 // uppåt }); } // I update: flytta kulor + kolla kollision for (let i = bullets.length - 1; i >= 0; i--) { bullets[i].y += bullets[i].vy * dt; if (bullets[i].y < 0) bullets.splice(i, 1); for (const enemy of enemies) { if (aabb(bullets[i], enemy)) { enemy.alive = false; bullets.splice(i, 1); score++; break; } } }
Speltyp

Steg 7: Pusselspel

Grid-baserade pussel — klicka för att matcha, vrid, eller flytta brickor. Grunden för Tetris, Bejeweled och match-3.

Memory-pussel — hitta alla par! 🎮 Klicka/Tryck
Drag: 0 | Par: 0/8
// Grid-baserat pussel const COLS = 4, ROWS = 4; const grid = []; // Skapa par const symbols = ['🌟','🎯','💎','🔥','🎵','🌙','⚡','🍀']; const pairs = [...symbols, ...symbols]; shuffle(pairs); // Klick → hitta grid-position canvas.addEventListener('click', (e) => { const col = Math.floor(e.offsetX / cellSize); const row = Math.floor(e.offsetY / cellSize); flipCard(col, row); });
Effekter

Steg 8: Partikelsystem

Explosioner, gnistor, rök, snö — partiklar ger liv åt spel. Varje partikel har position, hastighet och livstid.

Klicka/tryck för att skapa explosioner! 🎮 Klicka varsomhelst
// Partikelsystem class Particle { constructor(x, y) { this.x = x; this.y = y; this.vx = (Math.random() - .5) * 200; this.vy = (Math.random() - .5) * 200; this.life = 1; // 1 → 0 this.decay = 0.5 + Math.random() * 1.5; } update(dt) { this.x += this.vx * dt; this.y += this.vy * dt; this.life -= this.decay * dt; } }
Grafik

Steg 9: Sprites & Animation

Sprite sheets: en bild med alla frames. Byt frame över tid för att skapa animation — walk cycle, explosioner, effekter.

Procedurellt genererad sprite-animation
// Sprite-animation med spritesheet class SpriteAnimator { constructor(frameCount, fps) { this.frame = 0; this.frameCount = frameCount; this.frameTime = 1 / fps; this.elapsed = 0; } update(dt) { this.elapsed += dt; if (this.elapsed >= this.frameTime) { this.frame = (this.frame + 1) % this.frameCount; this.elapsed = 0; } } draw(ctx, img, x, y, frameW, frameH) { ctx.drawImage(img, this.frame * frameW, 0, frameW, frameH, x, y, frameW, frameH ); } }
Struktur

Steg 10: Spelarkitektur

Organisera koden med klasser, states och entity-system. Separera logik från rendering.

🏗️
Entity-Component mönster

Separera data (position, health) från beteende (gravity, input). Blanda fritt.

🔄
Game States

Menu → Playing → Paused → GameOver. Varje state har sin egen update/draw.

📦
Object Pooling

Återanvänd objekt (kulor, partiklar) istället för att skapa nya. Bättre prestanda.

🗺️
Tile Maps

Bygg levels med en 2D-array. Varje tal = en tile-typ. Verktyg: Tiled (gratis).

🎵
Web Audio API

Spela ljud med new Audio('sfx.mp3') eller Web Audio API för avancerad kontroll.

// ═══════════════════════════════ // KOMPLETT SPELARKITEKTUR // ═══════════════════════════════ class Game { constructor(canvas) { this.ctx = canvas.getContext('2d'); this.entities = []; this.state = 'menu'; // menu | playing | paused | gameover this.lastTime = 0; } start() { requestAnimationFrame(t => this.loop(t)); } loop(timestamp) { const dt = (timestamp - this.lastTime) / 1000; this.lastTime = timestamp; if (this.state === 'playing') { this.entities.forEach(e => e.update(dt)); this.checkCollisions(); } this.draw(); requestAnimationFrame(t => this.loop(t)); } addEntity(entity) { this.entities.push(entity); } removeEntity(e) { this.entities = this.entities.filter(x => x !== e); } } // Tile map const map = [ [1,1,1,1,1], // 1 = vägg [1,0,0,0,1], // 0 = golv [1,0,2,0,1], // 2 = spelare start [1,1,1,1,1], ];

Reklam