3.5 游戏框架
大多数知名企业的游戏都是基于Apache Struts、Spring和Hibernate等开发框架的。这些框架都是基于MVC设计模式的,实现了业务和逻辑的分离,这已经成为当前游戏开发的主流模式。本节将简单分析Android平台提供的View和Surfaceview类的基本知识。
3.5.1 View类
View视图类是Android系统中的一个超类,在此类中几乎包含了所有的屏幕类型。每一个View都有一个用于绘图的画布,这个画布可以进行任意扩展。在游戏开发中也可以自定义View视图,这个画布的功能更能满足我们在游戏开发中的需要。
在Android系统中,任何一个View类都只需重写onDraw()方法来实现界面显示,自定义的视图可以是复杂的3D实现,也可以是非常简单的文本形式。
在游戏中最重要的就是与玩家的交互,例如,键盘输入、触笔点击事件如何来处理呢?在Android中提供了onKeyUp、onKeyDown、onKeyMultiple、onKeyPreIme、onTouchEvent和onTrackballEvent等方法,通过这些方法可以轻松处理游戏中的事件信息。在继承View时需要重载这几个方法,当有按键按下或弹起事件发生时,按键代码自动会传输给这些相应的方法来处理。
游戏的核心功能是不断绘图和刷新界面,视图可以通过onDraw()方法绘制,接下来重点分析如何刷新界面。在Android中提供了invalidate()方法来刷新界面。不能直接在线程中调用方法invalidate(),特别是不能在子线程中调用,因为这违背了Android单线程模型。Android UI操作并不是线程安全的,并且这些操作必须在UI线程中执行,因此Android中最常用的方法就是利用Handler来实现UI线程的更新。
接下来通过一个具体实例,介绍在Android中使用View类实现屏幕更新显示的方法。
实例3-13 使用View类(daima\3\ViewC)。
在本实例中,要在屏幕上绘制了一个不停变换颜色的矩形,并且定义一些事件来通过模拟器的上下键调节矩形的位置(比如把这个矩形向上移动或者把这个矩形向下移动)。本实例的实现文件是ViewC.java,主要代码如下所示。
public class ViewC extends View { int miCount = 0; int y = 0; public ViewC(Context context) { super(context); } // //绘图处理 public void onDraw(Canvas canvas) { if (miCount < 100) { miCount++; } else { miCount = 0; } //绘图 Paint mPaint = new Paint(); switch (miCount%4) { case 0: mPaint.setColor(Color.BLUE); break; case 1: mPaint.setColor(Color.GREEN); break; case 2: mPaint.setColor(Color.RED); break; case 3: mPaint.setColor(Color.YELLOW); break; default: mPaint.setColor(Color.WHITE); break; } //绘制矩形--后面将详细讲解 canvas.drawRect((320-80)/2, y, (320-80)/2+80, y+40, mPaint); } }
执行后将在屏幕内绘制一个矩形,并随着线程的变化矩形的填充颜色随之变化,从而实现闪烁效果,如图3-35所示。通过键盘上的上下键来移动矩形,如图3-36所示。
图3-35 闪烁效果
图3-36 上下移动矩形
3.5.2 SurfaceView类
类SurfaceView在游戏开发中有着举足轻重的作用,它对画面的控制有更大的自由度。SurfaceView类有个双缓冲机制,在开发游戏时经常用到,提高整个效率。
1. SurfaceView类基础
在Android中开发游戏时,一般来说,要想写一个复杂一点的游戏,必须使用SurfaceView来实现。SurfaceView可直接访问一个可画图的界面,可以控制在界面顶部的子视图层。SurfaceView是提供给需要直接画像素而不是使用窗体部件的应用使用的。Android图形系统中的一个重要的概念和线索是Surface(界面)、View(视图)及其子类,如TextView和Button。
要想在Surface上绘图。则必须为每个Surface创建一个Canvas对象(但属性时常改变),用来管理View在Surface上的绘图操作,如画点与画线。还要注意的是,在使用SurfaceView的时候,一般都是出现在最顶层。
在使用SurfaceView的时候,一般情况下还要对创建、销毁、改变时的情况进行监视,格式如下所示。
//在 surface的大小发生改变时激发 public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){} //在创建时激发,一般在这里调用画图的线程 public void surfaceCreated(SurfaceHolder holder){} //销毁时激发,一般在这里将画图的线程停止、释放 public void surfaceDestroyed(SurfaceHolder holder) {}
surfaceCreated会首先被调用,然后是surfaceChanged,当程序结束时会调用surfaceDestroyed。
由于SurfaceHolder是一个共享资源,所以在对其操作时应该实行“互斥操作”,即需要使用synchronized的“封锁”机制。
渲染文字的工作实际上是主线程(也就是LunarView类)的父类View的工作,而并不属于工作线程LunarThread,在工作线程中是无法控制的,所以改为向主线程发送一个Message来代替,让主线程通过Handler对接收到的消息进行处理,从而更新界面文字信息。
接下来将通过一个具体的实例,介绍在Android中使用SurfaceView类实现屏幕更新显示的流程。
实例3-14 使用SurfaceView类实现屏幕内容的闪烁显示(daima\3\SurfaceC)。
本实例的实现文件是SurfaceC.java,具体代码如下所示。
package com.SurfaceC; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.SurfaceHolder; import android.view.SurfaceView; public class SurfaceC extends SurfaceView implements SurfaceHolder.Callback,Runnable { //控制循环 boolean mbLoop = false; //定义SurfaceHolder对象 SurfaceHolder mSurfaceHolder = null; int miCount = 0; int y = 50; public SurfaceC(Context context) { super(context); //实例化SurfaceHolder mSurfaceHolder = this.getHolder(); //添加回调 mSurfaceHolder.addCallback(this); this.setFocusable(true); mbLoop = true; } //在surface的大小发生改变时激发 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } //在surface创建时激发 public void surfaceCreated(SurfaceHolder holder) { //开启绘图线程 new Thread(this).start(); } //在surface销毁时激发 public void surfaceDestroyed(SurfaceHolder holder) { //停止循环 mbLoop = false; } //绘图循环 public void run() { while (mbLoop) { try { Thread.sleep(200); } catch (Exception e) { } synchronized( mSurfaceHolder ) { Draw(); } } } //绘图方法 public void Draw() { //锁定画布,得到canvas Canvas canvas= mSurfaceHolder.lockCanvas(); if (mSurfaceHolder==null || canvas == null ) { return; } if (miCount < 100) { miCount++; } else { miCount = 0; } //绘图 Paint mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setColor(Color.BLACK); //绘制矩形--清屏作用 canvas.drawRect(0, 0, 320, 480, mPaint); switch (miCount % 4) { case 0: mPaint.setColor(Color.BLUE); break; case 1: mPaint.setColor(Color.GREEN); break; case 2: mPaint.setColor(Color.RED); break; case 3: mPaint.setColor(Color.YELLOW); break; default: mPaint.setColor(Color.WHITE); break; } // 绘制矩形--后面将详细讲解 canvas.drawCircle((320 - 25) / 2, y, 50, mPaint); // 绘制后解锁,绘制后必须解锁才能显示 mSurfaceHolder.unlockCanvasAndPost(canvas); } }
执行后将在屏幕内绘制一个圆形,并随着线程的变化,圆形的填充颜色也随之变化,从而实现闪烁效果,如图3-37所示;可以通过键盘上的上下键来移动圆形,如图3-38所示。
图3-37 闪烁效果
图3-38 上下移动圆形
2. 双缓冲
双缓冲的是一种防止动画闪烁的多线程应用,基于SurfaceView的双缓冲实现很简单,只需开一条线程并在其中绘图即可。Android中的SurfaceView类就基于双缓冲机制,所以在开发游戏时应尽量使用SurfaceView而不要使用View,这样效率会较高,而且SurfaceView的功能也更加完善。
双缓冲的核心是先通过方法setBitmap()将要绘制的所有图形绘制到一个Bitmap(图像对象)上,然后调用方法drawBitmap()绘制这个Bitmap,并在屏幕上显示出来。下面将通过一个具体的实例,讲解在Android中使用SurfaceView类实现双缓冲。
实例3-15 使用双缓冲技术在屏幕中显示一幅图片(daima\3\shuanghuan)。
本实例的实现文件是shuanghuan.java,具体代码如下所示。
package com.shuanghuan; import com.shuanghuan.R; import Android.content.Context; import Android.graphics.Bitmap; import Android.graphics.Canvas; import Android.graphics.Paint; import Android.graphics.Bitmap.Config; import Android.graphics.drawable.BitmapDrawable; import Android.view.KeyEvent; import Android.view.MotionEvent; import Android.view.View; public class shuanghuan extends View implements Runnable { /* 声明Bitmap对象 */ Bitmap mBitQQ = null; Paint mPaint = null; /* 创建一个缓冲区 */ Bitmap mSCBitmap = null; /* 创建Canvas对象 */ Canvas mCanvas = null; public shuanghuan(Context context) { super(context); /* 装载资源 */ mBitQQ = ((BitmapDrawable) getResources().getDrawable(R.drawable.qq)).getBitmap(); /* 创建屏幕大小的缓冲区 */ mSCBitmap=Bitmap.createBitmap(320, 480, Config.ARGB_8888); /* 创建Canvas */ mCanvas = new Canvas(); /* 设置将内容绘制在mSCBitmap上 */ mCanvas.setBitmap(mSCBitmap); mPaint = new Paint(); /* 将mBitQQ绘制到mSCBitmap上 */ mCanvas.drawBitmap(mBitQQ, 0, 0, mPaint); /* 开启线程 */ new Thread(this).start(); } public void onDraw(Canvas canvas) { super.onDraw(canvas); /* 将mSCBitmap显示到屏幕上 */ canvas.drawBitmap(mSCBitmap, 0, 0, mPaint); } //触笔事件 public boolean onTouchEvent(MotionEvent event) { return true; } //按键按下事件 public boolean onKeyDown(int keyCode, KeyEvent event) { return true; } //按键弹起事件 public boolean onKeyUp(int keyCode, KeyEvent event) { return false; } public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { return true; } /*线程处理*/ public void run() { while (!Thread.currentThread().isInterrupted()) { try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } //使用postInvalidate可以直接在线程中更新界面 postInvalidate(); } } }
执行后将用双缓冲技术在屏幕中显示图片,效果如图3-39所示。
图3-39 执行效果