فصل اول روز پنجم : پس زمینه

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

 

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

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

۱-۵-۱-            اضافه کردن پس زمینه متحرک

ابتدا یک کلاس جدید با نام Background.java در بسته codeandroid ایجاد خواهیم کرد:

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

  • متغبر هایی ایجاد خواهیم کرد که به کمک آن ها پس زمینه را دستکاری کنیم.
  • یک سازنده (constructor) ایجاد خواهیم کرد.
  • متد بروز رسانی update(); ایجاد خواهیم کرد تا به کمک آن تصویر پس زمینه را جابجا کنیم.
  • Getters/setters ها را اضافه خواهیم کرد. در بخش قبل به توضیح این ها پرداختیم.

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

  • ایجاد متغیرها

متغیر های bgX، bgY و speedX را ایجاد می کنیم.

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

خوب تنها کاری که لازم است انجام دهیم اضافه کردن این متغیر ها زیر خطی است که کلاس را تعریف کردیم:

package codeandroid;

public class Background {
	private int bgX, bgY, speedX;

}
  • تعریف سازنده (construcror)

در بخش قبل، برای نمایش ربات، یک شیء از این کلاس تعریف کردیم. در اینجا نیز برای نمایش تصویر پس زمینه لازم به ایجاد یک شیء از کلاس Background می باشد.

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

اما این سازنده دارای چه متغیرهای ورودی باشد و ما به چه متغیر هایی نیاز داریم؟

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

سازنده به شکل زیر خواهد بود:

public Background(int x, int y) {
		bgX = x;
		bgY = y;
		speedX = 0;
	}

متغیرهای bgX ، bgY و speedX که در بالا تعریف کردیم حالا مقادیر x ، y و ۰ را خواهند پذیرفت.

لذا وقتی ما بخواهیم در کلاس شروع یک شیء از این کلاس تعریف کنیم نیاز است تا x و y را در آن مشخص کنیم که به این معناست که در حقیقت bgX و bgY را مشخص کرده ایم. همچنین چون در ابتدا می خواهیم تصویر پس زمینه ثابت باشد و حرکتی نداشته باشد مقدار speedX را برابر صفر قرار دادیم.

  • متد update()

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

در این درس ما یک پس زمینه با قابلیت جابجایی(پیمایش) بی نهایت را تعریف خواهیم کرد. پس زمینه شامل دو تصویر با طول بلند می باشد که متناوباُ در حلقه مثل زیر جایشان را به هم می دهند:

۱،۲،۱،۲،۱،۲،…

برگردیم به متد بروز رسانی. ابتدا بیاید اهداف آن را ببینیم:

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

در این بازی ما از تصویرهایی با اندازه ۲۱۶۰ در ۴۸۰ استفاده خواهیم کرد. آن چه را که درباره حلقه بین این تصاویر گفتم (۱،۲،۱،…) رابه خاطر داشته باشید. سعی کنید تا بفهمید کدهای زیر چه کاری انجام می دهند:

public void update() {
		bgX += speedX;
		if (bgX <= -2160) {
			bgX += 4320;
		}
	}

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

خوب. حالا که در مورد این متد فکر کردید بیاید ببینیم چکاری انجام می دهد.

bgX+=speedX این عبارت مقدار bgX را با اضافه کردن سرعت به آن تغییر می دهد.آسون بود!!!

حالا عبارت شرطی: هنگامی که مختصات طولی پس زمینه پایین تر از منفی ۲۱۶۰ باشد(بیشتر از آن دیگر قابل مشاهده نیست)، ما آن را به ۴۳۲۰ پیکسل منتقل می کنیم (یعنی آن میتواند به ۲۱۶۰ پیکسل جلوتر از عکس در حال نمایش برود. اگر این چیزی که گفتم گیج کننده است، در انتها با اجرای برنامه قابل فهم تر خواهد شد).

  • متدهای Getters/setters

این خیلی آسان است. اکلیپس این کار را برای ما انجام خواهد داد.

در انتهای کلاس پس زمینه راست کلیک کرده و سپس روی  Source رفته و از زیر شاخه های آن Generate Getters and Setters  را انتخاب کنید.

حالا کلاس پس زمینه باید این شکلی باشد:

package codeandroid;

public class Background {
	private int bgX, bgY, speedX;

	public Background(int x, int y) {
		bgX = x;
		bgY = y;
		speedX = 0;
	}

	public void update() {
		bgX += speedX;
		if (bgX <= -2160) {
			bgX += 4320;
		}
	}

	public int getBgX() {
		return bgX;
	}

	public int getBgY() {
		return bgY;
	}

	public int getSpeedX() {
		return speedX;
	}

	public void setBgX(int bgX) {
		this.bgX = bgX;
	}

	public void setBgY(int bgY) {
		this.bgY = bgY;
	}

	public void setSpeedX(int speedX) {
		this.speedX = speedX;
	}

}

۱-۵-۲-            اضافه کردن پس زمینه متحرک (قسمت ۲)

با کامل شدن کلاس پس زمینه، حالا می توانیم دو تصویر پس زمینه را اضافه کنیم!

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

به طور معمول شیوه زیر را به خاطر داشته باشیدLاین شیوه، وقتی میخواهید خودتان بازیی را طراحی کنید بسیار کاربردی است).

وقتی ما می خواهیم یک شیء را به بازی اضافه کنیم، کار های زیر را انجام خواهیم داد:

  • ابتدا یک کلاس از آن شیء تعریف می کنیم. (این کار را در قسمت قبل با ایجاد کلاس پس زمینه یا ربات انجام دادیم)
  • در داخل کلاس شروع(اصلی) ابتدا اشیاء را زیر خطی که کلاس را تعریف کردیم وارد می کنیم.مثلا

private Background background;

  • در داخل متد run(); از کلاس شروع، متد بروز رسانی آن شیء را و فرامی خوانیم.
  • ما باید آن شیء را در متد paint(); ترسیم کنیم.

حالا بیایید این روند را برای تصویر پس زمینه دنبال کنیم. از ۱ شروع می کنیم چون ۰ را قبلا انجام دادیم:

  • تعریف متغیرها و مقدار دهی آن ها

برای تعریف متغیرها در کلاس شروع، به محل تعریف ما بقی متغیر ها رفته و زیر آن ها متغیر جدیدمان را به صورت زیر تعریف میکنیم:

private static Background bg1, bg2;

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

برای مقدار دهی این متغیرها:

به متد start() رفته و بالای خط robot=new Robot(); خطوط زیر را وارد کنید:

bg1 = new Background(0, 0);
	bg2 = new Background(2160, 0);

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

  • فراخوانی متد بروز رسانی

ما می خواهیم این دو شیء از کلاس پس زمینه در هر تکرار حلقه بازی به روزرسانی شوند. لذا به محل قرار گیری متد run(); رفته و پایین خط robot.updat(); کدهای زیر را وارد کنید:

bg1.update();
bg2.update();
  • رسم تصویر پس زمینه

در نهایت، ما می خواهیم پس زمینه را ترسیم کنیم. ابتدا، اجازه دهید تا یک متغیر از نوع image ایجاد کنیم تا این تصویر را در خود ذخیره کند. تصویر پس زمینه که با نام background.jpg در پوشه این مقاله قرار دارد یا می توانید از سایت codeandroid.ir دانلود کنید را داخل پوشه data که در بخش قبل ایجاد کردید بیاندازید.

بعد از این کار:

تغییر زیر را در خط مربوطه ایجاد کنید:

private Image image, character ,background;
  • حالا به جایی که تصویر character را در متد init() مقدار دهی کردیم رفته و این متغیر را نیز مقدار دهی کنید.
Character = getImage(base, "data/character.png");
background = getImage(base, "data/background.png");

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

Arg0.drawImage(background, bg1.getBgX(), bg1.getBgY(), this);
arg0.drawImage(background, bg2.getBgX(), bg2.getBgY(), this);
arg0.drawImage(character, robot.getCenterX() – ۶۱,
robot.getCenterY() – ۶۳, this);

۱-۵-۳-            کامل کردن حرکت ربات

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

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

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

Package codeandroid;

import java.awt.Graphics;

public class Robot {
	// تعریف ثابت ها
	final int JUMPSPEED = -15;
	final int MOVESPEED = 5;
	final int GROUND = 382;
	private int centerX = 100;
	private int  oolea = GROUND;
	private  oolean jumped = false;
	private  oolean movingLeft = false;
	private  oolean movingRight = false;
	private  oolean ducked = false;
	private static Background bg1 = ShoroClass.getBg1();
	private static Background bg2 = ShoroClass.getBg2();
	private int speedX = 0;
	private int speedY = 1;

	public void update() {
		// حرکت ربات و پس زمبنه
		if (speedX < 0) {
			centerX += speedX;

		}
		if (speedX == 0 || speedX < 0) {
			bg1.setSpeedX(0);
			bg2.setSpeedX(0);
		}
		if (centerX <= 200 && speedX > 0) {
			centerX += speedX;
		}
		if (speedX > 0 && centerX > 200) {
			bg1.setSpeedX(-MOVESPEED);
			bg2.setSpeedX(-MOVESPEED);
		}
		// بروز رسانی مختصات عرضی
		 oolea += speedY;
		if ( oolea + speedY >= GROUND) {
			 oolea = GROUND;
		}
		// مدیریت پرش
		if (jumped == true) {
			speedY += 1;
			if ( oolea + speedY >= GROUND) {
				 oolea = GROUND;
				speedY = 0;
				jumped = false;
			}
		}
		// ممانعت از خروج ربات از سمت چپ
		if (centerX + speedX <= 60) {
			centerX = 61;
		}
	}

	public void moveRight() {
		if (ducked == false) {
			speedX = MOVESPEED;
		}
	}

	public void moveLeft() {
		if (ducked == false) {
			speedX = -MOVESPEED;
		}
	}

	public void stopRight() {
		setMovingRight(false);
		stop();
	}

	public void stopLeft() {
		setMovingLeft(false);
		stop();
	}

	private void stop() {
		if (isMovingRight() == false && isMovingLeft() == false) {
			speedX = 0;
		}
		if (isMovingRight() == false && isMovingLeft() == true) {
			moveLeft();
		}
		if (isMovingRight() == true && isMovingLeft() == false) {
			moveRight();
		}
	}

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

	public int getCenterX() {
		return centerX;
	}

	public int getCenterY() {
		return  oolea;
	}

	public  oolean isJumped() {
		return jumped;
	}

	public int getSpeedX() {
		return speedX;
	}

	public int getSpeedY() {
		return speedY;
	}

	public void setCenterX(int centerX) {
		this.centerX = centerX;
	}

	public void setCenterY(int  oolea) {
		this.centerY =  oolea;
	}

	public void setJumped( oolean jumped) {
		this.jumped = jumped;
	}

	public void setSpeedX(int speedX) {
		this.speedX = speedX;
	}

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

	public  oolean isDucked() {
		return ducked;
	}

	public void setDucked( oolean ducked) {
		this.ducked = ducked;
	}

	public  oolean isMovingRight() {
		return movingRight;
	}

	public void setMovingRight( oolean movingRight) {
		this.movingRight = movingRight;
	}

	public  oolean isMovingLeft() {
		return movingLeft;
	}

	public void setMovingLeft( oolean movingLeft) {
		this.movingLeft = movingLeft;
	}
}

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

توضیح تغییرات انجام شده :

  • سه ثابت در ابتدای کلاس برای راحتی کار تعریف شده است.
  • من اکثر کدهای out.println() را با کدهایی که متدی را فرامی خوانند جایگزین کردم. قبلا اشاره شد که پس زمینه باید جابجا شود لذا کدهایی که این کار را انجام می دهند اضافه کردیم.
  • متغیرهای بولین که وضعیت حرکت را مشخص میکنند اضافه شد. هر زمان که کاربر کلیدی را فشار می دهد وضعیت این متغیرها تغییر می کند.

برای مثال اگر من کلید سمت چپ را نگه داشته باشم (movingLeft==true) و همچنین کلید سمت راست (movingRight==true) و سپس یکی از آن ها را رها کنم، با کدهای قبلی ربات می ایستاد اما با این کدها ابتدا چک میشود آیا کلید دیگری فشار داده شده و در صورت درستی به آن سمت می رود.

  • شما با اعمال این کدها در کلاس شروع شاهد خطا خواهید بود. نگران نباشید به زودی آن ها را بر طرف خواهیم نمود.

ایجاد تغییرات در کلاس شروع:

از آن جایی که تغییرات ما در بخش حرکت ربات بود لذا خطا ها در آن جا بوجود آمده اند. به محل قرار گیری دو متد keyPressed() و keyReleased(); رفته و تغییرات زیر را اعمال یا برای راحتی کار این دو متد را به صورت copy/paste جایگزین قبلی ها کنید.

@Override
	public void keyPressed(KeyEvent e) {
		switch (e.getKeyCode()) {
		case KeyEvent.VK_UP:
			System.out.println("حرکت به بالا");
			break;
		case KeyEvent.VK_DOWN:
			currentSprite = characterDown;
			if (robot.isJumped() == false) {
				robot.setDucked(true);
				robot.setSpeedX(0);
			}
			break;
		case KeyEvent.VK_LEFT:
			robot.moveLeft();
			robot.setMovingLeft(true);
			break;
		case KeyEvent.VK_RIGHT:
			robot.moveRight();
			robot.setMovingRight(true);
			break;
		case KeyEvent.VK_SPACE:
			robot.jump();
			break;
		}
	}

	@Override
	public void keyReleased(KeyEvent e) {
		switch (e.getKeyCode()) {
		case KeyEvent.VK_UP:
			System.out.println("توقف حرکت به بالا");
			break;
		case KeyEvent.VK_DOWN:
			currentSprite = character;
			robot.setDucked(false);
			break;
		case KeyEvent.VK_LEFT:
			robot.stopLeft();
			break;
		case KeyEvent.VK_RIGHT:
			robot.stopRight();
			break;
		case KeyEvent.VK_SPACE:
			break;
		}
	} 

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

۱-۵-۴-            حالت های مشترک

برای آن که به بازی کمی پویایی دهیم، ظاهر ربات را برای دو حالت پرش و نشستن تغییر خواهیم داد.

ابتدا نیاز است تا دو عکس با نام های jumped.png و down.png را از پوشه ای که این مقاله در آن قرار دارد برداشته و در پوشه data پروژه قرار دهید.

هم اکنون همانطور که برای تصویر اصلی ربات یک شیء از نوع image تعریف کردیم، برای این دو تصویر نیز احتیاج است دو شیء از این نوع تعریف کنیم. همچنین از آن جایی که الان سه تصویر برای ربات داریم لذا برای آن که مشخص شود کدام عکس فعلا در حال نمایش است یک شیء دیگر برای ذخیره تصویر فعلی درحال نمایش ایجاد میکنیم. برای این کار سه متغیر با نام های characterDown و characterJumped و currentSprite به صورت زیر و در محل تعریف تصویر اصلی ربات ایجاد میکنیم:

private Image image, character, background,
currentSprite, characterDown, characterJumped;

برای توضیح بیشتر در مورد متغیر currentSprite ، تا قبل از این برای رسم ربات از متغیر character استفاده می کردیم اما از این به بعد از این متغیر استفاده خواهیم کرد، که به صورت متغیر، تصویر های اصلی، نشسته و پریده را، با توجه به حالت ربات در خود ذخیره می کند.

  • خوب زمان آن است که این متغیر ها را مقدار دهی کنیم. به متد init(); رفته و خطوط جدید زیر را وارد کنید:
character = getImage(base, "data/character.png");
background = getImage(base, "data/background.png");
characterDown = getImage(base, "data/down.png");
characterJumped = getImage(base, "data/jumped.png");
currentSprite = character;
  • به متد run(); رفته و تغییرات زیر را اعمال کنید:
@Override
	public void run() {
		while (true) {
			robot.update();
			if (robot.isJumped()) {
				currentSprite = characterJumped;
			} else if (robot.isJumped() == false && robot.isDucked() == false) {
				currentSprite = character;
			}
			bg1.update();
			bg2.update();
			repaint();// این خط متد پینت را فراخوانی میکند

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

	}

این تغییرات بررسی می کند حالت فعلی ربات چیست و آن را به currentSprite اعمال می کند.

  • آخرین کاری که باید انجام بگیرد این است که در متد paint(); به جای رسم character ، currentSprite را رسم کنیم. برای این کار به این متد رفته و خط زیر را
arg0.drawImage(character, robot.getCenterX() – ۶۱, 
robot.getCenterY() – ۶۳, this);

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

arg0.drawImage(currentSprite, robot.getCenterX() – ۶۱,
robot.getCenterY() – ۶۳, this);

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

به عنوان آخرین کاری که باید انجام دهیم تا خطاهای کلاس ربات را برطرف کنیم اضافه کردن getters برای دو متغیر bg1 و bg2 در انتهای کلاس شروع می باشد. برای این کار کد های زیر را به انتهای کلاس شروع اضافه کنید:

public static Background getBg1() {
		return bg1;
	}

	public static Background getBg2() {
		return bg2;
	}

خوب به امید خدا اینجا پایان روز پنجم است. برنامه را اجرا کنید و از تغییرات جدید لذت ببرید.

دانلود منابع بازی :دانلود

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

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