Membuat game adalah impian banyak developer. Rasanya magis ketika kode yang kita tulis tiba-tiba "hidup" dan bisa dimainkan. Kabar baiknya: kamu tidak perlu engine game canggih untuk memulai. Dengan HTML5 Canvas dan JavaScript murni, kita bisa membuat game 2D yang cukup kompleks.
Dalam tutorial ini, kita akan membuat game arcade sederhana: Space Shooter. Pesawat pemain bisa bergerak ke kiri-kanan, menembak peluru, dan musuh akan muncul dari atas layar. Siap? Let's code!
Mengapa HTML5 Canvas?
Canvas adalah elemen HTML yang menyediakan area gambar bitmap yang bisa dimanipulasi dengan JavaScript. Keunggulannya:
- Native browser support: Tidak perlu install plugin atau library tambahan
- Pixel-level control: Kamu mengontrol setiap pixel yang ditampilkan
- Performa baik: Hardware accelerated di browser modern
- Learning curve rendah: API sederhana, fokus pada logika game
Langkah 1: Setup HTML dan Canvas
Pertama, buat file HTML dasar dengan elemen canvas. Ukuran canvas bisa disesuaikan, tapi untuk game mobile-friendly, 480x640 adalah rasio yang baik.
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Space Shooter</title>
<style>
body {
margin: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #0a0a1a;
}
canvas {
border: 2px solid #00d9ff;
box-shadow: 0 0 20px rgba(0, 217, 255, 0.5);
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="480" height="640"></canvas>
<script src="game.js"></script>
</body>
</html>
Langkah 2: Game Loop Dasar
Setiap game membutuhkan game loop—siklus yang berjalan terus-menerus untuk mengupdate state game dan merender frame baru. Di JavaScript, kita gunakan requestAnimationFrame untuk loop yang smooth.
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Game state
const game = {
isRunning: true,
score: 0,
lastTime: 0
};
// Main game loop
function gameLoop(timestamp) {
if (!game.isRunning) return;
// Calculate delta time
const deltaTime = timestamp - game.lastTime;
game.lastTime = timestamp;
// Update game logic
update(deltaTime);
// Render
render();
// Next frame
requestAnimationFrame(gameLoop);
}
function update(deltaTime) {
// Update game objects here
}
function render() {
// Clear canvas
ctx.fillStyle = '#0a0a1a';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw game objects here
}
// Start game
requestAnimationFrame(gameLoop);
Mengapa requestAnimationFrame?
requestAnimationFrame sinkron dengan refresh rate monitor (biasanya 60fps), menghasilkan animasi yang jauh lebih halus daripada setInterval. Browser juga otomatis pause loop ketika tab tidak aktif—hemat baterai!
Langkah 3: Membuat Player
Sekarang kita buat objek player yang bisa bergerak. Untuk game ini, kita akan gunakan pendekatan OOP sederhana dengan class atau object literal.
const player = {
x: canvas.width / 2,
y: canvas.height - 80,
width: 40,
height: 40,
speed: 5,
color: '#00d9ff',
update() {
// Keyboard input
if (keys.ArrowLeft && this.x > this.width/2) {
this.x -= this.speed;
}
if (keys.ArrowRight && this.x < canvas.width - this.width/2) {
this.x += this.speed;
}
},
draw() {
ctx.save();
ctx.translate(this.x, this.y);
// Draw spaceship (triangle)
ctx.beginPath();
ctx.moveTo(0, -this.height/2);
ctx.lineTo(-this.width/2, this.height/2);
ctx.lineTo(0, this.height/2 - 10);
ctx.lineTo(this.width/2, this.height/2);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
}
};
// Input handling
const keys = {};
window.addEventListener('keydown', e => keys[e.key] = true);
window.addEventListener('keyup', e => keys[e.key] = false);
đź’ˇ Pro Tips: Input Handling
- Gunakan object
keysuntuk tracking, bukan event listener langsung di loop - Tambahkan
e.preventDefault()untuk arrow keys agar halaman tidak scroll - Consider touch events untuk mobile support
Langkah 4: Sistem Tembakan (Bullet System)
Game shooter tidak lengkap tanpa mekanisme tembak-menembak. Kita akan buat sistem bullet sederhana dengan array.
const bullets = [];
const bulletSpeed = 10;
function shoot() {
bullets.push({
x: player.x,
y: player.y - player.height/2,
width: 4,
height: 12,
speed: bulletSpeed,
active: true
});
}
// Spacebar to shoot
window.addEventListener('keydown', e => {
if (e.code === 'Space') shoot();
});
function updateBullets() {
bullets.forEach(bullet => {
bullet.y -= bullet.speed;
// Deactivate if off screen
if (bullet.y < -bullet.height) {
bullet.active = false;
}
});
// Remove inactive bullets
bullets = bullets.filter(b => b.active);
}
function drawBullets() {
ctx.fillStyle = '#ff00ff';
bullets.forEach(bullet => {
ctx.fillRect(
bullet.x - bullet.width/2,
bullet.y,
bullet.width,
bullet.height
);
});
}
Langkah 5: Sistem Musuh dan Collision
Sekarang kita tambahkan musuh yang muncul dari atas dan collision detection sederhana.
const enemies = [];
let enemySpawnTimer = 0;
const enemySpawnInterval = 1000; // ms
function spawnEnemy() {
enemies.push({
x: Math.random() * (canvas.width - 40) + 20,
y: -40,
width: 35,
height: 35,
speed: 2 + Math.random() * 2,
active: true
});
}
function updateEnemies(deltaTime) {
enemySpawnTimer += deltaTime;
if (enemySpawnTimer > enemySpawnInterval) {
spawnEnemy();
enemySpawnTimer = 0;
}
enemies.forEach(enemy => {
enemy.y += enemy.speed;
if (enemy.y > canvas.height) {
enemy.active = false;
}
// Check collision with bullets
bullets.forEach(bullet => {
if (bullet.active && checkCollision(bullet, enemy)) {
bullet.active = false;
enemy.active = false;
game.score += 100;
}
});
});
enemies = enemies.filter(e => e.active);
}
function checkCollision(a, b) {
return a.x < b.x + b.width/2 &&
a.x + a.width > b.x - b.width/2 &&
a.y < b.y + b.height/2 &&
a.y + a.height > b.y - b.height/2;
}
Langkah 6: Polishing dan Game Feel
Game yang baik bukan hanya tentang mekanisme yang berfungsi, tapi juga game feel—sensasi dan feedback yang membuat pemain betah. Beberapa teknik sederhana:
1. Screen Shake saat Hit
let screenShake = 0;
function triggerShake(amount) {
screenShake = amount;
}
function applyScreenShake() {
if (screenShake > 0) {
const dx = (Math.random() - 0.5) * screenShake;
const dy = (Math.random() - 0.5) * screenShake;
ctx.translate(dx, dy);
screenShake *= 0.9; // Decay
if (screenShake < 0.5) screenShake = 0;
}
}
2. Particle Effects
const particles = [];
function createExplosion(x, y, color) {
for (let i = 0; i < 10; i++) {
particles.push({
x: x,
y: y,
vx: (Math.random() - 0.5) * 8,
vy: (Math.random() - 0.5) * 8,
life: 1,
color: color
});
}
}
function updateParticles() {
particles.forEach(p => {
p.x += p.vx;
p.y += p.vy;
p.life -= 0.02;
});
particles = particles.filter(p => p.life > 0);
}
Kesimpulan dan Next Steps
Selamat! Kamu sekarang punya game shooter 2D yang playable. Dari sini, kamu bisa mengembangkan lebih lanjut:
- Tambahkan power-ups (shield, rapid fire, bomb)
- Buat sistem level dengan musuh yang semakin kuat
- Tambahkan boss battle setiap beberapa wave
- Implementasi high score dengan localStorage
- Tambahkan sound effects dengan Web Audio API
Game development adalah perjalanan panjang yang penuh pembelajaran. Setiap game yang kamu buat, sekecil apapun, akan mengajarkanmu sesuatu yang baru. Selamat berkarya!
🎮 Mainkan Game Eksklusif
Temukan lebih banyak game dan eksperimen interaktif di Studio Rhadzor. Dari prototype sederhana hingga game yang lebih polished.
Jelajahi Game →Ada pertanyaan tentang game dev? Atau punya game yang ingin dishare? Kunjungi studio.rhadzor.id