سورس بازی Breakout در اندروید

چهارشنبه 27 اردیبهشت 1396

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

سورس بازی Breakout در اندروید

در این بازی لایه ی xml به صورت زیر خواهد بود:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    />
</LinearLayout>

و کلاس جاوای آن به صورت زیر خواهد بود:


import java.util.ArrayList;
import java.util.List;

import org.anddev.andengine.audio.sound.Sound;
import org.anddev.andengine.audio.sound.SoundFactory;
import org.anddev.andengine.engine.Engine;
import org.anddev.andengine.engine.camera.Camera;
import org.anddev.andengine.engine.handler.IUpdateHandler;
import org.anddev.andengine.engine.options.EngineOptions;
import org.anddev.andengine.engine.options.EngineOptions.ScreenOrientation;
import org.anddev.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.entity.scene.background.ColorBackground;
import org.anddev.andengine.entity.sprite.AnimatedSprite;
import org.anddev.andengine.entity.sprite.Sprite;
import org.anddev.andengine.entity.text.ChangeableText;
import org.anddev.andengine.entity.util.FPSLogger;
import org.anddev.andengine.input.touch.TouchEvent;
import org.anddev.andengine.opengl.font.Font;
import org.anddev.andengine.opengl.texture.Texture;
import org.anddev.andengine.opengl.texture.TextureOptions;
import org.anddev.andengine.opengl.texture.region.TextureRegion;
import org.anddev.andengine.opengl.texture.region.TextureRegionFactory;
import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
import org.anddev.andengine.ui.activity.BaseGameActivity;
import org.anddev.andengine.util.Debug;

import android.graphics.Color;
import android.graphics.Typeface;

public class Breakout extends BaseGameActivity
{

	// the width of the game screen
	private static final int CAMERA_WIDTH = 720;
	// the height of the game screen
	private static final int CAMERA_HEIGHT = 480;
	// the speed of the game
	private static final float DEMO_VELOCITY = 200.0f;

	// variable used to manipulate the camera
	private Camera mCamera;
	// texture for the entire screen
	private Texture mTexture;
	// texture region for the ball
	private TextureRegion mPongTextureRegion;
	// texture region for the paddle
	private TextureRegion mBoxTextureRegion;
	// texture region for the tiles
	private TiledTextureRegion mBallTextureRegion;
	// List that holds all of the boxes
	private List<Sprite> boxSpritesList = new ArrayList<Sprite>();
	// holds the font for the scoreboard
	private Font mFont;
	// holds the texture for the scoreboard
	private Texture mFontTexture;
	// the actual text of the scoreboard
	ChangeableText scoreText;
	// initialize score to 0 when the game loads
	private int score = 0;

	// sounds used in the program
	private Sound mTileHit;
	private Sound mPaddleHit;
	private Sound mDeath;

	// the point where the engine starts drawing the tiles
	int startX;
	// the width of the blocks to be drawn
	final int blockWidth = 32;
	// number of rows to draw on the scene
	final int numRows = 5;

	// this is run when the game is first loaded.
	// i.e. when the application is launched
	@Override
	public Engine onLoadEngine()
	{
		// sets the camera to the dimensions declared above
		this.mCamera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
		// returns an engine with the properties set by the passed arguments
		// below
		// true: fullscreen
		// ScreenOrientation.LANDSCAPE: sets to landscape orientation
		// RatioResolutionPolicy({width},{height}): sets the software renderer's
		// resolution policy
		// this.mCamera: sets to this class' instantiation of the camera
		return new Engine(new EngineOptions(true, ScreenOrientation.LANDSCAPE,
				new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT),
				this.mCamera).setNeedsSound(true));
	}

	// runs when resources are loaded
	// that is, when the actual pictures and textures are being loaded into the
	// game
	@Override
	public void onLoadResources()
	{
		initTextures();

		initSounds();

		setupScoreBoard();
	}

	@Override
	public Scene onLoadScene()
	{
		this.mEngine.registerUpdateHandler(new FPSLogger());

		// creates a Scene with 1 player
		final Scene scene = new Scene(1);
		// this is an example of how to place a background image rather than a
		// solid color
		// scene.setBackground(new RepeatingSpriteBackground(CAMERA_WIDTH,
		// CAMERA_HEIGHT, this.mEngine.getTextureManager(), new
		// AssetTextureSource(this, "gfx/background.png")));
		scene.setBackground(new ColorBackground(0.1f, 0.5f, 0.5f));

		// sets the score display with the given font
		scoreText = new ChangeableText(0, CAMERA_HEIGHT - 50, this.mFont,
				"Score: ", "Score: xxxxx".length());

		// adds the score display to the screen
		scene.getTopLayer().addEntity(scoreText);

		// these are used to set the position for the paddle sprite
		final int centerX = (CAMERA_WIDTH - this.mPongTextureRegion.getWidth()) / 2;
		final int centerY = CAMERA_HEIGHT - this.mPongTextureRegion.getHeight();
		final Sprite paddle = setupPaddle(centerX, centerY);

		// creates the ball object that bounces around
		final Ball ball = new Ball(CAMERA_WIDTH / 2, CAMERA_HEIGHT / 3,
				this.mBallTextureRegion);
		// sets the speed of the ball according to the demo velocity
		ball.setVelocity(DEMO_VELOCITY, DEMO_VELOCITY);

		// adds the paddle and the ball to the current scene
		scene.getTopLayer().addEntity(paddle);
		scene.getTopLayer().addEntity(ball);

		fillBoxArea(scene);

		// adds a listener for the paddle in order to receive input
		scene.registerTouchArea(paddle);
		scene.setTouchAreaBindingEnabled(true);

		// handles all updates (frames) for the game
		scene.registerUpdateHandler(new IUpdateHandler()
		{
			@Override
			public void reset()
			{
			}

			@Override
			public void onUpdate(final float pSecondsElapsed)
			{
				// detects if the ball falls below the paddle
				// i.e. lose condition
				// this subtracts 1 point from the score
				// this may be changed to have more drastic consequences such as
				// restarting the entire game
				if (ballBelowPaddle(paddle, ball))
				{
					playerDeath(ball);
				}

				// reflect the ball when it hits the paddle
				if (paddle.collidesWith(ball))
				{
					Breakout.this.mPaddleHit.play();
					ball.setVelocityY(-DEMO_VELOCITY);
				}

				final List<Sprite> toRemoved = checkForCollisions(ball);

				removeSprite(scene, toRemoved);
			}

		});

		return scene;
	}

	private List<Sprite> checkForCollisions(final Ball ball)
	{
		final List<Sprite> toRemoved = new ArrayList<Sprite>();
		boxCheckLoop: for (Sprite box : boxSpritesList)
		{
			// if the ball collides with the box
			// AND it hits the bottom specifically
			if (ball.collidesWith(box))
			{
				// increment the score and change the text
				score++;
				scoreText.setText("Score: " + score);

				// add the box to the list of sprites to remove
				toRemoved.add(box);
				// play the "tile hit" sound
				Breakout.this.mTileHit.play();
				checkCollisionDirection(ball, box);

				break boxCheckLoop;
			}
		}
		return toRemoved;
	}

	private void removeSprite(final Scene scene, final List<Sprite> toRemoved)
	{
		// this runs once per update/frame
		Breakout.this.runOnUpdateThread(new Runnable()
		{

			@Override
			public void run()
			{
				for (Sprite sprite : toRemoved)
				{
					// remove all of the sprites in the "toRemoved" list
					scene.getTopLayer().removeEntity(sprite);
					boxSpritesList.remove(sprite);
				}
			}
		});
	}

	private void fillBoxArea(final Scene scene)
	{
		// this places blocks until there is no more room on the screen
		for (int h = 0; h < numRows * blockWidth; h += blockWidth)
		{
			startX = 10;
			for (int i = 0; i < Math.floor(CAMERA_WIDTH / blockWidth); i++)
			{
				Sprite boxSprite = new Sprite(startX, 10 + h,
						this.mBoxTextureRegion);
				boxSpritesList.add(boxSprite);
				startX += blockWidth;
			}
		}

		// adds all boxes to the screen
		for (Sprite boxSprite : boxSpritesList)
		{
			scene.getTopLayer().addEntity(boxSprite);
		}
	}

	private Sprite setupPaddle(final int centerX, final int centerY)
	{
		final Sprite paddle = new Sprite(centerX, centerY,
				this.mPongTextureRegion)
		{
			@Override
			public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
					final float pTouchAreaLocalX, final float pTouchAreaLocalY)
			{
				// this gets the X coordinate of the finger touch,
				// then subtracts the width of the paddle,
				// setting the paddle location to the bottom even if
				// the touch is not directly on top of the paddle.
				this.setPosition(pSceneTouchEvent.getX() - this.getWidth() / 2,
						centerY);
				return true;
			}
		};
		return paddle;
	}

	@Override
	public void onLoadComplete()
	{

	}

	// used to hold game logic information for the ball
	private static class Ball extends AnimatedSprite
	{
		public Ball(final float pX, final float pY,
				final TiledTextureRegion pTextureRegion)
		{
			super(pX, pY, pTextureRegion);
		}

		// occurs every update/frame
		// this handles the bouncing on the borders of the window
		@Override
		protected void onManagedUpdate(final float pSecondsElapsed)
		{
			// if the ball hits the left border
			if (this.mX < 0)
			{
				this.setVelocityX(DEMO_VELOCITY);
			} //
				// if the ball hits the right border
			else if (this.mX + this.getWidth() > CAMERA_WIDTH)
			{
				this.setVelocityX(-DEMO_VELOCITY);
			}

			// if the ball hits the bottom border
			if (this.mY < 0)
			{
				this.setVelocityY(DEMO_VELOCITY);
			} //
				// if the ball hits the top border
			else if (this.mY + this.getHeight() > CAMERA_HEIGHT)
			{
				this.setVelocityY(-DEMO_VELOCITY);
			}

			super.onManagedUpdate(pSecondsElapsed);
		}
	}

	private void initSounds()
	{
		// sets the root for the sound effects to the mfx folder
		SoundFactory.setAssetBasePath("mfx/");
		// initialize all the sounds
		try
		{
			this.mTileHit = SoundFactory.createSoundFromAsset(
					this.mEngine.getSoundManager(), this, "crush.ogg");
			this.mPaddleHit = SoundFactory.createSoundFromAsset(
					this.mEngine.getSoundManager(), this, "paddlehit.ogg");
			this.mDeath = SoundFactory.createSoundFromAsset(
					this.mEngine.getSoundManager(), this, "death.ogg");
		} catch (Exception e)
		{
			Debug.e("Error: " + e);
		}
	}

	private void initTextures()
	{
		// initializes all of the texture regions
		// do not change this area unless you know what you're doing
		this.mTexture = new Texture(256, 32,
				TextureOptions.BILINEAR_PREMULTIPLYALPHA);
		this.mPongTextureRegion = TextureRegionFactory.createFromAsset(
				this.mTexture, this, "gfx/pong.png", 0, 0);
		this.mBallTextureRegion = TextureRegionFactory.createTiledFromAsset(
				this.mTexture, this, "gfx/ball.png", 97, 0, 1, 1);
		this.mBoxTextureRegion = TextureRegionFactory.createFromAsset(
				this.mTexture, this, "gfx/box.png", 114, 0);
		// end initialize textures

		// loads the texture for the entire screen
		this.mEngine.getTextureManager().loadTexture(this.mTexture);
	}

	private void setupScoreBoard()
	{
		// sets up font for scoreboard
		this.mFontTexture = new Texture(256, 256, TextureOptions.BILINEAR);
		this.mFont = new Font(this.mFontTexture, Typeface.create(
				Typeface.DEFAULT, Typeface.BOLD), 48, true, Color.BLACK);
		this.mEngine.getTextureManager().loadTexture(this.mFontTexture);
		this.mEngine.getFontManager().loadFont(this.mFont);
	}

	private void checkCollisionDirection(final Ball ball, Sprite box)
	{
		// the tolerance for collision detection
		float eps = 0.2f;
		// coordinates for top left of the ball
		float aX = ball.getX();
		float aY = ball.getY();
		// height and width of the ball
		float aH = ball.getBaseHeight();
		float aW = ball.getBaseWidth();
		// coordinates for top left of the collided box
		float bX = box.getX();
		float bY = box.getY();
		// height and width of the collided box
		float bH = box.getBaseHeight();
		float bW = box.getBaseWidth();

		// bottom of ball hits the top of a block
		if (Math.abs((aY + aH) - bY) <= bH * eps)
		{
			ball.setVelocityY(-ball.getVelocityY());
		} // top of ball hits the bottom of a block
		else if (Math.abs(aY - (bH + bY)) <= bH * eps)
		{
			ball.setVelocityY(-ball.getVelocityY());
		} // right side of ball hits the left side of a block
		else if (Math.abs((aX + aW) - bX) <= bW * eps)
		{
			ball.setVelocityX(-ball.getVelocityX());
		} // left side of ball hits the right side of a block
		else if (Math.abs(aX - (bW + bX)) <= bW * eps)
		{
			ball.setVelocityX(-ball.getVelocityX());
		}
	}

	private void playerDeath(final Ball ball)
	{
		Breakout.this.mDeath.play();
		score -= 1;
		scoreText.setText("Score: " + score);
		ball.setPosition(CAMERA_WIDTH / 2, CAMERA_HEIGHT / 3);
	}

	private boolean ballBelowPaddle(final Sprite paddle, final Ball ball)
	{
		return ball.getY() + ball.getBaseHeight() > paddle.getY() * 1.05;
	}
}

در کلاس بالا رویدادی است که شما با آن صفحه ی گوشی خود را مدیریت می کنید

خروجی بازی به صورت زیر خواهد بود:

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

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

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید