Android游戏开发——飞机大作战 IG版

飞机大作战 IG版

    • 镇楼图
  • 先看为敬
  • 介绍

镇楼图

镇楼

先看为敬

在这里插入图片描述

介绍

IG拿了冠军,大家心中的喜悦就不用多说了,然鹅还木有结束,一组IG战队的王老板吃热狗的照片,也火爆了全网,上了热搜,网上看到好多拿王老板开涮的,其中就看到了一个用户发布的Java版王老板飞机大作,于是乎,闲来没事,我也写了一个android版的,不多说,接下来看代码。

首先,先创建一个所有物体的基类:Floating

public class Floating {/*** 图片  也就是物体的样子*/private Bitmap mBitmap;/*** w 宽 h高*/private int w, h;/*** 所占据的矩形区域*/private Rect mRect;/*** 中心点位置*/private Point mPoint;/*** 物体的唯一ID*/private long id;/*** tag  备用*/private Object tag;public Floating(Context context, @DrawableRes int img, int w, int h) {Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), img);this.mBitmap = XTools.BitmapUtil().zoomBmp(bitmap, w, h);this.w = w;this.h = h;mRect = new Rect();mPoint = new Point();id = System.currentTimeMillis();}public Bitmap getBitmap() {return mBitmap;}public int getW() {return w;}public int getH() {return h;}/*** 相对于之前的中心点做偏移** @param dx dx* @param dy dy*/public void addCenterPosition(int dx, int dy) {setCenterPosition(mPoint.x + dx, mPoint.y + dy);}/*** 设置中心点** @param x x* @param y y*/public void setCenterPosition(int x, int y) {mPoint.x = x;mPoint.y = y;int offsetW = w / 2;int offsetH = h / 2;//设置完中心点后,重新设置所在区域的矩形mRect.set(x - offsetW, y - offsetH, x + offsetW, y + offsetH);}public void setBitmap(Bitmap bitmap) {this.mBitmap = bitmap;}public Object getTag() {return tag;}public void setTag(Object tag) {this.tag = tag;}public Point getCenterPosition() {return mPoint;}public Rect getRect() {return mRect;}/*** h画到屏幕上** @param canvas canvas*/public void draw(Canvas canvas) {canvas.drawBitmap(getBitmap(), null, getRect(), null);}@Overridepublic int hashCode() {return String.valueOf(id).hashCode();}@Overridepublic boolean equals(Object obj) {return obj instanceof Floating && obj.hashCode() == this.hashCode();}
}

接下来创建一个自己的飞机:MyPlane

public class MyPlane extends Floating {private int w;private int h;public MyPlane(Context context, int w, int h) {super(context, R.drawable.bullet, w, h);this.w = w;this.h = h;}@Overridepublic int getW() {return w;}@Overridepublic int getH() {return h;}
}

接下来是敌机类:FoePlane

public class FoePlane extends Floating {/*** 是否被子弹销毁*/private boolean isDestroy;/*** 销毁时时间*/private long destroyTime;/*** 销毁后在屏幕中停留的时间*/private static final int DESTROY_TIME = 1000;private Context mContext;public FoePlane(Context context, int size) {super(context, R.drawable.fb_n, size, size);mContext = context;}public boolean isDestroy() {return isDestroy;}/*** @param destroy 是否销毁*/public void setDestroy(boolean destroy) {//如果之前没有被销毁,并且现在设置成销毁if (destroy && !isDestroy) {destroyTime = getTime();Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.fb);//设置销毁后的图片,即王老板吃到了热狗setBitmap(XTools.BitmapUtil().zoomBmp(bitmap, getW()));}isDestroy = destroy;}/*** 判断是否销毁并可以移除屏幕** @return 是否销毁并可以移除屏幕*/public boolean canDestroyAndRemove() {return isDestroy() && getTime() - destroyTime >= DESTROY_TIME;}private long getTime() {return System.currentTimeMillis();}
}

最后是子弹:Bullet

public class Bullet extends Floating {public Bullet(Context context, int w, int h) {super(context, R.drawable.bullet, w, h);}
}

搞定,最关键的一步,用SurfaceView绘制:

public class GameView extends SurfaceView implements SurfaceHolder.Callback {private SurfaceHolder mHolder;/*** 线程池*/private ScheduledExecutorService mThread;/*** 我方飞机(大热狗)*/private MyPlane myPlane;/*** GameView的宽高*/private int w, h;private Canvas mCanvas;/*** 出现在屏幕上的敌机(没有热狗的王老板)最大数量*/private static final int MAX_FOE_COUNT = 10;/*** 屏幕刷新间隔 毫秒*/private static final int REFRESH_TIME = 10;/*** 敌机(没有热狗的王老板)刷新间隔 毫秒*/private static final int FOE_REFRESH_TIME = 100;/*** 子弹(热狗)大小:宽度  这里默认长度是宽度的3倍*/private static final int BULLET_SIZE = 35;/*** 子弹(热狗)速度,每刷新一次屏幕,子弹移动的像素数量*/private static final int BULLET_SPEED = 10;/*** 发射子弹(热狗)的时间间隔*/private static final int BULLET_ADD_SPEED = 500;/*** 敌机(没有热狗的王老板)速度,每刷新一次屏幕,敌机移动的像素数量*/private static final int FOE_PLANE_SPEED = 3;/*** 敌机(没有热狗的王老板)集合,只有在集合里的敌机才会显示出来*/private List mFoeList;/*** 子弹(热狗)集合,只有在集合里的子弹才会显示出来*/private List mBulletList;/*** 敌机(没有热狗的王老板)大小*/private int mFoeSize;/*** 是否游戏结束*/private boolean gameOver;/*** 随机帮助类*/private Random r;public GameView(@NonNull Context context) {this(context, null);}public GameView(@NonNull Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public GameView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {super(context, attrs, defStyleAttr);r = new Random();// 设置可获得焦点setFocusable(true);setFocusableInTouchMode(true);// 设置常亮this.setKeepScreenOn(true);// 设置画布 背景透明setZOrderOnTop(true);mHolder = getHolder();mHolder.setFormat(PixelFormat.TRANSLUCENT);mHolder.addCallback(this);mFoeList = new ArrayList<>();mBulletList = new ArrayList<>();}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);this.w = w;this.h = h;int ph = w / 5;int pw = ph / 3;//根据窗口大小设置出自己的飞机大小myPlane = new MyPlane(getContext(), pw, ph);//根据窗口大小设置出敌机大小mFoeSize = w / 6;}private int x, y;/*** 是否点击在自己的飞机上*/private boolean touchOnPlant;@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:x = (int) event.getRawX();y = (int) event.getRawY();//如果点击的点在我们的飞机内if (myPlane.getRect().contains(x, y)) {//可以移动飞机touchOnPlant = true;}break;case MotionEvent.ACTION_MOVE://如果可以移动飞机if (touchOnPlant) {int dx = (int) (event.getRawX() - x);int dy = (int) (event.getRawY() - y);//设置飞机的偏移量myPlane.addCenterPosition(dx, dy);x = (int) event.getRawX();y = (int) event.getRawY();}break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL://不可以移动飞机touchOnPlant = false;break;default:break;}return true;}@Overridepublic void surfaceCreated(SurfaceHolder holder) {if (mThread == null) {//手动创建线程池,进行绘制mThread = new ScheduledThreadPoolExecutor(1, (ThreadFactory) Thread::new);mThread.scheduleWithFixedDelay(GameView.this::draw, 0, REFRESH_TIME, TimeUnit.MILLISECONDS);mThread.scheduleWithFixedDelay(GameView.this::addFoe, 0, FOE_REFRESH_TIME, TimeUnit.MILLISECONDS);mThread.scheduleWithFixedDelay(GameView.this::addBullet, 0, BULLET_ADD_SPEED, TimeUnit.MILLISECONDS);//初始化游戏again();}}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {//页面销毁,停止并回收线程池mThread.shutdown();mThread = null;}/*** 添加敌机*/private void addFoe() {//如果集合数量小于设置的最大敌机量if (mFoeList.size() < MAX_FOE_COUNT) {FoePlane foePlane = new FoePlane(getContext(), mFoeSize);//随机敌机出现的x点坐标int pos = r.nextInt(w);int min = mFoeSize;int max = w - mFoeSize;//为了防止敌机只在屏幕中出现一半,所以做了下面两个判断if (pos < min) {pos = min;}if (pos > max) {pos = max;}//设置敌机的出现坐标foePlane.setCenterPosition(pos, 0);mFoeList.add(foePlane);}}/*** 添加子弹*/private void addBullet() {Bullet bullet = new Bullet(getContext(), BULLET_SIZE, BULLET_SIZE * 3);Rect rect = myPlane.getRect();//设置子弹出现的坐标 从飞机的中间上方出现bullet.setCenterPosition(rect.centerX(), rect.top);mBulletList.add(bullet);}/*** 最重要的画*/private void draw() {try {mCanvas = mHolder.lockCanvas();if (mCanvas != null) {if (!gameOver) {//画背景drawBg();//画我的飞机drawMyPlane();//画敌机drawFoePlane();//画子弹drawBullet();//判断是否子弹击中敌机checkHit();//判断是否游戏结束checkGameOver();} else {drawGameOver();}}} catch (Exception ignored) {} finally {if (mCanvas != null) {mHolder.unlockCanvasAndPost(mCanvas);}}}/*** 画背景*/private void drawBg() {mCanvas.drawColor(Color.WHITE);}/*** 画我的飞机*/private void drawMyPlane() {myPlane.draw(mCanvas);}/*** 画敌机*/private void drawFoePlane() {if (mFoeList != null) {Iterator iterator = mFoeList.iterator();//遍历数组,这里之所以不用for循环,是因为遍历数组的时候不能remove元素,否则会抛异常while (iterator.hasNext()) {FoePlane foePlane = iterator.next();//画飞机foePlane.draw(mCanvas);//如果飞机没有被子弹打中销毁if (!foePlane.isDestroy()) {//让飞机向下飞一段距离foePlane.addCenterPosition(0, FOE_PLANE_SPEED);}//如果飞机的上边大于窗口下方,则飞机飞出了屏幕,那么移除就好了if (foePlane.getRect().top > h) {iterator.remove();}}}}/*** 画子弹*/private void drawBullet() {if (mBulletList != null) {Iterator iterator = mBulletList.iterator();//遍历数组,这里之所以不用for循环,是因为遍历数组的时候不能remove元素,否则会抛异常while (iterator.hasNext()) {Bullet bullet = iterator.next();//画子弹bullet.draw(mCanvas);//让子弹向下飞一段距离bullet.addCenterPosition(0, -BULLET_SPEED);//如果子弹的下边大于窗口上方,则子弹飞出了屏幕,那么移除就好了if (bullet.getRect().bottom < 0) {iterator.remove();}}}}/*** 判断是否子弹击中敌机*/private void checkHit() {Iterator foeIterator = mFoeList.iterator();//遍历数组,这里之所以不用for循环,是因为遍历数组的时候不能remove元素,否则会抛异常//遍历敌机while (foeIterator.hasNext()) {FoePlane foePlane = foeIterator.next();//获取敌机所在位置的矩形Rect foePlaneRect = foePlane.getRect();//如果敌机没有被子弹击中销毁,则进行下方判断子弹是否击中敌机的逻辑if (!foePlane.isDestroy()) {Iterator bulletIterator = mBulletList.iterator();//遍历子弹while (bulletIterator.hasNext()) {Bullet bullet = bulletIterator.next();//获取子弹所在位置的矩形Rect bulletRect = bullet.getRect();//判断子弹和敌机所在的矩形是否发生碰撞if (RectUtil.overlap(foePlaneRect, bulletRect)) {//如果子弹击中了敌机,设置敌机为销毁状态foePlane.setDestroy(true);//在屏幕中移除击中敌机的子弹bulletIterator.remove();}}}//判断敌机是否销毁并可以移除屏幕if (foePlane.canDestroyAndRemove()) {foeIterator.remove();}}}/*** 游戏结束后的画面*/private void drawGameOver() {mCanvas.drawColor(Color.BLACK);}/*** 判断是否游戏结束*/private void checkGameOver() {//遍历敌机for (Floating p : mFoeList) {//如果我方飞机和敌机所在矩形发生碰撞if (RectUtil.overlap(myPlane.getRect(), p.getRect())) {//设置游戏结束gameOver = true;if (mOnGameListener != null) {//在主线程回调XTools.UiUtil().runOnUIThread(() -> mOnGameListener.onGameOver());}break;}}}/*** 再来一次*/public void again() {mFoeList.clear();//设置初始位置myPlane.setCenterPosition(w / 2, h - myPlane.getH());gameOver = false;if (mOnGameListener != null) {//在主线程回调XTools.UiUtil().runOnUIThread(() -> mOnGameListener.onStart());}}private OnGameListener mOnGameListener;public interface OnGameListener {/*** 游戏开始*/void onStart();/*** 游戏失败*/void onGameOver();}public void setOnGameListener(OnGameListener onGameListener) {mOnGameListener = onGameListener;}
}

其中多次用到判断矩形碰撞的功能:

public class RectUtil {/*** 判断两个矩形是否碰撞** @param r1 矩形1* @param r2 矩形2* @return 是否碰撞*/public static boolean overlap(Rect r1, Rect r2) {//左if (r1.right < r2.left) {return false;}//右if (r1.left > r2.right) {return false;}//上if (r1.bottom < r2.top) {return false;}//下if (r1.top > r2.bottom) {return false;}return true;}
}

完成~~~~~~~~~~
完整源码请看:
https://github.com/610968110/Plane


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部