ورود

View Full Version : رفتار دادن به ابجکت ها



H_G_G_I
دوشنبه 24 مهر 1391, 22:43 عصر
سلام
من میخوام انجینم (البته انجینی در کار نیست فرض بگیرید) تو هر صحنه بینهایت ابجکت (کلاس GameObject) داشته باشه و هر کدوم از این ابجکت ها رفتار هایی داشته باشن که بازی ساز مینویسه !
یعنی میاد یه کلاس جدید میسازه و از کلاس Behavior من ارث میبره ! بعد می تونه این رفتار رو به هر ابجکتی نصبت بده ! مثل خیلی از موتور ها نمونش یونیتی .
الان مشکلم اینه که می خوام با دادن کلاس و یا نام کلاس که همون رفتار ما هست اگه ابجکت اون رو داشت برشگردونه اگه نداشت هم 0 بده !
میخوام به بهترین شکل باشه خطای زمان اجرا و .. نده سریع هم باشه !
-
-
اینم نمونه ! تابع GameObject::behavior_get رو درست کنید !:چشمک:

class Behavior
{
public:
bool m_enable;
GameObject* mp_obj;
virtual void onAttach();
virtual void onUpdate();
virtual void onDestroy();
}

class GameObject
{
public:
int m_x, m_y;
GameObject(int _x, int _y);
Behavior* m_behaviors[8];
Behavior* behavior_add(Behavior* b);
Behavior* behavior_get(/*type or string*/ b);
}
GameObject::GameObject(int _x, int _y)
{
m_behaviors[0] = m_behaviors[1]
= m_behaviors[2] = m_behaviors[3]
= m_behaviors[4] = m_behaviors[5]
= m_behaviors[6] = m_behaviors[7] = NULL;
m_x = _x;
m_y = _y;
}
Behavior* GameObject::behavior_add(Behavior* b)
{
for(int i = 0; i < 8; i++)
if(m_behaviors[i] == NULL)
{
m_behaviors[i].m_enable = true;
m_behaviors[i].mp_obj = this;
return m_behaviors[i];
}

return NULL;
}
Behavior* GameObject::behavior_get(/*type or string*/ b)
{
//???????????????????????????????????????
for(int i = 0; i < 8; i++)
if(m_behaviors[i] != NULL)
if(/*m_behaviors[i] TYPEIS b*/)
return m_behaviors[i];

return NULL;
//????????????????????????????????????????
}

class Scene
{
std::list<GameObject> m_objects;
GameObject* object_add(int _x, int _y);
}
GameObject* Scene::object_add(int _x, int _y)
{
m_objects.push_back(new GameObject(_x, _y));
return m_objects[m_objects.size()-1];
}

class MyBehavoir1 : Behavior
{
float m_speed;
};

class MyBehavoir2 : Behavior
{
int m_health;
};

int main(int argc, char** argv)
{
Scene scn;
GameObject* obj1 = scn.object_add(100, 100);
obj1->behavior_add(new MyBehavoir1);
obj1->behavior_add(new MyBehavoir2);
MyBehavoir1* l_behav1 = obj1->behavior_get( /* MyBehavoir1 OR "MyBehavoir1" */);
l_behav1->m_speed = 12;
MyBehavoir2* l_behav2 = obj1->behavior_get( /* MyBehavoir2 OR "MyBehavoir2" */);
l_behav2->m_health = 100;

}

-
هم اکنون نیازمنده کد های شما هستیم !:لبخند::لبخند:

SeganX
پنج شنبه 27 مهر 1391, 13:59 عصر
کارت خیلی خوبه. این همون پترن برنامه نویسی بر پایه کامپوننت هستش.

ببین چند تا راه برای برقراری ارتباط بین کامپوننت ها وجود داره. اما هیچ کدوم بهترین و کاملترین نیستن. خودت باید سعی کنی که با تریک های مختلف به کامپوننت ها درسترسی داشته باشی. بستگی به این داره که چه زمانی نیاز به دسترسی به کامپوننت یا مقادیر اون داری. من جند تا نکته می گم بعدش خودت می تونی تصمیم بگیری.

سعی کن از اسم کمتر استفاده کنی و واسه کامپوننت هات یه تایپ آی دی تعریف کنی و تا حد امکان هر آبجکت تنها یک کامپوننت از هر تایپ داشته باشه. یعنی مثلا دو تا کاکپوننت خون نداشته باشه.

اگر می خوای از طریق اسم ( نام ) هر کامپوننت بهش دسترسی پیدا کنی سعی کن که از map برای نگهداریشون استفاده کنی. به این صورت که اسمشون با یه الگوریتم ( مثلا crc32 ) تبدیل به یک عدد بشه و این عدد بشه همون key توی map. وقتی می خوای یک کامپوننت رو پیدا کنی می تونی سریع تر پیداش کنی.

اگر می خوای در هر آپدیت بطور مستمر به یه کامپوننت یا پارامترهای اون دسترسی داشته باشی، سعی کن یه پوینتر که به اون کامپوننت یا پارامترش اشاره می کنه تعریف کنی و بعداز یک بار پوینت کردن دیگه با همون پوینتر کار کنی. در غیر این صورت می تونی از message برای تبادل اطلاعات بین کامپوننت ها استفاده کنی. واسه این کار یه تابع به کامپوننت هات اضافه کن مثلا
void MsgProc( uint type, void* data )
گیم آبجکت ها نیز این تابع رو دارن. در گیم ابجتکت ها پیغام های رسیده رو برای همه کامپوننتها نیز بفرست. مثلا برای تغییر پارامترهای یک کامپوننت خاص می تونی یک استراکچر داشته باشی که توش مقادیر جدیدی که می خوای واسه کامپوننت اعمال کنی وجود داشته باشه. این استراکچر رو پر می کنی و با فلگ مربوطش به صورت یک پیغام می فرستی برای Owner ( صاحب کامپوننت یا همون گیم آبجکت ) مثلا
msg_AddHealth msg;
msg.addHeath = 10;
msg.addArmor = 2;
myGameObject->MsgProc( MT_HEAL, &msg );
به این ترتیب هر کامپوننتی که فلگ MT_HEAL رو پردازش کنه ( مثلا کامپوننت خون ) می تونه این مقادیر رو اعمال کنه.

استراکچری که برای پیغام ها استفاده میشه میتونه حاوی اطلاعات مختلفی باشه. مثلا یه اسم خاص. هر کامپونتی که اسمش با اسم موجود در پیغام یکی بود اون رو پردازش کنه. یا مثلا یه پونتر به یه آبجکت. یا مثلا یه پوینتر خالی که قراری توسط کامپوننتی که داره پیغام رو پردازش می کنه پر بشه.
مثلا در یک رویدادی می خوای ببنی گیم ابجکتت سپر دفاعی داره یا نه. یه پیغام بهش میدی که فقط توسط کامپوننت های مربوطش پردازش می شه. اگر کامپوننت سپر دفاعی در گیم آبجکت موجود باشه، اون کامپوننت می تونه خودش رو تو استراکچری که همراه پیغام اومده رجیستر کنه. بعدش تو می تونی بهش دسترسی داشته باشه. اگر هم نباشه که یعنی گیم آبجکتت سپر نداره دیگه. از خونش کم میشه.

هیچ کامپوننتی رو از داخل کامپوننت های دیگه delete نکن. چون ممکنه یه جایی یه نفر بهش پوینت کرده باشه.
برای پاک کردن کامپوننت می تونی یه پارامتر به نام m_dead داشته باشی. وقتی این پارامتر true بود تو حلقه آپدیت پاکش کن. اینطوری پاک شدن کامپونت یه فریم تاخیر پیدا می کنه و هر جایی که داره از این کامپوننت استفاده می کنه میتونه چک کنه ببنه کامپوننت قراره پاک شه یا نه. اونوقت اگر پوینتری بهش پوینت کرده باشه رو نال کنه.
یه روش دیگه هم اینه که یه پیغام تعریف کنی ( مثلا MT_I_DEAD ) و وقتی که کامپوننت finalize میشه این پیغام رو به Owner بگه تا بقیه هم بفهمن که این کامپوننت داره پاک میشه و تصمیم خودشون رو بگیرن.

روش های زیادی برای مدیریت کامپوننت ها در این سبک برنامه نویسی وجود داره. هر روز هم مقالات جدیدی در این رابطه منتشر میشه و بعضی از شرکت های غول بازی سازی از سیستم های پیچیده تری استفاده می کنند. در کامپوننت نویسی نکات زیادی هست که باید بهشون دقت کنی. این پترن دستت رو برای دیزاین خیلی باز می کنه اما اگه بد دیزاین بشه میتونه دردسر ساز بشه. بهترین کار اینه که خودت استارتش رو بزنی و کسب تجربه کنی.

عذر بابت غلطهای املایی و ترکیب فارسی و انگلیسی
موفق باشی

H_G_G_I
یک شنبه 30 مهر 1391, 13:00 عصر
ممنون .
گرفتن رفتار رو نوشتم . با typeid کارم راه افتاد . الان هم بارشته کار می کنه هم با تمپلیت ها !
اول کار نمیکرد نگو همه چی سر این بود :قهقهه:


virtual void onUpdate() = 0;

باید اینطوری می شد !:متفکر:


virtual void onUpdate(){};

الان اینطوری شده !

Object obj1;
obj1.beahvior_add(new MyBehav1);
obj1.beahvior_add(new MyBehav2);
MyBehav2* mb1 = obj1.beahvior_getby_typename<MyBehav2>();

-
الان می خوام عوض نیو کردن . نام کلاس یا همون رفتار رو به صورت رشته بگیره !
خودم به این نتیجه رسیدم که یه ماکرو مثل


REGISTER_BEHAVIOR(mybehav1);

درست کنم که تو یه std::map اطلاعات بره و یه یه تابع بهش بدم که اونو نیو کنه !
هرچی سرچ کردم چیز درت حسابی پیدا نشد !
کسی یه نمونه ای چیزی نداره !

pswin.pooya
یک شنبه 30 مهر 1391, 23:59 عصر
برای پاک کردن کامپوننت می تونی یه پارامتر به نام m_dead داشته باشی. وقتی این پارامتر true بود تو حلقه آپدیت پاکش کن. اینطوری پاک شدن کامپونت یه فریم تاخیر پیدا می کنه و هر جایی که داره از این کامپوننت استفاده می کنه میتونه چک کنه ببنه کامپوننت قراره پاک شه یا نه. اونوقت اگر پوینتری بهش پوینت کرده باشه رو نال کنه.به نظر من اگر از reference count استفاده بشه بهتر هست. هر وقت رفرنس به صفر برسه خوکار حذف بشه.

SeganX
پنج شنبه 04 آبان 1391, 12:37 عصر
به نظر من اگر از reference count استفاده بشه بهتر هست. هر وقت رفرنس به صفر برسه خوکار حذف بشه.

لبخند ملایم