السبت، 5 مارس 2011

الفيزياء في برمجة الألعاب بمحرك PhysX


تهدف هذه المقالة إلى إلقاء الضوء على أحد أهم عناصر الألعاب الحديثة وهو الفيزياء .

لن نغطّي بالطبع كل شيء عن الفيزياء في برمجة الألعاب فهذا شيء مستحيل , ولكن سنحاول أن نقوم معا بالتعرف على هذا المجال ,
والتطبيق العملي باستخدام أشهر المحركات الفيزيائية القادمة وبقوة nVedia PhysX .

سأفرض أن القارئ الكريم على علم بعالم البرمجة والتطوير باستخدام لغة برمجية معيّنة مثل ++C , كما سأفرض أن لديه الدراية الكافية لرسم بعض الأشكال والمجسمات على الكميوتر باستخدام مكتبات الرسم المتطوّرة مثل OpenGL أو DirectX , ليس المطلوب أن تكون محترفا , لكن المطلوب أن تملك الأساسيات اللازمة .
  • الفيزياء في الألعاب الحديثة .
  • المحركات الفيزيائية المتاحة .
  • مفاهيم أساسية في المحركات الفيزيائية .
  • تثبيت المحرك الفيزيائي PhysX .
  • برنامجك الأول باستخدام المحرك الفيزيائي PhysX ( مثال مرفق ) .
  • التعليق على البرنامج .
  • أين أذهب بعد ذلك ؟!
  • المراجع .

  • الفيزياء في الألعاب الحديثة .
في أي لعبة ( متطوّرة ) :
  • ستجد تلك الشخصية التي لا تستطيع اختراق الحائط لوجود نظام لاكتشاف التصادم بين الشخصية و الحائط .
  • في كرة القدم , ستجد الكرة تنطلق إلى الأعلى بعد ركلها , ثم تعود إلى الأرض , وترتد , وهكذا حتى تستقر , ارتفعت للأعلى لوجود قوّة أثرت عليها بمقدار معيّن , وعادت إلى الأرض لوجود قوة الجاذبية التي تؤثر عليها , ستجد أن الكرة في هذه الحالة "ديناميكية " لأنها استجابت للمؤثرات الخارجية , بينما أرض الملعب " ثابت" لا يتأثر بالجاذبية ولا با اصطدام الكرة .
  • ستجد أيضا الراية الركنية للملعب .. تتأثر بالرياح .. وستجد في لعبة أخرى , ملابس اللاعب تتأثر بالرياح أيضا ..
  • ستجد أن جزيئات الماء من شلاّل مياه , تأخذ شكل مجرى النهر الذي تسير فيه .
  • ستجد , في لعبة سيّارات , السيّارة وهي تنطلق بتسارع معيّن حتى تصل إلى سرعة 200 كلم في الساعة , ثم تنعطف , لتحدث سلسلة من " التنطيط " هنا وهناك , قبل أن تصطدم بالجدار لتتحوّل إلى كومة ركام , وكان بالامكان استخدام المكابح لايقاف هذا الحادث ..

 
تأثير الفيزياء على الملابس Clothes , المصدر : المحرك Nvidia PhysXتأثير القوى على الملابس , المصدر : المحرك Nvidia PhysXفيزياء السؤائل , المصدر : المحرك الفزيائي Nvidia PhysX .


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

  • تعريف المحرك الفيزيائي :

    كما قلت قبل قليل , يمكن استخدام محرك فيزيائي للقيام بمهمّة " العمليات الفيزيائية " المختلفة , دعني أشرح ولو بشكل بسيط مفهوم المحرك الفيزيائي :
    • المحرك الفيزيائي :
      المحرك الفيزيائي عبارة عن مكتبات برمجية ( تضم أصناف classes و دوال Functions مختلفة ) , تعتمد على لغة برمجية معينة مثل لغة ++C أو JAVA .. الخ , للقيام بحسابات ما , على مجسّم ما , للقيام بحسابات لتطبيق القوانين الفيزيائية الكلاسيكية على مجسم ما .

      اذا , المحرك الفيزيائي لايقوم برسم شيء على النافذة , انما يقوم باجراء حسابات وتخزينها في متغيرات ( كاحداثيات الجسم ) , ومن ثم تقوم أنت باستقبال هذه المتغيرات والاستفادة منها في رسم شيء على الشاشة باستخدام مكتبات أخرى مثل Direct3D و OpenGL أو أي مكتبة رسومية أخرى .

  • المحركات الفيزيائية المتاحة :
    هناك عدة محركات فيزيائية متاحة , بعضها مجاني والبعض الاخر بمقابل مادي , وبعضها مفتوح المصدر Open Source :
  1. Nvidia PhysX :



    أحد أشهر المحركات الفيزيائية , محرك متكامل , مجاني للألعاب والتطبيقات الغير تجارية :

    http://www.nvidia.com/object/nvidia_physx.html
  2. Havok :

    المحرك الأول في عالم الألعاب الاحترافية ( التي تشاهدها في PlayStation 3 مثلا .. ) , وصدر في الفترة الأخيرة نسخة مجانية منه.

    http://www.havok.com
  3. Newton :

    محرك مجاني للتطبيقات المجانية , قد لايرقى لمستوى الاحتراف , ولكن يتميّز بالسهولة على العموم , ودعمه لأكثر من لغة برمجية .

    http://www.newtondynamics.com


    سنركز في هذا المقال على المحرك الفيزيائي Nvidia PhysX , وسنتعلّم طريقة تثبيته ومثال تطبيقي عليه .

    من تطبيقات المحرك الفيزيائي PhysX

PhysX 2

من تطبيقات المحرك الفيزيائي PhysX






  • مفاهيم أساسية في المحركات الفيزيائية :
سنستخدم مصطلحات المحرك الفيزيائي PhysX ... المحرك الفيزيائي يتكون من : 

  • Scene - المشهد :
ويسمى في المكتبة ODE و Newton بالعالم ( World ) ..

العالم أو المشهد , يكون له حدود معينه ( مثلا 10 وحدات * 10 وحدات ) ( 10 أمتار *10 أمتار مثلا .. ) , حيث كل ماسيكون خارج حدود هذه المساحة , لن يتأثر بأي حسابات فيزيائية , وذلك لتسريع عملية الحسابات , ولأن هذا هو الواقع , فمثلا الجاذبية ستكون معدومة في خارج الكرة الأرضية .

كل مشهد أو عالم فيزيائي تنشئه سيكون عالم مستقل عن الاخر , وكل مشهد أو عالم ( Scene ) له خصائصه .. مثل :
  1. الجاذبية gravity ( اتجاهها ومقدارها ) .
  2. المجسمات Bodies أو Actors الموجودة في هذا العالم .
فمثلا , في الحالة الطبيعية تكون الجاذبية إلى الأسفل بمقدار 9.8 ... لذلك تسقط جميع المجسمات باتجاه المحور Y .


مثال على عالم فيزيائي Scene , حيث الجاذبية إلى الأسفل ,
ويحوي على مجسمات , ويوجد حدود لهذا العالم الفيزيائي
  • Actors - المجسمات :
وتسمى في المكتبة ODE و Newton بالBodies . . وهو عبارة عن مجسم وهمي .. وأقول مرة أخرى .. وهمي .. لانعلم شكله حتى الان ، كل مجسم تنشئه .. تضيفه الى ، المشهد Scene , حتى يتم ادخاله في الحسابات . تنقسم المجسمات بشكل عام إلى قسمين ( مجسمات صلبه Rigid Body لاتتغيّر أشكالها ومجسمات مرنة Soft Body تتغيّر أشكالها تبعا لضغط القوى عليها ) , في الغالب ستتعامل مع النوع الأول الذي لا يتغيّر شكله , فهو الأسهل والأكثر استخداما .

المجسمات لها خواص .. سنذكر منها ما يلي : 

  • نوع الجسم :
يوجد نوعان من Actor أو Body

  1. static Body
    المجسمات الساكنة .. وهي عبارة عن مجسمات لاتتأثر بالجاذبية .. بالقوى الموجودة حولها .. فهي كالجدار .. مجرد حاجز للتصادم فقط ..
  2. dynamic Body
    وهي مجسمات تتأثر بالجاذبية .. وبأي قوة موجودة .. وتتعرض للارتداد ,... وما الى ذلك من المؤثرات .
  • شكل الجسم :
يوجد عدة أشكال قياسية للمجسمات .. منها :
Box - صندوق , Sphere - كرة , والكبسولة و الاشكال المعقدة .... الخ .
لذلك كل Actor نقوم بانشائه .. فيجب علينا تحديد شكله .. واذا حددنا شكله فيجب تحديد أبعاده .. وموقعه .. الخ .
كما يمكننا تحديد كثافة الجسم density .. حيث كلما زادت الكثافة .. صار الجسم أكثر قوة في مواجهة القوى المؤثرة عليه ..

أمثلة على بعض الأشكال ( كرة - كبسولة - صندوق ) , لاحظ تأثير القوة على الكسبولة - المصدر : المحرك PhysX 


  • القوى المؤثرة على الجسم :
كل جسم ننشئه .. نحدد له القوى التي تؤثر عليه .. هناك قوى خارجية مثل الجاذبية .. وهناك قوى على الجسم نفسه مثل .. عزم التدوير Torque .. أو القوة العادية Force التي تدفع الجسم الى أي اتجاه نريده .. الخ .


  • Material / الخامة:
مثل معامل احتكاك الجسم Friction , و معامل ارتداد الجسم Restitution .
حيث نستطيع من خلال ال Material محاكاة خامات الاجسام مثل الحديد و الخشب والمطاط .. الخ . 

  • Joints - الروابط :
نستطيع أن نربط المجسمات فيما بينها Actors عن طريق الروابط Joints ..
فنربط بين اطار السيارة والسيارة .. ونربط بين جسم ما .. وحبل .. فيكون الجسم معلق على الحبل ويتدلى .. الخ .
يوجد عدة أنواع من ال Joints , انظر لهذه الصورة التي تشرح بعض أنواع الروابط :

بعض أنواع الروابط , المصدر : المحرك الفيزيائي Havok


  • اكتشاف التصادم - Collision Detection :
في كل محرك فيزيائي اكتشاف تصادم .. وتستطيع ان تستفيد من اكتشاف التصادم هذا .. بدون وجود اجسام ديناميكية ... حيث يوجد دوال وكلاسات مساعدة تسهل علينا أمر اكتشاف التصادم .


  • الدوال الرياضية :
المحرك الفيزيائي يقوم على مجموعة من الدوال الرياضية والكلاسات ... مثال :
Vector : المتجهات .
Matrix : المصفوفات .
Quaternion .
بالاضافة الى كلاسات أساسية أخرى .. مثل box و Ray و Plane و Sphere ,


  • الخلاصة :

تنشئ عالم فيزيائي Scene ,, وتضع فيه مجموعة من المجسمات Actors .. وكل مجسم له خصائصه وشكله .. وكل مجسمين تربطهم عن طريق Joints و يوجد عدة أنواع من الJoints , ثم بعد ذلك تطلب من المحرك الفيزيائي القيام بالحسابات , ومن ثم تستقبل هذه القيم , وتطبقها على مجسماتك .. سنأخذ مثالا بعد قليل ان شاء الله .


  • تثبيت المحرك الفيزيائي PhysX :
ستحتاج إلى 3 خطوات :
  1. تحميل وتثبيت برمجيات أساسية للمحرك الفيزيائي PhysX :

    http://www.nvidia.com/object/physx_system_software.html
  2. تحميل وتثبيت حزمة التطوير SDK للمحرك الفيزيائي PhysX :


    http://developer.download.nvidia.com/PhysX/2.8.1/PhysX_SDK_2.8.1_build_13_for_PC.msi
  3. يجب أن تملك المترجم Visual Cpp 2005 \ 2008 , ويمكن تحميلها مجانا من خلال موقع microsoft :

    http://www.microsoft.com/express/vc/
  4. بعد تثبيت جميع البرمجيات المطلوبة , قم بتطبيق ماهو موجود في هذا الفيديو :



  • برنامجك الأول باستخدام المحرك الفيزيائي PhysX .
يمكن تحميل المثال كاملا من خلال هذا الرابط ( قمت برفعه على سيرفر خارجي ) :
المثال كاملا 

سأستخدم لغة ++C , ومكتبة OpenGL لرسم المجسمات , يمكن تعميم هذه المثال باستخدام مكتبات أخرى كمكتبة Direct3D .

سنقوم بعمل ثلاثة أمور :

- انشاء مشهد Scene .. وسنضع فيه الجاذبية المعتادة .
- انشاء أرضية Floor من نوع static Actor ,, بمعنى ان الارضية لن تتأثر بالجاذبية وأي قوة أخرى .
- انشاء صندوق (مكعب) من نوع dynamic Actor تتأثر بالقوى .. كالجاذبية أو عزم الدوران او أي قوة أخرى .

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


  • الخطوة الأولى :
ملخص الخطوة :

سننشئ كائن فيزيائي من نوع NxPhysicsSDK وهو الذي سينشئ لنا أي Scene ( عالم) نريده ..
ولانشاء Scene نحتاج لتعيين بعض المواصفات . كالجاذبية في هذا العالم .. بعدها ننشئ المشهد Scene .


# pragma comment (lib, "PhysXLoader.lib") 
#define NOMINMAX
#include 
#include "Cube.h"
#include 
#include
using namespace std;

// Physics SDK globals متغيرات عامة
NxPhysicsSDK* gPhysicsSDK = NULL;
NxScene* gScene = NULL;
NxVec3 gDefaultGravity(0,-9.8,0);// الجاذبية
NxVec3 floorSize(8,0.5,8);// أبعاد الأرضية - حجمها
NxVec3 boxSize(0.5,0.5,0.5);// أبعاد الصندوق
NxReal gDeltaTime =0;
// Actor globals
NxActor* Floor = NULL;// مجسم يمثل الارضية
NxActor* Box = NULL;// مجسم يمثل الصندوقربطنا المكتبات واستدعينا الملفات الرأسية اللازمة .

أيضا عرفنا عدة متغيرات .. سنذكرها لاحقا


bool InitNx()
{
// Create the physics SDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
if (!gPhysicsSDK)
{
cout<<"error"<
return false;
}
// Set the physics parameters
gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.01);
gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);
//----------------------------------------------
NxSceneDesc sceneDesc;
sceneDesc.simType = NX_SIMULATION_SW;
sceneDesc.gravity = NxVec3(0,-9.8,0);
gScene = gPhysicsSDK->createScene(sceneDesc);
if(!gScene)
{
cout<<"error"<
return false;
}
  • gPhysicsSDK : وهو عبارة عن كلاس .. من نوع Singleton .. بمعنى انه لايمكنك انشاء أكثر من نسخة واحدة من هذا الكلاس ..
مهمة هذا الكلاس NxPhysicsSDK هو انشاء الكلاسات الاخرى .. فهو المدخل .. وهو بمثابة الاب لجميع الكلاسات الاخرى في المحرك ageia .

انظر هذه الصورة :




NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
حيث نمرر له رقم نسخة NX_PHYSICS_SDK_VERSION .

ثم بعد ذلك حددنا خصائص المحرك الفيزيائي عن طريق الدالة setParameter .. ولا اعرف حتى الان ماذا تعني هذه البارمترات .

  • sceneDesc : أنشأنا كلاس من نوع NxSceneDesc وهو كلاس يحمل عدة متغيرات .. حيث نعين له المعلومات التي نريد .. وأهمها :

gravity : وهو عباة عن متجه Vector .. حيث جعلنا الاحداثي x و z =0 .. وجعلنا الاحداثي الصادي = 9.8 بالسالب ,

هنا سؤال ,, ماهو نظام الاحداثيات في المحرك الفيزيائي physX .. الجواب ,, ولست متأكد منه حتى الان .. أنه كالتالي .

لذلك .. نحن عندما وضعنا متجه الجاذبية .. كالموجود على ارض الواقع وهو سالب 9.8 في الاحداثي الصادي .. هذا جعل الاجسام تسقط الى اسفل بفعل قوة الجاذبية.


NX_SIMULATION_SW : هذا يعني اننا نريد ان نعمل على السوفتوير وليس الهاردوير .. لاننا لانملك الهاردوير .

- بعد أن أنشأنا وصف للمشهد عن طريق sceneDesc سننشأ المشهد بناء على هذا الوصف ..

gScene = gPhysicsSDK->createScene(sceneDesc
بهذا نكون أنشأنا العالم الافتراضي .. وتنتهي هنا الخطوة الأولى .. وهذا أبسط ما يمكننا عمله بالمحرك .

الخطوة الثانية : الخامات materials :
الموضوع طويل ومن الصعب علينا فهمه الان .. لكن وباختصار ...

سنعين معامل الاحتكاك + معامل الارتداد للاجسام الموجودة في هذا المشهد Scene .. لذلك سنحصل على Material الموجودة الان .. ونعدل عليها .


// Create the default material
NxMaterial* defaultMaterial = gScene->getMaterialFromIndex(0);
defaultMaterial->setRestitution(0.5);//الارتداد
defaultMaterial->setStaticFriction(0.5);//الاحتكاك
defaultMaterial->setDynamicFriction(0.5);

ل ما عليك ان تعدل مثلا معامل الارتداد .. وضعه مثلا 2 .. لتشاهد الفرق .. لاحظ ان الارتداد يكسب الجسم طاقة .. لذلك في كل مرة يرتد قد يكسب طاقة اعلى مما يجعله يذهب بعيدا الى الاعلى

الخطوة الثالثة: انشاء الاجسام Actors ووضعها في هذا العالم Scene :

سننشئ جسمين .

أولا : الارضية .. لاحظ أن الأرضية ممكن تأخذ اي شكل نريده .. ولكن وحتى نحاكي الواقع سنضع الارضية على شكل يشبه الBox .( الصندوق) .

الارضية مهمتها التصادم فقط .. فلانريدها ان تتعرض للجاذبية فتسقط هي الاخرى .. لذلك يجب علينا مايلي :

//----------------انشاء الأرضية ------------------------------
NxBoxShapeDesc floorShape;// شكل السطح " صندوق" الشكل
NxActorDesc floorDesc; // كلاس سيحمل وصف للجسم

floorShape.dimensions.set(floorSize.x,floorSize.y,floorSize.z); // أبعاد الصندوق
floorDesc.shapes.push_back(&floorShape);//اضافة الشكل الى وصف الجسم
floorDesc.body = NULL;//مهم - الجسم ستاتيكي لايتأثر بأي شيء الا التصادم فقط
floorDesc.globalPose.t.set(0,0,0);//موقع الجسم
Floor = gScene->createActor(floorDesc);//انشاء الأرضية بناء على الوصف السابق

الكلاس floorDesc سيحمل مواصفات الActor وهو هنا يمثل الارضية .. لذلك سنحدد أولا شكل الارضية ..
ولتحديد شكلها نحتاج الى كلاس اخر من نوع NxBoxShapeDesc .. أي اننا نريده على شكل صندوق ..
ثم حددنا ابعاد الصندوق وبعد ذلك اضفنا هذا الشكل الى الكائن floorShape ..
لاحظ أنه يمكن اضافة أكثر من شكل الى جسم واحد ..

الان صار floorDesc يحمل شكل الجسم .

نضيف بعض الامور الاخرى الى floorDesc .. موقع الجسم عند بداية تشغيل المثال .. وأهم شيء هو هذا السطر

floorDesc.body = NULL;//

حيث أخبرنا المحرك أن الارضية ليست ديناميكية ولكن ثابتة .. أي لاتتأثر بالقوى حولها .
ثم أنشأنا الActor وهو الان Floor .

ثانيا : المكعب :
سنقوم بنفس الخطوات .. لاحظ ,

//--------------- انشاء المكعب ------------------------------- 
NxBodyDesc bodyDesc;// نريده ديناميكي
NxActorDesc boxDesc;// وصف للجسم الذي سنضيفه
NxBoxShapeDesc boxShape;// شكل الجسم وهو " صندوق" الشكل

boxShape.dimensions.set(boxSize.x,boxSize.y,boxSize.z);// تحديد أبعاد الصندوق
boxDesc.shapes.pushBack(&boxShape);// اضافة الشكل الى وصف الجسم
boxDesc.body = &bodyDesc;// نريده ديناميكي
boxDesc.density = 1;//كثافة الجسم .. قيمة أقل .. كثافة أقل .. والعكس صحيح
boxDesc.globalPose.t = NxVec3(0,15,0);// موقع الجسم - 15 وحدة للاعلى
Box = gScene->createActor(boxDesc); // أنشئ الجسم بناء على الوصف السابق
Box->addTorque(NxVec3(0,0,10));// نريد ان يدور الجسم اثناء الحركة
الجديد هنا هو أننا جعلنا الجسم ديناميكي عن طريق boxDesc.body حيث مررنا له الكلاس bodyDesc .
ثم حددنا موقع الجسم . حيث أنه يبدأ في الأعلى ثم سيسقط للاسفل لاحقا ..

أيضا الجديد هنا كثافة الجسم .. حيث كلما قل الكثافة .. يكون الجسم خفيفا ..بالتالي يتأثر أكثر بالقوى الموؤثرة عليه .
ومن الامور المهمة هي عزم الدوران Torque .. حيث مررنا بعض القيم عن طريق الدالة addTorque ..

عزم الدوران .. موضوع كبير .. سأحاول أن أعمل مثال لاحقا ان شاء الله ..

انتهت الخطوة الثالثة .. حيث انشأنا أرضية ثابتة واضفناها للمشهد ,,, وأنشأنا معكب ديناميكي وأضفناه للمشهد

الخطو الرابعة .. تشغيل المحرك الفيزيائي :

كل مانحتاجه هو Loop .. ليتم تحديث المحرك .. ويجب ان تعين مقدار الوقت المنقضي منذ اخر فريم .. لذلك نحتاج لحساب الوقت المنقضي .. أعتقد هذه سهلة وواضحة .

void StartPhysics()
{
gDeltaTime = UpdateTime();
// Start collision and dynamics for delta time since the last frame
gScene->simulate(gDeltaTime);
gScene->flushStream();

}
void GetPhysicsResults()
{
while (!gScene->fetchResults(NX_RIGID_BODY_FINISHED, false));
}أرى هناك علامات استفهمك فوق رأسك .. وهي شبيهة بالتي فوق رأسي الان .. لكن نحن لم نطور المكتبة لنعرف كل اسرارها بين عشية وضحاها .. على العموم الكود السابق ثابت ولا يتغير ان شاء الله الا في حالات خاصة .


الخطوة الخامسة : تحويل الفيزياء الى رسم على الشاشة .. :

نحتاج الى OpenGL أو Direct3D أو الى محرك مثل Ogre أو Irrlicht .. سأوضح المثال باستخدام OpenGL .

float glmat[16];
NxMat34 m;



void display(void)
{




// تحديث المحرك الفيزيائي وجلب البيانات منه

StartPhysics();
GetPhysicsResults();


//رسم الصندوق الأصفر

glPushMatrix();


m=Box->getGlobalPose();// الحصول على مصفوفة المحرك الفيزيائي
m.getColumnMajor44(glmat);// تحويل المصفوفة الى مصفوفة متوافقة مع اوبنجل
glMultMatrixf(&(glmat[0]));تحميل المصفوفة في اوبنجل
glScalef(boxSize.x,boxSize.y,boxSize.z);// تعيين حجم الصندوق وهو نفسه حجم الصنوق في المحرك الفيزيائي
glColor3f(1,1,0);
drawCube();//رسم الصندوق

glPopMatrix();



// رسم الأرضية

glPushMatrix();

m=Floor->getGlobalPose();
m.getColumnMajor44(glmat);
glMultMatrixf(&(glmat[0]));

glScalef(floorSize.x,floorSize.y,floorSize.z);

glColor3f(0.5,0.5,0.5);
drawCube();

glPopMatrix();



}
لاحظ كيف حملنا مصفوفة الصندوق الاصفر والتي تحتوي على الازاحة والوران للصندوق .. ثم حولناها الى مصفوفة متوافقة مع اوبنجل , حيث ان مصفوفة اوبنجل هي 4*4 ولها ترتيب خاص ,, واستخدمنا لهذا التحويل احد دوال المحرك الفيزيائي المساعدة , ثم غيرنا حجم الصندوق ليكون حجمه مساوي لحجم الصندوق الوهمي الموجود في المحرك الفيزيائي ,لذلك سيأخذ الصندوق المرسوم في اوبنجل نفس حجم ومكان الصندوق الوهمي في المحرك الفيزيائي , ورسمنا الارضية بنفس الطريقة , طبعا يوجد اكثر من طريقة لتحويل مصفوفة المحرك الى مصفوفة متوافقة مع اوبنجل او دايركت اكس .. لكن اتوقع هذه افضل الطرق واسهلها

أتمنى أن تكون الصورة واضحة الان .


  • أين أذهب بعد ذلك ؟؟الرحلة لاتنتهي .. لذلك يجب أن تقرأ المزيد والمزيد حتى تصل لمرحلة متقدمة , قم بقراءة ملف التعليمات SDK Documentation الذي يحوي على شرح مفصل لكل الشفرات البرمجية السابقة , اذا كان لديك أي سؤال , فلا تتردد .. أتمنى لكل التوفيق .
  • المراجع :
  • وثائق المحرك الفيزيائي
  • وثائق المحرك الفيزيائي Havok
  • مراجع أخرى

الفيزياء في برمجة الألعاب بمحرك PhysX


تهدف هذه المقالة إلى إلقاء الضوء على أحد أهم عناصر الألعاب الحديثة وهو الفيزياء .

لن نغطّي بالطبع كل شيء عن الفيزياء في برمجة الألعاب فهذا شيء مستحيل , ولكن سنحاول أن نقوم معا بالتعرف على هذا المجال ,
والتطبيق العملي باستخدام أشهر المحركات الفيزيائية القادمة وبقوة nVedia PhysX .

سأفرض أن القارئ الكريم على علم بعالم البرمجة والتطوير باستخدام لغة برمجية معيّنة مثل ++C , كما سأفرض أن لديه الدراية الكافية لرسم بعض الأشكال والمجسمات على الكميوتر باستخدام مكتبات الرسم المتطوّرة مثل OpenGL أو DirectX , ليس المطلوب أن تكون محترفا , لكن المطلوب أن تملك الأساسيات اللازمة .
  • الفيزياء في الألعاب الحديثة .
  • المحركات الفيزيائية المتاحة .
  • مفاهيم أساسية في المحركات الفيزيائية .
  • تثبيت المحرك الفيزيائي PhysX .
  • برنامجك الأول باستخدام المحرك الفيزيائي PhysX ( مثال مرفق ) .
  • التعليق على البرنامج .
  • أين أذهب بعد ذلك ؟!
  • المراجع .

  • الفيزياء في الألعاب الحديثة .
في أي لعبة ( متطوّرة ) :
  • ستجد تلك الشخصية التي لا تستطيع اختراق الحائط لوجود نظام لاكتشاف التصادم بين الشخصية و الحائط .
  • في كرة القدم , ستجد الكرة تنطلق إلى الأعلى بعد ركلها , ثم تعود إلى الأرض , وترتد , وهكذا حتى تستقر , ارتفعت للأعلى لوجود قوّة أثرت عليها بمقدار معيّن , وعادت إلى الأرض لوجود قوة الجاذبية التي تؤثر عليها , ستجد أن الكرة في هذه الحالة "ديناميكية " لأنها استجابت للمؤثرات الخارجية , بينما أرض الملعب " ثابت" لا يتأثر بالجاذبية ولا با اصطدام الكرة .
  • ستجد أيضا الراية الركنية للملعب .. تتأثر بالرياح .. وستجد في لعبة أخرى , ملابس اللاعب تتأثر بالرياح أيضا ..
  • ستجد أن جزيئات الماء من شلاّل مياه , تأخذ شكل مجرى النهر الذي تسير فيه .
  • ستجد , في لعبة سيّارات , السيّارة وهي تنطلق بتسارع معيّن حتى تصل إلى سرعة 200 كلم في الساعة , ثم تنعطف , لتحدث سلسلة من " التنطيط " هنا وهناك , قبل أن تصطدم بالجدار لتتحوّل إلى كومة ركام , وكان بالامكان استخدام المكابح لايقاف هذا الحادث ..

 
تأثير الفيزياء على الملابس Clothes , المصدر : المحرك Nvidia PhysXتأثير القوى على الملابس , المصدر : المحرك Nvidia PhysXفيزياء السؤائل , المصدر : المحرك الفزيائي Nvidia PhysX .


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

  • تعريف المحرك الفيزيائي :

    كما قلت قبل قليل , يمكن استخدام محرك فيزيائي للقيام بمهمّة " العمليات الفيزيائية " المختلفة , دعني أشرح ولو بشكل بسيط مفهوم المحرك الفيزيائي :
    • المحرك الفيزيائي :
      المحرك الفيزيائي عبارة عن مكتبات برمجية ( تضم أصناف classes و دوال Functions مختلفة ) , تعتمد على لغة برمجية معينة مثل لغة ++C أو JAVA .. الخ , للقيام بحسابات ما , على مجسّم ما , للقيام بحسابات لتطبيق القوانين الفيزيائية الكلاسيكية على مجسم ما .

      اذا , المحرك الفيزيائي لايقوم برسم شيء على النافذة , انما يقوم باجراء حسابات وتخزينها في متغيرات ( كاحداثيات الجسم ) , ومن ثم تقوم أنت باستقبال هذه المتغيرات والاستفادة منها في رسم شيء على الشاشة باستخدام مكتبات أخرى مثل Direct3D و OpenGL أو أي مكتبة رسومية أخرى .

  • المحركات الفيزيائية المتاحة :
    هناك عدة محركات فيزيائية متاحة , بعضها مجاني والبعض الاخر بمقابل مادي , وبعضها مفتوح المصدر Open Source :
  1. Nvidia PhysX :



    أحد أشهر المحركات الفيزيائية , محرك متكامل , مجاني للألعاب والتطبيقات الغير تجارية :

    http://www.nvidia.com/object/nvidia_physx.html
  2. Havok :

    المحرك الأول في عالم الألعاب الاحترافية ( التي تشاهدها في PlayStation 3 مثلا .. ) , وصدر في الفترة الأخيرة نسخة مجانية منه.

    http://www.havok.com
  3. Newton :

    محرك مجاني للتطبيقات المجانية , قد لايرقى لمستوى الاحتراف , ولكن يتميّز بالسهولة على العموم , ودعمه لأكثر من لغة برمجية .

    http://www.newtondynamics.com


    سنركز في هذا المقال على المحرك الفيزيائي Nvidia PhysX , وسنتعلّم طريقة تثبيته ومثال تطبيقي عليه .

    من تطبيقات المحرك الفيزيائي PhysX

PhysX 2

من تطبيقات المحرك الفيزيائي PhysX






  • مفاهيم أساسية في المحركات الفيزيائية :
سنستخدم مصطلحات المحرك الفيزيائي PhysX ... المحرك الفيزيائي يتكون من : 

  • Scene - المشهد :
ويسمى في المكتبة ODE و Newton بالعالم ( World ) ..

العالم أو المشهد , يكون له حدود معينه ( مثلا 10 وحدات * 10 وحدات ) ( 10 أمتار *10 أمتار مثلا .. ) , حيث كل ماسيكون خارج حدود هذه المساحة , لن يتأثر بأي حسابات فيزيائية , وذلك لتسريع عملية الحسابات , ولأن هذا هو الواقع , فمثلا الجاذبية ستكون معدومة في خارج الكرة الأرضية .

كل مشهد أو عالم فيزيائي تنشئه سيكون عالم مستقل عن الاخر , وكل مشهد أو عالم ( Scene ) له خصائصه .. مثل :
  1. الجاذبية gravity ( اتجاهها ومقدارها ) .
  2. المجسمات Bodies أو Actors الموجودة في هذا العالم .
فمثلا , في الحالة الطبيعية تكون الجاذبية إلى الأسفل بمقدار 9.8 ... لذلك تسقط جميع المجسمات باتجاه المحور Y .


مثال على عالم فيزيائي Scene , حيث الجاذبية إلى الأسفل ,
ويحوي على مجسمات , ويوجد حدود لهذا العالم الفيزيائي
  • Actors - المجسمات :
وتسمى في المكتبة ODE و Newton بالBodies . . وهو عبارة عن مجسم وهمي .. وأقول مرة أخرى .. وهمي .. لانعلم شكله حتى الان ، كل مجسم تنشئه .. تضيفه الى ، المشهد Scene , حتى يتم ادخاله في الحسابات . تنقسم المجسمات بشكل عام إلى قسمين ( مجسمات صلبه Rigid Body لاتتغيّر أشكالها ومجسمات مرنة Soft Body تتغيّر أشكالها تبعا لضغط القوى عليها ) , في الغالب ستتعامل مع النوع الأول الذي لا يتغيّر شكله , فهو الأسهل والأكثر استخداما .

المجسمات لها خواص .. سنذكر منها ما يلي : 

  • نوع الجسم :
يوجد نوعان من Actor أو Body

  1. static Body
    المجسمات الساكنة .. وهي عبارة عن مجسمات لاتتأثر بالجاذبية .. بالقوى الموجودة حولها .. فهي كالجدار .. مجرد حاجز للتصادم فقط ..
  2. dynamic Body
    وهي مجسمات تتأثر بالجاذبية .. وبأي قوة موجودة .. وتتعرض للارتداد ,... وما الى ذلك من المؤثرات .
  • شكل الجسم :
يوجد عدة أشكال قياسية للمجسمات .. منها :
Box - صندوق , Sphere - كرة , والكبسولة و الاشكال المعقدة .... الخ .
لذلك كل Actor نقوم بانشائه .. فيجب علينا تحديد شكله .. واذا حددنا شكله فيجب تحديد أبعاده .. وموقعه .. الخ .
كما يمكننا تحديد كثافة الجسم density .. حيث كلما زادت الكثافة .. صار الجسم أكثر قوة في مواجهة القوى المؤثرة عليه ..

أمثلة على بعض الأشكال ( كرة - كبسولة - صندوق ) , لاحظ تأثير القوة على الكسبولة - المصدر : المحرك PhysX 


  • القوى المؤثرة على الجسم :
كل جسم ننشئه .. نحدد له القوى التي تؤثر عليه .. هناك قوى خارجية مثل الجاذبية .. وهناك قوى على الجسم نفسه مثل .. عزم التدوير Torque .. أو القوة العادية Force التي تدفع الجسم الى أي اتجاه نريده .. الخ .


  • Material / الخامة:
مثل معامل احتكاك الجسم Friction , و معامل ارتداد الجسم Restitution .
حيث نستطيع من خلال ال Material محاكاة خامات الاجسام مثل الحديد و الخشب والمطاط .. الخ . 

  • Joints - الروابط :
نستطيع أن نربط المجسمات فيما بينها Actors عن طريق الروابط Joints ..
فنربط بين اطار السيارة والسيارة .. ونربط بين جسم ما .. وحبل .. فيكون الجسم معلق على الحبل ويتدلى .. الخ .
يوجد عدة أنواع من ال Joints , انظر لهذه الصورة التي تشرح بعض أنواع الروابط :

بعض أنواع الروابط , المصدر : المحرك الفيزيائي Havok


  • اكتشاف التصادم - Collision Detection :
في كل محرك فيزيائي اكتشاف تصادم .. وتستطيع ان تستفيد من اكتشاف التصادم هذا .. بدون وجود اجسام ديناميكية ... حيث يوجد دوال وكلاسات مساعدة تسهل علينا أمر اكتشاف التصادم .


  • الدوال الرياضية :
المحرك الفيزيائي يقوم على مجموعة من الدوال الرياضية والكلاسات ... مثال :
Vector : المتجهات .
Matrix : المصفوفات .
Quaternion .
بالاضافة الى كلاسات أساسية أخرى .. مثل box و Ray و Plane و Sphere ,


  • الخلاصة :

تنشئ عالم فيزيائي Scene ,, وتضع فيه مجموعة من المجسمات Actors .. وكل مجسم له خصائصه وشكله .. وكل مجسمين تربطهم عن طريق Joints و يوجد عدة أنواع من الJoints , ثم بعد ذلك تطلب من المحرك الفيزيائي القيام بالحسابات , ومن ثم تستقبل هذه القيم , وتطبقها على مجسماتك .. سنأخذ مثالا بعد قليل ان شاء الله .


  • تثبيت المحرك الفيزيائي PhysX :
ستحتاج إلى 3 خطوات :
  1. تحميل وتثبيت برمجيات أساسية للمحرك الفيزيائي PhysX :

    http://www.nvidia.com/object/physx_system_software.html
  2. تحميل وتثبيت حزمة التطوير SDK للمحرك الفيزيائي PhysX :


    http://developer.download.nvidia.com/PhysX/2.8.1/PhysX_SDK_2.8.1_build_13_for_PC.msi
  3. يجب أن تملك المترجم Visual Cpp 2005 \ 2008 , ويمكن تحميلها مجانا من خلال موقع microsoft :

    http://www.microsoft.com/express/vc/
  4. بعد تثبيت جميع البرمجيات المطلوبة , قم بتطبيق ماهو موجود في هذا الفيديو :



  • برنامجك الأول باستخدام المحرك الفيزيائي PhysX .
يمكن تحميل المثال كاملا من خلال هذا الرابط ( قمت برفعه على سيرفر خارجي ) :
المثال كاملا 

سأستخدم لغة ++C , ومكتبة OpenGL لرسم المجسمات , يمكن تعميم هذه المثال باستخدام مكتبات أخرى كمكتبة Direct3D .

سنقوم بعمل ثلاثة أمور :

- انشاء مشهد Scene .. وسنضع فيه الجاذبية المعتادة .
- انشاء أرضية Floor من نوع static Actor ,, بمعنى ان الارضية لن تتأثر بالجاذبية وأي قوة أخرى .
- انشاء صندوق (مكعب) من نوع dynamic Actor تتأثر بالقوى .. كالجاذبية أو عزم الدوران او أي قوة أخرى .

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


  • الخطوة الأولى :
ملخص الخطوة :

سننشئ كائن فيزيائي من نوع NxPhysicsSDK وهو الذي سينشئ لنا أي Scene ( عالم) نريده ..
ولانشاء Scene نحتاج لتعيين بعض المواصفات . كالجاذبية في هذا العالم .. بعدها ننشئ المشهد Scene .


# pragma comment (lib, "PhysXLoader.lib") 
#define NOMINMAX
#include 
#include "Cube.h"
#include 
#include
using namespace std;

// Physics SDK globals متغيرات عامة
NxPhysicsSDK* gPhysicsSDK = NULL;
NxScene* gScene = NULL;
NxVec3 gDefaultGravity(0,-9.8,0);// الجاذبية
NxVec3 floorSize(8,0.5,8);// أبعاد الأرضية - حجمها
NxVec3 boxSize(0.5,0.5,0.5);// أبعاد الصندوق
NxReal gDeltaTime =0;
// Actor globals
NxActor* Floor = NULL;// مجسم يمثل الارضية
NxActor* Box = NULL;// مجسم يمثل الصندوقربطنا المكتبات واستدعينا الملفات الرأسية اللازمة .

أيضا عرفنا عدة متغيرات .. سنذكرها لاحقا


bool InitNx()
{
// Create the physics SDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
if (!gPhysicsSDK)
{
cout<<"error"<
return false;
}
// Set the physics parameters
gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.01);
gPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
gPhysicsSDK->setParameter(NX_VISUALIZE_ACTOR_AXES, 1);
//----------------------------------------------
NxSceneDesc sceneDesc;
sceneDesc.simType = NX_SIMULATION_SW;
sceneDesc.gravity = NxVec3(0,-9.8,0);
gScene = gPhysicsSDK->createScene(sceneDesc);
if(!gScene)
{
cout<<"error"<
return false;
}
  • gPhysicsSDK : وهو عبارة عن كلاس .. من نوع Singleton .. بمعنى انه لايمكنك انشاء أكثر من نسخة واحدة من هذا الكلاس ..
مهمة هذا الكلاس NxPhysicsSDK هو انشاء الكلاسات الاخرى .. فهو المدخل .. وهو بمثابة الاب لجميع الكلاسات الاخرى في المحرك ageia .

انظر هذه الصورة :




NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION);
حيث نمرر له رقم نسخة NX_PHYSICS_SDK_VERSION .

ثم بعد ذلك حددنا خصائص المحرك الفيزيائي عن طريق الدالة setParameter .. ولا اعرف حتى الان ماذا تعني هذه البارمترات .

  • sceneDesc : أنشأنا كلاس من نوع NxSceneDesc وهو كلاس يحمل عدة متغيرات .. حيث نعين له المعلومات التي نريد .. وأهمها :

gravity : وهو عباة عن متجه Vector .. حيث جعلنا الاحداثي x و z =0 .. وجعلنا الاحداثي الصادي = 9.8 بالسالب ,

هنا سؤال ,, ماهو نظام الاحداثيات في المحرك الفيزيائي physX .. الجواب ,, ولست متأكد منه حتى الان .. أنه كالتالي .

لذلك .. نحن عندما وضعنا متجه الجاذبية .. كالموجود على ارض الواقع وهو سالب 9.8 في الاحداثي الصادي .. هذا جعل الاجسام تسقط الى اسفل بفعل قوة الجاذبية.


NX_SIMULATION_SW : هذا يعني اننا نريد ان نعمل على السوفتوير وليس الهاردوير .. لاننا لانملك الهاردوير .

- بعد أن أنشأنا وصف للمشهد عن طريق sceneDesc سننشأ المشهد بناء على هذا الوصف ..

gScene = gPhysicsSDK->createScene(sceneDesc
بهذا نكون أنشأنا العالم الافتراضي .. وتنتهي هنا الخطوة الأولى .. وهذا أبسط ما يمكننا عمله بالمحرك .

الخطوة الثانية : الخامات materials :
الموضوع طويل ومن الصعب علينا فهمه الان .. لكن وباختصار ...

سنعين معامل الاحتكاك + معامل الارتداد للاجسام الموجودة في هذا المشهد Scene .. لذلك سنحصل على Material الموجودة الان .. ونعدل عليها .


// Create the default material
NxMaterial* defaultMaterial = gScene->getMaterialFromIndex(0);
defaultMaterial->setRestitution(0.5);//الارتداد
defaultMaterial->setStaticFriction(0.5);//الاحتكاك
defaultMaterial->setDynamicFriction(0.5);

ل ما عليك ان تعدل مثلا معامل الارتداد .. وضعه مثلا 2 .. لتشاهد الفرق .. لاحظ ان الارتداد يكسب الجسم طاقة .. لذلك في كل مرة يرتد قد يكسب طاقة اعلى مما يجعله يذهب بعيدا الى الاعلى

الخطوة الثالثة: انشاء الاجسام Actors ووضعها في هذا العالم Scene :

سننشئ جسمين .

أولا : الارضية .. لاحظ أن الأرضية ممكن تأخذ اي شكل نريده .. ولكن وحتى نحاكي الواقع سنضع الارضية على شكل يشبه الBox .( الصندوق) .

الارضية مهمتها التصادم فقط .. فلانريدها ان تتعرض للجاذبية فتسقط هي الاخرى .. لذلك يجب علينا مايلي :

//----------------انشاء الأرضية ------------------------------
NxBoxShapeDesc floorShape;// شكل السطح " صندوق" الشكل
NxActorDesc floorDesc; // كلاس سيحمل وصف للجسم

floorShape.dimensions.set(floorSize.x,floorSize.y,floorSize.z); // أبعاد الصندوق
floorDesc.shapes.push_back(&floorShape);//اضافة الشكل الى وصف الجسم
floorDesc.body = NULL;//مهم - الجسم ستاتيكي لايتأثر بأي شيء الا التصادم فقط
floorDesc.globalPose.t.set(0,0,0);//موقع الجسم
Floor = gScene->createActor(floorDesc);//انشاء الأرضية بناء على الوصف السابق

الكلاس floorDesc سيحمل مواصفات الActor وهو هنا يمثل الارضية .. لذلك سنحدد أولا شكل الارضية ..
ولتحديد شكلها نحتاج الى كلاس اخر من نوع NxBoxShapeDesc .. أي اننا نريده على شكل صندوق ..
ثم حددنا ابعاد الصندوق وبعد ذلك اضفنا هذا الشكل الى الكائن floorShape ..
لاحظ أنه يمكن اضافة أكثر من شكل الى جسم واحد ..

الان صار floorDesc يحمل شكل الجسم .

نضيف بعض الامور الاخرى الى floorDesc .. موقع الجسم عند بداية تشغيل المثال .. وأهم شيء هو هذا السطر

floorDesc.body = NULL;//

حيث أخبرنا المحرك أن الارضية ليست ديناميكية ولكن ثابتة .. أي لاتتأثر بالقوى حولها .
ثم أنشأنا الActor وهو الان Floor .

ثانيا : المكعب :
سنقوم بنفس الخطوات .. لاحظ ,

//--------------- انشاء المكعب ------------------------------- 
NxBodyDesc bodyDesc;// نريده ديناميكي
NxActorDesc boxDesc;// وصف للجسم الذي سنضيفه
NxBoxShapeDesc boxShape;// شكل الجسم وهو " صندوق" الشكل

boxShape.dimensions.set(boxSize.x,boxSize.y,boxSize.z);// تحديد أبعاد الصندوق
boxDesc.shapes.pushBack(&boxShape);// اضافة الشكل الى وصف الجسم
boxDesc.body = &bodyDesc;// نريده ديناميكي
boxDesc.density = 1;//كثافة الجسم .. قيمة أقل .. كثافة أقل .. والعكس صحيح
boxDesc.globalPose.t = NxVec3(0,15,0);// موقع الجسم - 15 وحدة للاعلى
Box = gScene->createActor(boxDesc); // أنشئ الجسم بناء على الوصف السابق
Box->addTorque(NxVec3(0,0,10));// نريد ان يدور الجسم اثناء الحركة
الجديد هنا هو أننا جعلنا الجسم ديناميكي عن طريق boxDesc.body حيث مررنا له الكلاس bodyDesc .
ثم حددنا موقع الجسم . حيث أنه يبدأ في الأعلى ثم سيسقط للاسفل لاحقا ..

أيضا الجديد هنا كثافة الجسم .. حيث كلما قل الكثافة .. يكون الجسم خفيفا ..بالتالي يتأثر أكثر بالقوى الموؤثرة عليه .
ومن الامور المهمة هي عزم الدوران Torque .. حيث مررنا بعض القيم عن طريق الدالة addTorque ..

عزم الدوران .. موضوع كبير .. سأحاول أن أعمل مثال لاحقا ان شاء الله ..

انتهت الخطوة الثالثة .. حيث انشأنا أرضية ثابتة واضفناها للمشهد ,,, وأنشأنا معكب ديناميكي وأضفناه للمشهد

الخطو الرابعة .. تشغيل المحرك الفيزيائي :

كل مانحتاجه هو Loop .. ليتم تحديث المحرك .. ويجب ان تعين مقدار الوقت المنقضي منذ اخر فريم .. لذلك نحتاج لحساب الوقت المنقضي .. أعتقد هذه سهلة وواضحة .

void StartPhysics()
{
gDeltaTime = UpdateTime();
// Start collision and dynamics for delta time since the last frame
gScene->simulate(gDeltaTime);
gScene->flushStream();

}
void GetPhysicsResults()
{
while (!gScene->fetchResults(NX_RIGID_BODY_FINISHED, false));
}أرى هناك علامات استفهمك فوق رأسك .. وهي شبيهة بالتي فوق رأسي الان .. لكن نحن لم نطور المكتبة لنعرف كل اسرارها بين عشية وضحاها .. على العموم الكود السابق ثابت ولا يتغير ان شاء الله الا في حالات خاصة .


الخطوة الخامسة : تحويل الفيزياء الى رسم على الشاشة .. :

نحتاج الى OpenGL أو Direct3D أو الى محرك مثل Ogre أو Irrlicht .. سأوضح المثال باستخدام OpenGL .

float glmat[16];
NxMat34 m;



void display(void)
{




// تحديث المحرك الفيزيائي وجلب البيانات منه

StartPhysics();
GetPhysicsResults();


//رسم الصندوق الأصفر

glPushMatrix();


m=Box->getGlobalPose();// الحصول على مصفوفة المحرك الفيزيائي
m.getColumnMajor44(glmat);// تحويل المصفوفة الى مصفوفة متوافقة مع اوبنجل
glMultMatrixf(&(glmat[0]));تحميل المصفوفة في اوبنجل
glScalef(boxSize.x,boxSize.y,boxSize.z);// تعيين حجم الصندوق وهو نفسه حجم الصنوق في المحرك الفيزيائي
glColor3f(1,1,0);
drawCube();//رسم الصندوق

glPopMatrix();



// رسم الأرضية

glPushMatrix();

m=Floor->getGlobalPose();
m.getColumnMajor44(glmat);
glMultMatrixf(&(glmat[0]));

glScalef(floorSize.x,floorSize.y,floorSize.z);

glColor3f(0.5,0.5,0.5);
drawCube();

glPopMatrix();



}
لاحظ كيف حملنا مصفوفة الصندوق الاصفر والتي تحتوي على الازاحة والوران للصندوق .. ثم حولناها الى مصفوفة متوافقة مع اوبنجل , حيث ان مصفوفة اوبنجل هي 4*4 ولها ترتيب خاص ,, واستخدمنا لهذا التحويل احد دوال المحرك الفيزيائي المساعدة , ثم غيرنا حجم الصندوق ليكون حجمه مساوي لحجم الصندوق الوهمي الموجود في المحرك الفيزيائي ,لذلك سيأخذ الصندوق المرسوم في اوبنجل نفس حجم ومكان الصندوق الوهمي في المحرك الفيزيائي , ورسمنا الارضية بنفس الطريقة , طبعا يوجد اكثر من طريقة لتحويل مصفوفة المحرك الى مصفوفة متوافقة مع اوبنجل او دايركت اكس .. لكن اتوقع هذه افضل الطرق واسهلها

أتمنى أن تكون الصورة واضحة الان .


  • أين أذهب بعد ذلك ؟؟الرحلة لاتنتهي .. لذلك يجب أن تقرأ المزيد والمزيد حتى تصل لمرحلة متقدمة , قم بقراءة ملف التعليمات SDK Documentation الذي يحوي على شرح مفصل لكل الشفرات البرمجية السابقة , اذا كان لديك أي سؤال , فلا تتردد .. أتمنى لكل التوفيق .
  • المراجع :
  • وثائق المحرك الفيزيائي
  • وثائق المحرك الفيزيائي Havok
  • مراجع أخرى