Android 模仿flabby bird游戏开发
发布日期:2021-06-30 22:37:07 浏览次数:2 分类:技术文章

本文共 37706 字,大约阅读时间需要 125 分钟。

一、示意图:

1)开始画面:

这里写图片描述
2)游戏中画面:
这里写图片描述
3)结束画面:
这里写图片描述

二、分析:

1、游戏中的每个元素都可封装成对象,

1)开始按钮与结束按钮可封装成GameButton对象:
属性有:有坐标x,y;有原图与按下后的图片;另外还有判断是否点击了的属性
方法有:draw方法,用来绘制自己; isClick判断是否被点击了
另外提供点击的监听事件OnButtonClickListener
2)Bird对象:
属性有图片,坐标,位置,大小等
方法有draw方法,resetHeigt方法(用于在游戏结束后恢复其高度)
3)Floor地板对象:
属性有:坐标,BitmapShader填充物
方法有:draw方法,游戏运行时不断绘制,看起来想不断的移动
4)Grade分数对象
属性有:分数图片,宽高,单个分数的矩阵
方法有:draw方法,绘制分数从左到右绘制,每绘制一个分数,移动到下个分数,宽度是单个分数的宽度
5)管道对象
属性有:上管道高度,上管道与下管道之间的距离,图片
方法有:draw方法,根据随机数绘制管道;touchBird方法,判断小鸟是否触碰到了管道

2、游戏绘制在SurfaceView界面上

1)创建类FlyBirdView并继承SurfaceView 实现接口Callback, Runnable
2)在子线程里绘制绘制上面的对象
3)在onSizeChanged方法里初始化所有的对象,因为在这个方法里控件的宽高固定了下来
4)在构造函数里初始化图片等基本属性
3、除了绘制之外,游戏是有状态的,一般来说,游戏有三种状态:等待状态、运行状态和结束状态
在这里我们使用emum来设值,并且进入游戏时默认是等待状态
1)在等待状态里最主要绘制开始按钮
3)运行状态主要是对管道、地板等对象的不断绘制
3)结束状态绘制gameovew和重新开始的按钮

三、实体类代码:

1)Bird类:

/** * 鸟的实体类 *  * @Project App_View * @Package com.android.view.flybird * @author chenlin * @version 1.0 * @Date 2014年5月6日 */public class Bird {
public static final float RADIO_POS_HEIGHT = 1 / 3f;// 鸟所在的默认屏幕高度 private static final int BIRD_SIZE = 30; // 鸟的宽度 30dp private Bitmap mBirdBitmap;// 鸟图片 private int mHeight;// 鸟高度 private int mWidth;// 鸟宽度 private RectF mBirdRectF; // 鸟所在的范围 private int x, y;// 所在坐标 private int mGameHeight; public Bird(Context context, Bitmap bitmap, int gameWidth, int gameHeight) { this.mBirdBitmap = bitmap; this.mWidth = UITools.dip2px(context, BIRD_SIZE); this.mHeight = (int) (mWidth * 1.0f / bitmap.getWidth() * bitmap.getHeight()); // 给坐标赋值 this.x = gameWidth / 2 - bitmap.getWidth() / 2; this.y = (int) (gameHeight * RADIO_POS_HEIGHT); this.mBirdRectF = new RectF(); this.mGameHeight = gameHeight; } /** * 绘制鸟 * * @param canvas */ public void draw(Canvas canvas) { mBirdRectF.set(x, y, x + mWidth, y + mHeight); canvas.drawBitmap(mBirdBitmap, null, mBirdRectF, null); } public void resetHeigt() { y = (int) (mGameHeight * RADIO_POS_HEIGHT); } public int getY() { return y; } public void setY(int y) { this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getWidth() { return mWidth; } public int getHeight() { return mHeight; }}

2)Floor类

/** * 地板 *  * @Project App_View * @Package com.android.view.flybird * @author chenlin * @version 1.0 * @Date 2014年5月6日 */public class Floor {
// 地板位置游戏面板高度的4/5到底部 private static final float FLOOR_Y_POS_RADIO = 4 / 5F; // height of 4/5 private int x, y;// 坐标 private BitmapShader mBitmapShader;// 填充物 private int mGameWidth;// 地板宽高 private int mGameHeight; public Floor(int gameWidth, int gameHeight, Bitmap bgBitmap) { this.mGameHeight = gameHeight; this.mGameWidth = gameWidth; this.y = (int) (mGameHeight * FLOOR_Y_POS_RADIO); mBitmapShader = new BitmapShader(bgBitmap, TileMode.CLAMP, TileMode.CLAMP); } /** * 绘制自己 * * @param canvas */ public void draw(Canvas canvas, Paint paint) { // 进行平移,如果移出的部分超过屏幕的宽度,就重新让坐标移动到源位置 if (-x > mGameWidth) { x = x % mGameWidth; } /** * save() : 用来保存Canvas的状态,save()方法之后的代码,可以调用Canvas的平移、放缩、旋转、裁剪等操作! */ canvas.save(Canvas.MATRIX_SAVE_FLAG); //平移到指定位置 canvas.translate(x, y); paint.setShader(mBitmapShader); canvas.drawRect(x, 0, -x + mGameWidth, mGameHeight - y, paint); /** * restore():用来恢复Canvas之前保存的状态(可以想成是保存坐标轴的状态),防止save()方法代码之后对Canvas执行的操作,继续对后续的绘制会产生影响,通过该方法可以避免连带的影响 */ canvas.restore(); paint.setShader(null); } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; }}

3)GameButton类

/** * 开始按钮 * @Project    App_View * @Package    com.android.view.flybird * @author     chenlin * @version    1.0 * @Date       2014年5月19日 */public class GameButton {
private int x;//所在坐标 private int y; private Bitmap mBitmap;//原来按钮图片 private Bitmap mPressBitmap;//按下的按钮图片 private RectF mRectF; // 按钮所在的范围 private boolean isClick = false;//判断是否被点击了 public GameButton(Bitmap bitmap, Bitmap pressBitmap, int gameWidth, int gameHeight){ this.mBitmap = bitmap; this.mPressBitmap = pressBitmap; this.x = gameWidth/2-mBitmap.getWidth()/2;//左边距 this.y = gameHeight;//初始的位置在屏幕最下端 this.mRectF = new RectF(); } /** * 绘制自己 * @param canvas */ public void draw(Canvas canvas){ canvas.save(Canvas.MATRIX_SAVE_FLAG); mRectF.set(x, y, x + mBitmap.getWidth(), y + mBitmap.getHeight()); if (isClick) { canvas.drawBitmap(mBitmap, null , mRectF, null); }else { canvas.drawBitmap(mPressBitmap, null , mRectF, null); } canvas.restore(); } /** * 判断按钮是否可点击 * @return */ public boolean isClick(int newX, int newY) { Rect rect = new Rect(x, y, x + mPressBitmap.getWidth(), y + mPressBitmap.getHeight()); isClick = rect.contains(newX, newY); return isClick; } public void setClick(boolean isClick) { this.isClick = isClick; } /** * 提供向外的点击事件 */ public void click(){ if (mListener != null) { mListener.click(); } } private OnButtonClickListener mListener; public interface OnButtonClickListener{
void click(); } public void setOnButtonClickListener(OnButtonClickListener listener){ this.mListener = listener; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; }}

4)分数Grade类:

/** * 分数 *  * @Project App_View * @Package com.android.view.flybird * @author chenlin * @version 1.0 * @Date 2016年5月16日 */public class Grade {
private Bitmap[] mNumBitmap;//所有分数的图片集合 private RectF mSingleNumRectF;//单个分数的矩阵 private int mSingleGradeWidth;//单个分数的宽度 private int mGameWidth; private int mGameHeight; public Grade(Bitmap[] numBitmap, RectF rectF, int singleGradeWidth, int gameWidth, int gameHeight) { this.mNumBitmap = numBitmap; this.mSingleNumRectF = rectF; this.mSingleGradeWidth = singleGradeWidth; this.mGameWidth = gameWidth; this.mGameHeight = gameHeight; } /** * 绘制 * * @param mCanvas, int gameWidth */ public void draw(Canvas canvas, int score) { String grade = score + ""; canvas.save(Canvas.MATRIX_SAVE_FLAG); //移动屏幕的中间,1/8的高度 canvas.translate(mGameWidth / 2 - grade.length() * mSingleGradeWidth / 2, 1f / 8 * mGameHeight); // 依次绘制分数 for (int i = 0; i < grade.length(); i++) { //100,先绘制1, String numStr = grade.substring(i, i + 1); int num = Integer.valueOf(numStr); canvas.drawBitmap(mNumBitmap[num], null, mSingleNumRectF, null); //移动到下一个分数0 canvas.translate(mSingleGradeWidth, 0); } canvas.restore(); }}

5)管道类Pipe:

/** * 管道实体 *  * @Project App_View * @Package com.android.view.flybird * @author chenlin * @version 1.0 * @Date 2014年5月7日 */public class Pipe {
private static final float RADIO_BETWEEN_UP_DOWN = 1 / 5F;// 上下管道间的距离 private static final float RADIO_MAX_HEIGHT = 2 / 5F;// 上管道的最大高度 private static final float RADIO_MIN_HEIGHT = 1 / 5F;// 上管道的最小高度 private int x;// 管道x坐标 private int mTopHeight;// 上管道高度 private int mMargin;// 上下管道的距离 private Bitmap mTopBitmap;// 上管道图片 private Bitmap mBottomBitmap;// 下管道图片 private static Random random = new Random(); public Pipe(Context context, int gameWidth, int gameHeight, Bitmap topBitmap, Bitmap bottomBitmap) { mMargin = (int) (gameHeight * RADIO_BETWEEN_UP_DOWN); // 默认从最左边出现 ,小鸟往前飞时,管道往左移动 x = gameWidth; mTopBitmap = topBitmap; mBottomBitmap = bottomBitmap; // 高度随机 randomHeight(gameHeight); } /** * 随机生成一个高度 */ private void randomHeight(int gameHeight) { mTopHeight = random.nextInt((int) (gameHeight * (RADIO_MAX_HEIGHT - RADIO_MIN_HEIGHT))); mTopHeight = (int) (mTopHeight + gameHeight * RADIO_MIN_HEIGHT); } public void draw(Canvas canvas, RectF rect) { canvas.save(Canvas.MATRIX_SAVE_FLAG); // rect为整个管道,假设完整管道为100,需要绘制20,则向上偏移80 rect.bottom管的实际高度 canvas.translate(x, -(rect.bottom - mTopHeight)); // 绘制上管道 canvas.drawBitmap(mTopBitmap, null, rect, null); // 下管道,偏移量为,上管道高度+margin canvas.translate(0, rect.bottom + mMargin); //canvas.translate(0, mTopHeight + mMargin); //绘制下管道 canvas.drawBitmap(mBottomBitmap, null, rect, null); canvas.restore(); } /** * 判断和鸟是否触碰 * @param bird * @return */ public boolean touchBird(Bird bird){ /** * 如果bird已经触碰到管道 */ if (bird.getX() + bird.getWidth() > x && (bird.getY() < mTopHeight || bird.getY() + bird.getHeight() > mTopHeight + mMargin)) { return true; } return false; } public int getX() { return x; } public void setX(int x) { this.x = x; }}

四、具体实现:

1)我们先从简单的开始,实现游戏最基本的配置

public class FlyBirdView extends SurfaceView implements Callback, Runnable {
private SurfaceHolder mHolder; // private Thread mThread; private ExecutorService mPool; private Canvas mCanvas; private boolean isRunnging;// 是否运行 // 二.设置背景 private Bitmap mBgBitmap; //当前View的尺寸 private int mWidth; private int mHeight; private RectF mGamePanelRect = new RectF(); // ----构造函数处理--------------------------------------------- public FlyBirdView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FlyBirdView(Context context) { this(context, null); } public FlyBirdView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { // -初始化holer----------------------- mHolder = getHolder(); mHolder.addCallback(this); setZOrderOnTop(true); // 设置画布 背景透明 mHolder.setFormat(PixelFormat.TRANSLUCENT); // --焦点设置---------------------------- setFocusable(true); // 设置触屏 setFocusableInTouchMode(true); // 设置常亮 setKeepScreenOn(true); // --背景设置-------------------------------- mGamePanelRect = new RectF(); mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bgbird); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { mWidth = w; mHeight = h; mGamePanelRect.set(0, 0, w, h); super.onSizeChanged(w, h, oldw, oldh); } @Override public void run() { while (isRunnging) { long start = System.currentTimeMillis(); draw(); long end = System.currentTimeMillis(); if (start - end < 50) { SystemClock.sleep(50 - (start - end)); } } } private void draw() { try { if (mHolder != null) { mCanvas = mHolder.lockCanvas(); if (mCanvas != null) { //绘制背景 drawBg(); } } } catch (Exception e) { e.printStackTrace(); } finally { if (mHolder != null && mCanvas != null) { mHolder.unlockCanvasAndPost(mCanvas); } } } private void drawBg() { mCanvas.drawBitmap(mBgBitmap,null, mGamePanelRect, null); } // ---callback监听------------------------------------------------------ @Override public void surfaceCreated(SurfaceHolder holder) { // -线程处理-------------------------- isRunnging = true; mPool = Executors.newFixedThreadPool(5); // mThread = new Thread(this); // mThread.start(); mPool.execute(this); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 通知关闭线程 isRunnging = false; }}

2)在画布上添加对象

/** * 游戏主界面的绘制 *  * @Project App_View * @Package com.android.view.flybird * @author chenlin * @version 1.0 * @Date 2014年5月7日 */public class FlyBirdView1 extends SurfaceView implements Callback, Runnable {
private SurfaceHolder mHolder; // private Thread mThread; private ExecutorService mPool; private Canvas mCanvas; private boolean isRunnging;// 是否运行 // 二.设置背景 private Bitmap mBgBitmap; // 当前View的尺寸 private int mWidth; private int mHeight; private RectF mGamePanelRect = new RectF(); // 三、设置鸟 private Bird mBird; private Bitmap mBirdBitmap; // 四、添加地板 private Floor mFloor; private Bitmap mFloorBitmap; // 五、添加管道 /** 管道的宽度 60dp */ private static final int PIPE_WIDTH = 60; private Pipe mPipe; /** 上管道的图片 */ private Bitmap mPipeTopBitmap; /** 下管道的图片 */ private Bitmap mPipeBotBitmap; /** 管道的宽度 */ private int mPipeWidth; /** 管道矩阵 */ private RectF mPipeRectF; /** 管道集合 */ private List
mPipeList; // 六、添加分数 /** 分数 */ private final int[] mNums = new int[] { R.drawable.n0, R.drawable.n1, R.drawable.n2, R.drawable.n3, R.drawable.n4, R.drawable.n5, R.drawable.n6, R.drawable.n7, R.drawable.n8, R.drawable.n9 }; private Grade mGrade; /** 分数图片组 */ private Bitmap[] mNumBitmap; /** 分值 */ private int mScore = 100; /** 单个数字的高度的1/15 */ private static final float RADIO_SINGLE_NUM_HEIGHT = 1 / 15f; /** 单个数字的宽度 */ private int mSingleGradeWidth; /** 单个数字的高度 */ private int mSingleGradeHeight; /** 单个数字的范围 */ private RectF mSingleNumRectF; // ----构造函数处理--------------------------------------------- public FlyBirdView1(Context context, AttributeSet attrs) { super(context, attrs); init(); } public FlyBirdView1(Context context) { super(context); init(); } public FlyBirdView1(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { // -初始化holer----------------------- mHolder = getHolder(); mHolder.addCallback(this); setZOrderOnTop(true); // 设置画布 背景透明 mHolder.setFormat(PixelFormat.TRANSLUCENT); // --焦点设置---------------------------- setFocusable(true); // 设置触屏 setFocusableInTouchMode(true); // 设置常亮 setKeepScreenOn(true); // --背景设置-------------------------------- mGamePanelRect = new RectF(); mBgBitmap = loadImageByResId(R.drawable.bg1); // --添加鸟的图片--- mBirdBitmap = loadImageByResId(R.drawable.b1); // --添加地板--- mFloorBitmap = loadImageByResId(R.drawable.floor_bg2); // --管道的宽度初始化-- mPipeWidth = UITools.dip2px(getContext(), PIPE_WIDTH); // --添加管道图片-- mPipeTopBitmap = loadImageByResId(R.drawable.g2); mPipeBotBitmap = loadImageByResId(R.drawable.g1); mPipeList = new ArrayList
(); // ------------------------------------------------------- // 初始化分数图片 mNumBitmap = new Bitmap[mNums.length]; for (int i = 0; i < mNums.length; i++) { mNumBitmap[i] = loadImageByResId(mNums[i]); } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; mGamePanelRect.set(0, 0, w, h); // 初始化鸟 mBird = new Bird(getContext(), mBirdBitmap, mWidth, mHeight); // 初始化地板 mFloor = new Floor(mWidth, mHeight, mFloorBitmap); // 初始化管道范围 mPipeRectF = new RectF(0, 0, mPipeWidth, mHeight); // 初始化 管道 mPipe = new Pipe(getContext(), mWidth, mHeight, mPipeTopBitmap, mPipeBotBitmap); mPipeList.add(mPipe); // 初始化分数 mSingleGradeHeight = (int) (h * RADIO_SINGLE_NUM_HEIGHT);// 屏幕的1/15 mSingleGradeWidth = (int) (mNumBitmap[0].getWidth() * (1.0f * mSingleGradeHeight / mNumBitmap[0].getHeight())); mSingleNumRectF = new RectF(0, 0, mSingleGradeWidth, mSingleGradeHeight); mGrade = new Grade(mNumBitmap, mSingleNumRectF, mSingleGradeWidth, mWidth, mHeight); } @Override public void run() { while (isRunnging) { long start = System.currentTimeMillis(); draw(); long end = System.currentTimeMillis(); if (end - start < 50) { SystemClock.sleep(50 - (end - start)); } } } private void draw() { try { Logger.i("bird", "mHolder==" + mHolder); if (mHolder != null) { mCanvas = mHolder.lockCanvas(); Logger.i("bird", "mCanvas==" + mCanvas); if (mCanvas != null) { drawBg(); // 绘制背景 drawBird();// 绘制鸟 drawFloor();// 绘制地板 drawPipes();// 绘制管道 drawGrades();// 绘制分数 } } } catch (Exception e) { e.printStackTrace(); } finally { if (mHolder != null && mCanvas != null) { mHolder.unlockCanvasAndPost(mCanvas); } } } /** * 绘制分数 */ private void drawGrades() { mGrade.draw(mCanvas, mScore); } private int mSpeed = UITools.dip2px(getContext(), 2); private void drawFloor() { Paint paint = new Paint(); paint.setAntiAlias(true); paint.setDither(true); mFloor.draw(mCanvas, paint); // 更新我们地板绘制的x坐标 mFloor.setX(mFloor.getX() - mSpeed); } private void drawBird() { mBird.draw(mCanvas); } private void drawBg() { mCanvas.drawBitmap(mBgBitmap, null, mGamePanelRect, null); } private void drawPipes() { for (Pipe pipe : mPipeList) { // 先设定x坐标 pipe.setX(pipe.getX() - mSpeed); pipe.draw(mCanvas, mPipeRectF); } } // ---callback监听------------------------------------------------------ @Override public void surfaceCreated(SurfaceHolder holder) { // -线程处理-------------------------- isRunnging = true; mPool = Executors.newFixedThreadPool(5); mPool.execute(this); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 通知关闭线程 isRunnging = false; } /** * 根据resId加载图片 * * @param resId * @return */ private Bitmap loadImageByResId(int resId) { return BitmapFactory.decodeResource(getResources(), resId); }}

3)在画布上增加状态信息和开始与结束界面,游戏主界面就完成了

/** * 游戏主界面 *  * @Project App_View * @Package com.android.view.flybird * @author chenlin * @version 1.0 * @Date 2014年5月7日 */public class FlyBirdView extends SurfaceView implements Callback, Runnable {
//private static final String TAG = "bird"; private SurfaceHolder mHolder; private ExecutorService mPool; private Canvas mCanvas; private boolean isRunnging;// 是否运行 // 二.设置背景 private Bitmap mBgBitmap; // 当前View的尺寸 private int mWidth; private int mHeight; private RectF mGamePanelRect = new RectF(); // 三、设置鸟 private Bird mBird; private Bitmap mBirdBitmap; // 四、添加地板 private Floor mFloor; private Bitmap mFloorBitmap; // 五、添加管道 /** 管道的宽度 60dp */ private static final int PIPE_WIDTH = 60; private Pipe mPipe; /** 上管道的图片 */ private Bitmap mPipeTopBitmap; /** 下管道的图片 */ private Bitmap mPipeBotBitmap; /** 管道的宽度 */ private int mPipeWidth; /** 管道矩阵 */ private RectF mPipeRectF; /** 管道集合 */ private List
mPipeList; /** 管道移动的速度 */ private int mSpeed = UITools.dip2px(getContext(), 5); // 六、添加分数 /** 分数 */ private final int[] mNums = new int[] { R.drawable.n0, R.drawable.n1, R.drawable.n2, R.drawable.n3, R.drawable.n4, R.drawable.n5, R.drawable.n6, R.drawable.n7, R.drawable.n8, R.drawable.n9 }; private Grade mGrade; /** 分数图片组 */ private Bitmap[] mNumBitmap; /** 分值 */ private int mScore = 0; /** 单个数字的高度的1/15 */ private static final float RADIO_SINGLE_NUM_HEIGHT = 1 / 15f; /** 单个数字的宽度 */ private int mSingleGradeWidth; /** 单个数字的高度 */ private int mSingleGradeHeight; /** 单个数字的范围 */ private RectF mSingleNumRectF; // --七、添加游戏的状态------------------------------------------------------------------------- /** 刚进入游戏时是等待静止的状态 */ private GameStatus mStatus = GameStatus.WAITING; private enum GameStatus { WAITING, RUNNING, OVER } /** 触摸上升的距离,因为是上升,所以为负值 */ private static final int TOUCH_UP_SIZE = -16; /** 将上升的距离转化为px;这里多存储一个变量,变量在run中计算 */ private final int mBirdUpDis = UITools.dip2px(getContext(), TOUCH_UP_SIZE); /** 跳跃的时候的临时距离 */ private int mTmpBirdDis; // --八、按钮---------------------------------------- private GameButton mStart; private Bitmap mStartBitmap; private Bitmap mStartPressBitmap;// 开始按下图片 private GameButton mRestart; private Bitmap mRestartBitmap; private Bitmap mRestartPressBitmap;// 从新开始按下图片 // --九、游戏中的变量--------------------------- /** 两个管道间距离 **/ private final int PIPE_DIS_BETWEEN_TWO = UITools.dip2px(getContext(), 300); /** 鸟自动下落的距离 */ private final int mAutoDownSpeed = UITools.dip2px(getContext(), 2); //private Handler mHandler = new Handler(); // ----构造函数处理--------------------------------------------- public FlyBirdView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public FlyBirdView(Context context) { super(context); init(); } public FlyBirdView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } // ---初始化开始 ---------------------------------------------------------- private void init() { // -初始化holer----------------------- mHolder = getHolder(); mHolder.addCallback(this); setZOrderOnTop(true); // 设置画布 背景透明 mHolder.setFormat(PixelFormat.TRANSLUCENT); // --焦点设置---------------------------- setFocusable(true); // 设置触屏 setFocusableInTouchMode(true); // 设置常亮 setKeepScreenOn(true); // --背景设置-------------------------------- mGamePanelRect = new RectF(); mBgBitmap = loadImageByResId(R.drawable.bg1); // --添加鸟的图片--- mBirdBitmap = loadImageByResId(R.drawable.b1); // --添加地板--- mFloorBitmap = loadImageByResId(R.drawable.floor_bg2); // --管道的宽度初始化-- mPipeWidth = UITools.dip2px(getContext(), PIPE_WIDTH); // --添加管道图片-- mPipeTopBitmap = loadImageByResId(R.drawable.g2); mPipeBotBitmap = loadImageByResId(R.drawable.g1); mPipeList = new ArrayList
(); // ------------------------------------------------------- // 初始化分数图片 mNumBitmap = new Bitmap[mNums.length]; for (int i = 0; i < mNums.length; i++) { mNumBitmap[i] = loadImageByResId(mNums[i]); } // ---初始化按钮图片------------------------------------- mStartBitmap = BitmapUtil.getImageFromAssetsFile(getContext(), "start.png"); mStartPressBitmap = BitmapUtil.getImageFromAssetsFile(getContext(), "start2.png"); mRestartBitmap = BitmapUtil.getImageFromAssetsFile(getContext(), "restart1.png"); mRestartPressBitmap = BitmapUtil.getImageFromAssetsFile(getContext(), "restart2.png"); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; mGamePanelRect.set(0, 0, w, h); // 初始化鸟 mBird = new Bird(getContext(), mBirdBitmap, mWidth, mHeight); // 初始化地板 mFloor = new Floor(mWidth, mHeight, mFloorBitmap); // 初始化管道范围 mPipeRectF = new RectF(0, 0, mPipeWidth, mHeight); // 初始化 管道 mPipe = new Pipe(getContext(), mWidth, mHeight, mPipeTopBitmap, mPipeBotBitmap); mPipeList.add(mPipe); // 初始化分数 mSingleGradeHeight = (int) (h * RADIO_SINGLE_NUM_HEIGHT);// 屏幕的1/15 mSingleGradeWidth = (int) (mNumBitmap[0].getWidth() * (1.0f * mSingleGradeHeight / mNumBitmap[0].getHeight())); mSingleNumRectF = new RectF(0, 0, mSingleGradeWidth, mSingleGradeHeight); mGrade = new Grade(mNumBitmap, mSingleNumRectF, mSingleGradeWidth, mWidth, mHeight); // 初始化按钮 mStart = new GameButton(mStartBitmap, mStartPressBitmap, mWidth, mHeight); // 从新开始按钮 mRestart = new GameButton(mRestartBitmap, mRestartPressBitmap, mWidth, mHeight); if (mStatus == GameStatus.WAITING && mStart != null) { ObjectAnimator anim = ObjectAnimator.ofInt(mStart, "Y", mHeight, mHeight / 2); anim.setDuration(2000); anim.start(); } // 添加事件 mStart.setOnButtonClickListener(new OnButtonClickListener() { @Override public void click() { if (mStatus == GameStatus.WAITING) { // 按下的时候,游戏进入运行状态 mStatus = GameStatus.RUNNING; } } }); mRestart.setOnButtonClickListener(new OnButtonClickListener() { @Override public void click() { mStatus = GameStatus.WAITING; resetBirdStatus(); } }); } // ---初始化结束 ---------------------------------------------------------- // --处理触碰事件------------------------------------------------------------------------ private int mDownX = 0; private int mDownY = 0; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN:// 按下 mDownX = (int) event.getX(); mDownY = (int) event.getY(); if (mStatus == GameStatus.WAITING) { if (mStart.isClick(mDownX, mDownY)) { mStart.click(); } } else if (mStatus == GameStatus.RUNNING) { // 记录临时跳跃的高度 mTmpBirdDis = mBirdUpDis; // --增加难度--- if (mScore > 20) { mSpeed += UITools.dip2px(getContext(), 1); } else if (mScore > 40) { mSpeed += UITools.dip2px(getContext(), 2); } else if (mScore > 60) { mSpeed += UITools.dip2px(getContext(), 3); } else if (mScore > 80) { mSpeed += UITools.dip2px(getContext(), 4); } } else if (mStatus == GameStatus.OVER) {
// 游戏结束时 // 判断是否点击了重新开始图片 if (mRestart.isClick(mDownX, mDownY)) { mRestart.click(); } } break; case MotionEvent.ACTION_MOVE:// 移动 int moveX = (int) event.getX(); int moveY = (int) event.getY(); AnimatorSet set = new AnimatorSet(); ObjectAnimator animatorX = ObjectAnimator.ofInt(mBird, "X", mDownX, moveX); ObjectAnimator animatorY = ObjectAnimator.ofInt(mBird, "Y", mDownY, moveY); set.playTogether(animatorX, animatorY); set.setDuration(2000); set.start(); mDownX = (int) event.getX(); mDownY = (int) event.getY(); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: // 抬起 if (mStart != null) { mStart.setClick(false); } if (mRestart != null) { mRestart.setClick(false); } break; } return true; } private void resetBirdStatus() { // 设置鸟的高度 mBird.setY((int) (mHeight * Bird.RADIO_POS_HEIGHT)); // 重置下落速度 mTmpBirdDis = 0; } // --处理逻辑事物------------------------------------------------------------------------ /** 记录要移除的管道 为什么不用CopyOnWriteArrayList,因为其是线程安全的 */ private List
mNeedRemovePipe = new ArrayList
(); /** 记录要移动的距离 */ private int mTmpMoveDistance = 0; /** 记录要移除的管的个数 */ private int mRemovedPipe = 0; /** * 处理逻辑事物 */ private void logic() { switch (mStatus) { case WAITING:// 刚进入游戏的状态 break; case RUNNING:// 正在玩的状态] mScore = 0; // ---.移动地板----------- mFloor.setX(mFloor.getX() - mSpeed); // ---不断移动管道-------- logicPipe(); // ----处理鸟逻辑---- mTmpBirdDis += mAutoDownSpeed; mBird.setY(mBird.getY() + mTmpBirdDis); // ---处理分数--- mScore += mRemovedPipe; for (Pipe pipe : mPipeList) { if (pipe.getX() + mPipeWidth < mBird.getX()) { mScore++; } } // ----判断游戏是否结束---- checkGameOver(); break; case OVER:// 鸟落下 // 如果鸟还在空中,先让它掉下来 if (mBird.getY() < mFloor.getY() - mBird.getHeight()) { mTmpBirdDis += mAutoDownSpeed; mBird.setY(mBird.getY() + mTmpBirdDis); } else { // 清除生成的管道 clearAndInit(); } break; } } /** * 重置鸟的位置等数据 */ private void clearAndInit() { // 清除生成的管道 mPipeList.clear(); // 需要移除的管道集合 mNeedRemovePipe.clear(); // 清除移动的距离 mTmpMoveDistance = 0; // 管道的个数 mRemovedPipe = 0; } /** * 处理管道逻辑 */ private void logicPipe() { // 1.遍历所有的管道 for (Pipe pipe : mPipeList) { // 2.如果管子已经在屏幕外 if (pipe.getX() < -mPipeWidth) { mNeedRemovePipe.add(pipe); mRemovedPipe++; continue; } pipe.setX(pipe.getX() - mSpeed); } // 3.移除管道 mPipeList.removeAll(mNeedRemovePipe); // 4.记录移动距离 mTmpMoveDistance += mSpeed; // 5.生成一个管道 if (mTmpMoveDistance >= PIPE_DIS_BETWEEN_TWO) { Pipe pipe = new Pipe(getContext(), getWidth(), getHeight(), mPipeTopBitmap, mPipeBotBitmap); mPipeList.add(pipe); mTmpMoveDistance = 0; } } /** * 判断游戏是否结束 */ private void checkGameOver() { // 判断小鸟是否触碰到了地板 if (mBird.getY() > mFloor.getY() - mBird.getHeight()) { mStatus = GameStatus.OVER; } // 判断是否触碰到了管道 for (Pipe pipe : mPipeList) { // 已经穿过的 if (pipe.getX() + mPipeWidth < mBird.getX()) { continue; } // 如果是碰到了,游戏结束 if (pipe.touchBird(mBird)) { mStatus = GameStatus.OVER; break; } } } // ---游戏引擎------------------------------------------------------------ @Override public void run() { while (isRunnging) { long start = System.currentTimeMillis(); logic(); draw(); long end = System.currentTimeMillis(); if (end - start < 50) { SystemClock.sleep(50 - (end - start)); } } } // ----绘制开始------------------------------------------------------------------- private void draw() { try { if (mHolder != null) { mCanvas = mHolder.lockCanvas(); if (mCanvas != null) { drawBg(); // 绘制背景 drawBird();// 绘制鸟 drawFloor();// 绘制地板 drawGrades();// 绘制分数 if (mStatus == GameStatus.WAITING) { drawStart(); } if (mStatus == GameStatus.RUNNING) { drawPipes();// 绘制管道 } if (mStatus == GameStatus.OVER) { drawGameOver(); drawRestart(); } } } } catch (Exception e) { e.printStackTrace(); } finally { if (mHolder != null && mCanvas != null) { mHolder.unlockCanvasAndPost(mCanvas); } } } private FontMetrics fm; private int mTextHeight = 0;// 游戏结束时文本的高度 private void drawGameOver() { String mGameOver = "GAME OVER"; Typeface typeface = Typeface.createFromAsset(getContext().getAssets(), "BRITANIC.TTF"); Paint paint = new Paint(); paint.setAntiAlias(true); // 是否抗锯齿 paint.setTypeface(typeface); paint.setStrokeWidth(3); paint.setColor(Color.RED); paint.setTextSize(50); // paint.setShader(shader);//设置字体 paint.setShadowLayer(5, 3, 3, 0xFFFF00FF);// 设置阴影 paint.setTextAlign(Paint.Align.CENTER); // paint.setStyle(Paint.Style.STROKE); //空心 paint.setStyle(Paint.Style.FILL); // 实心 paint.setDither(true); fm = paint.getFontMetrics(); mTextHeight = (int) (Math.ceil(fm.descent - fm.ascent) + UITools.dip2px(getContext(), 4)); mCanvas.drawText(mGameOver, mWidth / 2, mHeight / 2, paint); } /** * 绘制开始按钮 */ private void drawStart() { mStart.draw(mCanvas); } /** * 绘制重新开始按钮 */ private void drawRestart() { mRestart.setY(mHeight/2 + mTextHeight); mRestart.draw(mCanvas);// Logger.i(TAG, "aaaa");// mHandler.postDelayed(new Runnable() {
// @Override// public void run() {
// if (mRestart != null) {
// Logger.i(TAG, "kkkk");// ObjectAnimator anim = ObjectAnimator.ofInt(mRestart, "Y", mHeight, mHeight / 2 + mTextHeight);// anim.setDuration(2000);// anim.start();// }// }// }, 0); } /** * 绘制分数 */ private void drawGrades() { mGrade.draw(mCanvas, mScore); } private void drawFloor() { Paint paint = new Paint(); paint.setAntiAlias(true); paint.setDither(true); mFloor.draw(mCanvas, paint); // 更新我们地板绘制的x坐标 mFloor.setX(mFloor.getX() - mSpeed); } private void drawBird() { mBird.draw(mCanvas); } private void drawBg() { mCanvas.drawBitmap(mBgBitmap, null, mGamePanelRect, null); } private void drawPipes() { for (Pipe pipe : mPipeList) { // 先设定x坐标 pipe.setX(pipe.getX() - mSpeed); pipe.draw(mCanvas, mPipeRectF); } } // ----绘制结束------------------------------------------------------------------- // ---callback监听------------------------------------------------------ @Override public void surfaceCreated(SurfaceHolder holder) { // -线程处理-------------------------- isRunnging = true; mPool = Executors.newFixedThreadPool(5); mPool.execute(this); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 通知关闭线程 isRunnging = false; } /** * 根据resId加载图片 * * @param resId * @return */ private Bitmap loadImageByResId(int resId) { return BitmapFactory.decodeResource(getResources(), resId); }}

五、做完后我们的运行,要通过activity来实现

public class FlyBirdActivity extends Activity{
FlyBirdView mBirdView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); requestWindowFeature(Window.FEATURE_NO_TITLE); mBirdView = new FlyBirdView(this); setContentView(mBirdView); }}

六、代码下载

链接: 密码:ol82

—————————————————————————————————————————————————–

java架构师项目实战,高并发集群分布式,大数据高可用视频教程,共760G

下载地址:

https://item.taobao.com/item.htm?id=555888526201

01.高级架构师四十二个阶段高

02.Java高级系统培训架构课程148课时
03.Java高级互联网架构师课程
04.Java互联网架构Netty、Nio、Mina等-视频教程
05.Java高级架构设计2016整理-视频教程
06.架构师基础、高级片
07.Java架构师必修linux运维系列课程
08.Java高级系统培训架构课程116课时
+
hadoop系列教程,java设计模式与数据结构, Spring Cloud微服务, SpringBoot入门

内容详情:

【入门篇】

J2SE的Socket网络编程应用
J2SE的反射机制高级应用
J2SE高深讲解
JAVA编程思想 中级教程
JAVA编程思想 初级教程
JAVA编程思想 高级教程
基于J2SE的QQ聊天工具开发
我来说说面向对象
【进阶篇】
CRM项目
Eclipse
Hibernate
JAVA WEB开发视频
JAVAWEB开发实战经典-高级案例篇
JAVAWEB
JAVA线程并发教程
java网上在线支付实战视频
java设计模式
jdbc
junit
mybatis
spring mvc
SpringMvc+Spring+MyBatis+Maven整合视频
SpringMVC
Spring
Struts2 开发实战讲解
Struts2+Spring3+Hibernate4+Maven+EasyUI整合入门视频
Struts
SVN
tomcat
weblogic
WebService从入门到上手企业开发
企业系统OA办公自动化
手机进销存系统
数据结构和算法视频
设计模式系列
【高级篇】
Cas单点登录
Extjs4.1+Spring3.2+hibernate4.1+MySql5商城
Git权威指南
groovy入门视频
Java 8新特性
Lucene
Mongodb
node.js
Nutch相关框架
OA办公自动化系统
Quartz Job定时任务
Solr高级应用视频
Spring Security权限控制
Spring源码解读与设计详析
Struts2源码剖析与架构指导
大型CMS内容管理系统项目
微信入门视频
深入JVM内核—原理、诊断与优化
深入浅出微信公众平台实战开发(微网站、LBS云、Api接口调用、服务号高级接口)
银行接口资料
【架构篇】
ActiveMQ实战
Apache-Tomcat集群搭建
Linux集群
Linux高级架构架构方案及实现指南
Memcached分布式集群
Mysql特级优化课程
Nginx+Tomcat+Memcached群集配置软件包
Nginx服务器搭建
Nginx网站架构实战(Web服务器负载均衡与反向代理)
SOA Dubbo
storm入门到精通
storm集群的搭建
storm项目实战
UML建模
互联网公司技术架构系列
京东B2C平台推荐搜索的实践和思考
京东大数据分析与创新应用
京东大规模存储持续研发
京东电商海量订单处理OFC系统的关键技术
优米网架构设计方案
基于SOA 思想下的百万数据架构
大型网站提速之MySQL优化
大型网站架构设计
大数据高并发架构实战案例
数据优化技术Redis
数据库高并发原理
深入Java程序性能调优
深入浅出MongoDB应用实战集群及系统架构
深度揭秘服务器端内幕
电商网站之Solr应用
系统架构设计师
阿里分布式数据库服务实践
—————————————————————————————————————————————————–

转载地址:https://lovoo.blog.csdn.net/article/details/51461556 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Android 获取dip数据的三种途径
下一篇:Android 图片切割工具类

发表评论

最新留言

不错!
[***.144.177.141]2024年04月26日 04时06分21秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章