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

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

نحن بانتظار انتهاء فترة الامتحانات. في حال كونكم قد تفرغتم الآن فيمكننا المتابعة على الفور.  ما قولكم؟

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

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

يا شباب. كلمات السر لم تعد تعمل في اللعبة.  اللعبة دائماً تخبرني بأن كلمة السر خاطئة حتى عندما أدخل واحدة صحيحة.
هل يمكنكم اكتشاف الخطأ وإصلاحه؟

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

خبير  أحمد عزالدين مشاركة 43

السلام عليكم جميعا

اولا: أحب أخبر الجميع انى تقريبا انهيت امتحاناتى وجاهز معكم ان شاء الله للاستمرار في المشروع

كما ذكر الاستاذ وسام أيضا حدثت معى مشكلة لما حاولت تجربة موضوع كلمات السر هذا
وبعد البحث كانت النتيجة كالاتى:


من الواضح ان الاخ سلوان عندما أضاف استخدام اسلوب ال input manager and DirectInput ولانه اضطر الي استخدام اسلوب مختلف في برمجة
شاشة كلمة السر بالاعتماد علي WM_CHAR وتقريبا الرسالة بترسل ال character code والاخ سلوان بيحوله كما هو موجود في الملف Game.cpp
في الدالة المسئولة عن رسائل الويندوز كالاتي:

case WM_CHAR:				
		InputManager::Instance->_injectCharacter((char)wParam);
		break;
بصراحة مش عارف السبب لكن لما السطر دا بيتنفذ
 الدالة injectCharacter بيوصلها قيمة الحرف دائما small letter ودا بعد البحث وجد انه بيسبب مشكلة لان كود شاشة كلمة السر والموجود
في الملف  stateflow.cpp وبالتحديد في الدالة GameState::FlowAction GameState_Password::Update(void)
يعتمد في مقارتة كلمة السر علي الحروف الكبيرة Capital

ولقد قمت بحل المشكلة بتعديل الدالة void InputManager::_injectCharacter(const char cCharacterCode) في الملف input.cpp
ليصبح شكلها كالاتي:
void InputManager::_injectCharacter(const char cCharacterCode)
{
	m_cInjectedChar = 0;
	if( (cCharacterCode >= 'A' && cCharacterCode <= 'Z') ||
		(cCharacterCode >= 'a' && cCharacterCode <= 'z') ||
		(cCharacterCode >= '0' && cCharacterCode <= '9') )
	{
		// new code -- by Ahmed Ezz
		if(cCharacterCode >= 'a' && cCharacterCode <= 'z')
		{
			// here it's small character, we need to upper it so we can process
			// it in password screen, as it only deal(compare) upper character
			m_cInjectedChar = cCharacterCode - 32;  // convert the small to upper
			return;
		}
		// end new code
		m_cInjectedChar = cCharacterCode;
	}
}
حيث قمت باضافة كود يعدل الاحرف الصغيرة small بطرح القيمة 32 منها لتصبح capital والفكرة تعتمد في الاصل علي ان الحرف a = 97
والحرف  A = 65 فيصبح الفرق بين الحرف الكبير والصغير 32

وبعد التجريب وجد ان الحل يصلح المشكلة
------------------------------------------------------------------------------

بالمناسبة وانا اتصفح كود كلمة السر مع انه معقد بعض الشئ وبالتحديد في الدالة
 static bool FillIntoGame(const char szPassword[kPasswordLength+1]) وفي الجزئية التي لا افهم معناها وهي
for (int i=0;i
		{
			if (szPassword[i] - 'A' >= 16)
				return false;
		}
ما الغرض من الرقم 16 بالتحديد ولما اصلا نحتاج هذا ال loop داخل كود الدلة
والله اعتذر واتمني لو يقوم احد بشرح هذه الدالة كاملة
اري الدالة تحتوي علي كود تابع للـ union وتستخدم اسلوب مقارنة لكلمة السر بناء علي عدة
عوامل منها عدد المحاولات والدرجة الحالية والمرحلة الحالية وتستخدم عمليات shifting and logical operation وعلي ما اعتقد
هذا النوع من العمليات معقد (علي الاقل بالنسبة لي) ولا يفهم المغزي منه بسهولة

ارفقت ملف يحتوي التعديل الجديد - فقط استبدلوه بالملف الاصلي وستصلح المشكلة

في انتظار اقتراحاتكم
والسلام عليكم

أحمد عزالدين
طالب دراسات عليا
جامعة كالجري

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

في 08 يونيو 2008 05:35 م، عقد ahmed ezz حاجبيه بتفكير وقال:

الدالة injectCharacter بيوصلها قيمة الحرف دائما small letter ودا بعد البحث وجد انه بيسبب مشكلة لان كود شاشة كلمة السر والموجود
في الملف  stateflow.cpp وبالتحديد في الدالة GameState::FlowAction GameState_Password::Update(void)
يعتمد في مقارتة كلمة السر علي الحروف الكبيرة Capital

ولقد قمت بحل المشكلة بتعديل الدالة void InputManager::_injectCharacter(const char cCharacterCode) في الملف input.cpp
ليصبح شكلها كالاتي

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

 

وفي 08 يونيو 2008 05:35 م، قال ahmed ezz متحمساً:

ما الغرض من الرقم 16 بالتحديد ولما اصلا نحتاج هذا ال loop داخل كود الدلة

فلنحاول سوية تحليل السطر جزءاً جزءاً:    (szPassword[i] - 'A' >= 16)
لكل محرف من كلمة السر، نطرح كود المحرف من رمز المحرف A في جدول آسكي. بالنظر إلى جدول آسكي، نلاحظ أن المحارف من A إلى Z تبدأ من 65 وتنتهي عند 90 كأرقام عشرية. فلنتخيل أن الحرف الأول الذي أدخله المستخدم من كلمة السر هو الحرف B... ماذا سيحدث؟
المحرف B له الرمز 66، نطرحه من قيمة المحرف A (وهي 65) فيكون الناتج لدينا 1، وهي لا تحقق الشرط.  إذن السؤال يصبح الآن، متى يتحقق الشرط؟
طبعاً يتحقق إذا كان حاصل الطرح أكبر من أو يساوي 16، وهذا يعني أن تكون قيمة المحرف المدخل في كلمة السر أكبر من أو تساوي A زائد 16. أو بعبارة أخرى، يجب أن تستخدم حرفاً ليس من الأحرف الـ 16 الأولى من الأبجدية الإنجليزية...
 
إذن فالحلقة تقوم باختبار إن كان أي من المحارف المدخلة خارج النطاق المسموح من المحارف... فكلمات السر المولدة لن تخرج أحرفها عن النطاق A إلى P.
 



وفي 08 يونيو 2008 05:35 م، ظهر شبح ابتسامة على وجه ahmed ezz وهو يقول:

اري الدالة تحتوي علي كود تابع للـ union وتستخدم اسلوب مقارنة لكلمة السر بناء علي عدة
عوامل منها عدد المحاولات والدرجة الحالية والمرحلة الحالية وتستخدم عمليات shifting and logical operation وعلي ما اعتقد
هذا النوع من العمليات معقد (علي الاقل بالنسبة لي) ولا يفهم المغزي منه بسهولة

الترميز بضغط البتات (باستخدام الـ union) يعتبر من أسهل وأضعف أنظمة الترميز، إلا أنه يفي بالغرض تماماً للعبة بسيطة كهذه.
الفكرة هي تحديد مجال القيم المسموح لكل متغير نود حفظه في كلمة السر، ومن ثم تجاهل البتات التي لا داعي لها...
 
كمثال:
نود أن نضع في كلمة السر رقم المرحلة التي وصل إليها اللاعب، وعدد المحاولات. نعلم أن في اللعبة يوجد فقط 15 مرحلة. أي نحن بحاجة إلى متغير يعبر عن القيم من 1 إلى 15 (أو 0 إلى 14). طبعاً لا يوجد متغير بهذا الصغر، إذ أن أصغر الأحجام الطبيعية المتوفرة في C هي الـ char وهو يستطيع التعبير عن 256 قيمة مختلفة، ويستخدم 8 بتات لذلك...  أما في حالتنا، فنحن نحتاج فقط إلى 4 بتات لحفظ القيمة التي تتراوح من 0-14.
هنا نستخدم ميزة جميلة من لغة C، وهي ما يدعى بالـ bit-field. حيث تستطيع أن تعلن عن متغير بحجم اعتيادي، لكنك تقسم البتات في داخله لتستخدمها كمتغيرات منفصلة كيفما تشاء.
البنية DATA ضمن الاتحاد ENCRYPTION تحوي الـ bit-fields المختلفة للقيم التي نريد:


struct DATA
{
    UINT64 uPyramidStarting   : 2; // Up to 4 values
    UINT64 uCurrentLevel      : 4; // Up to 16 values
    UINT64 uLevelClearedPlus1 : 4; // Up to 16 values
    UINT64 uAttempts          : 6; // Up to 64 values
    UINT64 uValidation        : 8;
}
 
الخانة المخصصة لحفظ المرحلة الحالية تستخدم 4 بتات من 64 بت هي الحجم الكامل للبنية. أما عدد المحاولات فيستخدم 6 بتات، وذلك لأننا نسمح بأن يقوم اللاعب بـ 50 محاولة كحد أقصى.
 
الآن بعد تعبئة هذه القيم ضمن بتات مضغوطة، نريد عرضها كسلسلة واحدة من المحارف... لذلك نقوم بإعادة تقسيم متغير الـ 64 بت بشكل مختلف يلائم التحويل إلى محارف من جدول آسكي... وذلك يتم كالآتي، نقسم المتغير إلى عدة أقسام، كل منها مكون من 4 بتات فقط... فينتج لدينا 6 أقسام، وكل منها يمكن أن يعبر عن 16 قيمة مختلفة (ومن هنا جاء الرقم 16 الذي كنت تتساءل عنه). إذن كلمة السر ستتكون من 6 أقسام، نحتاج إلى إظهارها للمستخدم بصورة لائقة... لذلك، نقوم بأخذ قيمة كل قسم، وإضافتها إلى المحرف A، لينتج لدينا محرفاً يقع بين A و P، وهو المحرف الذي نظهره للمستخدم!
 
كما قلت، هذا يعتبر أحد أشهر التطبيقات على الـ union (وقد واجهته أول مرة في كتابي الأصفر العزيز عن لغة C، وهو مؤلف بالعربية بالمناسبة).
 
إن كنتم تريدون المزيد من الشرح والأمثلة، فلنفتتح موضوعاً خاصاً بها في منتدى البرمجة، ولنتحاور بها!

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

خبير  أحمد عزالدين مشاركة 45

السلام عليكم

جزاك الله خيرا أخى وسام علي التوضيح

لقد عدلت بالفعل ملف input.cpp وأرجعته الى حالته الاصلية كما قام الاخ سلوان بكتابته في البداية
وايضا عدلت في الملف stateFlow.cpp وبالتحديد في الدالة
GameState::FlowAction GameState_Password::Update(void)
باضافة سطر يحول الحروف الي كبيرة كما أشرت حضرتك باستخدام strupr وذلك مباشرة قبل ارسال كلمة السر لمعالجتها
والان المشكلة تم حلها

ارفقت ملفات ال stateFlow.cpp وكذلك input.cpp في المرفقات - فقط استبدلوهم بما عندكم مباشرة

بالمناسبة موضوع تقسيم المتغير Data كما أشرت حضرتك حسب المتغيرات التي نريد اضافتها لكلمة السر
لا أعلم لماذا تم صنع المتغير اصلا او الوحدة Data بحجم 64 هل الحجم 32 بت لا يكفي
عذرا علي سوء فهمى ولكني لاحظت ان المتغير Data مقسم 5 اجزاء ومجموع بتات كل الاجزاء هو 24 فكان المفروض ان يكون الحجم 32 كافي؟

والسلام عليكم

أحمد عزالدين
طالب دراسات عليا
جامعة كالجري

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

عظيم. كالعادة، النسخة الأخيرة من الكود والتي تحوي جميع التعديلات النهائية تجدونها على الرابط الدائم:
http://www.agdn-online.com/source/kvalley_latest.zip
 
لقد قمنا بتعديلات طفيفة ضمن كود اللعبة لجعله أكثر صحة ونظافة. بعض هذه التعديلات كان ضرورياً كي نستطيع استخدام الكود نفسه في بناء وحدة الـ #C التي يستخدمها إخواننا لبناء مصمم المراحل، حيث أنهم يستطيعون تحرير المراحل ورؤيتها بشكل فوري كما ستظهر في النهاية، إضافة إلى أنهم يستطيعون لعب المرحلة أثناء تحريرها (مثلاً رص بعض الأحجار أمام الوحش وهو يلاحقك☺
لذلك فكل التعديلات التي نقوم بها هنا تنعكس لديهم أيضاً، مما يعني أننا يجب أن نكون حذرين في تغييراتنا كي لا نخرب أي شيء عليهم.
أحد التغييرات الأساسية كانت ضمن وحدة الـ input، حيث أن DInput يريد HWND لنافذة top-level، بينما في حالة مصمم المراحل فإن نافذة اللعبة تكون child للتحكم الموجود في الـ form. وهكذا فقد تم تعديل الكود ليستطيع التعامل مع مثل هذه الحالة...
 



وفي 09 يونيو 2008 04:10 ص، ظهر شبح ابتسامة على وجه ahmed ezz وهو يقول:

لاحظت ان المتغير Data مقسم 5 اجزاء ومجموع بتات كل الاجزاء هو 24 فكان المفروض ان يكون الحجم 32 كافي؟
 
ملاحظة صائبة تماماً. في هذه الحالة لا يوجد ضرورة لاستخدام متغير كلي بسعة 64 بت، بل 32 بت أكثر من كافية. لاحظ أيضاً أن المتغير Chars أيضاً يستخدم 24 بت كمجموع كلي، لكنها مقسمة بطريقة مختلفة.
 
 
الآن وبمناسبة الحديث عن وحدة مصمم المراحل، فإن فريق الـ #C يطالبنا بتقديم بعض المساعدة. يريد أن تقوم الوحدة برسم شبكة لتظهر تقسيمات الخلايا في اللعبة. كمثال، انظروا الصورة التالية:


 
نريد أن تكون الميزة مبنية بشكل أننا فقط نرفع flag من نوع bool، فيتم رسم الشبكة دائماً، وإن خفضناه فإن الشبكة تختفي. هذا العلم سيتم رفعه فقط في برنامج تصميم المراحل عندما يود المستخدم رؤية الشبكة لتساعده في تصميم المرحلة.
 
أرجو أن يكون الشرح واضحاً. والآن... من لها؟☺

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

خبير  أحمد عزالدين مشاركة 47

السلام عليكم

هذا الحل المبدئى للمطلوب وهو اضافة gird lines كشبكة تسهل التعامل مع الحجرة كمجموعة من الخلايا
لقد اضفت الاتي في الملف game.h
#define GRID_LINE_WIDTH 1
#define GRID_LINE_COLOR 0xFFFFFFFF
وذلك للتحكم في حجم خطوط الشبكة وكذلك لون الخطوط

وايضا اضفت الاتي:
bool GetDrawGrid(void) {return m_bDrawGrid;}	
للتأكد وفحص هل نريد رسم خطوط الشبكة أم لا -- وطبعا عرفت المتغير m_bDrawGrid في الكلاس GameApp
ووضعت قيمته والتى يمكننا التحكم فيها في الدالة bool GameApp::Init(void) بالقيمة true لرسم الخطوط مبدئيا

اما التعديل الاهم والمسئول عن رسم الشبكة فتم في الدالة void Pyramid::Draw(void) وذلك في اخر الدالة

ارفقت الملفات المعدلة ومنتظر اقتراحاتكم

والسلام عليكم

أحمد عزالدين
طالب دراسات عليا
جامعة كالجري

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

السلام عليكم...
 
ميزة رسم الشبكة تعمل كما يجب. شكراً أحمد، إلا أن النتيجة النهائية قاسية نوعاً ما، وحبذا لو كانت الشبكة يتم رسمها بشفافية معينة (مثلاً 30%) بحيث لا تكون مشتتة للانتباه. أعتقد أنه لا يوجد إجراء من مكتبة CoreLib يساعدك على رسم الخطوط مع دعم الـ alpha blending، إلا أنه يمكنك رسمها مباشرة عن طريق Direct3D. وتأكد من إعادة الـ render states إلى قيمها الأصلية بعد أن تنتهي من الرسم.
 
طبعاً الكود سيزداد تعقيداً، لذلك أنصح بنقل الكود ووضعه في إجراء خاص (مثلاً DrawGrid) يتم نداؤه من خلال الـ Pyramid::Draw.
 
في هذه الأثناء، نود التقدم خطوة أخرى للأمام... من يود إنجاز المهام الآتية:
 
* إضافة خيار الخروج في القائمة الرئيسية.
* إضافة شاشة الـ Credits وخيار خاص لرؤيتها من الشاشة الرئيسية.
 
 
هيا نريد استعادة النشاط بعد الامتحانات☺   لقد قطعنا شوطاً لا بأس به حتى الآن... فلنتابع حتى النهاية!

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

خبير  أحمد عزالدين مشاركة 49

السلام عليكم

لقد ارفقت اخر التعديلات التي التي تمت لجعل رسم الشبكة يدعم الشفافية بمساعدة الاستاذ وسام
وبناءا علي اقتراحه تم عمل flag بصورة #define اسمه ENABLE_GRID عندما يكون موجودا فانه يسبب وضع القيمة true للمتغير الذي يتم فحصه لرسم الشبكة

الان تم وضع الكود في الدالة Init في الكلاس GameApp وتم اضافة بعض الثوابت في الملف game.h
وتم عمل دالتين احدهما تنشئ vertex buffer يحتوي كل النقاط اللازمة لرسم خطوط الشبكة والدالة اسمها CreateGrid ويتم نداؤها مرة واحدة فقط
لتهيئة ال vertex buffer وذلك عند تفعيل رسم الشبكة كما هو مفعل مبدئيا بالقيمة
#define ENABLE_GRID
في الملف GAME.H

وتم ايضا رسم الشبكة بالدالة DrawGrid والتي تستخدم ال D3D
ولقد عملت تعليقات في الكود توضح ما تم بالظبط - بمساعدة الاستاذ وسام جزاه الله خيرا

سأحاول في الفترة القادمة تنفيذ المطلوب الاول باضافة الامر - خروج - في الشاشة الرئيسية للعبة
والسلام عليكم

أحمد عزالدين
طالب دراسات عليا
جامعة كالجري

خبير  أحمد عزالدين مشاركة 50

السلام عليكم

ارفقت اخر تعديل للعبة باضافة كود لشفافية وهو غير مفعل افتراضيا وكود رسم الشبكة يأخذ في الاعتبار حفظ حالة الـ device state
قبل الرسم ويسترجعها بعد الرسم كما هو موضح في الدالة DrawGrid
أيضا تم تعديل ال state flow لشاشة القاشمة الرئيسية للعبة بحيث تحتوي علي اربع اختيارات
- الاول للدخول للعبة جديدة
- الثاني للدخول لشاشة كلمات السر
- الثالث لعرض ال credit
- الرابع والاخير حاليا للخروج من اللعبة

عند الضغط علي الاختيار الثالث لعرض شاشة الـ credit تظهر شاشة فارغة تقريبا وسيتم ملؤها قريبا ان شاء الله

التعديلات تراعي ما طلبه الاستاذ وسام بحيث يتم فصل كود رسم الشبكة نهائيا في اللعبة النهائية وتراعي ايضا انشاء كود الشبكة عند بداية انشاء الهرم
وليس في حلقة الرسم كما كان سابقا

في انتظار المزيد
والسلام عليكم

أحمد عزالدين
طالب دراسات عليا
جامعة كالجري