Android游戏开发基础part1-View视图和SurfaceView视图

游戏开发基础part1 -View视图和SurfaceView视图

好久没来发表博客了,就是因为一直都在忙其他事情,忙着四级备考,还有学科复习,又因为关于游戏基础这一块内容有点多,断断续续学习,这不直到这周才把《Android游戏编程从零开始》这本书的第四章搞定。

第4章  游戏基础

主要讲了以下内容:

两种游戏框架:View游戏框架、SurfaceView游戏框架

Canvas画布、Paint画笔、Bitmap位图的渲染与操作、剪切区域、动画、碰撞检测、游戏音乐与音效和游戏数据存储

以上内容就是关于游戏基础必须要掌握的知识,接下来我将会分别总结这些内容的知识。

首先来介绍两种游戏框架,这本书主要是讲解进行2D游戏开发的,用到的游戏框架是View和SurfaceView框架,由于SurfaceView能适应更多的游戏类型,所以接下的项目都是基于SurfaceView游戏框架进行开发的。

1.View与SurfaceView的区别

开发2D游戏,可以选用View和SurfaceView这两种视图进行开发。但它们有什么区别呢?

主要有两点:

1.更新画布

在View视图中对于画布的重新绘制,是通过调用View提供的postInvalidate()与invalidate()这两个函数执行的,也就是说画布是由系统主UI进行更新的。然而在SurfaceView视图对应画布的重绘是由一个新的单独线程去处理。   

2.视图机制

Android中View视图是没有双缓冲机制的,而SurfaceView视图却有!

什么是双缓冲机制?其实就是除了能在屏幕中显示图形之外,在内存中也有图形绘制。

我们可以理解为,SurfaceView视图就是一个由View扩展出来的更加适合游戏开发的视图类。

2.View游戏框架

一个范例:新建一个项目GameView

项目源代码如下:

==>GameView.java

package com.gameView;

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

public class GameView extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //隐去标题栏(应用程序的名字)
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        //隐去状态栏部分(电池等图标和一切修饰部分)
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
        			WindowManager.LayoutParams.FLAG_FULLSCREEN);
        //设置显示View实例
        setContentView(new MyView(this));
    }
}


代码解析:GameView.java为主Activity,这里有设置全屏的操作和显示视图类

设置全屏的操作主要有两点:隐去状态栏部分,包括电池等图标;把应用程序的名字也隐去不显示。

显示视图就要设置显示的View实例:setContentView(new MyView(this));

==>MyView.java

package com.gameView;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;

public class MyView extends View{
	private int textX=20,textY=20;
	/**
	 * 重写父类构造函数
	 * 
	 */
	public MyView(Context context){
		super(context);
		setFocusable(true);
	}
	
	/**
	 * 重写父类绘图函数
	 */
	public void onDraw(Canvas canvas){
		//创建一个画笔的实例
		Paint paint = new Paint();
		//设置画笔的颜色
		paint.setColor(Color.WHITE);
		//绘制文本
		canvas.drawText("game", textX, textY, paint);
		super.onDraw(canvas);
	}
	
	/**
	 * 重写按键按下事件函数
	 */
	public boolean onKeyDown(int keyCode,KeyEvent event) {
		//判定用户按下的键值是否为方向键的“上下左右”键
		if (keyCode == KeyEvent.KEYCODE_DPAD_UP){
				//“上”按键被点击,应该让文本Y坐标变小
				textY -=2;
		} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN){
				//“下”按键被点击,应该让文本Y坐标变大
				textY +=2;
		} else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT){
				//“左”按键被点击,应该让文本X坐标变小
				textX -=2;
		} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT){
				//“右”按键被点击,应该让文本x坐标变大
				textX +=2;
		}
		invalidate();
		return super.onKeyDown(keyCode, event);
	}
	
	/**
	 * 重写按钮抬起的事件函数
	 */
	public boolean onKeyUp(int keyCode,KeyEvent event){
		return super.onKeyUp(keyCode, event);
	}
	
	/**
	 * 重写触屏事件函数
	 */
	public boolean onTouchEvent(MotionEvent event) {
		//获取用户手指触屏的x坐标赋值与文本的X坐标
		textX = (int)event.getX();
		//获取用户手指触屏的Y坐标赋值与文本的Y坐标
		textY = (int)event.getY();
		//重绘画布
		invalidate();
		//postInvalidate();
		//return super.onTouchEvent(event);
		return true;
	}	
}

代码解释:MyView这个类继承了View视图,重写了了父类View中的函数。

MyView.java就是一个View游戏框架,它重写了父类的构造函数、绘图函数、按键按下事件函数、按键抬起事件函数、还有触屏事件函数。

绘图函数onDraw

按键监听onKeyDown、onKeyUp

触屏监听onTouchEvent

注:要让文本的位置发生改变需要设置当前View获取焦点

在构造函数中设置焦点:setFocusable(true);

重新绘制画布:调用invalidate()或postInvalidate();

实现动态效果有两种方式:这里我们调用重绘函数:invaladate();

=>不断的绘制新的画布;

=>使用一张画布,通过刷屏来让这张画布恢复到初始空白画布的状态,然后再向画布上进行绘制;

注:使用了重绘函数就不要再去做刷屏操作。

项目运行效果:

 

3.SurfaceView游戏框架

创建实例:GameSurfaceVeiw

项目源代码:

==>MainActivity.java

package com.gsf;

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

public class MainActivity extends Activity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//设置全屏
		this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		//显示自定义的SurfaceView视图
		setContentView(new MySurfaceView(this));
	}
}

==>MySurfaceView.java(SurfaceView游戏框架)

package com.gsf;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
/**
 * 
 * @author Himi
 *
 */
public class MySurfaceView extends SurfaceView implements Callback, Runnable {
	//用于控制SurfaceView
	private SurfaceHolder sfh;
	//声明一个画笔
	private Paint paint;
	//文本的坐标
	private int textX = 10, textY = 10;
	//声明一条线程
	private Thread th;
	//线程消亡的标识位
	private boolean flag;
	//声明一个画布
	private Canvas canvas;
	//声明屏幕的宽高
	private int screenW, screenH;
	/**
	 * SurfaceView初始化函数
	 */
	public MySurfaceView(Context context) {
		super(context);
		//实例SurfaceHolder
		sfh = this.getHolder();
		//为SurfaceView添加状态监听
		sfh.addCallback(this);
		//实例一个画笔
		paint = new Paint();
		//设置画笔颜色为白色
		paint.setColor(Color.WHITE);
		//设置焦点
		setFocusable(true);
	}
	/**
	 * SurfaceView视图创建,响应此函数
	 */
	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		screenW = this.getWidth();
		screenH = this.getHeight();
		flag = true;
		//实例线程
		th = new Thread(this);
		//启动线程
		th.start();
	}
	/**
	 * 游戏绘图
	 */
	public void myDraw() {
		try {
			canvas = sfh.lockCanvas();
			if (canvas != null) {
				//-----------利用填充矩形的方式,刷屏
				////绘制矩形
				//canvas.drawRect(0,0,this.getWidth(),
				//this.getHeight(), paint);
				//-----------利用填充画布,刷屏
				//		canvas.drawColor(Color.BLACK);
				//-----------利用填充画布指定的颜色分量,刷屏
				canvas.drawRGB(0, 0, 0);
				canvas.drawText("Game", textX, textY, paint);
			}
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			if (canvas != null)
				sfh.unlockCanvasAndPost(canvas);
		}
	}
	/**
	 * 触屏事件监听
	 */
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		textX = (int) event.getX();
		textY = (int) event.getY();
		return true;
	}
	/**
	 * 按键事件监听
	 */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		return super.onKeyDown(keyCode, event);
	}
	/**
	 * 游戏逻辑
	 */
	private void logic() {
	}
	@Override
	public void run() {
		while (flag) {
			long start = System.currentTimeMillis();
			myDraw();
			logic();
			long end = System.currentTimeMillis();
			try {
				if (end - start < 50) {
					Thread.sleep(50 - (end - start));
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	/**
	 * SurfaceView视图状态发生改变,响应此函数
	 */
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
	}
	/**
	 * SurfaceView视图消亡时,响应此函数
	 */
	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		flag = false;
	}
}

代码解析:以上程序为一个完整的游戏框架,在项目中添加线程,用于不断重绘画布以及不停地执行游戏逻辑。

SurfaceView游戏框架的组成:

=>必要的成员变量SurfaceHolder对象、Paint(画笔类)对象、Canvas(画布类)对象、Thread(线程类)对象、用于标志线程的消亡的标志位flag、屏幕的宽高screenW,screenH

=>初始化函数MySurfaceView

调用父类构造函数、实例SurfaceHolder对象、为SurfaceView添加状态监听、实例画笔、设置画笔颜色、设置焦点

=>视图创建时响应的函数surfaceCreated(SurfaceHolder holder)

获取屏幕的宽高、设置标志位为true、实例线程、启动线程

=>游戏绘图函数myDraw()

使用SurfaceHolder的lockCanvas()函数获取SurfaceView的Canvas对象,并对画布加锁,这两部同时是同时实现的

刷屏:这里使用的刷屏是利用填充画布指定的颜色分量

解锁画布和提交:unlockCanvasAndPost(Canvas canvas)

=>触屏事件监听:onTouchEvent(MotionEvent event)

=>按键事件监听:onKeyDown(int keyCode,KeyEvent event)

=>游戏逻辑函数:logic()

=>重写线程运行函数:run()

用于循环刷新画布和更新游戏逻辑

为了让刷帧尽可能保证一致:有以下优化

步骤1:首先通过系统函数获取到一个时间戳

long start = System.currentTimeMillis();

//在线程中的绘图、逻辑等的函数

步骤2:处理完以上所有函数之后,再次通过系统函数获取到一个时间戳

long end = System.currentTimeMillis();

步骤3:通过以上两个时间戳,可以知道这些函数所消耗的时间(end - start)>X,那线程没有必要去休眠,如果(end - start)< X,那线程的休眠时间应该为 X - (end - start)。

项目运行效果:

以上就是两个游戏框架的学习总结了,现在还看不到什么有趣的东西,只是在屏幕上显示一个文本“game”,它可以通过按键和触屏来改变位置。这只是一个开始,要做出华丽的东西,要有过硬的技术支持才行哦,现在我还是菜鸟。下一part:Canvas画布,有了画布就能画出很多有趣的图形了。

 

posted on 2012-06-10 19:21  1.曲待续  阅读(372)  评论(0编辑  收藏  举报

导航