Android 模仿flabby bird游戏开发

254 篇文章 5 订阅
订阅专栏

一、示意图:

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<Pipe> 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<Pipe>();

        // -------------------------------------------------------

        // 初始化分数图片
        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<Pipe> 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<Pipe>();

        // -------------------------------------------------------

        // 初始化分数图片
        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<Pipe> mNeedRemovePipe = new ArrayList<Pipe>();
    /** 记录要移动的距离 */
    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);
    }
}

六、代码下载

链接:http://pan.baidu.com/s/1bpzAsgv 密码: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应用
系统架构设计师
阿里分布式数据库服务实践
—————————————————————————————————————————————————–

android版FlappyBird源码
07-21
android版FlappyBird源码
Android 模仿flabby bird游戏开发-附件资源
03-05
Android 模仿flabby bird游戏开发-附件资源
Android SurfaceView实战 带你玩转flabby bird (上)
modabobo
01-22 244
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/42965779,本文出自:【张鸿洋的博客】1、概述 哈,记得以前写过Android SurfaceView实战 打造抽奖转盘, 同属于SurfaceView系列,基本可以从这篇博文中学习到SurfaceView的用法,以及利用SurfaceView做抽奖转盘。但是其中缺少一部分的知...
Android 游戏开发入门指南(一)
最新发布
龙哥盟
08-13 1136
在我们过去的项目中,您可能还记得 Activity 组件用于显示 UI,它有两个部分——代码隐藏的 Java 程序和 XML 文件,在 XML 文件中,UI 被构造为 XML 中定义的视图对象的嵌套排列。这对于应用来说没问题,但是我们需要从图像文件中渲染自定义绘图,所以这种技术行不通。我们要做的是创建一个自定义视图对象,我们将在其中绘制我们需要的所有内容,然后我们将活动的内容视图设置为该自定义视图。我们可以通过创建一个扩展的 Java 类来创建自定义视图。!Figure 6-1向项目中添加一个类。
Android游戏开发教程汇总
weixin_34113237的博客
11-19 235
  把最近搜集到的Android游戏开发教程列出来,希望对大家有用。   1.Android2.0游戏开发实战宝典源代码,当然可能大家已经把书也买回家啦。   2.Pro.Android.Games英文版教程   3.Pro.Android.Games英文版源码   4.Hello.Android.3rd.Edition.pdf英文版教程,部分涉及。   5.Professional.A...
android游戏之flabby bird(上)
hfreeman2008的专栏
01-28 2379
1.写在前面的话:      一次无意中在csdn上找到一个资源《android高仿flappy bird源码》,下载下来运行一下,效果非常不错,再进去一看代码,乖乖,好东西啊,整个游戏的框架非常的nice,这其实是一个非常好的游戏框架,对于一般的游戏,我们都是可以直接使用噢。但是,唯一的遗憾是这位android高手,不知道为什么只有源码,没有好好的写一个博文详细的介绍一下自己的这个游戏
AndroidAndroid游戏开发
热门推荐
firstxueba的博客
08-12 1万+
本教材基于Google新推出的Android Studio开发环境,讲解在Android平台上进行游戏开发所需要用到的技术,希望给渴望加入Android游戏开发者行列的读者一些帮助。本教材需要读者有一些Java语言开发的基础。本教材分三个部分: 一、Android入门带领大家了解Android平台 二、Android游戏开发基础涵盖Android游戏开发的基本...
Android 游戏 flabby bird (绘图部分)
01-21
Android平台上开发游戏,特别是像Flappy Bird这样的简单但吸引人的游戏,对于初学者来说是一个很好的学习项目。本篇将重点讲解如何实现Flappy Bird游戏中的绘图部分,这部分是游戏视觉效果的基础。 首先,Flappy ...
Android SurfaceView 游戏 flabby bird 完整代码
01-24
本教程将通过一个名为“Flabby Bird”的游戏实例,深入探讨如何利用SurfaceView开发Android游戏。 **1. SurfaceView的基本概念** SurfaceView是Android中处理连续更新和低延迟显示的理想选择。它由两部分组成:...
Flabby bird
07-15
### Flabby Bird 游戏开发知识点详解 #### 一、Flabby Bird 游戏简介与市场表现 Flabby Bird(通常正确拼写为Flappy Bird)是一款由越南独立游戏开发者Dong Nguyen开发的休闲游戏游戏于2013年5月24日首次在苹果...
Android 游戏开发
黑人也疯狂
09-29 340
最近靠自学,稍微看懂了一些android游戏开发的流程。 关键是把绘图和游戏逻辑两个要独立开来,对于事件监听做得还不够好,还有就是关卡已经通过是否需要通过文件来控制?毕竟有些游戏通过了,你需要给它一个标志位
Android游戏开发
03-08
Android游戏开发
安卓游戏开发教程
12-28
安卓游戏开发视频教程以及源代码下载 网盘
精通Android 4 游戏开发(Advanced.Android.4.Games)
07-21
高清版 Book Description “Wolfenstein 3D”-like and ”Doom”-like game apps are some of the classic Android games presented in the original edition of this book. Since their release, Android has progressed with the debut of Android 4.0, adding better fonts, new User Interface and Experience (UI/UX) APIs, tablet considerations, multi-touch capabilities, multi-tasking, faster performance, and much more to the Android game app development repertoire. Multi-touch code gives these games and their players dynamic input and exchange ability, for a more realistic arcade game experience. Faster and better performance offers game players a more seamless, fun arcade experience like never before on Android. There is also improved native C/C++ integration with Android’s NDK as well, which makes coding, compiling, and converting both productive and efficient with gains in app performance. With actionable real-world source, Advanced Android 4 Games shows you how to build more sophisticated and addictive Android games, harnessing the power of these recent advancements. * Coverage of the new UI, UX, multi-touch and multi-tasking features available with Android 4.0. * Learn other techniques for improving the game playing experience including Wi-Fi tethering, better multi-tasking, new and better streaming Web video using WebM, and more. * By combining the elegant object-oriented features of Java and the raw power of C, there is no limit to the types of games that you can build for the platform, such as the “Quake 3D”-like game app case study in this book. You’ll definitely have fun, and perhaps you’ll even make some money. Enjoy! What you’ll learn * Key advanced Android gaming techniques using the new Android SDK 4 to help you earn more money in the app stores * How to compile native code (C) in Android using the NDK * How to add and integrate multi-touch * How to use Bluetooth controllers (Zeemote) * More gaming tricks and tips, such as hybrid 3D graphics with OpenGL and JNI * How to port and augment a 2D shooter game app similar to “Wolfenstein” for Android * How to port and augment another 2D shooter “Doom”-like game app for Android using OpenGL * How to build a 3D shooter game like “Quake” * How and where to best deploy these game apps Who this book is for This book is for savvy Android app developers who are looking for professional or advanced techniques for porting, augmenting and building 2D and 3D game apps that are complex, fun and lucrative. Table of Contents 1. Welcome to Android Gaming 2. Gaming Tricks: 3. More Gaming Tricks: Hybrid 3D Graphics with OpenGL and JNI 4. 2D Shooters: Wolfenstein-like App for Android 5. 2D Shooters with OpenGL: Doom-like App for Android 6. 3D Shooters Part I: Quake-like App 7. 3D Shooters Part II: Quake II 8. Appendix: Deployment and Compilation Tips
Android游戏开发方向游戏全程实战开发(飞行射击类游戏+RPG游戏开发)
11-09
Android游戏开发方向游戏全程实战开发(飞行射击类游戏+RPG游戏开发)
精通android游戏开发
12-03
精通android游戏开发
android游戏开发_Android游戏开发游戏循环
04-27 162
android游戏开发 游戏循环是每个游戏的心跳。 到目前为止,我们仅使用了非常简单的一种(您可以在此处找到),无法控制我们更新游戏状态的速度或速度以及要渲染的帧。 概括地说,最基本的游戏循环是while循环,该循环不断执行一些指令,直到我们发出信号指示完成为止,通常是通过将一个称为running的变量设置为false来完成 boolean running = true; while (!...
Android游戏开发教程
04-02 1077
几个月前,我们收到了来自Java开发人员Tamas Jano的电子邮件,要求加入我们的JCG合作伙伴计划。 令我们惊讶的是,他维护了一个名为“ Against The Grain ”的博客,该博客讨论了Android平台的游戏开发。 从那时起,我一直在阅读他的所有文章,我必须承认,他的著作对我和我在Java Code Geeks的同事们来说是一种灵感和动力,因此可以开始为Android平台开...
Android游戏开发之旅(一)
JavaTiger
11-25 2478
<br />      今天Android123开始新的Android游戏开发 之旅 系列,主要从控制方法(按键、轨迹球、触屏、重力感应、摄像头、话筒气流、光线亮度)、图形 View (高效绘图技术如双缓冲)、音效(游戏音乐)以及最后的OpenGL ES(Java 层)和NDK 的OpenGL和J2ME游戏移植 到Android方法,当然还有一些游戏实现惯用方法,比如地图编辑器,在Android OpenGL如何使用MD2文件 ,个部分讲述下Android游戏开发的过程最终实现一个比较完整的游
Cocos Creator入门:打造Flappy Bird风格游戏
"Flabby Bird" 是一款在Cocos Creator中制作的经典小游戏,它于2013年5月24日在苹果AppStore上线,并凭借其独特的玩法和广告收入模式取得了巨大成功。这款游戏的玩法简单却富有挑战性:玩家通过点击让小鸟不断上升,...
793
原创
399
点赞
1571
收藏
548
粉丝
关注
私信
写文章

热门文章

  • jquery判断字符串中是否存在某个的字符串 99318
  • JAVA工具类(17)--Java导入导出Excel工具类ExcelUtil 59143
  • 推荐几个好用的maven仓库镜像站 44721
  • Android 实现蓝牙客户端与服务器端通信 29323
  • BootstrapTable 实现toolbar删除操作 27909

分类专栏

  • 项目管理师总结 11篇
  • 项目架构师总结 1篇
  • 项目文档 8篇
  • 项目集群
  • vagrant 2篇
  • virtualbox 2篇
  • K8S管理 9篇
  • docker 12篇
  • zookeeper 4篇
  • 支付
  • 阿里支付 2篇
  • SpringCloud 1篇
  • dubbo 1篇
  • sleuth与zipkin 1篇
  • sentinel 1篇
  • schedule 1篇
  • seata 2篇
  • shiro 2篇
  • RabbitMQ 9篇
  • SpringSession 1篇
  • sso单点登录 1篇
  • Canal
  • ElasticSearch 5篇
  • oauth2 1篇
  • nginx 2篇
  • 对像存储OSS 1篇
  • gateway 1篇
  • git 1篇
  • Nacos 4篇
  • feign 3篇
  • Eureka 1篇
  • redis 38篇
  • 测试 3篇
  • Linux 12篇
  • 数据库
  • MySQL-优化 14篇
  • MYSQL-数据库设计 7篇
  • WEB开发
  • Vue+ElementUI 20篇
  • ES6 6篇
  • LayUI 4篇
  • JS技术 12篇
  • CSS 2篇
  • Html5 5篇
  • JQuery 15篇
  • Electron 8篇
  • Bootstrap 10篇
  • Java 2023面试大全 33篇
  • CMD命令 1篇
  • Java 3篇
  • poi 2篇
  • Java通信 25篇
  • thymeleaf 1篇
  • freemarker 9篇
  • 工具类 24篇
  • SpringBoot 32篇
  • Springboot问题 4篇
  • Activiti 23篇
  • MyBatisPlus 7篇
  • Java数据结构与算法 40篇
  • Java-线程 16篇
  • idea与eclipse 11篇
  • maven与gradle 9篇
  • java大数据 3篇
  • java8-集合处理 1篇
  • Android-总结 254篇
  • OpenGL 8篇
  • C语言 47篇
  • Objective C基础 19篇
  • Salesfore开发 8篇
  • UML 1篇
  • 微信小程序 2篇
  • 其它 4篇

最新评论

  • VisualStudio2017专业版和企业版激活密钥

    hhxljx: 2024.09.26可激活,谢谢博主

  • vmware中centos7实现与windows共享文件夹

    Witness762: 找了一大堆都不行,博主这个一次成,牛的表情包

  • vmware中centos7实现与windows共享文件夹

    lanjingfly: 好用,因为centos没有网,找了很多方法都不行,好些还要vip,博主的方法一次成功,感谢

  • VisualStudio2017专业版和企业版激活密钥

    lumosxxx: 2024.08.09可激活,谢谢博主

  • VisualStudio2017专业版和企业版激活密钥

    再也不社恐了: 2024.08.05可激活,谢谢博主

最新文章

  • Zookeeper 分布式锁案例
  • k8s 2003面试题(1):k8s有哪些特性?
  • 秒杀要关注哪些问题,以及如何解决
2023年110篇
2022年3篇
2021年140篇
2017年123篇
2016年438篇
2015年5篇

目录

目录

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lovoo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或 充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家山西大型玻璃钢雕塑福建季节性商场美陈哪家好深圳大型商场美陈销售公司10平米玻璃钢雕塑成本万圣节商场美陈气氛布置玻璃钢雕塑外观是什么样的宜宾玻璃钢卡通雕塑厂家南明区玻璃钢雕塑制作哪家好淮安玻璃钢仿铜雕塑定制玻璃钢雕塑卡通美陈气球商场美陈电话云浮河南玻璃钢雕塑郏县玻璃钢雕塑设计反转床商场美陈汕尾玻璃钢卡通雕塑批发栖霞商场节庆美陈正宗玻璃钢雕塑优质商家适合商场的美陈山西公园玻璃钢雕塑价位焦作玻璃钢雕塑工艺贵州玻璃钢广场雕塑玻璃钢雕塑彩绘招聘玻璃钢雕塑精工制作深圳做玻璃钢雕塑多少钱一个月上海酒店玻璃钢人物雕塑艺术摆件商场植物美陈蚌埠公园玻璃钢雕塑设计毕节玻璃钢商场美陈扬州玻璃钢雕塑公司山东大型商场创意商业美陈怎么样香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化