Back

HTML5 Canvas: Sprite Animation and Game Physics

10 min read

HTML5 Canvas: Animasi Sprite dan Fisika Game

Setelah kita paham Game Loop, manajemen Input, serta teknik memutar Transformasi kanvas, ini saatnya menggunakan kode pamungkas untuk menggabungkan semuanya secara bersamaan. Mari kita bedah struktur fondasi dari Player Class / Kelas Jagoan berdasarkan cuplikan kodingan game berstandar produksi nyata (production code). Kita akan menghidupkan hukum Gravitasi agar karakter bisa melompat, sekaligus menggunakan sistem transisi Animasi Gambar Sprite bersambung!

1. Mempersiapkan Koleksi Aset Ekstensif

Lupakan menggambar kotak kaku. Game asli wajib memainkan gambar berderet (mirip film animasi tradisional frame-by-frame). Gambar-gambar tersebut kita muat dalam RAM lalu dan dikelompokkan ke array berdasarkan Status Aktif emosinya (seperti kumpulan frame Idle, frame saat mundur MoveBackward, saat melompat Jump).

// Fungsi pembantu perakit objek DOM <img /> murni
function loadImg(src) {
  const img = new Image();
  img.src = src;
  return img; 
}

// Menghimpun array besar gambar dengan ketepatan file numbering
function loadImagePlayer(country) {
  const ImageState = { Idle: [], Jump: [], MoveForward: [] /* ... */ };
  
  // Contoh: Menyedot 18 potong frame terpisah untuk Animasi Nafas/Diam (Idle)
  for (let i = 0; i <= 17; i++) {
    // Trik Cerdas: String(i).padStart(2, "0") otomatis menyulap angka 1 menjadi teks "01"
    ImageState.Idle.push(loadImg(`./Sprites/Characters/${country}/Idle/Idle_0${String(i).padStart(2, "0")}.png`));
  }
  return ImageState;
}

2. Paradigma Kelas Pembungkus Organ Tubuh(Player Class)

Membungkus kode entitas dalam OOP Paradigma JavaScript class adalah tata cara teraman di jagad pembuatan game. Si Karakter (Objek) setidaknya harus mengingat perihal letak lokasi keberadaannya (x, y), variabel laju luncurannya (dx, dy), pemutar slide frame yang terputar (frame, state), serta massa hukum gravitasi beban tubuh (speed, gravity).

class Player {
  constructor(country, position, ctx) {
    this.ctx = ctx; // kanvas layar referensi
    this.position = position; // Dari pihak "left" atau "right"?
    this.image = loadImagePlayer(country); 
    
    // Dimensi dan Hukum Dunia
    this.size = 150;
    this.x = position === "left" ? 300 : 600; // Alokasi pijakan start base awal
    this.y = 0;
    this.dy = 0; // Delta Y: Kekuatan Tarik Turun/Keatas (Momentum Vertical)
    this.gravity = 0.2; // Tambahan berat badan yang menarik konstan jatuh ke bawah 

    // Ingatan Mesin Status Animasi saat ini (State machine)
    this.state = "Idle"; 
    this.frame = 0; // Penentu gambar keberapa yg wajib digambar dari array nya
    this.lastAnimate = Date.now(); // Stopwatch Delay laju putar Animasi (pacing kontroler)
  }
}

3. Penerapan Ilmu Fisika: Gravitasi & Lemparan Bebas

Konsep simulasi gravitasi sungguh brilian dan tak terduga! Di dalam update(), variabel momentum tubuh vertikal this.dy terus menerus ditambah nilainya setiap Frame oleh variabel beban konstan this.gravity. Oleh karenanya badannya senantiasa terdorongan turun semakin jatuh!

Kecuali ia menabrak daratan (groundLevel). Sewaktu kakinya persis menyentuh tanah, perhitungan Gravitasi seketika ter-reset hangus menjadi nol (dy = 0). Tapii apabila ia membentur tombol Jump saat berpijak di dasar itu? Kita langsung jejalkan suntikan angka lemparan Ke atas bertolak keras (- Minus) pada dy (dy = -8), membuatnya sejenak melambung melawan gravitasi layaknya rudal roket terbang sesaat menyentuh angkasa!

  update() {
    this.prevState = this.state; // Recall ingatan di masa lalu memonitor pergantian aksi layar
    
    const groundLevel = canvas.height - this.size - 90; // Kalkulasikan ketinggian batas tapak Lantai mutlak

    // Cek apakah sol sepatu `Y` nya tembus ke garis Tanah bumi?
    const onGround = this.y >= groundLevel;

    if (onGround) {
      this.y = groundLevel; // Paksa Posisi menempel sejajar di aspal (Anti-Glitch tembus lantai layar mati masuk neraka)
      this.dy = 0; // Matikan Beban Daya Tumbuk Jatuhnya

      // Trigger MELEDAKKAN Lompatan
      if (this.keys["arrowup"]) {
        this.dy = -8; // Tembakkan Rudal daya roket berakselerasi Ke-atas instan!
      }
    } else {
      // Jika nyatanya masih Melayang Udara, izinkan rantai hukum grafitasi mencekiknya memeras kekuatannya untuk jatuh ke tanah terus menerus tiap sekian MS 
      this.dy += this.gravity; 
    }

    // Akhirnya suntikan Total Akumulasi kalkulasi Vertikal (dy), mendarat dan mencemari Angka Titik Pijakan Lokasi Aslinya (y)
    this.y += this.dy; 

    // STATE MACHINE : Mesin Pendiktean Pergolakan Penggantian Aksi
    if (this.y < groundLevel) this.state = "Jump";
    else if (this.keys["space"]) this.state = "Kick";
    else if (this.keys["arrowright"]) this.state = "MoveForward";
    else this.state = "Idle";

    // Mekanisme Keamanan: Reset Auto-rewind penggulungan slide video kembali ke file img indeks '0' ketika pergantian antar Gerak Tiba (spti diam tau2 lompat)
    if (this.prevState !== this.state) this.frame = 0; 
  }

4. Render Animasi Frame Beruntun & Pemantulan Cermin Otomatis Kaca Tim

Keahlian terakhir metode core tersembunyi Sang Jagoan berada di animate(). Skrip ini menggunakan logika cerdas pembatas Timer rentang pias Waktu (Date.now()). Laju delay perulangan antar gambar dimanipulasi dengan batas angka "lebih dari 50 Milikdetik" supaya framenya tertahan ganti secara rasional. Bila limit ini tidak di rem, maka 18 frame tendangan animasimu akan habis secepat panah dalam wujud "Seper Enam-Puluh" (1/60) detik di layar monitor! Manusia tidak akan menyadarinya.

Dan Terakhir, mari gunakan materi Transformasi Flip (Minus Scala 1) yang baru saja diajarkan untuk Player pihak sebelah Tim Kanan (position === "right"). Daripada kerepotan melukis manual karakter dengan gambar pose menghindar mengarah sebaliknya hadap kiri, kita cukup menyuruh Program melakukan Rotasi pencerminan dimensi koordinat ctx.scale(-1, 1) sesaat sebelum nge-print (drawImage) ke hadap Tim Sang Pemain Kiri Musuhnya.

  animate() {
    // Intip Array Koleksi Foto yang aktif saat Status Animainya di call ini
    const KumpulanGambarAnimasiAktif = this.image[this.state];
    const jamSkrg = Date.now();
    
    // Gembok Stopwatch: Frame Tayang slide gambar selanjutnya HANYA tergeser apabila Waktu sudah terdiam berjaraknya melewati 50ms 
    if (jamSkrg - this.lastAnimate > 50) {
      this.frame++; // Geser penunjuk slider ke Next Nomor gambar 
      // Gulung pemutar klise gambar jadi 0, persis sama saat Film animasinya habis tak tersisa 
      if (this.frame >= KumpulanGambarAnimasiAktif.length) this.frame = 0; 
      
      this.lastAnimate = jamSkrg;
    }

    // Trik Pembalikan Kaca Cermin Ekslusif Hanya untuk Pemain Posisi Markas "Kanan" Lapangan
    if (this.position === "right") {
      this.ctx.save();
      // Pusat Geser pindah tembus menjarah ke sayap depan titik x Character supaya orientasi cermin tdk ngawur
      this.ctx.translate(this.x + this.size, this.y); 
      this.ctx.scale(-1, 1); // Flip Putaran Bayangan Cermin Horizontal 180 Drajat Mutlak!

      this.ctx.drawImage(
        this.image[this.state][this.frame], // Pilih selambar spesifik file gambar index persis sesuai penempatannya.
        0, 0, this.size, this.size
      );
      this.ctx.restore(); // Amankan ruang Kanvas kembali. Lepas bayangannya
    } else {
      // Menggambar Normal Biasa untuk kubu Pemain di sisi Kiri Lapangan yang emang fitrahnya aslinya tergambar menghadap arah kanan
      this.ctx.drawImage(
        this.image[this.state][this.frame],
        this.x, this.y, this.size, this.size
      );
    }
  }

Bila dinaluri sedalam mungkin, arsitektur pattern di dalam file OOP kelas ini adalah Masterpiece mutlak dalam pembentukan hampir di seluruh kodingan dari game model tarung pertarungan (Fight/Brawler), mapun Loncat-Loncatan Platform klasik Mario, Megaman yang ada belahan game manapun di muka dunia! Terus asah logika insting pengkodean keren hebat tersebut!