首页 > 代码库 > android:如何用一天时间,写出“飞机大战”这样的游戏!(无框架-SurfaceView绘制)

android:如何用一天时间,写出“飞机大战”这样的游戏!(无框架-SurfaceView绘制)

序言
作为一个android开发者,时常想开发一个小游戏娱乐一下大家,今天就说说,我是怎么样一天写出一个简单的“飞机大战”的.
体验地址:http://www.wandoujia.com/apps/edu.njupt.zhb.planegame
游戏分析
玩过“飞机大战”游戏的都知道,飞机大战中的主要“角色”有:
1.玩家飞机
2.敌方飞机
3.玩家飞机发送的子弹
4.敌方Boss飞机发送的子弹
我们需要控制的有
1.绘制屏幕内的角色
2.控制角色的逻辑,比如:敌方飞机与我方飞机的碰撞检测,我方飞机发射的子弹与敌方飞机之间的碰撞检测,敌方Boss飞机发射的子弹与我方飞机直接的碰撞检测等等。
资源:
要完成一个游戏,还要有资源的加载,比如飞机,子弹等图片的加载等,音效的加载。
游戏背景的绘制
其实是一张图,这张图可以首尾相接,也即是“卷轴”,原理就是卡马克卷轴算法的原理。
下面分析代码区

其实,抛开android平台,任何一个平台,做这样一个游戏,都需要这些逻辑。针对android平台,我们看一下,SurfaceView的绘制框架。直接贴代码:

package edu.njupt.zhb.game.view;

/**
 * 
 * @author Zheng Haibo
 * @webset: http://www.mobctrl.net
 * @android开发联盟QQ群:272209595
 */
public class PlaneView extends SurfaceView implements Callback, Runnable {

	private SurfaceHolder surfaceHolder;
	private long sleep_time = 16;//绘制周期
	private int screenHeight;
	private int screenWidth;
	private Thread thread;
	private Canvas canvas;
	private Paint paint;
	private GameScreen currentScreen;
	private int level = 0;
	private int backgroundSpeed = 1;

	public PlaneView(Context context) {
		super(context);
		System.out.println("debug:PlaneView()");
		surfaceHolder = this.getHolder();
		surfaceHolder.addCallback(this);
		surfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
		paint = new Paint();
		paint.setAntiAlias(true);
		paint.setDither(true);
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		System.out.println("debug:surfaceCreated");
		setZOrderOnTop(false);
		isGameOver = false;
		if (isPause) {
			return;
		}
		screenHeight = this.getHeight();
		screenWidth = this.getWidth();
		initPlane();
		thread = new Thread(this);
		thread.start();
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
		System.out.println("debug:surfaceChanged");
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		System.out.println("debug:surfaceDestroyed");
		if (lift > 0) {
			planeViewCallback.onGamePause();
		}
		isPause = true;
	}

	@Override
	public void run() {
		while (!isGameOver) {//控制绘制周期
			if (isPause) {
				try {
					Thread.sleep(sleep_time);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				continue;
			}
			long starttime = System.currentTimeMillis();
			drawScreen();
			long time = System.currentTimeMillis() - starttime;
			if (time < sleep_time) {
				try {
					Thread.sleep(sleep_time - time);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 绘制场景
	 */
	private void drawScreen() {
		canvas = surfaceHolder.lockCanvas();
		if (null == canvas) {
			return;
		}
		//清除
		canvas.drawColor(Color.WHITE, PorterDuff.Mode.CLEAR);
		paint.setAlpha(255);
		gameLogic();
		gameDraw();
		if (null != canvas) {
			surfaceHolder.unlockCanvasAndPost(canvas);
		}
	}

	/**
	 * 游戏逻辑
	 */
	private void gameLogic() {
		//TO DO 控制游戏逻辑
		...
	}

	private void gameDraw() {
	    //先绘制游戏背景
		drawBackground(backgroundSpeed * frameSeq);
		if (currentScreen == GameScreen.NORMAL) {
			synchronized (planes) {
				drawPlanes();
				drawBullets();
				drawMasterPlane();
			}
		} else if (currentScreen == GameScreen.BOSS) {
			drawBullets();
			drawBossPlane();
			drawBossBullets();
			drawMasterPlane();
		}
	}

	private void drawBossPlane() {
		if (null != bossPlane) {
			if (bossPlane.isClicked()) {// draw blast img
				bossPlane.onBlastDraw(canvas, paint);
				if (bossPlane.isBlastFrameEnd()) {
					bossPlane.setClicked(false);
				}
			}
			bossPlane.onDraw(canvas, paint);
		}
	}

	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		System.out.println("debug:onDraw");
	}

	@Override
	protected void onDetachedFromWindow() {
		super.onDetachedFromWindow();
		System.out.println("debug:onDetachedFromWindow...");
		isPause = false;
		isGameOver = true;
		//释放资源
		for (PlaneRes plane : planesRes) {
			plane.getBitmap().recycle();
		}
		for (BulletRes bulletRes : bulletsRes) {
			bulletRes.getBitmap().recycle();
		}
	}

	/**
	* 用户交互
	*/
	@Override
	public boolean onTouchEvent(MotionEvent e) {
		int x = (int) e.getX();
		int y = (int) e.getY();
		switch (e.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if (masterPlane.isContainPoint(x, y)) {
				isMove = true;
			}
			break;
		case MotionEvent.ACTION_MOVE:
			if (isMove) {
				synchronized (masterPlane) {
					masterPlane.updatePosition(x, y);//控制玩家飞机的移动
				}
			}
			break;
		case MotionEvent.ACTION_UP:
			isMove = false;
			break;
		}
		return true;
	}
}

对于背景的绘制,其实是循环绘制一张图:本游戏的绘制逻辑:

private void drawBackground(int yOffset) {
		yOffset %= screenHeight;
		if (yOffset == 0) {
			canvas.drawBitmap(backgroundBmp, 0, 0, paint);
		} else {
			canvas.drawBitmap(backgroundBmp, new Rect(0,
					screenHeight - yOffset, screenWidth, screenHeight),
					new Rect(0, 0, screenWidth, yOffset + 1), paint);
			canvas.drawBitmap(backgroundBmp, new Rect(0, 0, screenWidth,
					screenHeight - yOffset), new Rect(0, yOffset, screenWidth,
					screenHeight), paint);
		}
	}

然后,我们只需要在一个布局上,将PlaneView添加进去即可:

如:

planeView = new PlaneView(this);
		planeView.setPlaneViewCallback(this);
		planeView.setGameOverCallback(this);
		planeView.isMediaOpen = this.isMediaOpen;
		LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.MATCH_PARENT);
		rl_plane.addView(planeView, lp);

未完待续。。。。。