Kami bekerja dengan grafis dua dimensi dalam JavaScript

Selamat siang teman!

Membuat animasi realistis dari proses fisik mungkin tampak seperti tugas yang menakutkan, tetapi sebenarnya tidak. Algoritma yang digunakan untuk ini bisa sangat sederhana dan pada saat yang sama secara akurat mereproduksi fenomena fisik seperti gerak, akselerasi dan gravitasi (tarikan).

Ingin tahu bagaimana algoritma ini diterapkan di JS?



Contohnya dapat ditemukan di sini .

Kode sumber ada di sini .

Gerakan seragam dan dipercepat


Mari kita mulai dengan gerakan.

Untuk gerakan yang seragam, kita dapat menggunakan kode berikut:

function move(dt) {
    x += vx * dt
    y += vy * dt
}

Di sini x dan y adalah koordinat objek, vx dan vy adalah kecepatan objek di sepanjang sumbu horizontal dan vertikal, masing-masing, dt (time delta - time delta) adalah waktu antara dua tanda timer, yang dalam JS sama dengan dua panggilan untuk memintaAnimationFrame.

Misalnya, jika kita ingin memindahkan objek yang terletak di titik dengan koordinat 150, 50 ke barat daya, kita dapat melakukan hal berikut (satu tanda waktu atau satu langkah):

x = 150 += -1 * 0.1 - > 149.9
y = 50 += 1 * 0.1 - > 50.1

Bahkan gerakan itu membosankan, jadi mari kita akselerasi objek kita:

function move(dt) {
    vx += ax * dt
    vy += ay * dt
    x += vx * dt
    y += vy * dt
}

Di sini kapak dan ay adalah akselerasi sepanjang sumbu x dan y, masing-masing. Kami menggunakan akselerasi untuk mengubah kecepatan (vx / vy). Sekarang, jika kita mengambil contoh sebelumnya dan menambahkan akselerasi di sepanjang sumbu x (ke barat), kita mendapatkan yang berikut:

vx = -1 += -1 * 0.1 - > -1.1 // vx += ax * dt
vy = 1 += 0 * 0.1 - > 1 // vy += ay * dt
x = 150 += -1.1 * 0.1 - > 149.89 // x += vx * dt;     -0.01
y = 50 += 1 * 0.1 - > 50.1 // y += vy * dt

Gravitasi


Kami belajar cara memindahkan objek individual. Bagaimana kalau belajar cara memindahkan mereka relatif satu sama lain? Ini disebut gravitasi atau gravitasi. Apa yang perlu kita lakukan untuk ini?

Inilah yang ingin kami dapatkan:



Pertama, mari kita ingat beberapa persamaan dari sekolah menengah.

Gaya yang diterapkan pada benda dihitung dengan rumus berikut:
F = m * a ... gaya sama dengan massa kali akselerasi
a = F / m ... dari sini kita dapat menyimpulkan bahwa gaya bekerja pada objek dengan akselerasi.

Jika kita menerapkan ini pada dua objek yang berinteraksi, kita mendapatkan yang berikut:



Terlihat rumit (setidaknya untuk saya), jadi mari kita perbaiki. Dalam persamaan ini, | F | - ini adalah besarnya gaya, yang sama untuk kedua objek, tetapi diarahkan ke arah yang berlawanan. Objek diwakili oleh massa m_1 dan m_2. k adalah konstanta gravitasi dan r adalah jarak antara pusat massa benda. Masih belum jelas? Berikut ini ilustrasi:



Jika kita ingin melakukan sesuatu yang menarik, kita membutuhkan lebih dari dua objek.



Dalam gambar ini, kita melihat dua objek oranye menarik hitam dengan gaya F_1 dan F_2, namun, kami tertarik pada gaya resultan F, yang dapat kita hitung sebagai berikut:

  • pertama-tama kita menghitung gaya F_1 dan F_2 menggunakan rumus sebelumnya:
  • lalu terjemahkan semuanya menjadi vektor:

Hebat, kami memiliki semua perhitungan yang diperlukan. Bagaimana kita menerjemahkannya ke dalam kode? Saya tidak akan membuat Anda bosan dengan langkah-langkah perantara dan saya akan segera memberikan kode selesai dengan komentar. Jika Anda memerlukan informasi lebih lanjut, Anda dapat menulis kepada saya, saya akan menjawab semua pertanyaan Anda.

function moveWithGravity(dt, o) { // o -  ,    
    for (let o1 of o) { //   ()   
        o1.fx = 0
        o1.fy = 0
    }

    for (let [i, o1] of o.entries()) { //    
        for (let [j, o2] of o.entries()) {
            if (i < j) { //            
                let dx = o2.x - o1.x //     
                let dy = o2.y - o1.y
                let r = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2))
                if (r < 1) { //     0
                    r = 1
                }

                //     ; k = 1000
                let f = (1000 * o1.m * o2.m) / Math.pow(r, 2)
                let fx = f * dx / r
                let fy = f * dy / r
                o1.fx += fx //   
                o1.fy += fy
                o2.fx -= fx //      
                o2.fy -= fy
            }
        }
    }

    for (let o1 of o) { //    ...
        let ax = o1.fx / o1.m // 
        let ay = o1.fy / o1.m

        o1.vx += ax * dt // 
        o1.vy += ay * dt

        o1.x += o1.vx * dt // 
        o1.y += o1.vy * dt
    }
}

Bentrokan


Tubuh yang bergerak terkadang bertabrakan. Dari tabrakan, salah satu benda didorong keluar oleh orang lain, atau beberapa benda terpental dari yang lain. Pertama



, mari kita bicara tentang mendorong: Pertama-tama, kita perlu menentukan bahwa ada tabrakan:

class Collision {
    constructor(o1, o2, dx, dy, d) {
        this.o1 = o1
        this.o2 = o2

        this.dx = dx
        this.dy = dy
        this.d = d
    }
}

function checkCollision(o1, o2) {
    let dx = o2.x - o1.x
    let dy = o2.y - o1.y
    let d = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2))
    if(d < o1.r + o2.r){
        return {
            collisionInfo: new Collision(o1, o2, dx, dy, d),
            collided: true
        }
    }
    return {
        collisionInfo: null,
        collided: false
    }
}

Kami mendeklarasikan kelas Collision yang mewakili dua objek bertabrakan. Dalam fungsi checkCollision, pertama-tama kita menghitung selisih antara koordinat x dan y dari objek, lalu kita menghitung jarak aktualnya d. Jika jumlah jari-jari benda kurang dari jarak di antara mereka, maka ada tabrakan benda-benda ini - kembalikan objek Tabrakan.



Selanjutnya, kita perlu menentukan arah perpindahan dan besarnya (magnitude):
n_x = d_x / d ... ini adalah vektor
n_y = d_y / d

s = r_1 + r_2 - d ... ini adalah "magnitude" dari tabrakan (lihat gambar di bawah)



Di JS, mungkin terlihat Begitu:

function resolveCollision(info){ // "info" -   Collision   
    let nx = info.dx / info.d //  
    let ny = info.dy / info.d
    let s = info.o1.r + info.o2.r - info.d //   
    info.o1.x -= nx * s/2 //       
    info.o1.y -= ny * s/2
    info.o2.x += nx * s/2 //      
    info.o2.y += ny * s/2
}

Melambung


Bagian terakhir dari teka-teki adalah implementasi bouncing dari satu objek dari yang lain dalam tabrakan. Saya tidak akan memberikan semua perhitungan matematis, karena ini akan membuat artikelnya sangat panjang dan membosankan, saya akan membatasi diri pada kenyataan bahwa saya menyebutkan hukum kekekalan momentum dan hukum kekekalan energi, yang membantu untuk sampai pada rumus ajaib berikut:

k = -2 * ((o2.vx - o1.vx) * nx + (o2.vy - o1.vy) * ny) / (1 / o1.m + 1 / o2.m) ... * Magic *

Bagaimana kita menggunakan magic k? Kita tahu ke arah mana objek akan bergerak, tetapi kita tidak tahu seberapa jauh. Ini k. Ini adalah bagaimana vektor (z) dihitung, menunjukkan ke mana objek harus bergerak:





Kode terlihat seperti ini:

function resolveCollisionWithBounce(info){
    let nx = info.dx / info.dy
    let ny = info.dy / info.d
    let s = info.o1.r + info.o2.r - info.d
    info.o1.x -= nx * s/2
    info.o1.y -= ny * s/2
    info.o2.x += nx * s/2
    info.o2.y += ny * s/2

    // ...
    let k = -2 ((info.o2.vx - info.o1.vx) * nx + (info.o2.vy - info.o1.vy) * ny) / (1/info.o1.m + 1/info.o2.m)
    info.o1.vx -= k * nx / info.o1.m //   ,   "k"   "s/2"  "m"
    info.o1.vy -= k * ny / info.o1.m
    info.o2.vx += k * nx / info.o2.m
    info.o2.vy += k * ny / info.o2.m
}

Kesimpulan


Artikel ini memiliki banyak persamaan, tetapi kebanyakan sangat sederhana. Saya harap artikel ini sedikit membantu Anda memahami bagaimana fenomena dan proses fisik diimplementasikan di JS.

All Articles