فصل اول روز چهارم : ایجاد شخصیت اصلی بازی

دانلود نسخه pdf : دانلود

دانلود کد های امروز :دانلود

۱-۴-۱-            وارد کردن ربات

به روز چهارم خوش آمدید. بعد از این که در سه بخش قبل تنظیمات اولیه را انجام دادیم حالا نوبت به اضافه کردن شخصیت اصلی بازی یعنی ربات است.

برای انجام این کار ما یک کلاس جدید ایجاد خواهیم کرد. در داخل این کلاس کارهای زیر صورت خواهد گرفت:

  • مدیریت مختصات (طول و عرض) و سرعت ربات.
  • بروز رسانی این متغیر ها.
  • اجازه دسترسی سایر کلاس ها به این متغیرها.

بنابراین، متدهای این کلاس به دسته های زیر تقسیم خواهد شد:

  • متدهای بروز رسانی که در هر تکرار حلقه بازی فراخوانده می شوند.
  • متدهایی که به فشردن کلید ها توسط کاربر پاسخ می دهند.
  • متدهای کمکی که مقادیر متغیرهای این کلاس را برگردانده و یا تغییر می دهد.

خوب بیاید شروع کنیم.

ابتدا با ایجاد کلاس جدیدی به نام ربات (Robot) در بسته کد اندروید کار را آغاز می کنیم. بعد از ایجاد این کلاس کدهای زیر را در آن وارد کنید (می توانید این کدها را copy/paste نمایید).

Package codeandroid;

public class Robot {
	// در جاوا ، متغیر ها یاید به صورت خصوصی تعریف شود
	// تا تنها متدهای آن کلاس قادر به تغییر آن ها باشند
	private int centerX = 100;
	private int  enter = 382;
	private  enter  jumped = false;
	private int speedX = 0;
	private int speedY = 1;

	public void update() {
		// جابجایی ربات یا پس زمینه
		if (speedX < 0) {
			centerX += speedX;
		} else if (speedX == 0) {
			System.out.println("تصویر پس زمینه جابجا نشود");
		} else {
			if (centerX <= 150) {
				centerX += speedX;
			} else {
				System.out.println("اینجا تصویر پس زمینه جابجا شود");
			}
		}
		// بروز رسانی عرض ربات
		if ( enter + speedY >= 382) {
			 enter = 382;

		} else {
			 enter += speedY;
		}
		// مدیریت پرش
		if (jumped == true) {
			speedY += 1;
			if ( enter + speedY >= 382) {
				 enter = 382;
				speedY = 0;
				jumped = false;
			}
		}
		// مانع از حرکت ربات به سمت طول منفی صفحه مختصات
		if (centerX + speedX <= 60) {
			centerX = 61;
		}
	}

	public void moveRight() {
		speedX = 6;
	}

	public void moveLeft() {
		speedX = -6;
	}

	public void stop() {
		speedX = 0;
	}

	public void jump() {
		if (jumped == false) {
			speedY = -15;
			jumped = true;
		}
	}
}

این متغیرها چه معنایی می دهند؟

یک توصیف مختصر از این متغیرها  (توضیح بیشتر در بخش های پایین تر ارائه شده):

  • centerX و enter مختصات طول و عرض مرکز ربات ما می باشند.
  • speedX و speedY سرعت یا نرخ جابجایی ربات در محورهای افقی و عمودی می باشد.
  • jumpedمتغیری است که مشخص می کند ربات پریده یا روی زمین می باشد.

حالا بیاید در مورد هر کدام از متدهای بالا صحبت کنیم.

  • متدهایی که همواره صدا زده میشوند:

update() : این متد در هر تکرار “حلقه بازی فراخوانده می شود. این متد بسیار مهم است لذا باید روی آن وقت بیشتری بگذاریم. اما قبل از این که ادامه دهیم لطفا یکبار دیگر با دقت به آن نگاهی بیاندازید تا یک درکی از آن داشته باشید. وقتی به این متد نگاهی می اندازید موارد زیر را در نظر داشته باشید:

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

  • ما فرض میکنیم مختصات عرضی (y) زمین در ۴۴۰ پیکسل قرار دارد. یعنی اگر متغیر enter دارای مقدار ۳۸۲ پیکسل باشد که مختصات عرضی مرکز ربات است در نتیجه پاهای آن در مختصات ۴۴۰ پیکسل روی زمین قرار می گیرد.
  • اگر متغیر centerX دارای مقادیر کمتر از ۶۰ باشد یعنی دست چپ ربات ما از محدوده صفحه نمایش خارج شده است.

متد update() را که در زیر آورده شده است یکبار دیگر با توضیحاتی که قرار گرفته با دقت نگاه کنید:

public void update() {

            // جابجایی ربات یا پس زمینه

            if (speedX < 0) {

                  centerX += speedX;

در اینجا مقدار متغیر centerX به اندازه سرعت مشخص تغییر می کند

            } else if (speedX == 0) {

                  System.out.println(“تصویر پس زمینه جابجا نشود”);

            } else {

                  if (centerX <= 150) {

اگر مقدار مختصات طولی ربات کمتر از ۱۵۰ باشد دستور زیر اجرا می شود.

                        centerX += speedX;

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

                  } else {

                        System.out.println(“اینجا تصویر پس زمینه جابجا شود”);

                  }

            }

            // بروز رسانی عرض ربات

            if (enter + speedY >= 382) {

مقدار ۳۸۲برای متغیر enter زمانی است که ربات روی زمین قرار دارد

                  enter = 382;

            } else {

                  enter += speedY;

            }

            // مدیریت پرش

            if (jumped == true) {

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

                  speedY += 1;

                  if (enter + speedY >= 382) {

                        enter = 382;

                        speedY = 0;

                        jumped = false;

                  }

            }

            // مانع از حرکت ربات به سمت طول منفی صفحه مختصات

            if (centerX + speedX <= 60) {

اگر جمع مقدار centerX و speedX باعث شود ربات به خارج از محدوده صفحه نمایش رود لذا برای مانع شدن از این کار مقدار centerX روی ۶۱ ثابت می شود.

                  centerX = 61;

            }

      }

تشریح متد بروزرسانی :

ابتدا بحث را با بخشهایی که به صورت توضیح در کدها وارد شدند آغاز می کنیم:

// جابجایی ربات یا پس زمینه

  • اولین عبارت if :

برای آن که بازی ما ربات را در محل مناسبی ترسیم کند، centerX باید دائماُ بروزرسانی شود. هرچند، اگر ربات حرکت نکند(speedX==0 ) ، در نتیجه نیازی به بروزرسانی centerX با اضافه کردن سرعت به آن نیست.

  • لطفا فیلمی از بازی سربازان کوچک که در اینترنت موجود هست را مشاهده کنید تا مطالب زیر را درک کنید.

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

حالا بیاید در مورد توضیح کدهای بخش های زیر صحبت کنیم:

// بروز رسانی عرض ربات و // مدیریت پرش

 

از آن جایی که جاذبه زمین همواره وجود دارد، ربات باید دائما به زمین جذب شده و روی آن قرار گیرد. ما زمین را در فاصله ۴۴۰ پیکسل پایین تر از بالای صفحه نمایش در نظر می گیریم، و اگر موقعیت عرضی ربات بعلاوه سرعت عرضی(speedY) آن را به پایین تر از زمین آورند، ما آن را در موقعیت ۳۸۲ ثابت قرار خواهیم داد تا پایین تر از زمین نرود.

قسمت پرش نیز سرعت و موقعیت فعلی را بررسی میکند تا بفهمد ربات در روی زمین قرار دارد یا  در هوا.

سرانجام بیایید در مورد کدهایی که با توضیح زیر مشخص شده اند بحث کنیم:

// مانع از حرکت ربات به سمت طول منفی صفحه مختصات

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

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

  • متدهایی که با ورودی صفحه کلید در ارتباط اند:

moveRight(); ، که سرعت طولی ربات (speedX) را برابر ۶ قرار می دهد.

moveLeft(); ، که سرعت طولی را برابر منفی ۶ قرار می دهد.

Stop(); ، که سرعت طولی را برابر صفر قرار می دهد.

Jump(); ، که سرعت عرضی را برابر منفی ۱۵ قرار می دهد.

  • متدهای کمکی

ما این متدها را در انتهای این بخش اضافه و توضیح خواهیم داد.

 

۱-۴-۲-            گرافیک

در این بخش به توصیف چگونگی حرکات در بازی خواهیم پرداخت.

در داخل متد paint از کلاس شروع، ما عبارتی را خواهیم نوشت که شبیه زیر است:

drawRobot(centerX,enter) . این عبارت رباتی را در مختصات centerX و enter رسم خواهد کرد.

در هر تکرار از متد paint، که هر ثانیه ۶۰ بار اتفاق می افتد، ربات در مختصات های گفته شده که دائم در حال تغییر توسط کاربر هستند ترسیم می شود و نمودی از حرکت ربات را پدید خواهد آورد. برای آن که centerX و enter تغییر یابند، ما از متغیر های speedX و speedY استفاده میکنیم، که نرخ تغییر مقادیر centerX و enter را در هر بار تکرار حلقه نشان میدهند.

پس زمانی که من میگویم متد moveRight() سرعت طولی یا افقی ربات را برابر ۶ قرار می دهد، یعنی هر بار که moverRight() صدا زده می شود، سرعت ربات برابر ۶ پیکسل قرار می گیرد. یعنی ربات به اندازه ۶ پیکسل جابجا می شود. در این حالت متد update() مقدار سرعت یعنی ۶ را به centerX اضافه میکند. تغییر مقدار centerX به این معنی است که عبارت drawRobot(centerX,enter) ربات را به مکان جدید منتقل خواهد کرد. اگر این تغییر با سرعت کافی انجام گیرد ما احساس حرکت ربات در صفحه نمایش به صورت واقعی را خواهیم کرد.

ایجاد یک ربات با نام robot :

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

در زیر این خط در کلاس شروع شی ربات را درست کنید:

public class ShoroClass extends Applet implements Runnable, KeyListener {

پایین این خط کد زیر را بنویسید:

private Robot robot;

حالا در متد start() این شی را مقدار دهی کنید به صورت زیر:

robot = new Robot();

حالا که یک شی از این کلاس ایجاد کردیم، به کمک آن می توانیم به متد های آن دسترسی داشته باشیم.

۱-۴-۳-            رسم شخصیت اصلی بازی

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

هم اکنون ما در کلاس شروع کار میکنیم.

ایتدا مطمئن شوید که کلاس java.awt.Graphics رو به پروژه اضافه کردید.

  • حالا در زیر جایی که متد run(); را در این کلاس تعریف کرده اید رفته و دو متد جدید زیر را در آن جا تعریف کنید.

ابتدا متد update(Graphics g) را اضافه کنید برای این کار کافیست چند حرف اول آن را تایپ کرده و سپس با فشردن دکمه های  Ctrl+Space گزینه مورد نظر را انتخاب نمایید تا اکلیپس خودکار این متد را برای شما بوجود آورد.

بعد از ایجاد این متد نوبت به متد paint(Graphics g) می رسد این متد را نیز به همان نحو در زیر متد قبلی که ایجاد کردید بوجود آورید.

توصیف متد update :

ما از ابن متد برای double buffering استفاده خواهیم کرد. یک روش برای جلوگیری از پرش و سوسو زدن در ارائه یا نمایش بازی. شما می توانید به مطالعه بیشتر در مورد این موضوع بپردازید چون من در مورد این تکنیک کارشناس نیستم و نمی توانم مبسوط در مورد آن توضیح دهم. مبنای این روش این است که با نگه داشتن موقعیت عکس قبلی برای لحظاتی کوتاه نتیجه این است که حرکت تصویر ها در صفحه نمایش آرام و طبیعی خواهد بود.

  • خوب بیاید متغیر های جدید رو تعریف کنیم. برای این کار به پایین جایی که شیء ربات را تعریف کردیم رفته و متغیر های زیر را تعریف کنید:
private Image image;
private Graphics second;
  • اگر با خطایی رو به رو شدید دکمه های Ctrl+Shift+O را با هم فشار دهید.
  • حالا به متد update که تازگی تعریف کردید رفته و کد های زیر را به آن اضافه کنید.
@Override
	public void update(Graphics arg0) {
		if (image == null) {
			image = createImage(this.getWidth(), this.getHeight());
			second = image.getGraphics();
		}
		second.setColor(getBackground());
		second.fillRect(0, 0, getWidth(), getHeight());
		second.setColor(getForeground());
		paint(second);
		arg0.drawImage(image, 0, 0, this);
	}

کدهای اضافه شده به این متد به صورت مستقل، مشخص و آسان است. همان طور که میبینید این کدها مقدار دهی به دو متغیر جدید image و second  می یاشد. و همچنین اعمال یکسری تنظیمات از قبیل طول، عرض و رنگ. همان طور که گفتم درک این کدها به طور مستقل آسان است اما این کدها باهم همان روش double-buffering را تشکیل می دهند. شما می توانید در این مورد تحقیق بیشتری داشته باشید، اما م پیشنهاد میکنم آن را نادیده گرفته، پذیرفته و ادامه دهید. J

توصیف متد paint() :

این متد برای ترسیم گرافیک روی صفحه نمایش به کار میرود. الان ما تنها ربات را داریم که می خواهیم روی صفحه رسم کنیم پس تنها عبارت زیر را داخل آن قرار دهید:

@Override
	public void paint(Graphics arg0) {
		arg0.drawImage(character, robot.getCenterX() – ۶۱, robot.getCenterY() – ۶۳, this);
	}

متد arg0.drawImage() متغیرهای زیر را به عنوان ورودی می گیرد:

arg0.drawImage(img, x, y, observer)

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

در کدی که به متد paint اضافه کردیم، ما از عکسی به نام character استفاده خواهیم کرد که در حقیقت عکس ربات می باشد. اما مختصاتی که باید عکس ما در آن رسم شود همان طور که مشاهده می شود ۶۱ پیکسل سمت چپ و ۶۳ پیکسل بالای مختصات (centerX, enter) می باشد. و مقدار آخرین متغیر this خواهد بود که به عنوان ImageObserver خواهد بود.

تا اینجا شما باید تعدادی خطا در پروژه مشاهده کنید.

رفع این خطا ها :

  • متغیر character تعریف نشده است. بنابراین ابتدا ما باید آن را تعریف کنیم:

در کلاس شروع جایی که متغیر image را تعریف کردید این متغیر را نیز به شکل زیر تعریف کنید.

Private Image image,character;

  • حالا ما باید آن را مقدار دهی کنیم. ما در اینجا یک عکس به این متغیر اختصاص خواهیم داد. برای انجام این کار ابتدا باید یک شیء از نوع URL تعریف کنیم تا به کمک آن بتوانیم آدرس عکسی که می خواهیم را برگردانیم.

پس در پایین خطی که متغیر character را تعریف کردید کد زیر را وارد نمایید:

private URL base;

یادتان باشد که کلاس URL را به پروژه اضافه کنید(Ctrl+Shift+O).

  • در متد init(); ، ما این متغیر را مقدار دهی کرده و مقداری را به character اختصاص می دهیم.

تغییرات زیر را اعمال کنید.

@Override
	public void init() {
		setSize(800, 480);
		setBackground(Color.BLACK);
		setFocusable(true);
		addKeyListener(this);
		Frame frame = (Frame) this.getParent().getParent();
		frame.setTitle("بازی ربات");
		try {
			base = getDocumentBase();
		} catch (Exception e) {
			// TODO: handle exception
		}
		// تنظیم عکس
		character = getImage(base, "data/character.png");

	}
  • در نهایت ما باید یک پوشه در بسته بازی ایجاد کنیم. برای اینکار روی پوشه src راست کلیک کرده سپس گزینه New و بعد از آن Other را انتخاب کنید.

بعد از مطابق شکل زیر گزینه ایجاد پوشه را انتخاب کرده و ادامه را بزنید:

بعد از آن نام این پوشه را data قرار داده و گزینه اتمام را انتخاب کنید تا پوشه ایجاد شود.

داخل این پوشه عکس زیر را که در پوشه asset ازاین مقاله با نام character قرار دارد را بگذارید.

دانلود منابع پروژه : دانلود

اگر این عکس را پیدا نکردید آن را از سایت codeandroid.ir در بخش آموزش طراحی بازی دانلود کنید.

تا اینجا باید خطای ناشی از متغیر character رفع شده باشد.

رفع خطاهای باقی مانده:

خطاهی زیر هنوز در پروژه وجود دارند:

در توضیح قبلی من در مورد کلاس ربات، اشاره کردم که ما باید موارد زیر را در این کلاس ایجاد کنیم:

  • متدهای بروز رسانی که در هر تکرار حلقه بازی فراخوانده می شوند.
  • متدهایی که با فشردن کلید ها توسط کاربر پاسخ می دهند.
  • متدهای کمکی که مقادیر متغیرهای این کلاس را برگردانده و یا تغییر می دهد.

ما هنوز متدهای کمکی این کلاس را ایجاد نکردیم تا به کمک آن ها به متغیر های این کلاس دسترسی داشته باشیم.

  • ابتدا کلاس ربات را باز کنید.
  • وقتی این کلاس باز شد در هر فضای سفید موجود در این کلاس راست کلیک کرده، بعد از باز شدن لیست روی گزینه Source رفته و در نهایت روی گزینه Generate Getters and Setters کلیک کنید.

همان طور که در شکل بالا نشان داده شده است تمام گزینه ها را انتخاب کرده و ok را انتخاب کنید.

اما Getters و Setters چه چیزی هستند؟

در قسمت های قبل اشاره شد که در جاوا مرسوم است که متغیرها به صورت private تعریف شوند. برای آن که کلاس های دیگر به متغیرهای این کلاس دسترسی داشته باشند باید متدهای کمکی که به getters و setters معروف هستند ایجاد شوند.

به متدهای زیر به عنوان مثال نگاه کنید:

public int getSpeedY() {
		return speedY;
	}

	public void setSpeedY(int speedY) {
		this.speedY = speedY;
	}

متد اولی به ما این امکان را می دهد که سرعت عرضی را بگیریم (get) .

متد دومی نیز این امکان را به ما خواهد داد تا یه این متغیر مقدار دهیم (set). به طور مثال وقتی ما در کلاس شروع هستیم می توانیم با عبارت زیر این سرعت را تنظیم کنیم.

Robot.setSpeedY(15);

هم اکنون دیگر در پروژه نباید خطایی وجود داشته باشد.

۱-۴-۴-            جابجایی و حرکت ربات

تا اینجا با اجرای برنامه باید صفحه ای مثل زیر که ربات در آن قرار دارد نمایش داده شود.

هم اکنون ما آخرین تغییرات این بخش را برای حرکت ربات اعمال خواهیم کرد.

ابتدا در کلاس شروع به محل قرارگیری متد keyPressed(); رفته و تغییرات زیر را اعمال کنید:

  • بجای خط زیر
System.out.println("حرکت به چپ");

این خط را جایگزین کنید :

robot.moveLeft();
  • این خط
System.out.println("حرکت به راست");

با

robot.moveRight();
  • بجای خط زیر
System.out.println("پرش");

این خط را جایگزین کنید :

robot.jump();

و در متد keyReleased(); موارد زیر را :

۱-	System.out.println("توقف حرکت به چپ");
۲-	System.out.println("توقف حرکت به راست");

را با خط زیر جایگزین کنید:

robot.stop();

در مرحله دوم نیاز است در متد run(); ، متد بروز رسانی از کلاس ربات را فراخوانی کنیم:

robot.update();

@Override
	public void run() {
		while (true) {
			robot.update();
			repaint();// این خط متد پینت را فراخوانی میکند

			try {
				Thread.sleep(17);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

	}

هم اکنون ربات ما قادر به حرکت خواهد بود.

پایان روز چهارمJ

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *