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

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

السلام عليكم

كيف حالكم جميعا؟ اتمني أن تكونوا بخير حال 😄

لدي مشكلة في مشروع تخرجي تتعلق تحديدا بحركة شخصية في مسار محدد علي بيئة terrain
لتسهيل عرض المشكلة - يمكنكم ان تقوموا بزيارة صفحة مشروع تخرجي في موقعي الخاص
http://ahmedezz.110mb.com/bsc_demos.htm


وتقوموا بتحميل الديمو وتجربوه لتلاحظوا المشكلة بأنفسكم
حيث ان الموضوع ببساطة هو انه لدي برنامج تم عمله بالاستعانة بالمحرك "أوجر" حيث يمكنك كمستخدم وضع وحدات كما تريد بطريقة تفاعلية
ويمكنك ان تحدد مكان البداية والنهاية لكل وحدة تضعها وذلك بالاستعانة بالمفاتيح z و c و space
عموما بعد وضع كافة الوحدات وتحديد اماكن بدايتها ونهايتها يمكنكم الضغط علي زر enter وذلك لتبدأ عملية البحث عن افضل مسار path finding
وعندما تنتهي عملية البحث فان عملية تحريك الوحدات تبدأ حيث تتحرك كل وحدة في المسار الذي وجد لها

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


/*
this function is called to start the object moving to the next position in mWalkList
*/
bool CrowdListener::nextLocation( int i )
{
	// if we run out of point to go to then return false
	if( mRobotNodesWalkList[i].empty() )
		return false;

	mDestinationList[i] = mRobotNodesWalkList[i].front(); // this gets the front of the deque
	mRobotNodesWalkList[i].pop_front();            // this remove the front from the deque

	if(mRobotNodes[i] != 0)
		mDirectionList[i] = mDestinationList[i] - mRobotNodes[i]->getPosition();
	mDistanceList[i] = mDirectionList[i].normalise();

	Vector3 src = (mRobotNodes[i]->getPosition() - mDirectionList[i]) * Vector3::UNIT_X;
	//Vector3 src = mRobotNodes[i]->getOrientation() * Vector3::UNIT_X;
        if ((1.0f + src.dotProduct(mDirectionList[i])) < 0.0001f) 
       {
          mRobotNodes[i]->yaw(Degree(180));
       }
       else
       {
          Ogre::Quaternion quat = src.getRotationTo(mDirectionList[i]);		
		//mRobotNodes[i]->rotate(quat);
		// only apply the yaw rotation
		mRobotNodes[i]->yaw( quat.getYaw() );
       } // else


	return tue;
}

ايضا اتمني ان تجربوا الديمو وتخبروني بتعليقاتكم وملاحظاتكم
وايه مشاكل واجهتكم ولكم جزيل الشكر

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

مفصول عمر سمير  مشاركة 2

للأسف  انني  لا  أستخدم   هذا المحرك  اللي اسمه  ( اوجر ) 
 
لو وضعت مشروعك في محرك معروف يكون احسن مثل محرك XNA

OSF متخصص محترف

محترف  انس مشاركة 3

السلام عليكم .

 ***

سلام .

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

هل تستطيع أن تفسر ما الذي تقصده بهذه الحسبة؟


بتاريخ 12 ابريل 2009 07:22 ص، قطب ahmed ezz حاجبيه بشدة وهو يقول:

Vector3 src = (mRobotNodes[i]->getPosition() - mDirectionList[i]) * Vector3::UNIT_X;
if ((1.0f + src.dotProduct(mDirectionList[i])) < 0.0001f) 
{
  mRobotNodes[i]->yaw(Degree(180));
}
else
{
  Ogre::Quaternion quat = src.getRotationTo(mDirectionList[i]);		
  // only apply the yaw rotation
  mRobotNodes[i]->yaw( quat.getYaw() );
}

خاصة السطر الأول والثاني.

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

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

حاولت تحميل المشروع ولكن يبدو إن الباندودث لديك إنتهى؟

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

محترف مشرف عبد اللطيف حاجي علي مشاركة 6

ممم.. لم أقم بتحميل الـ Demo لكن إليك ما أعتقد.

أظنك تخطئ في حساب متجه حركة الجسم قبل الدوران. أظن أن الحساب الصحيح له يجب أن يكون في بداية الإجراء (قبل تعديل mDirectionList) و أن يأخذ قينة mDirectionList[i] السابقة (بالطبع يجب ان تحل مشكلة أول دوران للجسم. فعندها لا يكون mDirectionList قد حسب بعد)

كما أنك لا تقوم بتنفيذ عملية normalize غلى mDirectionList بل على mDistanceList وأظنك يجب أن تقوم بذلك. لا داعي لعمل نفس العملية على src باعتباره سيأخذ قيمة mDirectionList الواحدية نفسها في الخطوة القادمة

أخيراً فإني لا أعتقد أن هناك داع للشرط الأخير خاصة إذا قمت بتنفيذ عملية normalize على المتجهات (عندها لن يتحقق أبداً، تقريباً)

عبد اللطيف حاجي علي
مبرمج
In|Framez

محترف مشرف عبد اللطيف حاجي علي مشاركة 7

في 12 نيسان 2009 08:02 م، عقد وسام البهنسي حاجبيه بتفكير وقال:

هل تستطيع أن تفسر ما الذي تقصده بهذه الحسبة؟

أعتقد أنه يحاول اختيار جهة الدوران بحيث يكون قوس الدوران أقل ما يمكن؟ 😒
مع أني لا أعتقد أن هذه هي الطريقة الصحيحة

عبد اللطيف حاجي علي
مبرمج
In|Framez

خبير  سعيد بسيوني مشاركة 8

mDistanceList[i] = mDirectionList[i].normalise();
 
هزا السطر صحيح. فهو يقوم بجعل طول المتجه واحدي ويعيد طول المتجه سابقاً. إذن القيمة المحفوظة في mDistanceList هي فعلا المسافة، والقيمة المحفوظة في mDirectionList هي فعلا الاتجاه الصحيح من الموقع السابق إلى الموقع التالي.
 
فيما يلي زلك فإن الحسابات غير سليمة وزي ما قال وسام.. مش عارف بتقصد فيها إيه 😖
 
ببساطة لو انت عاوز توجه الروبوت على متجه معين، تقدر تستخدم الإجراء ده:
 

Ogre::SceneNode::setDirection
 
زي كده:


mRobotNodes[i]->setDirection(mDirectionList[i], TS_LOCAL, Vector3::NEGATIVE_UNIT_Z);
 
فقط. لو كان اتجاه المجسم بتاع الروبوت مواجه لمحور تاني غير الـ z السلبي فلا تنسى تحدد زلك في البارامتر الثالث.
 
يعني الكود حيبقى زي كده:


bool CrowdListener::nextLocation( int i )
{
  // if we run out of point to go to then return false
  if( mRobotNodesWalkList[i].empty() )
    return false;
 


  mDestinationList[i] = mRobotNodesWalkList[i].front(); // this gets the front of the deque
  mRobotNodesWalkList[i].pop_front();            // this remove the front from the deque
 

  if(mRobotNodes[i] != 0)
    mDirectionList[i] = mDestinationList[i] - mRobotNodes[i]->getPosition();
 

  mDistanceList[i] = mDirectionList[i].normalise();
  mRobotNodes[i]->setDirection(mDirectionList[i], TS_LOCAL, Vector3::NEGATIVE_UNIT_Z);
 

  return true;
}
 
والسلام عليكم

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

السلام عليكم

اعتذر عن عدم متابعة المشاركة حاليا نظرا لانقطاعي عن النت لعدة ايام

بالنسبة لعدم توفر الموقع بسبب bandwidth
يمكنكم تجربة تحميله في وقت اخر حيث ان المتاح فقط حاليا في الموقع 15 ميجا لكل ساعة
واعتذر عن ذلك وسأقوم بالنقل لموقع اخر قريبا

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

جزاكم الله خيرا علي سرعة الرد
ويهمني جدا ملاحظاتكم علي ديمو المشروع
والسلام عليكم

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

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

السلام عليكم

في البداية أود ان ان أشكركم علي ردكم لسؤالي وجزاكم الله خيرا

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

تم استخدام الكود الاتي لحل المشكلة:


bool CrowdListener::nextLocation( int i )
{
	// if we run out of point to go to then return false	
	if( mRobotNodesWalkList[i].empty() )
		return false;

	mDestinationList[i] = mRobotNodesWalkList[i].front(); // this gets the front of the deque
	mRobotNodesWalkList[i].pop_front();            // this remove the front from the deque	

	if(mRobotNodes[i] != 0)
		mDirectionList[i] = mDestinationList[i] - mRobotNodes[i]->getPosition();
	mDistanceList[i] = mDirectionList[i].normalise();

	// correct solution
	Vector3 src = mRobotNodes[i]->getOrientation() * Vector3::UNIT_X;
	if ((1.0f + src.dotProduct(mDirectionList[i])) < 0.0001f) 
	{
        	mRobotNodes[i]->yaw(Degree(180));
	}
	else
	{
        	Ogre::Quaternion quat = src.getRotationTo(mDirectionList[i]);		
		//mRobotNodes[i]->rotate(quat);

		// only apply the yaw rotation to prevent the unit from rotating in wrong way
		Radian angle = quat.getYaw();
		Degree da = Degree( angle );
		mRobotNodes[i]->yaw( angle );
	} // else	

	return true;
}

لمزيد من التفصيل حول شكل الكود ولماذا تم استخدام حساب المتجه وتطبيق الدوران بهذا الشكل
أود أن أذكر انني في ذلك اعتمدت بصفة رئيسية علي أول درس تعليمي للمستوي المتوسط من دروس المحرك "أوجر"
والذي يقوم بتحريك الروبوت علي مسار من عدة نقاط ويقوم بتطبيق الدوران ايضا - وهذا رابط الدرس وفيه الكود
http://www.ogre3d.org/wiki/index.php/Intermediate_Tutorial_1

لقد جربت طريقة الاخ سعيد ولم تنجح مع انها منطقية جدا وتتبع تسلسل التنفيذ المطلوب - لا أعرف لماذا لم تنجح 😒

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

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

اعتذر ان تأخري في الرد لظروف امتحاناتي هذه الايام وانا سعيد جدا بتفاعلكم وجزاكم الله خيرا
واتمني لو هناك حل اخر لهذه المشكلة كي نتعلمه

وهل لدي أحدكم فكرة لتطوير المثال ليصبح شبه لعبة صغيرة - مثل شخصيتين كل منهما تحاول الوصول لمنتصف الخريطة

والتي تصل أولا تقوم بقطع الطريق علي الثانية والفوز في اللعبة

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

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