Browser tidak mendukung

Algoritma Boids

Latar Belakang

Boids adalah sebuah program kehidupan buatan, yang dikembangkan oleh Craig Reynolds pada tahun 1986, yang mensimulasikan perilaku berkelompok burung, dan gerakan kelompok terkait. Nama "boid" merupakan versi singkat dari "bird-oid object", yang merujuk pada objek mirip burung.

Algoritma boids sering digunakan dalam video game dan simulasi untuk menghasilkan perilaku gerombolan atau kawanan yang tampak alami. Dalam game seperti Spore atau GTA V, algoritma boids digunakan untuk mengatur gerakan kelompok hewan atau musuh sehingga mereka tampak bergerak bersama secara alami, tanpa harus mengatur setiap individu secara manual.


Implementasi

Dalam simulasi ini, seperti banyak model lainnya, menggunakan tiga aturan dasar:

  1. Alignment (Penyelarasan)
  2. Cohesion (Kohesi)
  3. Separation (Pemisahan)

Teknologi yang digunakan dalam simulasi ini memanfaatkan TypeScript dan HTML Canvas.

Setiap instance dari boid akan diwakili oleh kelas Boid, yang mendefinisikan posisi, kecepatan, percepatan, serta atribut lain. Setiap aturan dasar akan diimplementasikan dalam bentuk method pada kelas Boid berikut:

class Boid {
  position: Vector2
  velocity: Vector2
  acceleration: Vector2
  maxSpeed: number
  maxForce: number

  constructor(x: number, y: number) {
    ...
  }

  ...
}

Alignment (Penyelarasan)

Menyelaraskan arah pergerakan suatu boid dengan boid-boid lain di sekitarnya, sehingga mereka bergerak ke arah yang serupa. Perilaku ini meniru cara burung atau ikan bergerak dalam kawanan, di mana setiap anggota akan menyesuaikan arahnya agar selaras dengan yang lain.

Aturan alignment diterapkan dengan cara menghitung rata-rata vektor arah (velocity) dari boid-boid terdekat saat ini. Kemudian menyesuaikan arah boid saat ini agar lebih mendekati rata-rata arah yang telah dihitung.

align(boids: Boid[]) {
  const perceptionRadius = 50
  let steering = new Vector2(0, 0)
  let total = 0

  // Iterasi setiap boid
  boids.forEach((other) => {
    const distance = this.position.distance(other.position)

    if (other !== this && distance < perceptionRadius) {
      // Tambahkan setiap `velocity` ke perhitungan vektor
      steering = steering.add(other.velocity)
      total++
    }
  })

  // Dapatkan rata-rata vektor arah
  if (total > 0) {
    steering = steering.devide(total)
    steering = steering.normalize()
    steering = steering.multiply(this.maxSpeed)
    steering = steering.substract(this.velocity)
    steering = steering.normalize()
    steering = steering.multiply(this.maxForce)
  }

  return steering
}

Cohesion (Kohesi)

Setiap boid berusaha mendekati pusat kelompok atau kawanan, sehingga mereka tetap berkumpul. Dengan kata lain, setiap boid akan mencoba untuk bergerak menuju posisi rata-rata dari boid-boid lain di sekitarnya. Perilaku ini menciptakan efek "keterpaduan" atau "cohesion" yang meniru bagaimana hewan-hewan seperti burung atau ikan saling menjaga jarak agar tetap bersama.

Aturan cohesion diterapkan dengan menghitung rata-rata vektor posisi (position) dari boid-boid terdekat saat ini dan menyesuaikan arah gerakan berdasarkan vektor tersebut.

cohesion(boids: Boid[]) {
  const perceptionRadius = 50
  let steering = new Vector2(0, 0)
  let total = 0

  // Iterasi setiap boid
  boids.forEach((other) => {
    const distance = this.position.distance(other.position)

    if (other !== this && distance < perceptionRadius) {
      // Tambahkan setiap `position` ke perhitungan vektor
      steering = steering.add(other.position)
      total++
    }
  })

  // Daptkan rata-rata vektor posisi
  if (total > 0) {
    steering = steering.devide(total)
    steering = steering.substract(this.position)
    steering = steering.normalize()
    steering = steering.multiply(this.maxSpeed)
    steering = steering.substract(this.velocity)
    steering = steering.normalize()
    steering = steering.multiply(this.maxForce)
  }

  return steering
}

Separation (Pemisahan)

Setiap boid berusaha untuk menghindari kerumunan kawanan lokal. Aturan ini memastikan bahwa setiap boid dalam kawanan menjaga jarak tertentu dari boid lain di sekitarnya untuk menghindari tabrakan dan kerumunan yang terlalu padat.

Dalam implementasi kode, aturan ini diterapkan dengan menghitung rata-rata vektor arah (velocity) yang berlawanan dari setiap boid saat ini dan menyesuaikan arah gerakan berdasarkan vektor tersebut.

separation(boids: Boid[]) {
  const perceptionRadius = 50
  let steering = new Vector2(0, 0)
  let total = 0

  // Iterasi setiap boid
  boids.forEach((other) => {
    const distance = this.position.distance(other.position)

    if (other !== this && distance < perceptionRadius) {
      // Dapatkan vektor arah yang berlawanan dengan `other`
      let diff = this.position.substract(other.position)

      // Normalisasikan dan bagi dengan jarak sehingga
      // semakin dekat dengan `other` semakin besar pengaruhnya
      diff = diff.normalize()
      diff = diff.devide(distance)
      steering = steering.add(diff)
      total++
    }
  })

  // Dapatkan rata-rata vektor
  if (total > 0) {
    steering = steering.devide(total)
    steering = steering.normalize()
    steering = steering.multiply(this.maxSpeed)
    steering = steering.substract(this.velocity)
    steering = steering.normalize()
    steering = steering.multiply(this.maxForce)
  }

  return steering
}

Update Pada Perulangan Animasi

Gabungkan 3 aturan dasar tadi menggunakan method flock. Method ini bertanggung jawab untuk menentukan arah gerakan akhir boid berdasarkan ketiga perilaku tersebut agar boid bisa bergerak secara alami dalam kelompok.

flock(boids: Boid[]) {
  const alignment = this.align(boids)
  this.acceleration = this.acceleration.add(alignment)

  const cohesion = this.cohesion(boids)
  this.acceleration = this.acceleration.add(cohesion)

  const separation = this.separation(boids)
  this.acceleration = this.acceleration.add(separation)
}

Setelah arah gerak akhir diketahui, update posisi dari setiap boid. Jangan lupa masukkan elapsTime atau waktu delta dalam kalkulasi untuk konsistensi animasi di berbagai perangkat dengan FPS yang berbeda. Untuk penjelasan lebih lanjut mengenai delta time, kunjungi.

update(elapsedTime: number) {
  // `timeScale` adalah faktor skala waktu yang dihitung
  // berdasarkan elapsedTime (waktu yang telah berlalu
  // antara frame saat ini dan frame sebelumnya)

  // Asumsi 60 FPS sebagai dasar
  const timeScale = elapsedTime / 16.67

  this.position = this.position.add(this.velocity.multiply(timeScale))
  this.velocity = this.velocity.add(this.acceleration.multiply(timeScale))
  this.velocity = this.velocity.normalize()
  this.velocity = this.velocity.multiply(this.maxSpeed)

  // Atur percepatan menjadi `0` setelah digunakan,
  // karena percepatan harus dihitung ulang setiap frame
  this.acceleration = this.acceleration.multiply(0)

  // Tambahkan perilaku lain jika perlu
  ...
}

Terakhir pada fungsi render (atau fungsi yang akan dipanggil setiap frame animasi):

function render(elapsedTime: number, ctx: CanvasRenderingContext2D) {
  boids.forEach((boid) => {
    boid.flock(boids)
    boid.update(elapsedTime)
    boid.show(ctx)
  })
}

Referensi

© 2024 Dalam pengembangan.