🔥 Bikin Game 2048 Sendiri Pakai HTML, CSS, dan JavaScript – Mudah & Seru!

 


📌 Pembuka

Pernah main game 2048? Game simpel tapi bikin nagih ini ternyata bisa kamu buat sendiri lho hanya dengan HTML, CSS, dan JavaScript! Dan tenang aja, nggak harus jago banget ngoding kok. Di artikel ini, kita akan bahas cara bikin game 2048 sendiri dari nol, lengkap dengan penjelasan kode dan tampilannya yang warna-warni dalam mode gelap!

Kalau kamu nonton videonya, kamu juga bisa langsung praktek sambil lihat kodenya di bawah ini. Yuk, langsung mulai!

🎯 Manfaat Proyek Ini

Bikin game 2048 bukan cuma seru, tapi juga punya banyak manfaat buat kamu yang ingin:

  • Belajar JavaScript dasar hingga menengah
  • Memahami cara kerja grid, event listener, dan logika game
  • Melatih kemampuan membuat animasi sederhana
  • Bikin proyek portofolio keren yang bisa dipamerin!

💻 Langkah 1: Struktur HTML-nya

Kita mulai dari HTML, yaitu kerangka utama tampilan game-nya. Di sini kita buat judul, skor, papan permainan, dan tombol level.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>2048 Neon Mode</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div id="hud">
    <h1>2048 Neon</h1>
    <div id="score-box">Score: <span id="score">0</span></div>
  </div>
  <div id="game-container"></div>
  <div id="game-over" style="display:none;">Game Over</div>
  <script src="script.js"></script>
</body>
</html>

🔍 Penjelasan:

  • <div id="board"> adalah tempat grid game akan muncul.
  • <select id="level"> buat milih ukuran papan (4x4, 5x5, dll).
  • <div id="game-over"> akan tampil saat permainan mentok alias gak bisa gerak lagi.

🎨 Langkah 2: Gaya CSS-nya

Agar tampilan lebih keren dan menyala-nyala, kita pakai mode gelap dengan warna cerah untuk tiap kotak.

body {
  margin: 0;
  background: #121212;
  font-family: 'Segoe UI', sans-serif;
  display: flex;
  flex-direction: column;
  align-items: center;
  color: #fff;
}

#hud {
  margin: 20px;
  text-align: center;
}

#score-box {
  font-size: 24px;
  margin-top: 10px;
  color: #00f2ff;
  text-shadow: 0 0 10px #00f2ff;
}

#game-container {
  display: grid;
  grid-template-columns: repeat(4, 100px);
  grid-template-rows: repeat(4, 100px);
  gap: 10px;
  background: #1e1e1e;
  padding: 10px;
  border-radius: 15px;
  box-shadow: 0 0 15px #0ff;
}

.tile {
  width: 100px;
  height: 100px;
  background: #2c2c2c;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 28px;
  font-weight: bold;
  color: #fff;
  border-radius: 10px;
  box-shadow: inset 0 0 10px #000;
  transition: all 0.2s ease-in-out;
  animation: pop 0.2s ease-in-out;
}

@keyframes pop {
  0% { transform: scale(1); }
  50% { transform: scale(1.2); }
  100% { transform: scale(1); }
}

/* Neon levels */
.tile[data-value="2"]    { background: #00f2ff; color: #000; box-shadow: 0 0 10px #00f2ff; }
.tile[data-value="4"]    { background: #0ff; color: #000; box-shadow: 0 0 10px #0ff; }
.tile[data-value="8"]    { background: #00ff9f; color: #000; box-shadow: 0 0 12px #00ff9f; }
.tile[data-value="16"]   { background: #55ff00; color: #000; box-shadow: 0 0 13px #55ff00; }
.tile[data-value="32"]   { background: #ffaa00; color: #000; box-shadow: 0 0 14px #ffaa00; }
.tile[data-value="64"]   { background: #ff5500; color: #fff; box-shadow: 0 0 15px #ff5500; }
.tile[data-value="128"]  { background: #ff0077; color: #fff; box-shadow: 0 0 16px #ff0077; }
.tile[data-value="256"]  { background: #bb00ff; color: #fff; box-shadow: 0 0 17px #bb00ff; }
.tile[data-value="512"]  { background: #5500ff; color: #fff; box-shadow: 0 0 18px #5500ff; }
.tile[data-value="1024"] { background: #00b3ff; color: #000; box-shadow: 0 0 19px #00b3ff; }
.tile[data-value="2048"] { background: #00ffdc; color: #000; box-shadow: 0 0 20px #00ffdc; }

#game-over {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: rgba(20, 20, 20, 0.95);
  color: #ff0077;
  font-size: 48px;
  padding: 30px 60px;
  border-radius: 20px;
  box-shadow: 0 0 20px #ff0077, 0 0 40px #ff0077;
  z-index: 100;
  text-align: center;
  font-weight: bold;
  text-shadow: 0 0 10px #ff0077;
}

✨ Penjelasan:

  • Warna latar belakang hitam (#121212) untuk mode gelap.
  • Elemen .tile punya animasi ringan dan warna mencolok dengan box-shadow.
  • .game-over disembunyikan dulu, nanti ditampilkan saat permainan selesai.

⚙️ Langkah 3: Logika JavaScript-nya

Sekarang bagian pentingnya: logika permainan. Di sinilah kita mengatur gerakan kotak, skor, level, dan pengecekan game over.

const size = 4;
let board = [];
let score = 0;

function createBoard() {
  const container = document.getElementById("game-container");
  container.innerHTML = "";
  board = [];
  for (let r = 0; r < size; r++) {
    board[r] = [];
    for (let c = 0; c < size; c++) {
      board[r][c] = 0;
      const tile = document.createElement("div");
      tile.classList.add("tile");
      tile.id = `tile-${r}-${c}`;
      container.appendChild(tile);
    }
  }
  score = 0;
  updateScore();
  addRandomTile();
  addRandomTile();
  updateBoard();
}

function updateScore() {
  document.getElementById("score").textContent = score;
}

function addRandomTile() {
  let empty = [];
  for (let r = 0; r < size; r++) {
    for (let c = 0; c < size; c++) {
      if (board[r][c] === 0) empty.push({ r, c });
    }
  }
  if (empty.length === 0) return;
  const { r, c } = empty[Math.floor(Math.random() * empty.length)];
  board[r][c] = Math.random() < 0.9 ? 2 : 4;
}

function updateBoard() {
  for (let r = 0; r < size; r++) {
    for (let c = 0; c < size; c++) {
      const tile = document.getElementById(`tile-${r}-${c}`);
      tile.textContent = board[r][c] === 0 ? "" : board[r][c];
      tile.dataset.value = board[r][c] || "";
      tile.classList.remove("merge");
      void tile.offsetWidth; // Restart animation
      tile.classList.add("tile");
    }
  }
}

function slide(row) {
  let arr = row.filter(val => val);
  for (let i = 0; i < arr.length - 1; i++) {
    if (arr[i] === arr[i + 1]) {
      arr[i] *= 2;
      score += arr[i];
      arr[i + 1] = 0;
    }
  }
  return arr.filter(val => val).concat(Array(size - arr.filter(val => val).length).fill(0));
}

function rotateLeft(matrix) {
  const result = [];
  for (let c = 0; c < size; c++) {
    result[c] = [];
    for (let r = size - 1; r >= 0; r--) {
      result[c][size - 1 - r] = matrix[r][c];
    }
  }
  return result;
}

function rotateRight(matrix) {
  const result = [];
  for (let c = 0; c < size; c++) {
    result[c] = [];
    for (let r = 0; r < size; r++) {
      result[c][r] = matrix[size - 1 - r][c];
    }
  }
  return result;
}

function handleInput(direction) {
  let newBoard = board.map(row => row.slice());

  if (direction === "ArrowLeft") {
    newBoard = newBoard.map(slide);
  } else if (direction === "ArrowRight") {
    newBoard = newBoard.map(row => slide(row.reverse()).reverse());
  } else if (direction === "ArrowUp") {
    newBoard = rotateLeft(newBoard);
    newBoard = newBoard.map(slide);
    newBoard = rotateRight(newBoard);
  } else if (direction === "ArrowDown") {
    newBoard = rotateLeft(newBoard);
    newBoard = newBoard.map(row => slide(row.reverse()).reverse());
    newBoard = rotateRight(newBoard);
  }

  if (JSON.stringify(board) !== JSON.stringify(newBoard)) {
    board = newBoard;
    addRandomTile();
    updateBoard();
    updateScore();
  }
}

window.addEventListener("keydown", (e) => {
  if (["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(e.key)) {
    e.preventDefault();
    handleInput(e.key);
  }
});

createBoard();

function isGameOver() {
  for (let r = 0; r < size; r++) {
    for (let c = 0; c < size; c++) {
      if (board[r][c] === 0) return false; // masih ada ruang
      if (c < size - 1 && board[r][c] === board[r][c + 1]) return false; // horizontal bisa digabung
      if (r < size - 1 && board[r][c] === board[r + 1][c]) return false; // vertikal bisa digabung
    }
  }
  return true;
}

📘 Penjelasan:

  • Fungsi setupBoard() digunakan untuk inisialisasi game baru.
  • move(dir) mengatur pergerakan kotak sesuai arah tombol panah.
  • addRandomTile() menambahkan angka baru di papan.
  • isGameOver() memeriksa apakah tidak ada gerakan tersisa.

✅ Penutup

Itu dia tutorial lengkap membuat game 2048 versi kamu sendiri! Dari tampilan sampai logika, semua dibahas agar kamu bisa belajar dan bereksperimen sendiri.

Cocok banget buat kamu yang ingin bikin game mini atau pengen punya karya keren untuk dipamerin. Jangan lupa modifikasi sesuka hati — ubah warna, ukuran, atau tambahkan suara!

Semoga bermanfaat dan selamat ngoding! 🚀

Komentar