Senin, 31 Agustus 2020

Algorithma Path Finding #6: Jalan yang lebih halus

In adalah pembahasan ke 6 dari seri Algorithma Path Finding. Pembahasan pertama bisa Anda baca pada tautan ini.

Pada tulisan sebelumnya, kita telah membahas bagaimana membuat karakter yang berjalan mengikuti jalur path-finding. Karakter berjalan dengan cara 'melompat-lompat' dari satu grid ke grid berikutnya.

Pada pembahasan kali ini, kita akan melakukan penyempurnaan cara berjalan. Karakter tidak lagi berjalan secara melompat-lompat, tapi berjalan perlahan-lahan dari satu grid ke grid berikutnya.

Kode sumber nya bisa diunduh di tautan ini:
https://drive.google.com/file/d/1-uGzY1QBcaCyZbUr4SgJSHZyXSQZMMw_/view?usp=sharing

Demo daring bisa dilihat di tautan ini:
https://hagarden.netlify.app/moon/400_jalan/

Video Youtube juga tersedia sbb:



Perubahan kode dari pembahasan sebelumnya.

Perubahan pertama adalah pada struktur data karakter.

let karakter = {
    jalur: [],
    jalurn: 0,
    pindahJml: 4,
    pindahn: 0,
    pos: {
        x: 32,
        y: 32
    },
    status: st_idle
};

Kita tambahkan dua property baru, yaitu pindahJml dan pindahn.
pindahJml berisi informasi jumlah langkah yang dibutuhkan untuk berpindah dari satu grid ke grid berikutnya. pindahn berisi informasi langkah yang ke-berapa.

Posisi karakter sekarang dirubah dari posisi di grid menjadi posisi di layar. Pada tulisan sebelumnya, posisi karakter berada pada posisi 1,1. Posisi ini merujuk pada posisi grid. Sekarang posisi karakter adalah 32, 32 yang merujuk pada posisi di layar. Satu grid adalah 32 x 32 pixel.

Perubahan berikutnya adalah penambahan fungsi-fungsi baru, antara lain:

function krkPosisiGrid(karakter) {
    return {
        x: Math.floor(karakter.pos.x / 32),
        y: Math.floor(karakter.pos.y / 32)
    };
}

Fungsi ini akan menghasilkan informasi posisi grid dimana karakter sekarang berdiri.

Fungsi baru berikutnya adalah fungsi krkCheckPosisiDiGrid().

function krkCheckPosisiDiGrid(karakter) {
    if (karakter.pos.x % 32)
        return false;
    if (karakter.pos.y % 32)
        return false;
    return true;
}

Fungsi ini mengecek apakah karakter sekarang sedang berada di grid atau berada diantara grid. Saat karakter berpindah dari grid satu ke grid berikutnya, maka posisi karakter sedang berada di antara grid sampai karakter tersebut sampai di grid berikutnya.

Fungsi berikutnya adalah fungsi krkPindahGrid()

function krkPindahGrid(karakter) {
    let posAwalX;
    let posAwalY;
    let posSelanjutnyaX;
    let posSelanjutnyaY;
    let jarakX;
    let jarakY;
    let posBaruX;
    let posBaruY;

    karakter.pindahn++;

    //posisi grid sekarang
    posAwalX = karakter.jalur[karakter.jalurn][0] * 32;
    posAwalY = karakter.jalur[karakter.jalurn][1] * 32;

    //posisi grid target
    posSelanjutnyaX = karakter.jalur[karakter.jalurn + 1][0] * 32;
    posSelanjutnyaY = karakter.jalur[karakter.jalurn + 1][1] * 32;

    //jarak dari grid sekarang ke target
    jarakX = posSelanjutnyaX - posAwalX;
    jarakY = posSelanjutnyaY - posAwalY;

    //posisi karakter baru
    posBaruX = posAwalX + (karakter.pindahn / karakter.pindahJml) * jarakX;
    posBaruY = posAwalY + (karakter.pindahn / karakter.pindahJml) * jarakY;

    karakter.pos.x = posBaruX;
    karakter.pos.y = posBaruY;

}

Fungsi ini adalah fungsi yang menangani perpindahan karakter dari grid satu ke grid berikutnya. Proses penghitungannya adalah sebagai berikut.

Pertama kita tambah nilai dari pindahn. Variable ini berisi informasi kita sedang di langkah ke berapa

    karakter.pindahn++;

Selanjutnya kita dapatkan posisi grid awal sebelum karakter berpindah.

    //posisi grid sekarang
    posAwalX = karakter.jalur[karakter.jalurn][0] * 32;
    posAwalY = karakter.jalur[karakter.jalurn][1] * 32;

Nilainya kita kali 32 untuk merubah dari posisi grid ke posisi di layar.

Setelah itu kita dapatkan posisi grid berikutnya, yang merupakan grid tujuan.

    //posisi grid target
    posSelanjutnyaX = karakter.jalur[karakter.jalurn + 1][0] * 32;
    posSelanjutnyaY = karakter.jalur[karakter.jalurn + 1][1] * 32;

Kemudian kita hitung jarak perpindahan dari grid sekarang ke grid berikutnya.

    //jarak dari grid sekarang ke target
    jarakX = posSelanjutnyaX - posAwalX;
    jarakY = posSelanjutnyaY - posAwalY;

Posisi karakter yang baru dihitung dari posisi awal + posisi perpindahan

//posisi karakter baru
    posBaruX = posAwalX + (karakter.pindahn / karakter.pindahJml) * jarakX;
    posBaruY = posAwalY + (karakter.pindahn / karakter.pindahJml) * jarakY;

Hasil dari posisi baru ini disimpan di karakter.

    karakter.pos.x = posBaruX;
    karakter.pos.y = posBaruY;

Berikutnya Kita akan membahas perubahan pada fungsi berikutnya, yaitu fungsi update():

function update() {
    if (karakter.status == st_idle) {
    }
    else if (karakter.status == st_jalan) {
        if (krkCheckPosisiDiGrid(karakter)) {
            karakter.jalurn++;
            if (karakter.jalurn >= karakter.jalur.length - 1) {
                karakter.status = st_idle;
            }
            else {
                karakter.status = st_jalan;
                karakter.pindahn = 0;
                krkPindahGrid(karakter);
            }
        }
        else {
            krkPindahGrid(karakter);
        }
    }
}

Kita merubah cara karakter berjalan. Tiap kali posisi karakter berada tepat di grid, maka kita tambahkan nilai dari jalurn.

if (krkCheckPosisiDiGrid(karakter)) {
  karakter.jalurn++;

Kemudian kita check apakah jalurn sudah mencapai maksimal, artinya karakter sudah sampai pada index terakhir. Bila ya, maka kita ubah status karakter menjadi idle.

if (karakter.jalurn >= karakter.jalur.length - 1) {
  karakter.status = st_idle;
}

Bila tidak, maka kita ubah statusnya jadi jalan lagi.


karakter.status = st_jalan;
karakter.pindahn = 0;

krkPindahGrid(karakter);

Untuk selanjutnya, maka kode sumbernya sama dengan sebelumnya,

Terima kasih telah mampir dan membaca.














Minggu, 23 Agustus 2020

Algorithma Path Finding #5: Membuat karakter berjalan mengikuti jalur yang dibuat oleh algorithma path-finding

In adalah pembahasan ke 5 dari seri Algorithma Path Finding. Pembahasan pertama bisa Anda baca pada tautan ini.

Dalam tulisan sebelumnya Kita telah membahas tentang penerapan algoritma path-finding untuk mencari jalur pada daerah yang di klik di layar pada layar yang bersifat responsif.

Dalam tulisan kali ini, Kita akan membahas bagaimana membuat karakter berjalan mengikuti jalur yang telah dibuat oleh algoritma path-finding tersebut, tentu saja pada kanvas yang responsif karena kita akan mencobanya bukan hanya di PC tapi juga di telepon gengam.

Demonya bisa dilihat disini:

Kode sumbernya bisa di unduh disini:

Videonya juga tersedia di youtube:



Penjelasan kode sumber:

Pembahasan sekarang lebih kompleks dari sebelumnya sehingga sekarang file javascript-nya Kita pisah menjadi beberapa bagian antara lain:
  • Pathfinder.js: menangani algorithma path finding
  • Game.js: file utama 
  • Peta.js: menangani peta
  • Window.js: menangani segala yang berhubungan dengan window seperti saat window di- resize.
Untuk selanjutnya Saya akan memakai file js terpisah.

Struktur Object Karakter:

Dalam pembahasan kali ini kita akan memiliki sebuah karakter yang berjalan mengikuti jalur path finding. Untuk itu kita perlu menyusun struktur untuk karakter ini.

let karakter = {
    jalur: [],
    jalurn: 0,
    pos: {
        x: 1,
        y: 1
    },
    status: st_idle
};

Struktur karakter ini adalah struktur minimal untuk karakter yang bisa berjalan. 

jalur: berisi informasi jalur hasil path-finding. Jalur disimpan dalam bentuk Array.
jalurn: informasi index ke berapa dari jalur di atas.
pos: berisi informasi posisi x dan y
status: status karakter saat ini

Karakter memiliki status yang didefinisikan dalam konstanta sbb:

const st_idle = 1;
const st_jalan = 2;

window.onload()

Saat saat event window.onload() Kita membuat interval untuk proses perulangan. Dalam perulangan ini kita memanggil fungsi update() dan render(). Fungsi update() akan memperbaharui informasi karakter sedangkan fungsi render() akan melakukan proses penggambaran.

setInterval(() => {
  update();
  render();
}, 100);


canvas.onclick()

Pada saat kanvas diklik, kita melakukan pengecekan terdahulu apakah saat ini status karakter sedang berjalan atau tidak. Bila karakter sedang berjalan maka kita langsung mengakhiri fungsi ini. Kita harus menunggu karakter selesai berjalan sebelum memberi perintah baru.

  if (karakter.status != st_idle)
    return;

Bila ternyata karakter sedang diam, maka kita lanjutkan prosesnya. Kita mencari posisi klik di layar. Metodenya sama dengan tulisan sebelumnya.

let rect = canvas.getBoundingClientRect();
let poslx = (e.clientX - rect.x) * canvasScaleX;
let posly = ((e.clientY - rect.y) * canvasScaleY);
let posx = Math.floor(poslx / 32);
let posy = Math.floor(posly / 32);

Kemudian kita mencari jalur ke posisi tersebut dari posisi karakter yang terakhir.

let hasil = pfCariJalan(karakter.pos.x, karakter.pos.y, posx, posy);

Kemudian kita mengubah status karakter dengan mengisi informasi yang dibutuhkan agar karakter mulai berjalan.

karakter.status = st_jalan;
karakter.jalur = hasil;
karakter.jalurn = -1;

Kita mengubah status karakter yang awalnya diam ke berjalan. Kita juga memasukkan jalur hasil algoritma path-finding.
Saat karakter belum berjalan, isi dari variable jalurn adalah -1. Saat karakter berjalan, maka jalurn akan mulai berjalan dari angka 0 dst. Hal ini karena Array dimulai dari angka 0.

fungsi Update():

Fungsi update berisi proses untuk memperbaharui informasi karakter.

function update() {
  if (karakter.status == st_idle) {}
  else if (karakter.status == st_jalan) {
    if (karakter.jalurn >= (karakter.jalur.length - 1)) {
      karakter.status = st_idle;
    }
    else {
      karakter.jalurn++;
      karakter.pos.x = karakter.jalur[karakter.jalurn][0];
      karakter.pos.y = karakter.jalur[karakter.jalurn][1];
   }
  }
}

Kita mengecek status dari karakter.
Bila statusnya st_idle, maka tidak ada yang dilakukan.
Bila statusnya st_jalan maka kita memperbaharui proses jalan.
Saat berjalan, kita mengecek apakah proses jalan sudah selesai. Hal ini ditandai dengan jalurn yang sudah melebihi panjang dari jalur path-finding.

Bila karakter sudah selesai berjalan, maka statusnya kita rubah ke st_idle.

if (karakter.jalurn >= (karakter.jalur.length - 1)) {
  karakter.status = st_idle;
}

Bila proses jalan belum selesai, maka kita menambah isi dari jalurn. Kemudian kita mengubah posisi karakter sesuai dengan informasi dari jalur path finding.

karakter.jalurn++;
karakter.pos.x = karakter.jalur[karakter.jalurn][0];
karakter.pos.y = karakter.jalur[karakter.jalurn][1];

Fungsi Render():

Fungsi ini berisi proses pengambaran. Kita menggambar peta, jalan dan jalur sesuai informasi yang ada. Kita tidak membahas terlalu dalam mengenai fungsi ini karena isinya Saya rasa sudah cukup jelas dan sudah sering dibahas di tulisan-tulisan sebelumnya.


Terima kasih sudah mampir dan membaca. Bila ada pertanyaan, jangan sungkan untuk bertanya.







Rabu, 19 Agustus 2020

Algorithma Path Finding #4 Mencari posisi klik pada kanvas yang di skala

In adalah pembahasan ke 4 dari seri Algorithma Path Finding. Pembahasan pertama bisa Anda baca pada tautan ini.

Pada tulisan sebelumnya kita telah membahas bagaimana menggunakan algorithma path-finding untuk mencari jalan pada posisi yang di klik pada kanvas. Dan kita juga membahas bagaimana membuat kanvas bersifat responsif, agar bisa menyesuaikan dengan ukuran layar.

Sekarang kita akan melanjutkan dengan menggabungkan keduanya. Kita akan mencari jalur pada posisi yang diklik pada kanvas yang bersifat responsif.

Hasil nya bisa Anda dicoba di sini.

Silahkan buka linknya, dan ubah ukuran browsernya kemudian klik pada kanvas.  Anda bisa lihat bagaimana algorithma path-finding ini bekerja pada ukuran kanvas yang berbeda-beda.

Kode sumber bisa diunduh di sini

Atau Anda bisa mencoba mengedit kodenya secara langsung disini:



Mendeteksi posisi yang di klik pada kanvas yang bersifat responsif memiliki tantangan tersendiri. Hal ini karena kanvas tidak hanya memiliki ukuran yang berbeda-beda namun juga memiliki posisi yang berbeda-beda pula.

Kode sumber dari pembahasan kali diambil dari dua tulisan sebelumnya dengan sedikit perubahan. Disini saya akan membahas perubahannya saja. Sisanya sama saja dan bisa dilihat pada artikel sebelumnya, atau anda bisa mengunduh kode sumbernya.

canvas.onclick = (e) => {
    let rect = canvas.getBoundingClientRect();
    let poslx = (e.clientX - rect.x) * canvasScaleX;
    let posly = ((e.clientY - rect.y) * canvasScaleY);
    let posx = Math.floor(poslx / 32);
    let posy = Math.floor(posly / 32);
    bersihkanLayar();
    hasil = pfCariJalan(1, 1, posx, posy);
    gambarPeta();
    gambarJalan(hasil);
};

Perubahan yang paling mendasar adalah pada saat canvas.onclick(). Pada tulisan sebelumnya kita mencari posisi klik di kanvas dengan menggunakan e.clientX dan e.clientY saja. Hal ini tidak berlaku untuk kanvas yang responsif.

Proses penghitungannya sbb:

Pertama kita mencari dulu posisi kanvas dilayar. Posisi kanvas sekarang selalu berada di tengah menyesuaikan dengan dimensi layar.

    let rect = canvas.getBoundingClientRect();

getBoundingClientRect() akan menghasilkan posisi, panjang dan lebar kanvas. Kemudian kita menggunakan nilainya untuk melakukan perhitungan selanjutnya.

    let poslx = (e.clientX - rect.x) * canvasScaleX;
    let posly = (e.clientY - rect.y) * canvasScaleY;

e.clientX dan e.clientY adalah posisi diklik di layar, bukan di kanvas.. 

rect.x dan rect.y adalah posisi kanvas.

canvasScaleX dan canvasScaleY adalah skala dari kanvas yang dihitung saat kanvas diskala mengikuti ukuran layar. Anda bisa melihat pada fungsi resize() saat kanvas diskala

let posx = Math.floor(poslx / 32);
let posy = Math.floor(posly / 32);

Karena kita menggunakan grid dengan panjang dan lebar 32 maka nilai posisi akhirnya kita bagi 32.

Selanjutnya seperti biasa, kita melakukan proses pencarian jalur, update kanvas, dst seperti pada tulisan sebelumnya.

Pada tulisan berikutnya, Saya akan membahas bagaimana membuat karakter berjalan mengikuti jalur yang telah dibuat oleh algorithma path-finding.


Terima kasih sudah mampir dan membaca. Bila ada pertanyaan, silahkan bertanya di komentar

Minggu, 09 Agustus 2020

Macam kanvas responsif

Tulisan ini akan membahas macam kanvas responsif. Responsif disini maksudnya adalah kanvas itu akan berubah ukurannya mengikuti resolusi layar. Hal ini sangat penting dalam pembuatan aplikasi pada telepon genggam karena banyaknya resolusi yang dimiliki.

Secara umum ada empat macam pendekatan untuk responsif yang pernah Saya temui. Tiga pertama sering dipakai di applikasi umum sedangkan yang terakhir hanya saya temukan di aplikasi Animate. Saya tidak membahas yang terakhir karena penggunaannya kurang umum dan Saya kurang paham juga.


Yang pertama disebut letterbox, pan atau kotak. Memiliki ciri-ciri sebagai berikut:
Rasio kanvas tetap
Kanvas diskala hingga memenuhi layar, tapi tidak sampai membuat gambar melebihi layar.
Ada jeda kosong di pinggir samping atau atas, karena rasio kanvas dan layar kadang tidak sama.

Yang kedua disebut strecth atau melar.
Rasio kanvas tidak dijaga, membuat gambar tampak melar tidak merata.
Kanvas diskala hingga memenuhi layar.
Tidak ada jeda kosong.

Yang ketiga disebut background atau cover, atau layar-penuh
Rasio kanvas tetap
Kanvas diskala memenuhi layar hingga melebihi layar dan gambar terpotong.
Tidak ada jeda kosong.

Penjelasan Saya cukupkan sampai sini dulu, berikutnya kita akan membahas yang pertama di tulisan berikutnya.