• Android SurfaceView实战 带你玩转flabby bird (上)


    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/42965779 ,本文出自:【张鸿洋的博客】

    1、概述

    哈,记得以前写过Android SurfaceView实战 打造抽奖转盘 , 同属于SurfaceView系列,基本可以从这篇博文中学习到SurfaceView的用法,以及利用SurfaceView做抽奖转盘。但是其中缺少一部分的知识点,就是与用户交互时界面的改变,所以今天给大家再带来本篇博文教大家如何做flabby bird这款游戏,这游戏虽然不难,但是也为其作者挣了不少钱,大家在学会以后,可以尽可能发挥自己的创意,做属于自己的游戏,说不定下一个火的奏是你。

    ok,那么首先上下效果图:

    再来张动态的:

    由于上传图片最大限制为2M,所以做了压缩处理,凑合看吧 ~~~

     

    2、分析

    仔细观察游戏,需要绘制的有:背景、地板、鸟、管道、分数;

    游戏开始时:

    地板给人一种想左移动的感觉;

    管道与地板同样的速度向左移动;

    鸟默认下落;

    当用户touch屏幕时,鸟上升一段距离后,下落;

    运动过程中需要判断管道和鸟之间的位置关系,是否触碰,是否穿过等,需要计算分数。

    好了,大概就这么多,那我们首先开始考虑绘制~~~

     

    3、SurfaceView的一般写法

    接下来,我们首先编写下SurfaceView的一般写法:

     

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package com.zhy.view;  
    2.   
    3. import android.content.Context;  
    4. import android.graphics.Canvas;  
    5. import android.graphics.PixelFormat;  
    6. import android.util.AttributeSet;  
    7. import android.view.SurfaceHolder;  
    8. import android.view.SurfaceHolder.Callback;  
    9. import android.view.SurfaceView;  
    10.   
    11. public class GameFlabbyBird extends SurfaceView implements Callback, Runnable  
    12. {  
    13.   
    14.     private SurfaceHolder mHolder;  
    15.     /** 
    16.      * 与SurfaceHolder绑定的Canvas 
    17.      */  
    18.     private Canvas mCanvas;  
    19.     /** 
    20.      * 用于绘制的线程 
    21.      */  
    22.     private Thread t;  
    23.     /** 
    24.      * 线程的控制开关 
    25.      */  
    26.     private boolean isRunning;  
    27.   
    28.     public GameFlabbyBird(Context context)  
    29.     {  
    30.         this(context, null);  
    31.     }  
    32.   
    33.     public GameFlabbyBird(Context context, AttributeSet attrs)  
    34.     {  
    35.         super(context, attrs);  
    36.   
    37.         mHolder = getHolder();  
    38.         mHolder.addCallback(this);  
    39.   
    40.         setZOrderOnTop(true);// 设置画布 背景透明  
    41.         mHolder.setFormat(PixelFormat.TRANSLUCENT);  
    42.   
    43.         // 设置可获得焦点  
    44.         setFocusable(true);  
    45.         setFocusableInTouchMode(true);  
    46.         // 设置常亮  
    47.         this.setKeepScreenOn(true);  
    48.   
    49.     }  
    50.   
    51.     @Override  
    52.     public void surfaceCreated(SurfaceHolder holder)  
    53.     {  
    54.   
    55.         // 开启线程  
    56.         isRunning = true;  
    57.         t = new Thread(this);  
    58.         t.start();  
    59.     }  
    60.   
    61.     @Override  
    62.     public void surfaceChanged(SurfaceHolder holder, int format, int width,  
    63.             int height)  
    64.     {  
    65.         // TODO Auto-generated method stub  
    66.   
    67.     }  
    68.   
    69.     @Override  
    70.     public void surfaceDestroyed(SurfaceHolder holder)  
    71.     {  
    72.         // 通知关闭线程  
    73.         isRunning = false;  
    74.     }  
    75.   
    76.     @Override  
    77.     public void run()  
    78.     {  
    79.         while (isRunning)  
    80.         {  
    81.             long start = System.currentTimeMillis();  
    82.             draw();  
    83.             long end = System.currentTimeMillis();  
    84.   
    85.             try  
    86.             {  
    87.                 if (end - start < 50)  
    88.                 {  
    89.                     Thread.sleep(50 - (end - start));  
    90.                 }  
    91.             } catch (InterruptedException e)  
    92.             {  
    93.                 e.printStackTrace();  
    94.             }  
    95.   
    96.         }  
    97.   
    98.     }  
    99.   
    100.     private void draw()  
    101.     {  
    102.         try  
    103.         {  
    104.             // 获得canvas  
    105.             mCanvas = mHolder.lockCanvas();  
    106.             if (mCanvas != null)  
    107.             {  
    108.                 // drawSomething..  
    109.             }  
    110.         } catch (Exception e)  
    111.         {  
    112.         } finally  
    113.         {  
    114.             if (mCanvas != null)  
    115.                 mHolder.unlockCanvasAndPost(mCanvas);  
    116.         }  
    117.     }  
    118. }  


    这个基础的类,在Android SurfaceView实战 打造抽奖转盘已经出现过,就不多说了,大家以后写SurfaceView的相关程序,可以直接拷贝,在此类基础上编写。

     

     

    4、绘制

     

    1、绘制背景

    最简单的当然是背景了,直接drawBitmap即可。

    我们添加需要的成员变量,以及初始化一些参数,然后添加drawBg方法,最后在draw中调用drawBg;

     

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public class CopyOfGameFlabbyBird extends SurfaceView implements Callback,  
    2.         Runnable  
    3. {  
    4.     /** 
    5.      * 当前View的尺寸 
    6.      */  
    7.     private int mWidth;  
    8.     private int mHeight;  
    9.     private RectF mGamePanelRect = new RectF();  
    10.   
    11.     /** 
    12.      * 背景 
    13.      */  
    14.     private Bitmap mBg;  
    15.   
    16.     public CopyOfGameFlabbyBird(Context context, AttributeSet attrs)  
    17.     {  
    18.         //省略了很多代码  
    19.         initBitmaps();  
    20.     }  
    21.   
    22.     /** 
    23.      * 初始化图片 
    24.      */  
    25.     private void initBitmaps()  
    26.     {  
    27.         mBg = loadImageByResId(R.drawable.bg1);  
    28.     }  
    29.   
    30.     private void draw()  
    31.     {  
    32.         //省略了很多代码  
    33.         drawBg();  
    34.         //省略了很多代码  
    35.     }  
    36.   
    37.     /** 
    38.      * 绘制背景 
    39.      */  
    40.     private void drawBg()  
    41.     {  
    42.         mCanvas.drawBitmap(mBg, null, mGamePanelRect, null);  
    43.     }  
    44.   
    45.     /** 
    46.      * 初始化尺寸相关 
    47.      */  
    48.     @Override  
    49.     protected void onSizeChanged(int w, int h, int oldw, int oldh)  
    50.     {  
    51.         super.onSizeChanged(w, h, oldw, oldh);  
    52.   
    53.         mWidth = w;  
    54.         mHeight = h;  
    55.         mGamePanelRect.set(0, 0, w, h);  
    56.     }  
    57.   
    58.     /** 
    59.      * 根据resId加载图片 
    60.      *  
    61.      * @param resId 
    62.      * @return 
    63.      */  
    64.     private Bitmap loadImageByResId(int resId)  
    65.     {  
    66.         return BitmapFactory.decodeResource(getResources(), resId);  
    67.     }  
    68. }  


    基本就是添加成员变量,然后初始化,然后绘制,上述代码经过删减,贴出的都是与前面基础代码不同的部分,大家可以将代码对号入座进行填充。

     

    好了,现在背景图绘制好了,接下来,我们绘制小鸟~~~

     

    2、绘制bird

     

    鸟在我们的屏幕中,初始化时需要一个位置,x上,肯定是居中,y上我们取2/3的高度;

    关于bird,我们单独创建一个类:

     

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package com.zhy.view;  
    2.   
    3. import android.content.Context;  
    4. import android.graphics.Bitmap;  
    5. import android.graphics.Canvas;  
    6. import android.graphics.RectF;  
    7.   
    8. public class Bird  
    9. {  
    10.     /** 
    11.      * 鸟在屏幕高度的2/3位置 
    12.      */  
    13.     private static final float RADIO_POS_HEIGHT = 2 / 3F;  
    14.     /** 
    15.      * 鸟的宽度 30dp 
    16.      */  
    17.     private static final int BIRD_SIZE = 30;  
    18.   
    19.     /** 
    20.      * 鸟的横坐标 
    21.      */  
    22.     private int x;  
    23.     /** 
    24.      * 鸟的纵坐标 
    25.      */  
    26.     private int y;  
    27.     /** 
    28.      * 鸟的宽度 
    29.      */  
    30.     private int mWidth;  
    31.     /** 
    32.      * 鸟的高度 
    33.      */  
    34.     private int mHeight;  
    35.   
    36.     /** 
    37.      * 鸟的bitmap 
    38.      */  
    39.     private Bitmap bitmap;  
    40.     /** 
    41.      * 鸟绘制的范围 
    42.      */  
    43.     private RectF rect = new RectF();  
    44.   
    45.     public Bird(Context context, int gameWith, int gameHeight, Bitmap bitmap)  
    46.     {  
    47.   
    48.         this.bitmap = bitmap;  
    49.         //鸟的位置  
    50.         x = gameWith / 2 - bitmap.getWidth() / 2;  
    51.         y = (int) (gameHeight * RADIO_POS_HEIGHT);  
    52.   
    53.         // 计算鸟的宽度和高度  
    54.         mWidth = Util.dp2px(context, BIRD_SIZE);  
    55.         mHeight = (int) (mWidth * 1.0f / bitmap.getWidth() * bitmap.getHeight());  
    56.     }  
    57.   
    58.     /** 
    59.      * 绘制自己 
    60.      *  
    61.      * @param canvas 
    62.      */  
    63.     public void draw(Canvas canvas)  
    64.     {  
    65.         rect.set(x, y, x + mWidth, y + mHeight);  
    66.         canvas.drawBitmap(bitmap, null, rect, null);  
    67.   
    68.     }  
    69.   
    70.     public int getY()  
    71.     {  
    72.         return y;  
    73.     }  
    74.   
    75.     public void setY(int y)  
    76.     {  
    77.         this.y = y;  
    78.     }  
    79.   
    80.     public int getWidth()  
    81.     {  
    82.         return mWidth;  
    83.     }  
    84.   
    85.     public int getHeight()  
    86.     {  
    87.         return mHeight;  
    88.     }  
    89.   
    90. }  


    定义了一个类,代表我们的鸟,以及一堆成员变量,并且提供一个draw方法对外;

     

    在GameFlabbyBird中,只需要,初始化我们的Bird,在draw里面调用bird.draw即可;

    部分筛检后代码:

     

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public class CopyOfGameFlabbyBird extends SurfaceView implements Callback,  
    2.         Runnable  
    3. {  
    4.     /** 
    5.      * *********鸟相关********************** 
    6.      */  
    7.     private Bird mBird;  
    8.     private Bitmap mBirdBitmap;  
    9.   
    10.     /** 
    11.      * 初始化图片 
    12.      */  
    13.     private void initBitmaps()  
    14.     {  
    15.         mBg = loadImageByResId(R.drawable.bg1);  
    16.         mBirdBitmap = loadImageByResId(R.drawable.b1);  
    17.   
    18.     }  
    19.   
    20.     private void draw()  
    21.     {  
    22.         // drawSomething..  
    23.   
    24.         drawBg();  
    25.         drawBird();  
    26.   
    27.     }  
    28.   
    29.     private void drawBird()  
    30.     {  
    31.         mBird.draw(mCanvas);  
    32.     }  
    33.   
    34.     /** 
    35.      * 初始化尺寸相关 
    36.      */  
    37.     @Override  
    38.     protected void onSizeChanged(int w, int h, int oldw, int oldh)  
    39.     {  
    40.         // 初始化mBird  
    41.         mBird = new Bird(getContext(), mWidth, mHeight, mBirdBitmap);  
    42.   
    43.     }  
    44.   
    45. }  

    是不是很简单,下面看下此时效果图:

     

    Activity里面这么调用即可:

     

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package com.zhy.surfaceViewDemo;  
    2.   
    3. import com.zhy.view.GameFlabbyBird;  
    4.   
    5. import android.app.Activity;  
    6. import android.os.Bundle;  
    7. import android.view.Window;  
    8. import android.view.WindowManager;  
    9.   
    10. public class MainActivity extends Activity  
    11. {  
    12.     GameFlabbyBird mGame;  
    13.   
    14.     @Override  
    15.     protected void onCreate(Bundle savedInstanceState)  
    16.     {  
    17.         super.onCreate(savedInstanceState);  
    18.         getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,  
    19.                 WindowManager.LayoutParams.FLAG_FULLSCREEN);  
    20.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
    21.         mGame = new GameFlabbyBird(this);  
    22.         setContentView(mGame);  
    23.   
    24.     }  
    25.   
    26. }  


    效果图:

     

    不管咋样,我们的鸟已经在指定的位置了~~~有木有一点小激动~~

    下面开始添加地板;

    3、绘制地板

    绘制地板相比来说会难一点,因为我们需要考虑怎么让地板运动,起初我截取了两个大图,希望通过两张图不断变化,产生动画效果,but,动画的太卡,有跳跃感;

    于是,我忽然想到了一个东西可以做,我就把基础图变成了这样:

    很小的一块图,先不考虑运动,如何填充成我们目标效果呢?

    还记得有个类叫做BitmapShader么?我们可以利用它进行填充。

    相关知识可以参考:Android BitmapShader 实战 实现圆形、圆角图片

    首先我们依旧是定义一个地板类:Floor

     

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package com.zhy.view;  
    2.   
    3. import java.util.concurrent.TimeUnit;  
    4.   
    5. import com.zhy.surfaceViewDemo.Config;  
    6.   
    7. import android.content.Context;  
    8. import android.graphics.Bitmap;  
    9. import android.graphics.BitmapShader;  
    10. import android.graphics.Canvas;  
    11. import android.graphics.Paint;  
    12. import android.graphics.Paint.Style;  
    13. import android.graphics.Shader.TileMode;  
    14.   
    15. public class Floor  
    16. {  
    17.     /* 
    18.      * 地板位置游戏面板高度的4/5到底部 
    19.      */  
    20.     private static final float FLOOR_Y_POS_RADIO = 4 / 5F; // height of 4/5  
    21.   
    22.     /** 
    23.      * x坐标 
    24.      */  
    25.     private int x;  
    26.     /** 
    27.      * y坐标 
    28.      */  
    29.     private int y;  
    30.     /** 
    31.      * 填充物 
    32.      */  
    33.     private BitmapShader mFloorShader;  
    34.   
    35.     private int mGameWidth;  
    36.   
    37.     private int mGameHeight;  
    38.   
    39.     public Floor(int gameWidth, int gameHeight, Bitmap floorBg)  
    40.     {  
    41.         mGameWidth = gameWidth;  
    42.         mGameHeight = gameHeight;  
    43.         y = (int) (gameHeight * FLOOR_Y_POS_RADIO);  
    44.         mFloorShader = new BitmapShader(floorBg, TileMode.REPEAT,  
    45.                 TileMode.CLAMP);  
    46.     }  
    47.   
    48.     /** 
    49.      * 绘制自己 
    50.      *  
    51.      * @param mCanvas 
    52.      * @param mPaint 
    53.      */  
    54.     public void draw(Canvas mCanvas, Paint mPaint)  
    55.     {  
    56.         if (-x > mGameWidth)  
    57.         {  
    58.             x = x % mGameWidth;  
    59.         }  
    60.         mCanvas.save(Canvas.MATRIX_SAVE_FLAG);  
    61.         //移动到指定的位置  
    62.         mCanvas.translate(x, y);  
    63.         mPaint.setShader(mFloorShader);  
    64.         mCanvas.drawRect(x, 0, -x + mGameWidth, mGameHeight - y, mPaint);  
    65.         mCanvas.restore();  
    66.         mPaint.setShader(null);  
    67.     }  
    68.   
    69.     public int getX()  
    70.     {  
    71.         return x;  
    72.     }  
    73.   
    74.     public void setX(int x)  
    75.     {  
    76.         this.x = x;  
    77.     }  
    78.   
    79. }  


    定义了一堆成员变量,核心就在于,我们传入地板背景的填充物,然后初始化我们的mFloorShader,横向重复,纵向拉伸(这里的拉伸是指,纵向的最后一个像素不断重复)。

     

    我们对外公布了draw方法,传入Canvas,我们首先调用canvas.save(),然后将canvas移动到指定的位置,然后绘制我们的矩形,矩形的填充就是我们的地板了~~;

    这里,注意一下,我们这里使用了一个变量x,而不是0;为什么呢?因为我们的地板需要利用这个x运动。

    那么现在我们如何才能动呢?

    首先我们在GameFlabbyBird定义一个变量,表示移动速度mSpeed,然后在draw中不断更新mFloor的x坐标为:mFloor.setX(mFloor.getX() - mSpeed);

    这样的画,每次绘制我们floor的起点,会向左移动mSpeed个位置,就形成了运行的效果;但是呢?不能一直减下去,不然最终我们的x岂不是负无穷了,那得绘制多大?

    所以我们:

    if (-x > mGameWidth)
    {
    x = x % mGameWidth;
    }

    如果x的正值大于宽度了,我们取余一下~~~

    最终我们的绘制范围是:

    mCanvas.drawRect(x, 0, -x + mGameWidth, mGameHeight - y, mPaint);

    ok,贴下筛检后GameFlabbyBird代码:

     

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package com.zhy.view;  
    2.   
    3. import android.content.Context;  
    4. import android.graphics.Bitmap;  
    5. import android.graphics.BitmapFactory;  
    6. import android.graphics.Canvas;  
    7. import android.graphics.Paint;  
    8. import android.graphics.PixelFormat;  
    9. import android.graphics.RectF;  
    10. import android.util.AttributeSet;  
    11. import android.util.Log;  
    12. import android.view.SurfaceHolder;  
    13. import android.view.SurfaceHolder.Callback;  
    14. import android.view.SurfaceView;  
    15.   
    16. import com.zhy.surfaceViewDemo.R;  
    17.   
    18. public class CopyOfGameFlabbyBird extends SurfaceView implements Callback,  
    19.         Runnable  
    20. {  
    21.     private Paint mPaint;  
    22.     /** 
    23.      * 地板 
    24.      */  
    25.     private Floor mFloor;  
    26.     private Bitmap mFloorBg;  
    27.   
    28.     private int mSpeed;  
    29.   
    30.     public CopyOfGameFlabbyBird(Context context, AttributeSet attrs)  
    31.     {  
    32.         super(context, attrs);  
    33.   
    34.         mPaint = new Paint();  
    35.         mPaint.setAntiAlias(true);  
    36.         mPaint.setDither(true);  
    37.   
    38.         initBitmaps();  
    39.   
    40.         // 初始化速度  
    41.         mSpeed = Util.dp2px(getContext(), 2);  
    42.   
    43.     }  
    44.   
    45.     /** 
    46.      * 初始化图片 
    47.      */  
    48.     private void initBitmaps()  
    49.     {  
    50.         mFloorBg = loadImageByResId(R.drawable.floor_bg2);  
    51.   
    52.     }  
    53.   
    54.     private void draw()  
    55.     {  
    56.   
    57.         // drawSomething..  
    58.   
    59.         drawBg();  
    60.         drawBird();  
    61.         drawFloor();  
    62.   
    63.         // 更新我们地板绘制的x坐标  
    64.         mFloor.setX(mFloor.getX() - mSpeed);  
    65.   
    66.     }  
    67.   
    68.     private void drawFloor()  
    69.     {  
    70.         mFloor.draw(mCanvas, mPaint);  
    71.     }  
    72.   
    73.     @Override  
    74.     protected void onSizeChanged(int w, int h, int oldw, int oldh)  
    75.     {  
    76.         // 初始化地板  
    77.         mFloor = new Floor(mWidth, mHeight, mFloorBg);  
    78.   
    79.     }  
    80.   
    81. }  


    其实很简单,就是声明几个变量,初始化一下;记得在draw中更新mFloor的x即可。

     

    现在的效果:

    好了,最后剩下个管道了~~~

     

    4、绘制管道

    然后是写搞一个管道类Pipe,注意我们的管道分为上下,每个管道的高度可能不同,所以会多一些成员变量;

     

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package com.zhy.view;  
    2.   
    3. import java.util.Random;  
    4.   
    5. import android.content.Context;  
    6. import android.graphics.Bitmap;  
    7. import android.graphics.Canvas;  
    8. import android.graphics.RectF;  
    9.   
    10. /** 
    11.  * 管道分为上下 
    12.  *  
    13.  * @author zhy 
    14.  *  
    15.  */  
    16. public class Pipe  
    17. {  
    18.     /** 
    19.      * 上下管道间的距离 
    20.      */  
    21.     private static final float RADIO_BETWEEN_UP_DOWN = 1 / 5F;  
    22.     /** 
    23.      * 上管道的最大高度 
    24.      */  
    25.     private static final float RADIO_MAX_HEIGHT = 2 / 5F;  
    26.     /** 
    27.      * 上管道的最小高度 
    28.      */  
    29.     private static final float RADIO_MIN_HEIGHT = 1 / 5F;  
    30.     /** 
    31.      * 管道的横坐标 
    32.      */  
    33.     private int x;  
    34.     /** 
    35.      * 上管道的高度 
    36.      */  
    37.     private int height;  
    38.     /** 
    39.      * 上下管道间的距离 
    40.      */  
    41.     private int margin;  
    42.     /** 
    43.      * 上管道图片 
    44.      */  
    45.     private Bitmap mTop;  
    46.     /** 
    47.      * 下管道图片 
    48.      */  
    49.     private Bitmap mBottom;  
    50.   
    51.     private static Random random = new Random();  
    52.   
    53.     public Pipe(Context context, int gameWidth, int gameHeight, Bitmap top,  
    54.             Bitmap bottom)  
    55.     {  
    56.         margin = (int) (gameHeight * RADIO_BETWEEN_UP_DOWN);  
    57.         // 默认从最左边出现  
    58.         x = gameWidth;  
    59.   
    60.         mTop = top;  
    61.         mBottom = bottom;  
    62.   
    63.         randomHeight(gameHeight);  
    64.   
    65.     }  
    66.   
    67.     /** 
    68.      * 随机生成一个高度 
    69.      */  
    70.     private void randomHeight(int gameHeight)  
    71.     {  
    72.         height = random  
    73.                 .nextInt((int) (gameHeight * (RADIO_MAX_HEIGHT - RADIO_MIN_HEIGHT)));  
    74.         height = (int) (height + gameHeight * RADIO_MIN_HEIGHT);  
    75.     }  
    76.   
    77.     public void draw(Canvas mCanvas, RectF rect)  
    78.     {  
    79.         mCanvas.save(Canvas.MATRIX_SAVE_FLAG);  
    80.         // rect为整个管道,假设完整管道为100,需要绘制20,则向上偏移80  
    81.         mCanvas.translate(x, -(rect.bottom - height));  
    82.         mCanvas.drawBitmap(mTop, null, rect, null);  
    83.         // 下管道,便宜量为,上管道高度+margin  
    84.         mCanvas.translate(0, (rect.bottom - height) + height + margin);  
    85.         mCanvas.drawBitmap(mBottom, null, rect, null);  
    86.         mCanvas.restore();  
    87.     }  
    88.   
    89.     public int getX()  
    90.     {  
    91.         return x;  
    92.     }  
    93.   
    94.     public void setX(int x)  
    95.     {  
    96.         this.x = x;  
    97.     }  
    98.   
    99. }  


    我们直接看draw方法,我们的传入的rect是固定的一个矩形,我们的上下管道都是完整的绘制在这个rect中;

     

    然后根据height,去偏移canvas的y,让rect显示出height部分,主要是因为,这样可以保证每个管道样子是一样的(如果根据height,使用不同的rect,会产生缩放);

    Pipe写好了~~我们需要在GameFlabbyBird中去使用;但是考虑一下,游戏中的管道不像鸟和地面,有很多个,且是在运行中不断生成新的~~~

    所以我们保存Pipe最起码是个List<Pipe>

    筛检后的代码:

     

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public class GameFlabbyBird extends SurfaceView implements Callback, Runnable  
    2. {  
    3.   
    4.     /** 
    5.      * *********管道相关********************** 
    6.      */  
    7.     /** 
    8.      * 管道 
    9.      */  
    10.     private Bitmap mPipeTop;  
    11.     private Bitmap mPipeBottom;  
    12.     private RectF mPipeRect;  
    13.     private int mPipeWidth;  
    14.     /** 
    15.      * 管道的宽度 60dp 
    16.      */  
    17.     private static final int PIPE_WIDTH = 60;  
    18.   
    19.     private List<Pipe> mPipes = new ArrayList<Pipe>();  
    20.   
    21.     public GameFlabbyBird(Context context, AttributeSet attrs)  
    22.     {  
    23.         super(context, attrs);  
    24.         mPipeWidth = Util.dp2px(getContext(), PIPE_WIDTH);  
    25.   
    26.     }  
    27.   
    28.     /** 
    29.      * 初始化图片 
    30.      */  
    31.     private void initBitmaps()  
    32.     {  
    33.         ;  
    34.         mPipeTop = loadImageByResId(R.drawable.g2);  
    35.         mPipeBottom = loadImageByResId(R.drawable.g1);  
    36.     }  
    37.   
    38.     private void draw()  
    39.     {  
    40.   
    41.         drawBg();  
    42.         drawBird();  
    43.         drawPipes();  
    44.         drawFloor();  
    45.   
    46.     }  
    47.   
    48.     /** 
    49.      * 绘制管道 
    50.      */  
    51.     private void drawPipes()  
    52.     {  
    53.         for (Pipe pipe : mPipes)  
    54.         {  
    55.             pipe.setX(pipe.getX() - mSpeed);  
    56.             pipe.draw(mCanvas, mPipeRect);  
    57.         }  
    58.     }  
    59.   
    60.     /** 
    61.      * 初始化尺寸相关 
    62.      */  
    63.     @Override  
    64.     protected void onSizeChanged(int w, int h, int oldw, int oldh)  
    65.     {  
    66.         super.onSizeChanged(w, h, oldw, oldh);  
    67.   
    68.         // 初始化管道范围  
    69.         mPipeRect = new RectF(0, 0, mPipeWidth, mHeight);  
    70.         Pipe pipe = new Pipe(getContext(), w, h, mPipeTop, mPipeBottom);  
    71.         mPipes.add(pipe);  
    72.   
    73.     }  
    74.   
    75. }  


    我们在onSizeChanged中初始化了一个Pipe,添加到了mPipes中,然后在draw里面,动态改变Pipe的x为pipe.setX(pipe.getX() - mSpeed);

     

    下面来看下效果:

    我们的管道从右侧进入界面,然后消失在左侧~

    当然了,关于管道还有很多需要编写,比如管道每隔多远生成一个,也不能让无限生成,当管道从界面移除应该从mPipes中移出;

    以及判断管道和鸟的碰撞,这些都放置到下一篇博客叙述~~

     

    5、绘制分数

    分数的绘制比较简单,我准备了10个图,对应于0-9

    没有单独定义类了,直接写了~~

    筛检后的代码:

     

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public class GameFlabbyBird extends SurfaceView implements Callback,  
    2.         Runnable  
    3. {  
    4.     /** 
    5.      * 分数 
    6.      */  
    7.     private final int[] mNums = new int[] { R.drawable.n0, R.drawable.n1,  
    8.             R.drawable.n2, R.drawable.n3, R.drawable.n4, R.drawable.n5,  
    9.             R.drawable.n6, R.drawable.n7, R.drawable.n8, R.drawable.n9 };  
    10.     private Bitmap[] mNumBitmap;  
    11.     private int mGrade = 100;  
    12.     /** 
    13.      * 单个数字的高度的1/15 
    14.      */  
    15.     private static final float RADIO_SINGLE_NUM_HEIGHT = 1 / 15f;  
    16.     /** 
    17.      * 单个数字的宽度 
    18.      */  
    19.     private int mSingleGradeWidth;  
    20.     /** 
    21.      * 单个数字的高度 
    22.      */  
    23.     private int mSingleGradeHeight;  
    24.     /** 
    25.      * 单个数字的范围 
    26.      */  
    27.     private RectF mSingleNumRectF;  
    28.   
    29.     /** 
    30.      * 初始化图片 
    31.      */  
    32.     private void initBitmaps()  
    33.     {  
    34.   
    35.         mNumBitmap = new Bitmap[mNums.length];  
    36.         for (int i = 0; i < mNumBitmap.length; i++)  
    37.         {  
    38.             mNumBitmap[i] = loadImageByResId(mNums[i]);  
    39.         }  
    40.     }  
    41.   
    42.     private void draw()  
    43.     {  
    44.         // drawSomething..  
    45.   
    46.         drawBg();  
    47.         drawBird();  
    48.         drawPipes();  
    49.         drawFloor();  
    50.         drawGrades();  
    51.   
    52.     }  
    53.     /** 
    54.      * 绘制分数 
    55.      */  
    56.     private void drawGrades()  
    57.     {  
    58.         String grade = mGrade + "";  
    59.         mCanvas.save(Canvas.MATRIX_SAVE_FLAG);  
    60.         mCanvas.translate(mWidth / 2 - grade.length() * mSingleGradeWidth / 2,  
    61.                 1f / 8 * mHeight);  
    62.         // draw single num one by one  
    63.         for (int i = 0; i < grade.length(); i++)  
    64.         {  
    65.             String numStr = grade.substring(i, i + 1);  
    66.             int num = Integer.valueOf(numStr);  
    67.             mCanvas.drawBitmap(mNumBitmap[num], null, mSingleNumRectF, null);  
    68.             mCanvas.translate(mSingleGradeWidth, 0);  
    69.         }  
    70.         mCanvas.restore();  
    71.   
    72.     }  
    73.   
    74.   
    75.     /** 
    76.      * 初始化尺寸相关 
    77.      */  
    78.     @Override  
    79.     protected void onSizeChanged(int w, int h, int oldw, int oldh)  
    80.     {  
    81.   
    82.         super.onSizeChanged(w, h, oldw, oldh);  
    83.         // 初始化分数  
    84.         mSingleGradeHeight = (int) (h * RADIO_SINGLE_NUM_HEIGHT);  
    85.         mSingleGradeWidth = (int) (mSingleGradeHeight * 1.0f  
    86.                 / mNumBitmap[0].getHeight() * mNumBitmap[0].getWidth());  
    87.         mSingleNumRectF = new RectF(0, 0, mSingleGradeWidth, mSingleGradeHeight);  
    88.   
    89.     }  
    90.   
    91.   
    92.   
    93. }  


    我们定义了单个数字的范围,然后假设现在为100分,注意在绘制的时候,直接提取数字,把数字作为下标,找到对于的图片进行绘制;

     

    绘制前,根据数字的位数,对画布进行偏移到中心位置,然后绘制;绘制过程中,每绘制完成一个数字则偏移一个数字的宽度;

    现在的效果:

    ok,到此为止,我们完成了所有需要绘制的东西~~由于篇幅原因,下一篇,将在此基础上完善剩下的所有内容~~~

    有兴趣的,可以在此基础上直接尝试写了~~~

     

     

    源码点击下载

  • 相关阅读:
    (转)oracle 11g安装后用户名忘记怎么办
    svn
    (转)ublox公司AGPS解决方案简介
    转(Google 全国 地图 纠偏数据 偏移数据 火星坐标修正 方案 )
    (转)真实经纬度的最简单获得方法
    (转)64bit上安装32位oracle 10 g出现错误:无法定位承诺工序输入点 getprocessimagifilenamew 于动态链接库PSAPI.DLL
    转】PPT带备注演示(只有讲解者看到备注)[转载]
    iphone应用程序结构
    ObjC 初识
    并行编程(PLINQ)学习笔记
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4545479.html
Copyright © 2020-2023  润新知