محاكاة بسيطة للمساحة باستخدام Python و Box2D

مرحبا يا هابر.

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

سأجرؤ على ارتكاب خطأ ، لكن هذا هو الوصف الأول لـ Box2D لـ Python على Habré ، نحن نملأ هذه الفجوة.



بالنسبة لأولئك الذين يهتمون بكيفية عمل ذلك ، فإن التفاصيل تحت القطع.

Box2D هي مكتبة مجانية عبر منصة تم إنشاؤها بواسطة Blizzard Erin Catto. تم تقديم المكتبة في عام 2007 ، واليوم تم نقلها إلى جميع المنصات تقريبًا. هناك منفذ لـ Python أيضًا ، ووصفه مربك إلى حد ما ، ولكن آمل أن يصبح كل شيء أكثر وضوحًا بمساعدة هذه المقالة.

المقدمة


تتكون مكتبة pybox2d من مكونين - Box2D نفسها ، ومكتبة عبر منصة للنمذجة المادية ، ووحدة عرض منفصلة تسمى Framework. العرض مطلوب إذا أردنا رؤية الكائنات التي تم إنشاؤها على الشاشة ، وهي مناسبة بما يكفي لمحاكاة لدينا. يمكن أن تستخدم فئة Framework طرق إخراج مختلفة (المزيد هنا ) ، سنستخدم لعبة pygame. إذا تم تثبيت مكتبة pygame ، فسيتم "التقاطها" تلقائيًا ، ولا يلزم عمل أي شيء آخر. للتثبيت ، ما عليك سوى إدخال الأمر pygame Box2D .

يظهر أدناه أصغر برنامج يعمل باستخدام Box2D. الكود متعدد المنصات ، وسيعمل في كل مكان ، على كل من Linux و Windows.

from Box2D.examples.framework import Framework
from Box2D import *

class Simulation(Framework):
    def __init__(self):
        super(Simulation, self).__init__()

        # Ground body
        self.world.CreateBody(shapes=b2LoopShape(vertices=[(20, 0), (20, 40), (-20, 40), (-20, 0)]))
        # Dynamic body
        circle = b2FixtureDef(shape=b2CircleShape(radius=2), density=1, friction=1.0, restitution=0.5)
        self.world.CreateBody(type=b2_dynamicBody, position=b2Vec2(0,30), fixtures=circle, linearVelocity=(5, 0))

    def Step(self, settings):
        super(Simulation, self).Step(settings)

if __name__ == "__main__":
    Simulation().run()

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

هذا كل شيء ، قم بتشغيل البرنامج وشاهد المحاكاة النهائية:



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

أطلقنا القمر الصناعي


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

كود المصدر ككل:

from Box2D import *
from Box2D.examples.framework import Framework


class Simulation(Framework):
    def __init__(self):
        super(Simulation, self).__init__()

        # Default gravity disable
        self.world.gravity = (0.0, 0.0)
        # Gravity constant
        self.G = 100

        # Planet
        circle = b2FixtureDef(shape=b2CircleShape(radius=5), density=1, friction=0.5, restitution=0.5)
        self.world.CreateBody(type=b2_dynamicBody, position=b2Vec2(0,0), fixtures=circle)

        # Satellite
        circle_small = b2FixtureDef(shape=b2CircleShape(radius=0.2), density=1, friction=0.5, restitution=0.2)
        self.world.CreateBody(type=b2_dynamicBody, position=b2Vec2(0, 10), fixtures=circle_small, linearVelocity=(20, 0))

    def Step(self, settings):
        super(Simulation, self).Step(settings)

        # Simulate the Newton's gravity
        for bi in self.world.bodies:
            for bk in self.world.bodies:
                if bi == bk:
                    continue

                pi, pk = bi.worldCenter, bk.worldCenter
                mi, mk = bi.mass, bk.mass
                delta = pk - pi
                r = delta.length
                if abs(r) < 1.0:
                    r = 1.0

                force = self.G * mi * mk / (r * r)
                delta.Normalize()
                bi.ApplyForce(force * delta, pi, True)

if __name__ == "__main__":
    Simulation().run()

كما ترون ، فإننا "نوقف" الجاذبية القياسية عن طريق تعيين المعلمة self.world.gravity إلى 0. كما نضيف المعلمة G ، وهذا هو "ثابت الجاذبية" لعالمنا الافتراضي ، والذي يُستخدم في حساب طريقة الخطوة. أنشأنا أيضًا شيئين - قمر صناعي وكوكب. من المهم ملاحظة معلمات الكثافة ونصف القطر. وفقًا لهذه المعلمات ، تقوم مكتبة Box2D نفسها بحساب الكتلة المستخدمة في الحساب. لحساب قوة التفاعل ، يتم استخدام صيغة "المدرسة" المعتادة لقانون الجاذبية لنيوتن :



الآن نبدأ المحاكاة. لم نصل إلى السرعة الكونية الأولى ، وعلى الرغم من أن القمر الصناعي لا يزال يدور حول الكوكب بأكمله ، إلا أنه من الصعب تسميته "طيران":



نزيد السرعة بتغيير خط الكود إلى خطي خطي = (28 ، 0):



دخل "قمرنا الصناعي" المدار بنجاح حول "الكوكب"! إذا زادت السرعة ، سيصبح المدار إهليلجيًا:



وأخيرًا ، سنصور شيئًا أكثر تشابهًا مع "النظام الشمسي" ، بإضافة ثلاثة كواكب بأحجام مختلفة في مدارات مختلفة:

circle_small = b2FixtureDef(shape=b2CircleShape(radius=0.2), density=1, friction=0.5, restitution=0.2)
circle_medium = b2FixtureDef(shape=b2CircleShape(radius=0.3), density=1, friction=1.0, restitution=0.5)
self.world.CreateBody(type=b2_dynamicBody, position=b2Vec2(0, 6), fixtures=circle_small, linearVelocity=(37, 0))
self.world.CreateBody(type=b2_dynamicBody, position=b2Vec2(0, 10), fixtures=circle_small, linearVelocity=(28, 0))
self.world.CreateBody(type=b2_dynamicBody, position=b2Vec2(0, 15), fixtures=circle_medium, linearVelocity=(22, 0))

النتيجة:



نرى أنه كلما ابتعد الكوكب عن "الشمس" ، زادت فترة ثورته (قانون كيبلر الثالث). لسوء الحظ ، لا يسمح محرك Box2D برسم مسارات الحركة على الشاشة ، لذلك من الصعب "رؤية" قوانين Keppler الأولى والثانية ، ولكن يمكنك التأكد من تنفيذها أيضًا.

استنتاج


كما ترى ، مع Box2D ، يمكن إجراء عمليات محاكاة بسيطة بأقل جهد. بالطبع ، لا يزال هذا المحرك عبارة عن محرك ألعاب ، وليس محركًا علميًا ، لذلك لا يجب أن تتوقع منه محاكاة صحيحة لتصادم المجرات أو توسيع المادة خلال الانفجار الكبير. لكن بعض الأنماط مثيرة للاهتمام لمشاهدة.

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

All Articles