Android SurfaceView实战 带你玩转flabby bird

 

分析

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

游戏开始时:

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

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

鸟默认下落;

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

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

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


3、SurfaceView的一般写法

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

  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. }  
package com.zhy.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;

public class GameFlabbyBird extends SurfaceView implements Callback, Runnable
{

	private SurfaceHolder mHolder;
	/**
	 * 与SurfaceHolder绑定的Canvas
	 */
	private Canvas mCanvas;
	/**
	 * 用于绘制的线程
	 */
	private Thread t;
	/**
	 * 线程的控制开关
	 */
	private boolean isRunning;

	public GameFlabbyBird(Context context)
	{
		this(context, null);
	}

	public GameFlabbyBird(Context context, AttributeSet attrs)
	{
		super(context, attrs);

		mHolder = getHolder();
		mHolder.addCallback(this);

		setZOrderOnTop(true);// 设置画布 背景透明
		mHolder.setFormat(PixelFormat.TRANSLUCENT);

		// 设置可获得焦点
		setFocusable(true);
		setFocusableInTouchMode(true);
		// 设置常亮
		this.setKeepScreenOn(true);

	}

	@Override
	public void surfaceCreated(SurfaceHolder holder)
	{

		// 开启线程
		isRunning = true;
		t = new Thread(this);
		t.start();
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height)
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder)
	{
		// 通知关闭线程
		isRunning = false;
	}

	@Override
	public void run()
	{
		while (isRunning)
		{
			long start = System.currentTimeMillis();
			draw();
			long end = System.currentTimeMillis();

			try
			{
				if (end - start < 50)
				{
					Thread.sleep(50 - (end - start));
				}
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}

		}

	}

	private void draw()
	{
		try
		{
			// 获得canvas
			mCanvas = mHolder.lockCanvas();
			if (mCanvas != null)
			{
				// drawSomething..
			}
		} catch (Exception e)
		{
		} finally
		{
			if (mCanvas != null)
				mHolder.unlockCanvasAndPost(mCanvas);
		}
	}
}

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


4、绘制


1、绘制背景

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

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

  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(00, 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. }  
public class CopyOfGameFlabbyBird extends SurfaceView implements Callback,
		Runnable
{
	/**
	 * 当前View的尺寸
	 */
	private int mWidth;
	private int mHeight;
	private RectF mGamePanelRect = new RectF();

	/**
	 * 背景
	 */
	private Bitmap mBg;

	public CopyOfGameFlabbyBird(Context context, AttributeSet attrs)
	{
		//省略了很多代码
		initBitmaps();
	}

	/**
	 * 初始化图片
	 */
	private void initBitmaps()
	{
		mBg = loadImageByResId(R.drawable.bg1);
	}

	private void draw()
	{
		//省略了很多代码
		drawBg();
		//省略了很多代码
	}

	/**
	 * 绘制背景
	 */
	private void drawBg()
	{
		mCanvas.drawBitmap(mBg, null, mGamePanelRect, null);
	}

	/**
	 * 初始化尺寸相关
	 */
	@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);
	}

	/**
	 * 根据resId加载图片
	 * 
	 * @param resId
	 * @return
	 */
	private Bitmap loadImageByResId(int resId)
	{
		return BitmapFactory.decodeResource(getResources(), resId);
	}
}

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

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


2、绘制bird

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

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

  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. }  
package com.zhy.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.RectF;

public class Bird
{
	/**
	 * 鸟在屏幕高度的2/3位置
	 */
	private static final float RADIO_POS_HEIGHT = 2 / 3F;
	/**
	 * 鸟的宽度 30dp
	 */
	private static final int BIRD_SIZE = 30;

	/**
	 * 鸟的横坐标
	 */
	private int x;
	/**
	 * 鸟的纵坐标
	 */
	private int y;
	/**
	 * 鸟的宽度
	 */
	private int mWidth;
	/**
	 * 鸟的高度
	 */
	private int mHeight;

	/**
	 * 鸟的bitmap
	 */
	private Bitmap bitmap;
	/**
	 * 鸟绘制的范围
	 */
	private RectF rect = new RectF();

	public Bird(Context context, int gameWith, int gameHeight, Bitmap bitmap)
	{

		this.bitmap = bitmap;
		//鸟的位置
		x = gameWith / 2 - bitmap.getWidth() / 2;
		y = (int) (gameHeight * RADIO_POS_HEIGHT);

		// 计算鸟的宽度和高度
		mWidth = Util.dp2px(context, BIRD_SIZE);
		mHeight = (int) (mWidth * 1.0f / bitmap.getWidth() * bitmap.getHeight());
	}

	/**
	 * 绘制自己
	 * 
	 * @param canvas
	 */
	public void draw(Canvas canvas)
	{
		rect.set(x, y, x + mWidth, y + mHeight);
		canvas.drawBitmap(bitmap, null, rect, null);

	}

	public int getY()
	{
		return y;
	}

	public void setY(int y)
	{
		this.y = y;
	}

	public int getWidth()
	{
		return mWidth;
	}

	public int getHeight()
	{
		return mHeight;
	}

}

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

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

部分筛检后代码:

  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. }  
public class CopyOfGameFlabbyBird extends SurfaceView implements Callback,
		Runnable
{
	/**
	 * *********鸟相关**********************
	 */
	private Bird mBird;
	private Bitmap mBirdBitmap;

	/**
	 * 初始化图片
	 */
	private void initBitmaps()
	{
		mBg = loadImageByResId(R.drawable.bg1);
		mBirdBitmap = loadImageByResId(R.drawable.b1);

	}

	private void draw()
	{
		// drawSomething..

		drawBg();
		drawBird();

	}

	private void drawBird()
	{
		mBird.draw(mCanvas);
	}

	/**
	 * 初始化尺寸相关
	 */
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh)
	{
		// 初始化mBird
		mBird = new Bird(getContext(), mWidth, mHeight, mBirdBitmap);

	}

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

Activity里面这么调用即可:

  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. }  
package com.zhy.surfaceViewDemo;

import com.zhy.view.GameFlabbyBird;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends Activity
{
	GameFlabbyBird mGame;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
				WindowManager.LayoutParams.FLAG_FULLSCREEN);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		mGame = new GameFlabbyBird(this);
		setContentView(mGame);

	}

}

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

下面开始添加地板;

3、绘制地板

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

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

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

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

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

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

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值