نحن نعمل مع رسومات ثنائية الأبعاد في JavaScript

يوم جيد يا اصدقاء!

قد يبدو إنشاء رسوم متحركة واقعية للعمليات الجسدية مهمة شاقة ، لكنها ليست كذلك. يمكن أن تكون الخوارزميات المستخدمة لهذا بسيطة للغاية وفي نفس الوقت تتكاثر بدقة مثل الظواهر الفيزيائية مثل الحركة والتسارع والجاذبية (الجذب).

هل تريد أن تعرف كيف يتم تنفيذ هذه الخوارزميات في JS؟



يمكن العثور على أمثلة هنا .

كود المصدر هنا .

حركة موحدة ومعجلة


لنبدأ بالحركة.

للحركة المنتظمة ، يمكننا استخدام الكود التالي:

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

هنا x و y هي إحداثيات الكائن ، vx و vy هي سرعة الكائن على طول المحاور الأفقية والرأسية ، على التوالي ، dt (دلتا دلتا - دلتا دلتا) هي الوقت بين علامتي توقيت ، والتي في JS تساوي مكالمتين إلى requestAnimationFrame.

على سبيل المثال ، إذا أردنا نقل كائن موجود عند نقطة ذات إحداثيات 150 ، 50 إلى الجنوب الغربي ، فيمكننا القيام بما يلي (علامة توقيت واحدة أو خطوة واحدة):

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

حتى الحركة مملة ، لذلك دعونا نعطي جسمنا تسارعًا:

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

الفأس و ay هما التسارع على طول المحورين x و y ، على التوالي. نستخدم التسارع لتغيير السرعة (vx / vy). الآن ، إذا أخذنا المثال السابق وأضفنا تسارعًا على طول المحور x (إلى الغرب) ، فسوف نحصل على ما يلي:

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

الجاذبية


تعلمنا كيفية تحريك الأشياء الفردية. ماذا عن تعلم كيفية نقلها بالنسبة لبعضها البعض؟ وهذا ما يسمى بالجاذبية أو الجاذبية. ماذا علينا أن نفعل لهذا؟

إليك ما نريد الحصول عليه:



أولاً ، لنتذكر بعض المعادلات من المدرسة الثانوية.

يتم حساب القوة المطبقة على الجسم بالصيغة التالية:
F = m * a ... القوة تساوي تسارع كتلة الكتلة
a = F / m ... من هذا يمكننا أن نستنتج أن القوة تعمل على الجسم مع التسارع

إذا طبقنا هذا على اثنين تفاعل الأشياء ، نحصل على ما يلي:



يبدو الأمر معقدًا (على الأقل بالنسبة لي) ، لذا دعنا نصلح ذلك. في هذه المعادلة | F | - هذا هو حجم القوة ، وهو نفس الشيء لكلا الجسمين ، ولكنه موجه في اتجاهين متعاكسين. يتم تمثيل الكائنات بالكتل m_1 و m_2. k هو ثابت الجاذبية و r هو المسافة بين مراكز كتلة الأجسام. أما زلت غير واضح؟ هنا توضيح:



إذا أردنا القيام بشيء مثير للاهتمام ، فنحن بحاجة إلى أكثر من شيئين.



في هذه الصورة نرى كائنين برتقاليين يجذبان الأسود بالقوات F_1 و F_2 ، ولكننا مهتمون بالقوة الناتجة F ، والتي يمكننا حسابها على النحو التالي:

  • أولاً نحسب القوى F_1 و F_2 باستخدام الصيغة السابقة:
  • ثم يترجم كل شيء إلى ناقلات:

رائع ، لدينا جميع الحسابات اللازمة. كيف نترجم هذا إلى كود؟ لن أتحمل عليك الخطوات الوسيطة وسأعطي على الفور الرمز النهائي مع التعليقات. إذا كنت بحاجة إلى مزيد من المعلومات ، يمكنك الكتابة إلي ، سأجيب على جميع أسئلتك.

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
    }
}

صدام


تتصادم الأجسام المتحركة أحيانًا. من التصادم ، يتم دفع الأشياء من قبل الآخرين ، أو ترتد بعض الأشياء من الآخرين. أولاً



، لنتحدث عن الدفع: أولاً وقبل كل شيء ، نحتاج إلى تحديد وجود تصادم:

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
    }
}

نعلن عن فئة تصادم تمثل كائنين متصادمين. في دالة checkCollision ، نحسب أولاً الفرق بين إحداثيات س و ص للكائنات ، ثم نحسب المسافة الفعلية د. إذا كان مجموع نصف قطر الأشياء أقل من المسافة بينهما ، فعندئذ كان هناك تصادم بين هذه الأشياء - قم بإرجاع كائن التصادم.



بعد ذلك ، نحتاج إلى تحديد اتجاه الإزاحة وحجمه (الحجم):
n_x = d_x / d ... هذا هو المتجه
n_y = d_y / d

s = r_1 + r_2 - d ... هذا هو "حجم" التصادم (انظر الصورة أدناه)



في JS ، قد يبدو وبالتالي:

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
}

وثب، ارتداد


الجزء الأخير من اللغز هو تنفيذ ارتداد كائن من آخر في التصادم. لن أعطي جميع الحسابات الرياضية ، لأن هذا سيجعل المقالة طويلة ومملة للغاية ، وسأقتصر على حقيقة أنني أذكر قانون الحفاظ على الزخم وقانون الحفاظ على الطاقة ، مما يساعد على الوصول إلى الصيغة السحرية التالية:

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

كيف يمكننا استخدام Magic نحن نعرف في أي اتجاه ستتحرك الأجسام ، لكننا لا نعرف إلى أي مدى. هذا ك. هذه هي الطريقة التي يتم بها حساب المتجه (z) ، مما يوضح أين يجب أن تتحرك الكائنات:





الرمز يبدو كما يلي:

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
}

استنتاج


تحتوي المقالة على العديد من المعادلات ، ولكن معظمها بسيط للغاية. آمل أن تكون هذه المقالة قد ساعدتك قليلاً في فهم كيفية تنفيذ الظواهر والعمليات الفيزيائية في JS.

All Articles