الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

مثال عمل animation في SDL

مغلق
بدأه hasan_aljudy في 2 يوليو 2006 · 4 رد · 2,460 مشاهدة · في قسم برمجة الألعاب و الرسوميات العام
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

بسم الله الرحمن الرحيم

ساقوم بإذن الله باستخدام SDL لعرض مثال بسيط لعمل animation ثنائي الأبعاد باستخدام لغة D. لن اتطرق الى تفاصيل اعداد و تحميل مكتبة SDL او طريقة استخدامها مع D, بل ساطرح الفكرة الرئيسية و اللتي اظن انها يمكن ان تطبق في C++‎ و حتى المكتبات الأخرى غير SDL.

(يعني اذا اردت التطبيق فأنت مجبر ان تقوم بأكثر من مجرد copy-paste :P)

اولا, لا بد من عمل initialization للـ SDL و النافذة, و اعداد الـ program loop او الـ game loop.

import derelict.sdl.sdl;
import derelict.sdl.image;

void init()
{
	//derelict initialization/loading
	//(You don't need this if you're using C++)
	DerelictSDL.load();
	DerelictSDLImage.load();

	//sdl initialization
	SDL_Init( SDL_INIT_EVERYTHING );

	//set some attributes
	//I think it will work even without these
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); 
	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);

	//create the window
	screen = SDL_SetVideoMode( 640, 480, 32, SDL_SWSURFACE );	
	//set the window title
	SDL_WM_SetCaption( "SDL Experiments!", null );
}

SDL_Surface * screen;

void main()
{
	init();	
	SDL_Event event;

	mainLoop:
	while( true )
	{
		if( SDL_PollEvent( &event ) )
		{
			if( event.type == SDL_QUIT )
			{
				break mainLoop;
			}
		}		
	}	
}

في هذه المرحلة لن يحدث شيء سوى ظهور نافذة سوداء.

اول شي فعلناه هو الـ initialization, عن طريق SDL_Init

هناك ايضا initialization خاصة بـ Derelict و هذه الخطوة خاصة فقط بلغة D, فإذا كنت تستعمل C++‎ فلن تحتاج لذلك.

السبب في حاجتنا لها هي طريقة عمل Derelict, و هو امر لن اتطرق اليه هنا.

هناك ايضا عدة استدعائات لـ SDL_GL_SetAttribute و هي فقط لإعداد بعض القيم الخاصة بالفيديو .. و لكني اعتقد انها ليست ضرورية تماما. يمكنكم ان تجربوا عدم استدعائها و رؤية النتائج.

الخطوة الثانية هي انشاء surface للنافذة و استدعاء SDL_SetVideoMode لإنشائها.

دعوني اوضح قليلا .. الـ surface يعني حرفيا سطح .. و لكنه في sdl يعني صورة, او مكان لتخزين الصور.

النافذة اللتي سنفتحها لا بد لها من surface, هذا السطح يعتبر بمثابة الـ frame buffer الخاص بالبرنامج, أو هكذا يمكن التفكير فيها.

SDL_SetVideoMode تقوم بإنشاء النافذة و تعطينا مؤشر للـ surface الخاص بها. يعني اذا اردنا رسم شيء على الشاشة نقوم برسمه على الـ surface الخاص بنافذة البرنامج.

الخطوة الثالثة هي الدخول في حلقة لا نهائية .. يمكن تسميتها main loop او program loop. معالجة الـ events يتم من خلال استدعاء SDL_PollEvent و اللتي تقوم بفحص الـ events, و في حالة وجود event جديد فإنها تقوم بوضعه داخل الـ parameter المرسل لها. عندما يضغط المستخدم على زر اغلاق النافذة, فإن الـ event اللذي سيحدث هو SDL_QUIT.

هذا كل شيء كبداية, اعتقد انها ابسط كثيرا من glut فضلا عن win32 api.

في الخطوة الثانية سنقوم بتحميل صورة و عرضها في منتصف الشاشة .. و سأقوم ايضا بتحديث الشاشة باستمرار داخل الـ main loop.

اولا نقوم بإنشاء مجلد باسم معين مثل images و نضع داخله الصورة اللتي نحملها, هذا افضل من ناحية تنظيمية.

بالنسبة لي, وضعت صورة لـ kaito kid مأخوذة من دعاية لاحد افلام المحقق كونان, بإمكانكم بالطبع اختيار أي صورة.

سنقوم أولا بكتابة الكود بطريقة مسبطة, فقط من اجل توضيح بعض الأشياء. يعني سوف استخدم global variables و ما إلى ذلك.

نقوم بإنشاء surface جديد من اجل تحميل او تخزين محتويات الصورة في الذاكرة, ثم نقوم بتحميل الصورة من ملف bmp. و نضيف شيئ جديد داخل الـ main loop من أجل رسم الصورة.

لنرى كيف حدث ذلك, دعوني اريكم اولا التغييرات اللتي اجريتها على الـ main

void main()
{
	init();	
	loadImages();
	SDL_Event event;

	mainLoop:
	while( true )
	{
		drawImages();

		if( SDL_PollEvent( &event ) )
		{
			if( event.type == SDL_QUIT )
			{
				break mainLoop;
			}
		}	
	}	
}

نلاحظ وجود استدعائين جديدين, الأول هو loadImages و يقوم يتحميل الصورة و وضعها في الـ surface الجديد .. لا تنسوا انني عرفته كـ global variable.

و الأمر الثاني هو drawImages و قد وضعته داخل الـ main loop. و هذا الأمر يقوم برسم الصورة على الـ screen surface, و سبب وضعه داخل الـ loop هو ان تبقى الصورة داخل النافذة حتى لو حركنا النافذة او غيرنا شكلها .. الخ, و ايضا لأننا سنكرر عمل نفس الشي عند عرض الـ animation بإذن الله.

بالنسبة للـ loadImages فقد قمت باستخدام SDL_LoadBitmap و هي عملية موجودة ضمن SDL و تقوم بتحميل الصورة من ملف bmp ووضعه على شكل surface. و لكن هناك مشكلة صغيرة, و هي ان هيئة الصورة قد تكون مختلفة عن هيئة الـ surface الخاص بالشاشة. لذلك, نقوم بعد تحميل الصورة يتغيير هيئتها لكي تكون متطابقة مع هيئة الشاشة, و ذلك عن طريق SDL_DisplayFormat.

SDL_Surface * image;

void loadImages()
{
	SDL_Surface * temp = SDL_LoadBMP("images/kaitokid.bmp");
	image = SDL_DisplayFormat( temp );
	SDL_FreeSurface( temp );
}

لاحظوا انني استخدمت surface مؤقت و قمت بتحريره بعد الانتهاء من استخدامه, لأنني لو لم افعل ذلك فإنني سوف اضيع اجزاء من الذاكرة .. و لو كنت اقوم بتحميل العديد من الصور بنفس الطريقة من دون التخلص من الـ temp surface فسيحصل عندنا memory leak بسرعة.

أما بالنسبة لـ drawImages, فهي تقوم بعرض الصورة على الشاشة .. او بالأحرى على النافذة.

كيف نقوم بذلك؟

سبق و قلت أن النافذة عندها surface خاص بها, و في حالتنا هذه فهذا الـ surface هو المتغير screen. هل تذكرون لماذا؟ لأن هذا الـ surface نحصل عليه من الدالة SDL_SetVideoMode.

إذن, لكي نرسم اي شي على الشاشة, علينا اولا ان نرسم هذا الشي على screen عن طريق SDL_BlitSurface و بعد ذلك نقوم باستدعاء SDL_Flip.

void drawImages()
{
	SDL_Rect offset; 
	offset.x = 100;
	offset.y = 100;
	SDL_BlitSurface( image, null, screen, &offset );
	SDL_Flip( screen );
}

الدالة SDL_BlitSurface تقوم بنسخ محتويات surface الى surface آخر, او صورة الى صورة أخرى. البارامتر الأول هو الصورة اللتي تريد النسخ منها, البارامتر الثاني لا ادري ما هو بصراحة .. دعه NULL (في لغة D نكتب null بالحروف الصغيرة), البارامتر الثالث هو الصورة اللتي تريد النسخ اليها, و البارامتر الرابع هو المكان اللذي تريد النسخ اليه .. او مقدار الإزاحة اللتي ستنسخ اليها. اعتقد انه واضح ولا يحتاج شرح, اليس كذلك؟

أما SDL_Flip فإعتقد كما هو واضح .. ان SDL يستخدم double buffering, لذلك لن تظهر الصورة الموجودة في المتغير screen قبل ان نستدعي عليه SDL_Flip.

و الآن, الكود كاملا:

import derelict.sdl.sdl;
import derelict.sdl.image;

void init()
{
	//derelict initialization/loading
	//(You don't need this if you're using C++)
	DerelictSDL.load();
	DerelictSDLImage.load();

	//sdl initialization
	SDL_Init( SDL_INIT_EVERYTHING );

	//set some attributes
	//I think it will work even without these
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); 
	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);

	//create the window
	screen = SDL_SetVideoMode( 640, 480, 32, SDL_SWSURFACE );	
	//set the window title
	SDL_WM_SetCaption( "SDL Experiments!", null );
}

SDL_Surface * screen;

SDL_Surface * image;

void loadImages()
{
	SDL_Surface * temp = SDL_LoadBMP("images/kaitokid.bmp");
	image = SDL_DisplayFormat( temp );
	SDL_FreeSurface( temp );
}

void drawImages()
{
	SDL_Rect offset; 
	offset.x = 100;
	offset.y = 100;
	SDL_BlitSurface( image, null, screen, &offset );
	SDL_Flip( screen );
}

void main()
{
	init();	
	loadImages();
	SDL_Event event;

	mainLoop:
	while( true )
	{
		drawImages();

		if( SDL_PollEvent( &event ) )
		{
			if( event.type == SDL_QUIT )
			{
				break mainLoop;
			}
		}	
	}	
}

النتيجة كما تظهر عندي:

01_07_06_07_32_28_1151807548sdl1.jpg

طيب, نحن الآن نعرف كيف نقوم بإنشاء نافذة في SDL و كذلك كيف نقوم بتحميل صورة bmp و عرضها.

نريد الأن تطوير مهاراتنا قليلا و تحميل عدة صورة ثم جعلها تظهر على شكل animation. فكيف نقوم بذلك؟

أولا, نقوم بتحميل عدة صور, و نضعها مثلا في مصفوفة. و نقوم بتغيير الدالة اللتي تعرض الصورة بحيث تقوم بعرض صورة مختلفة في كل مرة, او بالأحرى, نقوم بتغيير الصورة اللتي نعرضها باستمرار لكي نحصل على تأثير و كأن الصورة تتحرك.

من اجل التنظيم, نقوم بوضع هذه الصور في مجلد images اللذي انشأناه في الخطوة السابقة. و نضع عدة صور من اجل ان نقوم بتكوين الـ animation منها. بالنسبة لي فقد استخدمت مجموعة صور ايضا لـ kaitokid و هي في الحقيقة نفس اللقطة .. و لكني تلاعبت بها قليلا لجعلها تبدو متكررة .. على كل حال هذا ليس موضوعنا. المهم هو ان تكون لديك مجموعة من الصور معدة مسبقا, و سنقوم بتحميلها واحدا وراء الآخر, لا يهم كيف, المهم ان نحملها كلها, حتى لو بشكل يدوي manual. بالنسبة لي فقد رقمت الصور من 0000‎.bmp الى 0019‎.bmp و قمت بعمل for loop لتحميلها, و لكن هذا ليس ضروريا حيث يمكن تحميلها من دون for loop, كما انه لا يجب استخدام 20 صورة .. بل يمكن الاكتفاء بخمس او ست صور. المهم ان نحمل الصور الخاصة بالأنيميشن.

SDL_Surface *[20] images;

void loadImages()
{
	for(int i = 0; i < 20; i++ )
	{
		char[] nextFrame;
		char[] num = toString(i);
		if( i < 10 )
		{
			nextFrame = "000" ~ num ~ ".bmp";
		}
		else
		{
			nextFrame = "00" ~ num ~ ".bmp";
		}
		SDL_Surface * temp = SDL_LoadBMP("images/" ~ nextFrame);
		images = SDL_DisplayFormat( temp );
		SDL_FreeSurface( temp );
	}
}

لاحظوا اني قمت بتغيير images بحيث اصبح مصفوفة surfaces.

لاحظوا اني استخدمت لغة D, يعني استفدت من بعض خواصها مثل الـ array concatenation و ما إلى ذلك من الأمور الغير موجودة في الـ C++‎. لاحظوا ايضا انني لا احذف الـ strings بعد استخدامها لأن الـ garbage collector يتولي المهمة. و هنا قد يسأل سائل .. لماذا لا يتولى ايضا مسألة الـ temp surface عند تحميل الصورة؟ الجواب هو ان تحرير الـ surface ليس له علاقة بعملية new و delete بل هو شي خاص بـ SDL .. او هكذا يمكن ان نفكر فيه.

في لفة D فإن تعريف متغير SDL_Surface *[20] images يعني ان images هي مصفوفة مؤشرات الى SDL_Surface ووضع 20 داخل الأقواس يعني ان المصفوفة هي static array و حجمها محدد مسبقا بعشرين عنصر. اعتقد ان الأمر في الـ C++‎ أعقد قليلا .. لا احد يسالني .. فانا لم احاول في حياتي انشاء متغيرات معقدة لهذه الدرجة في الـ C++‎ و لا اريد تخيل ذلك. في الـ D الأمر بسيط, فقط قم بقراءة الـ type من اليمين الى اليسار.

SDL_Surface * [20] images

اقراها من اليمين الى اليسار, تصبح:

array of pointers to SDL_Surface

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

الحركة هي عبارة عن عدة صور تتناوب على الظهور .. بين كل صورة و أخرى هناك فترة زمنية بسيطة جدا.

نحتاج اولا ان نعرف كم من الوقت مضى منذ ان عرضنا الصورة السابقة, و حين نجد ان فترة كافية قد مرت, نقوم بعرض الصورة اللتي بعدها. هذه الصور تمسى أُطُر (جمع إطار) او frame بالانجليزي.

من اجل الحصول على الوقت, سنستخدم SDL_GetTicks و هي تعطينا الوقت المنقضى على عمل البرنامج بالـ millisecond يعني جزء من الف من الثانية.

من اجل عرض الأنيميشن, نقوم اولا بعرض اول صورة و تسجيل الوقت الحالي, ثم في كل مرة نأتي لعرض الصورة, نقوم بتشييك (فحص) الوقت الحالي و نطرحه من وقت عرض الفريم (الاطار) السابق, اذا تعدى الفرق بينهما مثلا 60 ميليثانية, نقوم بعرض الصورة التالية. و الا نبقى على نفس الصورة الحالية.

int currentFrame = 0;
int prevTime = 0;
void drawImages()
{
	int ticks = SDL_GetTicks();
	if( ticks - prevTime > 60 )
	{
		currentFrame = (currentFrame + 1) % 20;
		prevTime = ticks;
	}

	SDL_Rect offset; 
	offset.x = 100;
	offset.y = 100;
	SDL_BlitSurface( images[currentFrame], null, screen, &offset );
	SDL_Flip( screen );
}

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

هذه هي كل التغييرات المطلوبة, عند القيام بها سنحصل على انيميشن بسيط.

هذا هو الكود كاملا:

import derelict.sdl.sdl;
import derelict.sdl.image;
import std.string;

void init()
{
	//derelict initialization/loading
	//(You don't need this if you're using C++)
	DerelictSDL.load();
	DerelictSDLImage.load();

	//sdl initialization
	SDL_Init( SDL_INIT_EVERYTHING );

	//set some attributes
	//I think it will work even without these
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); 
	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);

	//create the window
	screen = SDL_SetVideoMode( 640, 480, 32, SDL_SWSURFACE );	
	//set the window title
	SDL_WM_SetCaption( "SDL Experiments!", null );
}

SDL_Surface * screen;

SDL_Surface *[20] images;

void loadImages()
{
	for(int i = 0; i < 20; i++ )
	{
		char[] nextFrame;
		char[] num = toString(i);
		if( i < 10 )
		{
			nextFrame = "000" ~ num ~ ".bmp";
		}
		else
		{
			nextFrame = "00" ~ num ~ ".bmp";
		}
		SDL_Surface * temp = SDL_LoadBMP("images/" ~ nextFrame);
		images = SDL_DisplayFormat( temp );
		SDL_FreeSurface( temp );
	}
}

int currentFrame = 0;
int prevTime = 0;
void drawImages()
{
	int ticks = SDL_GetTicks();
	if( ticks - prevTime > 60 )
	{
		currentFrame = (currentFrame + 1) % 20;
		prevTime = ticks;
	}

	SDL_Rect offset; 
	offset.x = 100;
	offset.y = 100;
	SDL_BlitSurface( images[currentFrame], null, screen, &offset );
	SDL_Flip( screen );
}

void main()
{
	init();	
	loadImages();
	SDL_Event event;

	mainLoop:
	while( true )
	{
		drawImages();

		if( SDL_PollEvent( &event ) )
		{
			if( event.type == SDL_QUIT )
			{
				break mainLoop;
			}
		}	
	}	
}

في هذه الحالة فإن التحكم بالـ animation صعب بعض الشيء, و كذلك من الصعب عرض عدة animations في وقت واحد, لذلك من الأفضل اعادة تصميم الكود ووضع الانيميشن في كلاس class. و سنقوم بذلك إن شاء الله في المرة القادمة.

و هنا مثال الـ animation مع exe و كذلك الصور المستخدمة. (مرفق)

simple_animation.rar

تم تعديل هذه المشاركة بواسطة hasan_aljudy في 2 يوليو 2006 في 05:59

#2

أخي حسان هل من إقتراح حول كتاب معين برأيك أنه الأفضل ؟؟؟

#3

من ناحية SDL, فقد اعتمدت على هذه الدروس

http://lazyfooproductions.com/SDL_tutorials/index.php

ليس لدي كتاب معين .. و بصراحة انا من النوع اللذي لا يصبر على قراءة الكتب .. :(

#4

سنقوم الان ان شاء الله بتحويل الكود السابق الى كلاس ...

أولا, نلاحظ المتغيرات العامة اللتي نستخدمها حاليا: هناك مصفوفة surfaces و هناك متغير لوقت آخر فريم, و متغير لرقم الفريم الحالي. و هناك اربعة اسطر من الكود تقوم بقراءة الوقت الحالي, ثم تقوم بناءا على فرق الزمن بتغيير الصورة الحالية او عدم تغييرها.

إذن, فلنضع هذه الأشياء في الكلاس الجديد:

class Animation
{
	SDL_Surface *[] frames;
	int currentFrame = 0;
	int prevFrameTime = 0;

	public:
	this( SDL_Surface*[] pframes )
	{
		frames = pframes;
	}

	void update()
	{
		int ticks = SDL_GetTicks();
		if( ticks - prevTime > 60 )
		{
			currentFrame = (currentFrame + 1) % frames.length;
			prevFrameTime = ticks;
		}
	}
}

لاحظوا ان this( SDL_Surface*[] pframes )‎ هو عبارة عن constructor في لغة D. و لاحظوا ايضا ان الـ array في هذه المرة dynamic يعني يحتوي معه على معلومات حول طوله, حيث ان frames.length هي عدد الفريمات الموجودة في الـ animation, و لكن هذا غير ممكن في الـ C++‎ الا اذا قمت باستخدام std::vector او قمت بتمرير حجم المصفوفة كمتغير إضافي الى الـ constructor.

و الآن, نلاحظ اننا لا زلنا نستخدم شرط ( ticks - prevTime > 60 ) يعني ان الفرق بين كل فريم هو 60 ميليثانية على الأقل. و لكن بما أننا الآن نستخدم كلاس فالمفروض ان نعطي لأنفسنا مرونة أكثر و نسمع لأنفسنا بتغيير هذه القيمة. و لنكن أكثر دقة, فتغيير هذه القيمة يغير سرعة الأنيميشن, و سرعة الأنيميشن تحسب بكم فريم في الثانية, يعني frame per second. فلنقوم بإضافة متغير جديد هو frameTime و نقوم بتغيير الـ update بحيث تستخدم المتغير الجديد بدل من القيمة 60. و ربما ايضا نضع بعض الدوال لتغيير هذه القيمة و الحصول عليها. و اخيرا نحتاج دالة للحصول على الصورة الحالية او الاطار الحالي في الانيميشن.

class Animation
{
	SDL_Surface *[] frames;
	int currentFrame = 0;
	int prevFrameTime = 0;
	int frameTime = 0;

	public:
	this( SDL_Surface*[] pframes, int pframeTime = 60 )
	{
		frames = pframes;
		frameTime = pframeTime;
	}

	void update()
	{
		int ticks = SDL_GetTicks();
		if( ticks - prevTime > frameTime )
		{
			currentFrame = (currentFrame + 1) % frames.length;
			prevFrameTime = ticks;
		}
	}

	SDL_Surface * getCurrentFrame()
	{
		return frames[currentFrame];
	}

	void setFrameTime( int time )
	{
		frameTime = time;
	}

	void setFPSSpeed( double speed )
	{
		setFrameTime( cast(int)(1000.0 / speed) );
	}

	int getFrameTime()
	{
		return frameTime;
	}

	double getFPSSpeed()
	{
		return 1000.0 / cast(double)frameTime;
	}
}

لاحظوا انه لكي يعمل الكلاس بطريقة صحيحة فلا بد من ان يتم استدعاء update طوال الوقت ..

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

SDL_Surface * screen;
Animation kaitoKid;

void loadImages()
{
	SDL_Surface *[] images = new SDL_Surface*[20];
	for(int i = 0; i < 20; i++ )
	{
		char[] nextFrame;
		char[] num = toString(i);
		if( i < 10 )
		{
			nextFrame = "000" ~ num ~ ".bmp";
		}
		else
		{
			nextFrame = "00" ~ num ~ ".bmp";
		}
		SDL_Surface * temp = SDL_LoadBMP("images/" ~ nextFrame);
		writefln("loading %s", "images/" ~ nextFrame );
		images = SDL_DisplayFormat( temp );
		SDL_FreeSurface( temp );
	}

	kaitoKid = new Animation( images, 100 );	
}

void drawImages()
{
	SDL_Rect offset; 
	offset.x = 100;
	offset.y = 100;
	SDL_BlitSurface( kaitoKid.getCurrentFrame(), null, screen, &offset );
	SDL_Flip( screen );
}

void main()
{
	init();	
	loadImages();
	SDL_Event event;

	mainLoop:
	while( true )
	{
		kaitoKid.update();
		drawImages();

		if( SDL_PollEvent( &event ) )
		{
			if( event.type == SDL_QUIT )
			{
				break mainLoop;
			}
		}	
	}	
}

لاحظوا انني في هذا الكود:

void loadImages()
{
	SDL_Surface *[] images = new SDL_Surface*[20];

........

قمت بعمل new على المصفوفة .. بحيث تكون في الـ heap و ليس في الـ stack, لأنها لو كانت على الـ stack فإن المصفوفة ستختفي عند انتهاء الدالة .. و هذه مشكلة, لأنني في constructor الكلاس Animation اقوم باستقبال مصفوفة و الاحتفاظ بمؤشر (او reference) لها, فإذا اختفت المصفوفة الأصلية فإن الكلاس لن يعمل, و بالأحرى سيكون عندي null pointer و بالتالي سيخرب البرنامج.

هنا هناك حلين, إما ان نقوم بعمل new كما فعلت أنا .. او ان نغير الـ constructor تبع الـ Animation بحيث انه يستنسخ مصفوفة الـ surfaces بدلا من ان يحتفظ فقط بمؤشرٍ لها. يعني ان نغيره بحيث يصبح:

	public:
	this( SDL_Surface*[] pframes, int pframeTime = 60 )
	{
		frames = pframes.dup;
		frameTime = pframeTime;
	}

لاحظوا pframes.dup و هي طريقة الـ D لاستنساخ المصفوفات.

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

و على كل حال ربما اغيره فيما بعد اذا اكتشفت انني احتاج لذلك.

#5

اخر .. رد لم اقرأه .

لكن اعتقد افضل طريقة في عمل صور متحركة .. ان نعتمد على sprite ... سأضع ذلك في الرد القادم .ان شاء الله

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

هذا الموضوع مغلق.

مواضيع مشابهة