الشبكة العربية لمطوري الألعاب

خبير مدير همام البهنسي مشاركة 1

السلام عليكم...
 
كنا قد ناقشنا عدة نقط حول مشروع تعلم الـ scripting في المشاركة:
http://www.agdn-online.com/communities.aspx?view=posts&threadid=528
 
كما أخذنا نظرة خاطفة وجولة سريعة في أحد الـ scripts التي توظف معظم المفاهيم الهامة في الـ Softimage SDK والتي علينا تعلمها في هذا المشروع بإذن الله:
http://www.agdn-online.com/communities.aspx?view=posts&threadid=529
 
في هذه المشاركة التي تعتبر الأولى من حيث البدء بالتعلم والتنفيذ. سنبدأ بسرد الأدوات والمراجع الهامة بالنسبة لهذا المشروع:
1- نسخة من Softimage، يمكنكم الحصول على نسخة مجانية من البرنامج من الموقع: http://www.softimage.com/products/modtool/
2- وثائق الـ Softimage SDK التي سيتم تنصيبها بشكل تلقائي عند تثبيت البرنامج. تعتبر هذه الوثائق هي المرجع الأساسي التي سيتم الاعتماد عليها أثناء المشروع.
3- موقع الـ Softimage SDK Wiki الذي يقدم العديد من المقالات الهامة للـ scripting في Softimage
http://softimage.wiki.softimage.com/index.php/Category:XSISDK
 
لابد من التأكد من تحقيق جميع النقط السابقة قبل الانتقال للخطوة التالية في التعرف على كيفية الحصول على المجسمات برمجياً والتحكم بخصائصها.

خبير مدير همام البهنسي مشاركة 2

كما ذكرت سابقاً، في هذه المشاركة سنبدأ بالتعرف على كيفية الحصول على العناصر في البرنامج وتخزينها في متغير للتعامل معها لاحقاً.
في الحقيقة لدينا الكثير من الطرق للوصول للعناصر الموجودة في البرنامج، اختيار الطريقة المناسبة يعتمد على تصميم الـ Script.
لنبدأ مباشرة:
 

--- الطريقة الأولى ---
وهي الأسهل، تتلخص في الحصول على العنصر من خلال معرفة أسمه المحدد في البرنامج. من المعروف أن لكل عنصر أسم يميزه و يمكن معرفته من خلال أي طريقة ضمن واجهة البرنامج. على سبيل المثال يمكن استخدام مستكشف البرنامج (explorer) -اختصار 8 على لوحة المفاتيح- لاستعراض جميع العناصر الموجودة في البرنامج مع أسمائها.



على سبيل المثال، نلاحظ أنه لدينا في الصورة التوضيحية عنصر أسمه "cube"، الآن وبعد معرفة أسم هذا العنصر يمكننا كتابة الكود الذي يخزنه ضمن متغير على الشكل التالي:

var oObj = GetValue(“cube”); // إعلان عن متغير وتخزين عنصر ضمنه

ملاحظة الكود المستخدم هو Jscript لذلك تأكد من أن هذه هي اللغة المستخدمة لديك في البرنامج.
للتأكد من أننا قمنا بتخزين العنصر في المتغير بشكل صحيح، سنقوم بطباعة محتوى المتغير oObj الذي يجب أن يكون طبعاً بالنسبة لمثالنا هو: cube.

logmessage (oObj);
 

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

var oSel = GetValue(“SelectionList”);
 
الملاحظة الهامة التي يجب الانتباه لها عند استخدام الطريقة الثانية، هي أن محتوى المتغير عبارة عن مصفوفة من نوع خاص تشمل جميع العناصر التي يختارها المستخدم حالياً. لذلك للوصول لهذه العناصر يمكن استخدام حلقة للدوران عليها واحداً تلو الآخر.
الكود التالي يوضح كيفية طباعة جميع المجسمات الموجودة في المصفوفة oSel:

for (var i=0; i<osel.count;>{
	logmessage (oSel(i));
}
 
 
--- الطريقة الثالثة ---
هذه الطريقة تشبه الطريقة السابقة في الحصول على العنصر دون الحاجة لمعرفة أسمه أو أي شيء عنه. الاختلاف هنا هو أن الحصول على العنصر يتم بشكل تفاعلي من خلال الطلب من المستخدم انتقاء المجسم من نوافذ العرض في البرنامج.

الأمر المسؤول عن هذه المهمة في الـ XSI|SDK هو PickElement. هذا الأمر يحتاج لتحديد عدد من البارمترات لتنفيذه. سأقوم بالمرور عليها بشكل سريع وبالطبع للحصول على الشرح التفصيلي حول هذا الأمر يفضل الرجوع لوثائق الـ SDK:

PickElement(SelFilter,LeftMessage,MiddleMessage,[PickedElement],[ButtonPressed],SelRegionMode,[ModifierPressed])

SetFilter: ثابت لتحديد نوع العنصر المسموح للمستخدم انتقاءه (إضاءة، كاميرا، مضلعات،... الخ)
LeftMessage: قيمة نصية تصف وظيفة زر الفأرة الأيسر.
MiddleMessage: قيمة نصية تصف وظيفة زر الفأرة الأوسط.
[PickedElement]: المتغير الذي سيحوي العناصر التي تم انتقاؤها.
[ButtonPressed]: متغير يعيد قيمة تعبر عن زر الفأرة الذي استجاب المستخدم بالضغط عليه.
SelRegionMode: ثابت يحدد طريقة الانتقاء المتاحة للمستخدم (انتقاء من خلال تحديد مساحة مستطيلة، انتقاء بواسطة الفراشاة... الخ).
[ModifierPressed]: متغير يعيد قيمة تعبر عن الأزرار الإضافية التي قام المستخدم بالضغط عليها أثناء عملية الانتقاء مثل (Shift, Ctrl,…)

لتوضيح الأمر بشكل أفضل دعنا نأخذ نظرة على المثال التالي:


var temp;
var rtn = PickElement( siGenericObjectFilter, "Select Object", "Select Object Also!", temp, temp, 0, temp);
 
logmessage( "Picked Element: " + rtn.Value("PickedElement"));
logmessage( "Button: " + rtn.Value("ButtonPressed"));
logmessage( "Modifier: " + rtn.Value("ModifierPressed"));

ستلاحظ أننا عند استخدام الأمر PickElement في لغة الـ Jscript قمنا بتعريف متغير مؤقت temp وتميريره لكل من PickedElement , ButtonPressed و ModifierPressed.
السبب في هذا يعود إلى أن الـ Jscript هنا لا تدعم إعادة أكثر من قيمة من الإجراء على شكل متغيرات ممرة.
لذلك للحصول على القيم الناتجة من هذا الأمر، يمكننا استخدام الخاصية Value للمتغير الأساسي rtn الذي يحوي العنصر البرمجي الناتج من الأمر PickElement.
طبعاً لاستخدام الخاصية Value نحن بحاجة لتحديد بارمتر إضافي يعبر عن المعلومة التي نريد الحصول عليها. الأسطر الثلاثة الأخيرة في الكود السابق توضح كيفية استخدام هذه الخاصية لطباعة جميع المعلومات الممكنة الناتجة من تنفيذ الأمر PickedElement.
 

--- الطريقة الرابعة ---
هذه الطريقة بصراحة مشابهة للطريقة الأولى التي تعتمد على أسم العنصر للحصول عليه ولكنها أعم بكثير. بواسطة هذه الطريقة يمكننا الحصول على أي مجموعة من العناصر من خلال الاستفسار عن خصائصها المميزة. مثلاً يمكننا الحصول على جميع العناصر من نوع Nurbs أو جميع الكاميرات في المشهد أو جميع العناصر التي تبدأ بحرف w والخ من هذه الاستفسارات.

استخدام هذه الطريقة يتم من خلال أي من الـ methods التالية الموجودة في لجميع العناصر البرمجي من نوع X3DObject:


X3DObject.FindChild( [Name], [Type], [Family], [Recursive] )
X3DObject.FindChildren( [Name], [Type], [Family], [Recursive] )
 
الفرق بين الإجرائين السابقين هو أن الـ FindChild يقوم بإعادة أول عنصر يطابق خصائص البحث المطلوبة بينما الإجراء FindChildren يعيد جميع الأجسام التي تطابق خصائص البحث المطلوبة.

النوع X3DObject يشمل تقريباً معظم العناصر في البرنامج بما فيها جذر المشهد الرئيسي: SceneRoot، لذلك هذه الاجراءت متوفرة بشكل افتراضي في جميع هذه العناصر.

قبل الانتقال للأمثلة، سنتعرف بشكل سريع على بارمترات أحد الإجرائين (نفسها للآخر):
[Name]: تعبير يعبر عن أسم العنصر المطلوب البحث عنها، يمكن استخدام رموز البحث المعروفة في هذه التعبير مثل: * و ؟... الخ.
[Type]: النوع التفصيلي للعنصر المطلوب الحصول عليه.
[Family]: النوع العام للعنصر المطلوب الحصول عليه.
[Recursive]: ثابت منطقي لتحديد إذا كانت عملية البحث عودية ضمن جميع للعناصر المتشعبة.

فيما يلي مجموعة من الأمثلة المختلفة حول استخدام هذه الإجراءات:


// الحصول على جميع العناصر في المشهد بلا استثناء
allObjects = Application.ActiveSceneRoot.FindChildren();
 
// الحصول على جميع العناصر التي تبدأ بالأحرف cam
allCams = Application.ActiveSceneRoot.FindChildren("cam*");
 
// الحصول على جميع عناصر الإضاءة في المشهد
allLights = Application.ActiveSceneRoot.FindChildren("","",siLightPrimitiveFamily);

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

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

شكراً...
إلى اللقاء في المشاركة الثانية.

خبير مدير همام البهنسي مشاركة 3

في هذا الجزء وبعد أن تعرفنا على كيفية الحصول على العناصر بشكل برمجي سنقوم هنا بالتعرف على كيفية تعديل خصائص (properties) هذه العناصر والتحكم بها من خلال الإجرائات (methods) الخاصة بكل منها.

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

لنبدأ إذن...

بداية كيف يمكننا معرفة ماهي الخصائص أو الإجراءات التي يملكها عنصر ما في البرنامج؟؟؟ الإجابة وبدون تفكير هي وثائق الـ SDK والتي سنمضي معظم وقتنا في استضافتها لذلك نحن بحاجة للتعرف عليها والتأقلم معها.
لنلقي نظرة على سبيل المثال على خصائص العنصر X3DObject الذي تحدثنا عنه في المشاركة السابقة.



سنلاحظ كيف أن جميع الإجراءات الخاصة بهذا العنصر مذكورة ضمن جدول Methods كما أن جميع الخصائص ضمن الجدول Properties.

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

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

لنبدأ بالمثال الأول، سنقوم في هذا المثال باستدعاء إجرائية من العنصر البرمجي Model. كما هو واضح من الأسم، يمثل هذا العنصر أي جذر جزئي (Sub-Root) في المشهد الحالي. وكما ذكرنا سابقاً للتعرف على الإجراءات التي يمكن تنفيذها عليه علينا البحث عنه في وثائق الـ SDK.
 
الإجراء الذي سنقوم بتنفيذه هو AddGeometry. من خلال هذا الإجراء يمكننا إضافة أي مجسم فراغي للجذر المطلوب. طبعاً قد يكون هناك عدد من البارمتارات المطلوب تحديدها لتنفيذه بشكل جيد، لذلك من المفيد القيام بإلقاء نظرة عليه في الوثائق للتعرف على التفاصيل قبل استخدام الإجراء.

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

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



var oSceneRoot = ActiveSceneRoot; // الحصول على جذر المشهد الحالي
var oSphere = oSceneRoot.AddGeometry( "Sphere", "MeshSurface" ); // إضافة كرة لجذر المشهد
 
الآن سنقوم بتوسيع المثال السابق للتعامل هذه المرة مع خصائص العنصر بدل من استدعاء إجراءاته. لنفترض أننا وبعد إضافة الكرة في المثال السابق نود تغير خاصية الأسم لهذه الكرة لقيمة معينة، مثلاً: myBall

كل ما علينا هو تغيير قيمة الخاصية name للعنصر المطلوب. بالنسبة لمثالنا السابق:


oSphere.name = “myBall”;

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

إلى اللقاء!