前面有几篇文章写的是对Android示例程序贪吃蛇Snake程序的剖析,本文继续分析Android自带的另一个小游戏LunarLander的程序。在贪吃蛇Snake程序中采用了“定时器+系统调用onDraw”的架构,而LunarLander程序采用的是“多线程+强制自行绘制”的架构思路,比前者更为实用。

与贪吃蛇Snake程序的对比

就界面Layout来说,这个程序其实和Snake没有什么不同,同样是采用了FrameLayout,而且游戏的主界面由一个自定义的View来实现,这里是LunarView。读过贪吃蛇程序剖析文章的朋友也许会发现,Snake的架构是“定时器+系统调用onDraw”来实现的,这里有一个最大的缺陷就是onDraw是由Android系统来调用的,我们只能依赖它,却无法自行控制。这就好比一个黑盒,当然,总是能把我们要的东西给做出来,可却无法控制其做事的细节,这对于游戏这样高效率的东西可是不利的,因此最好的解决之道当然是把绘制这部分工作自己”承包“过来,告别吃大锅饭的,进入”联产承包制”时代。

此外,由于游戏的本质就是连续两帧图片之间发生些许差异,那么要不断催生这种差异的发生,只要有某种连续不断发生的事件在进行就可以,例如Snake中使用的定时器,就是在不断地产生这种“差异源”,与此类似,一个线程也是不断在运行中,通过它也是可以不断产生这种“差异源”的。

如果说Snake中使用的Layout加自定义View是一把小型武器的话,那在SurfaceView对于android中游戏的开发来说就算是重型武器了。我们使用前者时总是容易把游戏中某个对象(比如上文的每一个方格)当做一个小组件来处理,而后者则根本没有这种划分的概念,在它眼中,所有东西都是在Canvas(画布)中自行绘制出来的(背景,人物等)。

SurfaceView提供直接访问一个可画图的界面,可以控制在界面顶部的子视图层。SurfaceView是提供给需要直接画像素而不是使用窗体部件的应用使用的。Android图形系统中一个重要的概念和线索是surface。View及其子类(如TextView, Button)要画在surface上。每个surface创建一个Canvas对象(但属性时常改变),用来管理view在surface上的绘图操作,如画点画线。还要注意的是,使用它的时候,一般都是出现在最顶层的:The view hierarchy will take care of correctly compositing with the Surface any siblings of the SurfaceView that would normally appear on top of it. 使用的SurfaceView的时候,一般情况下还要对其进行创建、销毁、改变时的情况进行监视,这就要用到SurfaceHolder.Callback。

Java代码classLunarViewextendsSurfaceViewimplementsSurfaceHolder.Callback

{

publicvoidsurfaceChanged(SurfaceHolder holder,intformat,intwidth,intheight){}

//在surface的大小发生改变时激发

publicvoidsurfaceCreated(SurfaceHolder holder){}

//在创建时激发,一般在这里调用画图的线程。

publicvoidsurfaceDestroyed(SurfaceHolder holder) {}

//销毁时激发,一般在这里将画图的线程停止、释放。

}

surfaceCreated会首先被调用,然后是surfaceChanged,当程序结束时会调用surfaceDestroyed。下面来看看LunarView最重要的成员变量,也就是负责这个View所有处理的线程。

Java代码privateLunarThread thread;// 实际工作线程

thread =newLunarThread(holder, context,newHandler() {

@Override

publicvoidhandleMessage(Message m)

{

mStatusText.setVisibility(m.getData().getInt("viz"));

mStatusText.setText(m.getData().getString("text"));

}

});

这个线程由私有类LunarThread实现,它里面还有一个自己的消息队列处理器,用来接收游戏状态消息,并在屏幕上显示当前状态(而这个功能在Snake中是通过View自己控制其包含的TextView是否显示来实现的,相比之下,LunarThread的消息处理机制更为高效)。由于有了LunarThread这个负责具体工作的对象,所以LunarView的大部分工作都委托给后者去执行。

Java代码publicvoidsurfaceChanged(SurfaceHolder holder,intformat,intwidth,intheight){

thread.setSurfaceSize(width, height);

}

publicvoidsurfaceCreated(SurfaceHolder holder)

{//启动工作线程结束

thread.setRunning(true);

thread.start();

}

publicvoidsurfaceDestroyed(SurfaceHolder holder)

{

booleanretry =true;

thread.setRunning(false);

while(retry)

{

try

{//等待工作线程结束,主线程才结束

thread.join();

retry =false;

}

catch(InterruptedException e)

{

}

}

}

工作线程LunarThread

由于SurfaceHolder是一个共享资源,因此在对其操作时都应该实行“互斥操作“,即需要使用synchronized进行”封锁“机制。

再来讨论下为什么要使用消息机制来更新界面的文字信息呢?其实原因是这样的,渲染文字的工作实际上是主线程(也就是LunarView类)的父类View的工作,而并不属于工作线程LunarThread,因此在工作线程中式无法控制的。所以我们改为向主线程发送一个Message来代替,让主线程通过Handler对接收到的消息进行处理,从而更新界面文字信息。再回顾Android示例程序剖析之Snake贪吃蛇(三:界面UI、游戏逻辑和Handler)中SnakeView里的文字信息更新,由于是SnakeView自己(就这一个线程)对其包含的TextView做控制,当然没有这样的问题了。

Java代码publicvoidsetState(intmode, CharSequence message)

{

synchronized(mSurfaceHolder)

{

mMode = mode;

if(mMode == STATE_RUNNING)

{//运行中,隐藏界面文字信息

Message msg = mHandler.obtainMessage();

Bundle b =newBundle();

b.putString("text","");

b.putInt("viz", View.INVISIBLE);

msg.setData(b);

mHandler.sendMessage(msg);

}

else

{//根据当前状态设置文字信息

mRotating =0;

mEngineFiring =false;

Resources res = mContext.getResources();

CharSequence str ="";

if(mMode == STATE_READY)

str = res.getText(R.string.mode_ready);

elseif(mMode == STATE_PAUSE)

str = res.getText(R.string.mode_pause);

elseif(mMode == STATE_LOSE)

str = res.getText(R.string.mode_lose);

elseif(mMode == STATE_WIN)

str = res.getString(R.string.mode_win_prefix)

+ mWinsInARow +" "

+ res.getString(R.string.mode_win_suffix);

if(message !=null) {

str = message +"\n"+ str;

}

if(mMode == STATE_LOSE)

mWinsInARow =0;

Message msg = mHandler.obtainMessage();

Bundle b =newBundle();

b.putString("text", str.toString());

b.putInt("viz", View.VISIBLE);

msg.setData(b);

mHandler.sendMessage(msg);

}

}

}

下面就是LunaThread这个工作线程的执行函数了,它一直不断在重复做一件事情:锁定待绘制区域(这里是整个屏幕),若游戏还在进行状态,则更新底层的数据,然后直接强制界面重新绘制。

Java代码publicvoidrun()

{

while(mRun)

{

Canvas c =null;

try

{

//锁定待绘制区域

c = mSurfaceHolder.lockCanvas(null);

synchronized(mSurfaceHolder)

{

if(mMode == STATE_RUNNING)

updatePhysics();//更新底层数据,判断游戏状态

doDraw(c);//强制重绘制

}

}

finally

{

if(c !=null) {

mSurfaceHolder.unlockCanvasAndPost(c);

}

}

}

}

这里要注意的是最后要调用unlockCanvasAndPost来结束锁定画图,并提交改变。

强制自行绘制

doDraw这段代码就是在自己的Canvas上进行绘制,具体的绘制就不解释了,主要就是用drawBitmap,drawRect,drawLine。值得注意的一段代码是下面这个:

Java代码canvas.save();

canvas.rotate((float) mHeading, (float) mX, mCanvasHeight

- (float) mY);

if(mMode == STATE_LOSE) {

mCrashedImage.setBounds(xLeft, yTop, xLeft + mLanderWidth, yTop

+ mLanderHeight);

mCrashedImage.draw(canvas);

}elseif(mEngineFiring) {

mFiringImage.setBounds(xLeft, yTop, xLeft + mLanderWidth, yTop

+ mLanderHeight);

mFiringImage.draw(canvas);

}else{

mLanderImage.setBounds(xLeft, yTop, xLeft + mLanderWidth, yTop

+ mLanderHeight);

mLanderImage.draw(canvas);

}

canvas.restore();

在绘制火箭的前后,调用了save()和restore(),它是先保存当前矩阵,将其复制到一个私有堆栈上。然后接下来对rotate的调用还是在原有的矩阵上进行操作,但当restore调用后,以前保存的设置又重新恢复。不过,在这里还是看不出有什么用处。

暂停/继续机制

LunarLancher的暂停其实并没有不再强制重绘制,而是没有对底层的数据做任何修改,依然绘制同一帧画面,而继续则是把mLastTime设置为当前时间+100毫秒的时间点,因为以前暂停时mLastTime就不再更新了,这样做事为了与当前时间同步起来。

Java代码publicvoidpause()

{//暂停

synchronized(mSurfaceHolder)

{

if(mMode == STATE_RUNNING)

setState(STATE_PAUSE);

}

}

publicvoidunpause()

{// 继续

// Move the real time clock up to now

synchronized(mSurfaceHolder)

{

mLastTime = System.currentTimeMillis() +100;

}

setState(STATE_RUNNING);

}

这样做的目的是为了制造“延迟“的效果,都是因为updatePhysics函数里这两句:

Java代码if(mLastTime > now)return;

doubleelapsed = (now - mLastTime) /1000.0;

至于游戏的控制逻辑和判定部分就不介绍了,没有多大意思。

2012年9月10日

android程序开发实例,Android示例程序剖析之LunarLander游戏相关推荐

  1. Android 蓝牙开发实例--蓝牙聊天程序的设计和实现

    作者在这里介绍的这个实例是Google SDK中提供的一个蓝牙聊天程序,简单但信息量巨大,非常适合初学者学习蓝牙方面的知识. 在学习这个实例前请读者仔细阅读并理解Socket的工作原理和实现机制,作者 ...

  2. php小程序开发实例,微信小程序全局配置开发实例

    本文主要和大家分享微信小程序全局配置开发实例,主要以代码的形式和大家分享,希望能帮助到大家. 一.app.json 使用app.json文件来对微信小程序进行全局配置,决定页面文件的路径.窗口表现.设 ...

  3. android 图库开发实例,Android中从图库中选取图片实例详解

    android 从图库中选取图片 在android中,如何从图库gallary中挑选图片呢,其实很简单,步骤如下 1) 设计一个imageview,用来显示图库选出来的图片 android:orien ...

  4. android rfid开发实例,Android NFC读卡 高频卡 RFID

    [实例简介] Android NFC读卡 高频卡 RFID 仅供参考,有需要这方面资料的可以联系我 961500278@qq.com [实例截图] [核心代码] d303bda7-45a0-4b95- ...

  5. android备忘录开发实例,android 备忘录源码(超精细)

    [实例简介] 备忘录开发全部流程,与数据库的连接,讲解超级精细. [实例截图] [核心代码] 02e73e17-9290-42f1-8d21-54e6bdd12788 └── Chapter17_To ...

  6. android mdns开发实例,android resolve .local (mDNS)

    问题 I'm looking for a solution to resolve .local host names with Android 4.0.4 (no NSD, due to API le ...

  7. android mdns开发实例,Android开发_mDNS移植Android系统方案介绍

    mDNS移植Android系统简介.最近有个客户,需要在他的机顶盒上支持Airplay, 其机顶盒系统是Android4.0.4, 此版本系统没有mDNS(需要Android4.1+)及MediaCo ...

  8. 养车记账本小程序开发实例

    前言 自微信6.5.3版本开始,小程序正式跟大家见面了.最近利用业余时间做了个小程序,命名"养车记账本".作为IT狗,经历了从注册开发者资质开始到正式上线的全过程,微信小程序官方  ...

  9. OpenCV android sdk配置OpenCV android NDK开发实例

    OpenCV android sdk配置OpenCV android NDK开发实例 [尊重原创,转载请注明出处]http://blog.csdn.net/guyuealian/article/det ...

最新文章

  1. hadoop 2.x安装:不能加载本地库 - 解决libc.so.6 version GLIBC_2.14 not found问题
  2. Keil代码整体偏移和查找功能
  3. jQuery的效果方法
  4. 纸板箱机器人制作比例图纸_造一个黄油机器人(Butter Robot)
  5. 无连接网络通信程序UDP
  6. 从零开始学_JavaScript_系列(四)——jquery(基础,选择器,触发条件,动画,回调函数)...
  7. 企业如何选择数据分析架构?——谈谈3种架构的利弊
  8. Objective-C中小怪兽的逻辑
  9. 9-51单片机ESP8266学习-AT指令(测试TCP服务器--51单片机程序配置8266,C#TCP客户端发信息给单片机控制小灯的亮灭)...
  10. windows之2012缺少api-ms-win-crt**.dll
  11. CVPR学习(二):CVPR2019-行人重识别
  12. php 5.3 include 上层 function,php5.3开始出现的Function ereg() is deprecated Error问题解决办法...
  13. vijos1655萌萌的糖果博弈
  14. cad如何多选对象_CAD快速选择和选择类似对象怎么用
  15. 云计算服务三层架构-IaaS-PaaS-SaaS解析
  16. php加图片源码_php给现有的图片加文字水印代码
  17. pca图解读_主成分分析pca图解读,主成分分析散点图解读
  18. linux可变剪切分析,生信实操|一个生信素人的上道经验分享-转录组测序(可变剪接篇)...
  19. 网口压线顺序_网线水晶头接法口诀顺序及接法详细步骤
  20. Holt-Winters模型原理分析及代码实现(python)

热门文章

  1. python连接fanuc机器人、fanuc机器人以太网通信、发那科机器人以太网通信 fanuc socket 、fanuc TCP协议 通信 fanuc机器人与PC通讯
  2. C. Serval and Toxel‘s Arrays
  3. Python:网络爬虫爬取某表情包网站
  4. 盘点4大下载神器,教你分分钟搞定文件下载
  5. 南昌市-中安协-安防工程企业设计施工维护能力评价
  6. postman如何测试php接口_接口测试工具postman
  7. http://www.w3schools.com/
  8. 计算机博士复试英语自我介绍,博士复试英语自我介绍
  9. Java、JSP货运管理系统
  10. 台式电脑中病毒了文件夹变exe应用程序文件找到法子