制作游戏是怎么限制自适应屏幕代码出界的 求代码

游戏开发实例《是男人就坚持20秒》_第9章
成为游戏开发家族的一员_华初网
所在位置:
成为游戏开发家族的一员<font color="#.3
游戏开发实例《是男人就坚持20秒》
&div>《是男人就坚持20秒》是一款大家都非常熟悉的经典游戏,玩家需要操作飞机,不停地躲避铺天盖地的炮弹袭击,存活的时间越长越好。图9-1~图9-3展示了该游戏的部分截图。&/div>&div>
&/div>&div>图9-1& 《是男人就坚持20秒》游戏截图1&&&& 图9-2& 《是男人就坚持20秒》游戏截图2 &/div>&div>这款游戏影响非常广泛,其开发过程相对简单,游戏类型具备一定的代表性,因而经常被用于游戏开发教学。本节将通过这款普通的游戏,向读者讲述Android游戏的开发思路。这里完整展示了Android游戏的开发过程,在保留核心游戏规则的基础上,对游戏进行了适当的简化,以便于讲述和学习。通过本节的学习,读者可以体验到: &/div>&div>l&&&&&&&&& 完整的游戏开发流程;&/div>&div>l&&&&&&&&& 通用性的游戏开发架构;&/div>&div>l&&&&&&&&& 精妙而细腻的游戏算法;&/div>&div>l&&&&&&&&& Android游戏开发的独特之处;&/div>&div>游戏开发的无穷魅力。&/div>&div>&/div>&div>图9-3& 《是男人就坚持20秒》游戏截图3&/div>&div>9.3.1& 游戏策划 &/div>&div>游戏策划是游戏开发的第一步,是至关重要的一个阶段。在进行游戏策划时,必须敏锐地把握市场需求的微弱变化。Android游戏策划案的形式往往是多种多样的,但是一份优秀的策划案应该包含两方面的内容:需求满足和游戏创意。&/div>&div>1. 需求满足&/div>&div>很多策划师每天都会有不计其数的创意闪现,而纠结于如何取舍。优秀的策划师则会站在市场的角度去筛选和评估这些创意。需求满足文档需要详细分析目标顾客的刚性需求,并完整展示满足需求的策略。需求满足将起到提纲挈领的作用,将直接影响到产品定位以及开发投入。 &/div>&div>2. 游戏创意&/div>&div>游戏的创意点是策划师智慧的结晶,它与“需求满足”部分紧密联系,前者是果,后者是因;前者是叶,后者是根。完美而饱含激情地展示游戏创意,不仅能让开发人员更加充满信心地投入开发,而且能为将来的推广助一臂之力。&/div>&div>本节介绍的《是男人就坚持20秒》游戏的策划非常简单,表9-1~表9-3仅列出了与下文联系比较紧密的部分策划案内容,其他部分不再赘述。 &/div>&div>表9-1& 总 体 策 划&/div>&div align=center>名  称&/div>&div align=center>内  容&/div>&div>图像&/div>&div>二维图像&/div>&div>游戏帧&/div>&div>每秒固定30帧&/div>&div>图像风格&/div>&div>卡通/冷色&/div>&div>音乐&/div>&div>未配音乐&/div>&div>&&/div>&div>&&/div>&div>表9-2& 游戏界面设计 &/div>&div align=center>名  称&/div>&div align=center>描  述&/div>&div align=center>参考大小(像素)&/div>&div align=center>备  注&/div>&div>背景&/div>&div>星空背静图&/div>&div>320×480&/div>&div>&&/div>&div>运动体一&/div>&div>飞机&/div>&div>48×48&/div>&div>能在整个背景图上移动&/div>&div>运动体二&/div>&div>炮弹&/div>&div>8×8&/div>&div>能在整个背景图上移动&/div>&div>文本框&/div>&div>计时器&/div>&div>&&/div>&div>显示坚持时间&/div>&div>该游戏的界面二(开始界面)只有两个游戏选项的菜单:开始游戏和离开游戏;界面三(结束界面)显示游戏坚持的时间,包括两个选项:开始游戏和退出。&/div>&div>表9-3& 动作设计 &/div>&div align=center>名  称&/div>&div align=center>移 动 方 向&/div>&div align=center>运动速度(像素/桢)&/div>&div align=center>补 充 说 明&/div>&div>飞机&/div>&div>二维平面,任意角度&/div>&div>恒定:3&/div>&div>中弹后触发爆炸特效,游戏结束&/div>&div>低速炮弹&/div>&div>二维平面,任意角度&/div>&div>可变:2+t/3其中t为游戏时间,单位为秒)&/div>&div>每2秒产生一批炮弹,每批炮弹数量为:20+t*5 (其中t表示游戏时间,单位为秒)&/div>&div>高速炮弹&/div>&div>二维平面,任意角度&/div>&div>可变:4+t/2(其中t为游戏时间,单位为秒)&/div>&div>每5秒产生一批炮弹,每批炮弹数量为:5+t*2 (其中t表示游戏时间,单位秒)&/div>&div>&&/div>&div>&&/div>&div>l&&&&& 游戏风格:在策划阶段明确指定美术风格能够避免不少的麻烦。 &/div>&div>l&&&&& 炮弹数量:当sprite(即炮弹)较多时,制约游戏运行流畅性的瓶颈不再是游戏逻辑,而是画面绘制。因此,在不影响游戏性的基础上,可以考虑适当提高炮弹的速度,以减少炮弹的数量。 &/div>&div>&&/div>&div>游戏策划完成之后,程序开发人员和美术设计人员将接手游戏开发工作,实现从创意到产品的飞跃。&/div>&div>9.3.2& 游戏构架的搭建&/div>&div>在编写游戏代码之前,必须要仔细地理顺思路,清晰地构建出整个游戏的框架。有的开发者经常抱怨说,游戏开发到最后总是千头万绪,一旦出现Bug就不知道该如何修改。其实不然,只要搭建好了游戏的架构,游戏开发就会比较容易。接下来,将逐步展示《是男人就坚持20秒》游戏架构的搭建过程。 &/div>&div>在掌握策划案的基础上,程序员应该迅速作出判断,细分出游戏的功能模块。《是男人就坚持20秒》的功能模块划分如表9-4所示。 &/div>&div>表9-4& 游戏的功能模块划分&/div>&div align=center>名  称&/div>&div align=center>说  明&/div>&div>移动模块&/div>&div>实现飞机与炮弹的移动&/div>&div>炮弹生成模块&/div>&div>生成炮弹&/div>&div>碰撞判断模块&/div>&div>碰撞判断&/div>&div>绘图模块&/div>&div>绘制游戏界面&/div>&div>内存管理模块&/div>&div>高效管理内存资源&/div>&div>资源初始化模块&/div>&div>加载图片,初始化数据,计算飞机碰撞范围&/div>&div>划分功能模块之后,可以做出项目的流程图,如图9-4和图9-5所示。&/div>&div>&&&& &/div>&div>& 图9-4& 程序总流程图&&&&&&& 图9-5& 游戏运行阶段的细化流程图 &/div>&div>完成了项目流程图之后,还不能直接进行Android游戏编程,项目执行流程和游戏运行流程是存在着较大差别的,因为项目流程图只是从用户体验角度(或者策划角度)进行的宏观描述,它只起到过渡作用;而用于编程的流程图则是基于程序角度给出的微观描述,它将涉及到编程的实际环境。因此,我们还需要分析游戏实际运行的流程,即“状态转换”。&/div>&div>《是男人就坚持20秒》游戏的状态相对比较简单,只有如表9-5所示的4种状态。&/div>&div>表9-5& 游戏状态 &/div>&div align=center>名  称&/div>&div align=center>说  明&/div>&div>游戏菜单&/div>&div>游戏入口、游戏选项&/div>&div>游戏运行&/div>&div>游戏运行状态&/div>&div>游戏暂停&/div>&div>游戏被暂停的状态&/div>&div>资源加载&/div>&div>游戏初始资源加载,或关卡资源加载状态&/div>&div>由于这个游戏总共就只有一关,所以关卡资源加载就只能加载唯一的一关资源。 &/div>&div>接下来,需要做游戏状态的转换图。状态转换一般通过有限自动机来表示,《是男人就坚持20秒》的状态转换比较简单,下面给出一种状态转换的参考图,如图9-6所示。&/div>&div>&/div>&div>图9-6& 状态转换图 &/div>&div>&&/div>&div>l&&&&& 如果开发大型的游戏,除了游戏状态转换外,还需要设计sprite状态转换,甚至是特效状态转换,比如玩家控制一个人,这个人可以从行走状态转换为跑步状态,可以从跑步状态转换到跳跃上升状态,跳跃上升状态满足一定的条件后将转换为跳跃下降状态,但是跳跃上升状态是不能直接转换到跑步状态的。可见,行为比较复杂的sprite是需要设计状态转换的。sprite的每一种运动状态将对应一系列的连贯图画。 &/div>&div>l&&&&& sprite状态转换和特效状态转换的实现方式与游戏状态转换大同小异,可以举一反三。&/div>&div>&&/div>&div>完成了前面几步的准备工作后,框架基本上就出来了。虽然《是男人就坚持20秒》的架构比较简单,但是通用性和扩展性都比较强。下面的这段伪代码给出了一种高效的游戏框架,以供参考。&/div>&div>While(游戏未结束) &/div>&div>{ &/div>&div>&&& State_change();//状态转换 &/div>&div>&&& Update_fps();//状态执行 &/div>&div>&&& Draw();//绘制 &/div>&div>&} &/div>&div>& &/div>&div>其中,State_change()函数完成游戏状态间的转换,我们可以根据图9-6状态转换图实现,在此不再赘述。Update_fps()函数框架的伪代码如下: &/div>&div>Update_fps() &/div>&div>{ &/div>&div>&&& Switch(游戏状态)& &/div>&div>&&& Case 资源加载: &/div>&div>&&&&&&& Switch(游戏状态) &/div>&div>&&&&&&& Case 进入游戏: &/div>&div>&&&&&&&&&&& Load_game();//加载全局资源 &/div>&div>&&&&&&& Case 进入关卡: &/div>&div>&&&&&&&&&&& Load_gate();//加载背景、飞机、炮弹的图片&/div>&div>&&& Case 游戏菜单: &/div>&div>&&&&&&& If (游戏结束) 计算游戏结果 &/div>&div>&&& Case 游戏进行: &/div>&div>&&&&&&& New_paodan();//产生新炮弹 &/div>&div>&&&&&&& Move();//计算出该时刻飞机以及所有炮弹所在的位置 &/div>&div>&&&&&&& Is_pengzhuang();//碰撞判断 &/div>&div>&&& Case 游戏暂停: &/div>&div>&&&&&&& Thread_pause();//游戏暂停操作 &/div>&div>} &/div>&div>&Draw()函数框架的伪代码如下: &/div>&div>Draw() &/div>&div>{ &/div>&div>&&& Switch(游戏状态) &/div>&div>&&& Case 游戏进行: &/div>&div>&&&&&&& Draw_background();//绘制背景 &/div>&div>&&& &&&&Draw_paodan();//画炮弹 &/div>&div>&&&&&&& Draw_feiji();//画飞机 &/div>&div>&&& Case 其他: &/div>&div>&&&&&&& 略……&/div>&div>} &/div>&div>&&/div>&div>&&/div>&div>&&/div>&div>上述的draw()函数参考框架并不适用于所有的游戏。特别是使用了3D特效,或者需要实现图层管理器的游戏。draw()函数应该根据实际需要量身定制。draw()函数参考框架并不能保证绘图性能最优,高性能的图像绘制框架将非常复杂。 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&/div>&div>&&/div>&div>完成了游戏架构之后,建议完成如下文档: &/div>&div>l&&&&&&&&& 项目开发时程表;&/div>&div>l&&&&&&&&& 功能模块划分及接口表;&/div>&div>l&&&&&&&&& 技术备案表。&/div>&div>这三个表将帮助游戏开发人员明晰责权、监管制作,并为数据统计提供便利。 &/div>&div>9.3.3& 美工制作 &/div>&div>美术是软件灵魂的窗口,是用户体验的指挥棒,美术图片的成色将直接影响到游戏的成败。现阶段,Android游戏市场尚处于待开发阶段,游戏数量少,游戏竞争不激烈。在这种市场形势下,顾客关注的重心往往停留在表层,谁的游戏能够在视觉和听觉上征服玩家,谁就能主宰这个初期市场。如果程序相对弱一些,美工还能撑起一片天;如果美术相对弱一些,程序再强也不能得到用户的青睐。《是男人就坚持20秒》游戏也一样,美术制作至关重要。该游戏需要的美术图片较少,美术制作相对比较简单,文档如表9-6所示。&/div>&div>表9-6& 美术制作 &/div>&DIV align=center>&div align=center>编&&& 号&/div>&div align=center>大&&& 小(像素)&/div>&div align=center>图&/div>&div>背景图:1&/div>&div>320×480&/div>&div>略&/div>&div>炮弹:1&/div>&div>8×8&/div>&div>&/div>&div>飞机:1&/div>&div>48×48&/div>&div>&/div>&div>爆炸特效&/div>&div>1&/div>&div>80×80&/div>&div>&/div>&div>2&/div>&div>80×80&/div>&div>&/div>&div>3&/div>&div>80×80&/div>&div>&/div>&div>4&/div>&div>80×80&/div>&div>&/div>&div>5&/div>&div>80×80&/div>&div>&/div>&div>6&/div>&div>80×80&/div>&div>&/div>&div>7&/div>&div>80×80&/div>&div>&/div>&div>8&/div>&div>80×80&/div>&div>&/div>&div>9.3.4& 编程实现 &/div>&div>下面将介绍《是男人就坚持20秒》的代码实现过程。&/div>&div>1. 编程前需要认真考虑的问题&/div>&div>实现《是男人就坚持20秒》游戏编程之前,需要先考虑以下三个问题: &/div>&div>需要用到几个Activity;&/div>&div>需要用到几个线程;&/div>&div>是用View还是SurfaceView来实现画面绘制。&/div>&div>《是男人就坚持20秒》游戏比较简单,不需要使用高级组件,也不会涉及到底层开发。因此,不需要在各种功能Activity间切换,在主Activity绘制游戏画面就可以了。 &/div>&div>UI线程必不可少,除此之外,需要一个游戏主线程。由于需要加载的资源较少,不需要开辟资源加载线程;游戏没有使用网络功能,也没有音乐效果。因此,也不需要开辟额外的线程。 &/div>&div>游戏策划规定每秒钟实现30帧的游戏画面,即每1/30秒刷新一帧,用View实现的画面绘制不能严格恒帧,游戏画面的流畅性不能满足要求。因此,必须使用SurfaceView直接控制游戏界面绘制。&/div>&div>现在就可以回答之前提到的三个问题了: &/div>&div>需要用到几个Activity:一个Activity;&/div>&div>需要用到几个线程:两个线程;&/div>&div>用View还是surfaceView来实现画面绘制:SurfaceView。&/div>&div>l&&&&& Surface是Android图形系统中一个重要的概念,View及其子类(如TextView、Button)都得画在Surface上,Surface的内容直接对应屏幕上显示的内容。每个Surface创建一个Canvas对象(但属性时常改变),用来管理View在Surface上的绘图操作。&/div>&div>l&&&&& View和SurfaceView的区别在于:View是通过信息(message)机制间接指挥Surface完成绘制,SurfaceView则是直接操作底层接口,接管Surface的绘制。&/div>&div>2. 游戏主线程&/div>&div>考虑清楚了上述的三个问题之后,就可以开始编写代码了。首先,用两个变量mMode和pMode来保存游戏状态,变量取值及对应关系最好能够写到一个文档中,以便查看,如表9-7所示。&/div>&div>表9-7& 游戏状态表 &/div>&div align=center>mMode取值&/div>&div align=center>pMode取值&/div>&div align=center>表 示 状 态&/div>&div>1(STATE_MENU)&/div>&div>1&/div>&div>游戏开始菜单&/div>&div>2&/div>&div>显示游戏得分&/div>&div>续表&/div>&div align=center>mMode取值&/div>&div align=center>pMode取值&/div>&div align=center>表 示 状 态&/div>&div>2(STATE_OVER)&/div>&div>任意&/div>&div>游戏结束状态&/div>&div>3(STATE_LOAD)&/div>&div>1&/div>&div>加载游戏全局资源&/div>&div>2&/div>&div>加载关卡资源&/div>&div>5(STATE_RUNNING)&/div>&div>0&/div>&div>飞机移动状态&/div>&div>1~8&/div>&div>爆炸特效1~8&/div>&div>6(STATE_PAUSE)&/div>&div>任意&/div>&div>暂停状态&/div>&div>申明一个线程,作为游戏主线程。 &/div>&div>& class ManThread extends Thread &/div>&div>ManThread线程中的变量与常量: &/div>&div>private SurfaceHolder mSurfaceH &/div>&div>private Context mC &/div>&div>private Handler mH &/div>&div>private Bitmap mBackgroundI//背景 &/div>&div>private Bitmap pB/ /"飞机"的bitmap &/div>&div>private D//飞机 &/div>&div>private Drawable bomb1,bomb2,bomb3,bomb4,bomb5,bomb6,bomb7,bomb8;//爆炸图 &/div>&div>private D//炮弹 &/div>&div>private int mWidth,mH//"飞机"的宽和高 &/div>&div>private int mCanvasW//屏幕宽 &/div>&div>private int mCanvasH//屏幕高 &/div>&div>private Paint paint,paint2; &/div>&div>private//飞机位置→x坐标 &/div>&div>private//飞机位置→y坐标 &/div>&div>//状态 &/div>&div>public static final int STATE_MENU = 1; &/div>&div>public static final int STATE_OVER=2; &/div>&div>public static final int STATE_LOAD=3; &/div>&div>public static final int STATE_RUNNING=5; &/div>&div>public static final int STATE_PAUSE=6; &/div>&div>&&& &/div>&div>//按键 &/div>&div>public static final int DIRECTION_UP = 1; &/div>&div>public static final int DIRECTION_DOWN = 2; &/div>&div>public static final int DIRECTION_RIGHT = 4; &/div>&div>public static final int DIRECTION_LEFT = 8; &/div>&div>public static final int STATE_RESUME=16; &/div>&div>public static final int STATE_RESTART=32; &/div>&div>&&&& &/div>&div>public static final int STEP = 3;//飞机每一帧的移动距离 &/div>&div>public static final long time_fps=1000/30;//每秒的帧数 &/div>&div>private long startT//游戏开始时间 &/div>&div>private long endT//结束/当前时间 &/div>&div>private long lastTime_//上一次生成快速炮弹的时间 &/div>&div>private long lastTime_//上一次生成慢速炮弹的时间 &/div>&div>private long pauseT//暂停时间 &/div>&div>public int key_//按键信息 &/div>&div>&&&& &/div>&div>private int p_//中弹信息。p_hurt&0表示飞机中弹&/div>&div>&&& &/div>&div>private int//偏移量,用于飞机碰撞图层 &/div>&div>&&&& &/div>&div>private int k1,k2;//数组偏移变量,用于滚动数组 &/div>&div>private int end1,head1,end2;//滚动数组的首和尾 &/div>&div>ManThread线程的构造函数如下: &/div>&div>public ManThread(SurfaceHolder sh,Context context,Handler ha) &/div>&div>{ &/div>&div>&&& mSurfaceHolder = &/div>&div>&&& mContext = &/div>&div>&&& mHandler = &/div>&div>&&&&&&&&&&&& &/div>&div>&&& //进入游戏资源加载状态 &/div>&div>&&& mMode=STATE_LOAD; &/div>&div>&&&& pMode=0; &/div>&div>&&&& &/div>&div>&&&& p_hurt=0;//飞机未中弹 &/div>&div>&&&& pauseTime=-1;//游戏未暂停 &/div>&div>} &/div>&div>重载游戏主线程中的run()方法,将之前设计好的游戏框架移植其中,代码如下: &/div>&div>@Override &/div>&div>public void run() &/div>&div>{ &/div>&div>&&& long t1,t2; &/div>&div>&&& t1 = System.currentTimeMillis();//前一次刷新时间 &/div>&div>&&& while (mMode!=STATE_OVER)//游戏未结束 &/div>&div>&&& { &/div>&div>&&&&& &&t2=System.currentTimeMillis(); //获取当前时间 &/div>&div>&&&&&&& if (t2-t1&time_fps) &/div>&div>&&&&&&& { &/div>&div>&&&&&&&&&& &try& &/div>&div>&&&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&& sleep(time_fps+t1-t2); &/div>&div>&&&&&&&&&&& } catch (InterruptedException e) { &/div>&div>&&&&&&&&&&&&&&& e.printStackTrace(); &/div>&div>&&&&&&&&&&& &&&} &/div>&div>&&&&&&& } &/div>&div>&&&&&&& else&/div>&div>&&&&&&& { &/div>&div>&&&&&&&&&&& t1+=time_//刷新时间 &/div>&div>& &/div>&div>&&&&&&&&&&& State_Change();//状态转移 &/div>&div>&&&&&&&&&&& if (pauseTime!=-1)//用于处理游戏暂停时的状态,可以先跳过 &/div>&div>&&&&&&&&&&&&&&&&&&&&&&&&&&& &/div>&div>&&&&&&&&&&& Update_Fps();//状态执行 &/div>&div>&&&&&&&&&&& Canvas c = null; &/div>&div>&&&&&&&&&&& try &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&& &c = mSurfaceHolder.lockCanvas(null);//开始对surfaceView进行编辑 &/div>&div>&&&&&&&&&&&&&& synchronized(mSurfaceHolder) &/div>&div>&&&&&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&&&&& doDraw(c);//通过canvas绘制surface &/div>&div>&&&&&&&&&&&&&&& } &/div>&div>&&&&&& &&&&&} &/div>&div>&&&&&&&&&&& finally &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&& if (c != null) &/div>&div>&&&&&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&&&&& mSurfaceHolder.unlockCanvasAndPost(c);&/div>&div>&&&&&&&&&&&&&&&&&&& //释放对surfaceView 的编辑,并将surfaceView的内容显示到屏幕 &/div>&div>&&&&&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& } &/div>&div>&&& &&&&} &/div>&div>&&& } &/div>&div>} &/div>&div>实现State_Change()、Update_Fps()、doDraw()这三个方法之前,先来看一下获取按键输入部分的代码。 &/div>&div>boolean doKeyDown(int key,KeyEvent msg) &/div>&div>{ &/div>&div>&&& if (key == KeyEvent.KEYCODE_DPAD_UP) key_down|=DIRECTION_UP; &/div>&div>&&& if (key == KeyEvent.KEYCODE_DPAD_DOWN) key_down|=DIRECTION_DOWN; &/div>&div>&&& if (key == KeyEvent.KEYCODE_DPAD_RIGHT) key_down|=DIRECTION_RIGHT; &/div>&div>&&& if (key == KeyEvent.KEYCODE_DPAD_LEFT) key_down|=DIRECTION_LEFT; &/div>&div>&&& if (key == KeyEvent.KEYCODE_DPAD_CENTER) key_down|=STATE_RESUME; &/div>&div>&&&
&/div>&div>} &/div>&div>boolean doKeyup(int key,KeyEvent msg) &/div>&div>{ &/div>&div>&&& if (key == KeyEvent.KEYCODE_DPAD_UP) key_down&=63-DIRECTION_UP; &/div>&div>&&& if (key == KeyEvent.KEYCODE_DPAD_DOWN) key_down&=63-DIRECTION_DOWN; &/div>&div>&&& if (key == KeyEvent.KEYCODE_DPAD_RIGHT) key_down&=63-DIRECTION_RIGHT; &/div>&div>&&& if (key == KeyEvent.KEYCODE_DPAD_LEFT) key_down&=63-DIRECTION_LEFT; &/div>&div>&&& if (key == KeyEvent.KEYCODE_DPAD_CENTER) key_down&=63-STATE_RESUME; &/div>&div>&&&
&/div>&div>} &/div>&div>代码解释:用变量key_down保存按键信息,key_down的每一位对应一个按键。当某一个按键被按下后,key_down的对应位将被置1;当该按键弹起时,key_down的对应位置0。 &/div>&div>3. 实现状态转移&/div>&div>状态转移函数State_Change()非常简单,只需要根据图9-3的状态转移图写几个条件判断,就能够实现。具体的代码如下: &/div>&div>void State_Change() &/div>&div>{ &/div>&div>&&&& switch (mMode) &/div>&div>&&&& { &/div>&div>&&&&&&&& case STATE_MENU: &/div>&div>&&&&&&&& { &/div>&div>&&&&&&&&&&&& if ((key_down&STATE_RESTART)&0) &/div>&div>&&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&&& //转换到关卡资源加载状态 &/div>&div>&& &&&&&&&&&&&&&&&key_down-=STATE_RESTART; &/div>&div>&&&&&&&&&&&&&&&&& mMode=STATE_LOAD; &/div>&div>&&&&&&&&&&&&&&&&& pMode=2; &/div>&div>&&&&&&&&&&&&& } &/div>&div>&&&&&&&&&&&&&
&/div>&div>&&&&&&&& } &/div>&div>&&&&&&&&&&&&& case STATE_PAUSE: &/div>&div>&&&&&&&& { &/div>&div>&&&&&&&&&&&&& if ((key_down&STATE_RESUME)&0) &/div>&div>&&&&&&& &&&&&&{ &/div>&div>&&&&&&&&&&&&&&&&& //转换到游戏执行状态 &/div>&div>&&&&&&&&&&&&&&&&& key_down-=STATE_RESUME; &/div>&div>&&&&&&&&&&&&&&&&& mMode=STATE_RUNNING; &/div>&div>&&&&&&&&&&&&&&&&& //修复由暂停造成的时间误差 &/div>&div>&&&&&&&&&&&&&&&&& long k=System.currentTimeMillis()-pauseT &/div>&div>&&&&&&&&&&&&&&&&& startTime+=k; &/div>&div>&&&&&&&&&& &&&&&&&endTime+=k; &/div>&div>&&&&&&&&&&&&&&&&& lastTime_fast+=k; &/div>&div>&&&&&&&&&&&&&&&&& lastTime_slow+=k; &/div>&div>&&&&&&&&&&&& } &/div>&div>&&&&&&&&&&&&
&/div>&div>&&&&&&&&& } &/div>&div>&&&&&&&&& case STATE_LOAD: &/div>&div>&&&&&&&&& { &/div>&div>&&&&&&&&&&&&& if (pMode==0) &/div>&div>&&&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&&& //进入游戏资源加载状态 &/div>&div>&&&&&&&&&&&&&&&&& pMode=1; &/div>&div>&&&&&&&&&&&&& } &/div>&div>&&&&&&&&&&&&& else if (pMode==1) &/div>&div>&&&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&&& //转换为游戏菜单状态 &/div>&div>&&&&&&&&&&&&&&&&& mMode=STATE_MENU; &/div>&div>&&&&&&&&&&&&&&&&& pMode=1; &/div>&div>&&&&&&&&&&&&& } &/div>&div>&&&&&&&&&&&&& else if (pMode==2) &/div>&div>&&&&& &&&&&&&&{ &/div>&div>&&&&&&&&&&&&&&&&& //转换为游戏进行状态 &/div>&div>&&&&&&&&&&&&&&&&& mMode=STATE_RUNNING; &/div>&div>&&&&&&&&&&&&&&&&& pMode=0; &/div>&div>&&&&&&&&&&&&& } &/div>&div>&&&&&&&&&&&&&
&/div>&div>&&&&&&&&&&&&& } &/div>&div>&&&&&&&&&&&&& case STATE_RUNNING: &/div>&div>&&&&&&&&& { &/div>&div>&&&&&&&&&&&&& if ((key_down&STATE_RESUME)&0) &/div>&div>&&&&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&&& // 游戏暂停 &/div>&div>&&&&&&&&&&&&&&&&&&&& key_down&=63-STATE_RESUME; &/div>&div>&&&&&&&&&&&&&&&&& mMode=STATE_PAUSE; &/div>&div>&&&&&&&&&&&&& } &/div>&div>&&&&&&&&&&&&& else if (pMode&0) pMode++;//爆炸状态转换 &/div>&div>&&&&&&&&&&&&& else &/div>&div>&&&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&& &&if (p_hurt&0) pMode=1;//进入爆炸状态 &/div>&div>&&&&&&&&&&&&& } &/div>&div>&&&&&&&&&&&&& if (pMode&8) &/div>&div>&&&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&&& //爆炸特技完成,进入菜单状态 &/div>&div>&&&&&&&&&&&&&&&&& mMode=STATE_MENU; &/div>&div>&&&&&&&&&&&&&&&&& pMode=2; &/div>&div>&&&&&&&&&&&&& } &/div>&div>&&&&&&&&&&&&&
&/div>&div>&&&&&&&&& } &/div>&div>&&&& } &/div>&div>} &/div>&div>4. 实现Update_Fps()方法&/div>&div>Update_Fps()函数用来执行状态,实现主要的游戏逻辑。它的主框架代码如下: &/div>&div>&private void Update_Fps() { &/div>&div>&&&& &/div>&div>&&&& switch(mMode) &/div>&div>&&&& { &/div>&div>&&&&&&&&& case STATE_LOAD: &/div>&div>&&&&&&&&& { &/div>&div>&&&&&&&&&&&&& if (pMode==1) &/div>&div>&&&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&&& load_game();//加载游戏整体资源 &/div>&div>&&&&&&&&&&&&& } &/div>&div>&&&&&&&&&&&&& else if (pMode==2) &/div>&div>&&&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&&& load_gate();//加载关卡资源 &/div>&div>&&&&&&&&&&&&& } &/div>&div>&&&&&&&&&&&&&
&/div>&div>&&&&&&&&& } &/div>&div>&&&&&&&&& case STATE_MENU: &/div>&div>&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&
&/div>&div>&&&&&&&&& } &/div>&div>&&&&&&&&& case STATE_RUNNING: &/div>&div>&&&&&&&&& { &/div>&div>&&&&&&&&&&&&& pauseTime=-1; &/div>&div>&&&&&&&&&&&&& new_paodan();//生成炮弹 &/div>&div>&&&&&&&&&&&&& move();//"飞机"与炮弹的移动 &/div>&div>&&&&&&&&&&&&& is_pengzhuang();//碰撞判断 &/div>&div>&&&&&&&&&&&&&
&/div>&div>&&&&&&&&& } &/div>&div>&&&&&&&&& case STATE_PAUSE: &/div>&div>&&&&&&&&& { &/div>&div>&&&&&&&&&&&&& thead_pause();//暂停 &/div>&div>&&&&&&&&&&&&& pauseTime=System.currentTimeMillis();//记录暂停时刻 &/div>&div>&&&&&&&&&&&&&& & &/div>&div>&&&&&&&&& } &/div>&div>&&&& } &/div>&div>} &/div>&div>Update_Fps()方法中有6个子函数需要实现:load_game()、load_gate()、new_paodan()、move()、is_pengzhuang()和thead_pause()。&/div>&div>接下来,将逐一实现这几个函数。 &/div>&div>load_game()函数需要完成4个方面的初始化工作:加载图片、初始化画笔、碰撞预处理和预生成随机数。其中,碰撞预处理比较复杂,具体分析与代码实现将在9.4.1节中给出。下面给出其他三部分的代码。&/div>&div>private void load_game() { &/div>&div>&&& &/div>&div>&&& /* &/div>&div>&&&& *& 加载图片资源 &/div>&div>&&&& */ &/div>&div>&&& Resources res = mContext.getResources(); &/div>&div>&&& p = res.getDrawable(R.drawable.icon);& &/div>&div>&&& bomb1=res.getDrawable(com.example.R.drawable.bomb01); &/div>&div>&&& bomb2=res.getDrawable(com.example.R.drawable.bomb02); &/div>&div>&&& bomb3=res.getDrawable(com.example.R.drawable.bomb03); &/div>&div>&&& bomb4=res.getDrawable(com.example.R.drawable.bomb04); &/div>&div>&&& bomb5=res.getDrawable(com.example.R.drawable.bomb05); &/div>&div>&&& bomb6=res.getDrawable(com.example.R.drawable.bomb06); &/div>&div>&&& bomb7=res.getDrawable(com.example.R.drawable.bomb07); &/div>&div>&&& bomb8=res.getDrawable(com.example.R.drawable.bomb08); &/div>&div>&&& paodan=res.getDrawable(com.example.R.drawable.dd); &/div>&div>&&&&&&&&&&&& &/div>&div>&&& mBackgroundImage = BitmapFactory.decodeResource(res, R.drawable.bg_game); &/div>&div>&&& pBitmap=BitmapFactory.decodeResource(res, R.drawable.icon); &/div>&div>&&&&&&&&&&& &/div>&div>&&& mWidth = p.getIntrinsicWidth(); &/div>&div>&&& mHeight = p.getIntrinsicHeight(); &/div>&div>&& &&&&&&&&&&&/div>&div>&&& /* &/div>&div>&&&& * 初始化画笔 &/div>&div>&&&& */ &/div>&div>&&& paint = new Paint(); &/div>&div>&&& paint.setAntiAlias(true); &/div>&div>&&& paint.setARGB(255, 0, 255, 0); &/div>&div>&&& paint.setTextSize(28); &/div>&div>&&&&&&&&&&&& &/div>&div>&&& paint2 = new Paint(); &/div>&div>&&& paint2.setAntiAlias(true); &/div>&div>&&& paint2.setARGB(200, 0, 255, 0); &/div>&div>& &&paint2.setTextSize(18); &/div>&div>&&&&&&&&&&&& &/div>&div>&&& /* &/div>&div>&&&& * 一种简单易行,并且离散性较优的初级随机数产生方法 &/div>&div>&&&& */ &/div>&div>&&& randData=new int[1000]; &/div>&div>&&& randEnd=-1;& &/div>&div>&&& k=(int) (Math.random()*1000); &/div>&div>&&& while (randEnd&1000-1) &/div>&div>&&& { &/div>&div>&&&&&&& randEnd++; &/div>&div>&&&&&&& k*=691; &/div>&div>&&&&&&& k+=863; &/div>&div>&&&&&&& k%=997; &/div>&div>&&&&&&& randData[randEnd]=k; &/div>&div>&&&& } &/div>&div>&&& randCurr=0;&&&&&&&& &/div>&div>} &/div>&div>代码解释: &/div>&div>l&&&&&&&&& 最后一段代码用来为《是男人就坚持20秒》量身打造一组“随机”数。 &/div>&div>l&&&&&&&&& 生成的“随机”数保存在数组randData中,“随机数”的取值范围在0~1000之间。&/div>&div>l&&&&&&&&& 随机数生成规则:若前一个随机数为k,则下一个随机数为:(k*691+863)%997。 &/div>&div>&&/div>&div>&&/div>&div>&&/div>&div>当需要加载的资源较多时,代码可以进行一些简化,简化方法如下所示。&/div>&div>&&/div>&div>之前的方法:& &/div>&div>&&& private Drawable bomb1,bomb2,bomb3,bomb4,bomb5,bomb6,bomb7, bomb8;&&& bomb1=res.getDrawable(com.example.R.drawable.bomb01); &/div>&div>&&& bomb2=res.getDrawable(com.example.R.drawable.bomb02); &/div>&div>&&& bomb3=res.getDrawable(com.example.R.drawable.bomb03); &/div>&div>&&& bomb4=res.getDrawable(com.example.R.drawable.bomb04); &/div>&div>&&& bomb5=res.getDrawable(com.example.R.drawable.bomb05); &/div>&div>&&& bomb6=res.getDrawable(com.example.R.drawable.bomb06); &/div>&div>&&& bomb7=res.getDrawable(com.example.R.drawable.bomb07); &/div>&div>&&& bomb8=res.getDrawable(com.example.R.drawable.bomb08); &/div>&div>简化方法: &/div>&div>&&& private Drawable[] bomb=new Drawable[8]; &/div>&div>&&&& &/div>&div>&&& for (int i=0;i&8;i++) &/div>&div>bomb[i]=res.getDrawable(com.example.R.drawable.bomb01+i);&/div>&div>&&/div>&div>《是男人就坚持20秒》游戏只有一个关卡,几乎没有关卡资源需要加载,只需要完成一些游戏初始化工作。&/div>&div>private void load_gate() { &/div>&div>&&& //对于这个游戏,没有什么需要加载的关卡资源,只需要初始化相关变量 &/div>&div>&&& px=(mCanvasWidth-mWidth)/2;//"飞机初始位置"&/div>&div>&&& py=(mCanvasHeight-mHeight)/2;//"飞机初始位置"&/div>&div>&&& startTime=System.currentTimeMillis();//设置初始时间 &/div>&div>&&& lastTime_fast=startTime-5000;//初始化上一次出现快速炮弹的时间 &/div>&div>&&& lastTime_slow=startTime-2000;//初始化上一次出现慢速炮弹的时间 &/div>&div>&&& /* &/div>&div>& &&&* 滚动数组的相关初始化工作,详细说明将在9.4.2节给出 &/div>&div>&&&& * 读者可以暂时跳过 &/div>&div>&&&& */ &/div>&div>&&& k1=k2=0; &/div>&div>&&& k2|=(1&&12)+(1&&13); &/div>&div>&&& end1=head1=-1; &/div>&div>&&& p_hurt=0;//飞机未中弹 &/div>&div>&&& pauseTime=-1;//游戏未暂停 &/div>&div>} &/div>&div>在设计多关卡游戏的时候,可以通过load_gate()函数加载不同关卡的资源,以实现关卡切换。需要特别注意的是,如果资源加载的时间比较长,那么就需要额外开辟一个线程完成资源加载工作,主线程显示加载进度。图9-7所示为crystallight游戏加载关卡资源时的截图。 &/div>&div>&&/div>&div>&&/div>&div>&/div>&div>图9-7& crystallight游戏资源加载截图 &/div>&div>实现new_paodan()函数需要用到大量的随机数,为了提高随机数的提取效率,不采用系统提供的Math.random()方法,而是通过下面的代码实现“随机数”提取。初始化伪随机数的代码在游戏资源加载部分,可以回顾load_game()函数的最后一段代码。 &/div>&div>int getrand() &/div>&div>{ &/div>&div>&&& randCurr++; &/div>&div>&&& if (randCurr&=randEnd) randCurr=0; &/div>&div>&&& return randData[randCurr]; &/div>&div>} &/div>&div>&&/div>&div>&&/div>&div>&&/div>&div>l&&&&&&&& Android系统提供的Math.random()方法返回的“随机数”并不是真正随机的,它也是根据更复杂一些的四则运算计算出的伪随机数。《是男人就坚持20秒》游戏并没有苛刻的离散性要求,因此,只需要生成1000以内的伪随机整数,即可满足游戏要求,用小整数替代浮点数运算,用简单的四则运算替代复杂的四则运算,游戏逻辑的运算速度将明显提高。&/div>&div>l&&&&&&&& 将可预处理的运算从游戏执行阶段前移到资源加载阶段进行处理,是一种常用的提高游戏运行效率的优化方法。 &/div>&div>&&/div>&div>回到new_paodan()函数的实现。每经过一段特定的时间,将产生一批新炮弹,时间间隔与炮弹数量的计算公式在游戏策划案中有完整的描述。炮弹的信息将保存在一个一维整型数组中,这样做的好处是减小内存开销并优化运算效率,具体的分析和讨论见9.4.2节的内存管理知识。当然,也可以使用最常用的list或者map来保存炮弹信息。参考代码如下:&/div>&div>private void new_paodan() { &/div>&div>&&& long &/div>&div>&&& t=System.currentTimeMillis(); &/div>&div>&&& endTime=t; &/div>&div>&&& if (t-lastTime_slow&=2000) &/div>&div>&&& { &/div>&div>&&&&&&& lastTime_slow+=2000;//刷新时间 &/div>&div>&&&&&&& int i,k,j; &/div>&div>&&&&&&& k=(int) (20+(t-startTime)/200);//计算生成的炮弹的数量 &/div>&div>&&&&&&& for (i=0;i&k;i++) &/div>&div>&&&&&&& { &/div>&div>&&& &&&&&&&&j=getrand(); &/div>&div>&&&&&&&&&&& if (j&250)//& 1/4的概率 &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&& //上方生成炮弹 &/div>&div>&&&&&&&&&&&&&&& j=getrand()%320; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&& &&&&&&&&&&&data[k1+end1]=0-8; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& j=getrand()%180+90; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else if (j&500)//& 1/4的概率 &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&& //下方生成炮弹 &/div>&div>&&&&&&&&&&&&&&& j=getrand()%320; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=mCanvasH &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& j=getrand()%180+270; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else if (j&750)//& 1/4的概率 &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&& //左方生成炮弹 &/div>&div>&&&&&&&&&&&&&&& j=getrand()%480; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=0-8; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&& &&&&&&&&&&&data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& j=getrand()%180+0; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else if (j&1000) //& 1/4的概率 &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&& //下方生成炮弹 &/div>&div>&&&&&&&&&&&&&&& j=getrand()%480; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=mCanvasW &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& j=getrand()%180+180; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&& } &/div>&div>&&& } &/div>&div>&&& if (t-lastTime_fast&=5000) &/div>&div>&&& { &/div>&div>&&&&&&& lastTime_fast+=5000;//刷新时间 &/div>&div>&&&&&&& int i,k,j; &/div>&div>&&&&&&& k=(int) (5+(t-startTime)/500);//计算生成的炮弹数量 &/div>&div>&&&&&&& for (i=0;i&k;i++) &/div>&div>&&&&&&& { &/div>&div>&&&&&&&&&&& j=getrand(); &/div>&div>&&&&&&&&&&& if (j&250)//& 1/4的概率 &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&& //上方生成炮弹 &/div>&div>&&&&&&&&& &&&&&&j=getrand()%320; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=0-mH &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& j=getrand()%180+90; &/div>&div>&&&&&&&&&&&&&&& j|=(1&&10); &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else if (j&500)//& 1/4的概率 &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&& //下方生成炮弹 &/div>&div>&&&&&&&&&&&&&&& j=getrand()%320; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=mCanvasH &/div>&div>&&&&&&&&& &&&&&&data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& j=getrand()%180+270; &/div>&div>&&&&&&&&&&&&&&& j|=(1&&10); &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else if (j&750)//& 1/4的概率 &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&& //左方生成炮弹 &/div>&div>&&&&&&&& &&&&&&&j=getrand()%480; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=0-mW &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& j=getrand()%180+0; &/div>&div>&&&&&&&&&&&&&&& j|=(1&&10); &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else //& 1/4的概率 &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&& //下方生成炮弹 &/div>&div>&&&&&&&&&&&&&&& j=getrand()%480; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=mCanvasW &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&&&&& &data[k1+end1]*=10; &/div>&div>&&&&&&&&&&&&&&& j=getrand()%180+180; &/div>&div>&&&&&&&&&&&&&&& j|=(1&&10); &/div>&div>&&&&&&&&&&&&&&& end1++; &/div>&div>&&&&&&&&&&&&&&& data[k1+end1]=j; &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&& } &/div>&div>&&& } &/div>&div>} &/div>&div>move()函数需要实现“飞机”的移动和炮弹的移动,代码如下: &/div>&div>private void move() { &/div>&div>&&& /* &/div>&div>&&&& *"飞机"移动 &/div>&div>&&&& */ &/div>&div>&&& if ((key_down & DIRECTION_UP)&0) py-=STEP; &/div>&div>&&& if ((key_down & DIRECTION_DOWN)&0) py+=STEP; &/div>&div>&&& if ((key_down & DIRECTION_RIGHT)&0) px+=STEP; &/div>&div>&&& if ((key_down & DIRECTION_LEFT)&0) px-=STEP; &/div>&div>&&& if (px&mCanvasWidth-mWidth) px=mCanvasWidth-mW &/div>&div>&&& if (px&0) px=0; &/div>&div>&&& if (py&mCanvasHeight-mHeight) py=mCanvasHeight-mH &/div>&div>&&& if (py&0) py=0; &/div>&div>&&& /* &/div>&div>&&&& * 炮弹移动 &/div>&div>&&&& */ &/div>&div>&&& int i,k,x,y; &/div>&div>&&& double &/div>&div>&&& for (i=0;i&=end1;) &/div>&div>&&& { &/div>&div>&&&&&&& x=data[k1+i];//炮弹x坐标 &/div>&div>&&&&&&& i++; &/div>&div>&&&&&&& y=data[k1+i];//炮弹y坐标 &/div>&div>&&&&&&& i++; &/div>&div>&&&&&&& f=(double)(data[k1+i]); &/div>&div>&&&&&&& f=f*Math.PI/180;//炮弹运动方向 &/div>&div>&&&&&&& if ((data[k1+i]&(1&&10))&0) &/div>&div>&&&&&&&&&&& k=(int) (4+(endTime-startTime)/2000); &/div>&div>&&&&&&& else k=(int) (2+(endTime-startTime)/3000); &/div>&div>&&&&& &&k*=10;//运动距离 &/div>&div>&&&&&&& i++; &/div>&div>&&&&&&& x+=k*Math.sin(f); &/div>&div>&&&&&&& y-=k*Math.cos(f); &/div>&div>&&&&&&& if (x&-80&&x&mCanvasWidth*10&&y&-80&&y&mCanvasHeight*10) &/div>&div>&&&&&&& { &/div>&div>&&&&&&&&&&& //炮弹没有出界 &/div>&div>&&&&&&&&&&& end2++; &/div>&div>&&&&&&&&&&& data[k2+end2]=x; &/div>&div>&&&&&&&&&&& end2++; &/div>&div>&&&&&&&&&&& data[k2+end2]=y; &/div>&div>&&&&&&&&&&& end2++; &/div>&div>&&&&&&&&&&& data[k2+end2]=data[k1+i-1]; &/div>&div>&&&&&&& } &/div>&div>&&& } &/div>&div>&&& /* &/div>&div>&&&& * 下面的代码用于维护滚动数组,读者可以先跳过 &/div>&div>&&&& */ &/div>&div>&&& end1=end2; &/div>&div>&&& end2=-1; &/div>&div>&&& k1^=(1&&12)+(1&&13); &/div>&div>&&& k2^=(1&&12)+(1&&13); &/div>&div>} &/div>&div>&&/div>&div>代码解释: &/div>&div>炮弹的坐标不是炮弹中心的坐标,而是炮弹左上角的坐标,炮弹的大小为8×8像素。因此,炮弹所处的矩形空间为[x,x+8],[y,y+8]。此外,为了提高精度,坐标值被扩大了10倍,详细分析见9.4.2节的内容。因此,判断炮弹出界的条件为:x&-80,且x&mCanvasWidth*10,且y&-80,且y&mCanvasHeight*10。 &/div>&div>碰撞判断往往是游戏开发中最复杂,同时也是最有意思的部分。有关碰撞判断的深层讨论将在9.4.1节中展开。在《是男人就坚持20秒》游戏中,实现“飞机”与炮弹的碰撞可以采用一种非常简单,但是也很粗糙的矩形碰撞方法。矩形碰撞可以描述为:若两个矩形的顶点坐标分别为(x1(横坐标),y1(纵坐标),x2(横坐标,且x2&x1),y2(纵坐标,且y2&y1)),x3(横坐标),y3(纵坐标),x4(横坐标,且x4&x3),y4(纵坐标,且y4&y3)),如果同时满足下列4个条件,则发生碰撞。&/div>&div>l&&&&&&&&& x1&x4;&/div>&div>l&&&&&&&&& x2&x3;&/div>&div>l&&&&&&&&& y1&y4;&/div>&div>l&&&&&&&&& y2&y3。&/div>&div>矩形碰撞判断的伪代码如下: &/div>&div>& if (x1&x4&&x2&x3&&y1&y4&&y2&y3)&/div>&div>& {&/div>&div>&&&&& //发生碰撞&/div>&div>&&&&& 碰撞处理&/div>&div>&&} &/div>&div>《是男人就坚持20秒》游戏的碰撞检测也可以采用一些高效而优美的算法,9.4.1节将给出一种美妙的碰撞检测算法,代码详见9.4.1节。 &/div>&div>thead_pause()函数很简单,只需要修改游戏状态即可。 &/div>&div>private void thead_pause() { &/div>&div>&&& mMode=STATE_PAUSE; &/div>&div>} &/div>&div>到此为止,Update_Fps()方法已经完成了,接下来实现最后一个方法doDraw()。 &/div>&div>5. 实现doDraw()方法&/div>&div>在不同的游戏状态需要绘制不同的内容。游戏运行状态的图像绘制是doDraw()方法中最重要,也是最复杂的一部分。对于《是男人就坚持20秒》游戏,游戏运行状态一共需要绘制以下4个内容: &/div>&div>l&&&&&&&&& 绘制背景;&/div>&div>l&&&&&&&&& 绘制炮弹;&/div>&div>l&&&&&&&&& 绘制“飞机”,或者是爆炸特效;&/div>&div>l&&&&&&&&& 绘制坚持时间。&/div>&div>doDraw()方法的实现代码如下: &/div>&div>private void doDraw(Canvas c) { &/div>&div>&&&& int &/div>&div>&&& switch (mMode) &/div>&div>&&& { &/div>&div>&&&&&&& case STATE_RUNNING://游戏运行状态 &/div>&div>&&&&&&& { &/div>&div>&&&&& //绘制背景 &/div>&div>&&& &&&&&&&&c.drawBitmap(mBackgroundImage,0,0,null); &/div>&div>&&&&&&&&&&&& &/div>&div>&&&&& //绘制炮弹 &/div>&div>&&&&&&&&&&& for (i=0;i&=end1;i+=3) &/div>&div>&&&&&&&&&&& {&& &/div>&div>&&&& &&&&&&&&&&&paodan.setBounds(data[k1+i]/10, data[k1+i+1]/10, data[k1+i]/10+8, data[k1+i+1]/10+8); &/div>&div>&&&&&&&&&&&&&&& paodan.draw(c); &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&& //绘制“飞机”&/div>&div>&&&&&&&&&&& if (pMode&1) &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&& p.setBounds(px, py, px+mWidth, py+mHeight); &/div>&div>&&&&&&&&&&&&&&& p.draw(c); &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& //爆炸特效 &/div>&div>&&&&&&&&&&& //下面的这种实现方式比较笨拙,当特效图片较多时,可以把图片放到一个数组中,&/div>&div>&&&&&&&&&&& //以缩短代码量&/div>&div>&&&&&&&&&&& else if (pMode==1) &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&& bomb1.setBounds(px-20, py-10, px+60, py+70); &/div>&div>&&&&&&&&&&&&&&&& bomb1.draw(c); &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else if (pMode==2) &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&& bomb2.setBounds(px-20, py-10, px+60, py+70); &/div>&div>&&&&&&&&&&&&&&&& bomb2.draw(c); &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else if (pMode==3) &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&& bomb3.setBounds(px-20, py-10, px+60, py+70); &/div>&div>&&&&&&&&&&&&&&&& bomb3.draw(c); &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else if (pMode==4) &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&& bomb4.setBounds(px-20, py-10, px+60, py+70); &/div>&div>&&&&&&&&&&&&&&&& bomb4.draw(c); &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else if (pMode==5) &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&& bomb5.setBounds(px-20, py-10, px+60, py+70); &/div>&div>&&& &&&&&&&&&&&&&bomb5.draw(c); &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else if (pMode==6) &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&& bomb6.setBounds(px-20, py-10, px+60, py+70); &/div>&div>&&&&&&&&&&&&&&&& bomb6.draw(c); &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else if (pMode==7) &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&& &&&&&&&&&&bomb7.setBounds(px-20, py-10, px+60, py+70); &/div>&div>&&&&&&&&&&&&&&&& bomb7.draw(c); &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else if (pMode==1) &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&&& bomb8.setBounds(px-20, py-10, px+60, py+70); &/div>&div>&&&&&&&&&&&&&&&& bomb8.draw(c); &/div>&div>&&&&&&&& &&&} &/div>&div>&&&&&&&&&&& //在屏幕左上角绘制游戏时间 &/div>&div>&&&&&&&&&&& c.drawText("坚挺时间:"+((endTime-startTime)/1000)+". &/div>&div>"+(((endTime-startTime)%), 5, 25, paint2); &/div>&div>&&&&&&&&&&& &/div>&div>&&&&&&& } &/div>&div>&&&&&&& case STATE_MENU: &/div>&div>&&&&&&& { &/div>&div>&&&&&&&&&&& c.drawBitmap(mBackgroundImage,0,0,null); &/div>&div>&&&&&&&&&&& if (pMode==1) &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&& // 刚进入游戏时的界面 &/div>&div>&&&&&&&&&&&&&&& // 绘制"欢迎进入游戏"&/div>&div>&&&&&&&&&&&&&&& 代码略。 &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&& else &/div>&div>&&&&&&&&&&& { &/div>&div>&&&&&&&&&&&&&&& // 完成游戏之后的界面 &/div>&div>&&&&&&&&&&&&&&& // 显示生存时间,并评分 &/div>&div>&&&&&&& &&&&&&&&&代码略。 &/div>&div>&&&&&&&&&&& } &/div>&div>&&&&&&&&&&&
&/div>&div>&&&&&&& } &/div>&div>&&&&&&& case STATE_PAUSE: &/div>&div>&&&&&&& { &/div>&div>&&&&&&&&&&& // 显示游戏暂停 &/div>&div>&&&&&&&&&&& c.drawText("游戏暂停", mCanvasWidth/3, mCanvasHeight/3+30, paint); &/div>&div>&&&&&&&&&&&
&/div>&div>&&&&&&& } &/div>&div>&&& } &/div>&div>} &/div>&div>&&/div>&div>&&/div>&div>&&/div>&div>&&/div>&div>l&&&&&&&& 如果游戏背景不改变,可以只重绘精灵(也就是炮弹或者“飞机”)所占的区域,然后在新的位置画精灵,这种优化策略即是“脏矩形”优化策略。然而,当重绘区域超过整个背景的10%以上时,加速效果将大幅度衰减。《是男人就坚持20秒》游戏中炮弹的数量较多,需要重绘的区域较大,“脏矩形”策略的优化效果不明显。所以,没有必要使用这种加速绘制的策略。&/div>&div>l&&&&&&&& 拖尾渲染将大幅度提升游戏的视觉冲击感,但是将造成额外的内存开销和时间开销,代码量也将明显增加。对于《是男人就坚持20秒》游戏,有一种非常简易的方法实现拖尾效果:将背景图设为半透明,令Alpha值的取值范围在150~200之间,炮弹和“飞机”都将会有一条神奇的“尾巴”。需要注意的是,如果Alpha值太低,那么游戏背景图将模糊不清,甚至产生画面振荡,surface的绘制速度也将大幅度衰减;如果Alpha值太高,拖尾效果将不明显。 &/div>&div>&&/div>&div>&&/div>&div>&&/div>&div>&&/div>&div>本节并没有给出游戏所有的代码,而是从游戏框架的角度,与读者一同完成了架构中最核心的部分。完整的代码参见本书的技术支持网站eoeAndroid。 &/div>&div>&&/div>&div>9.3.5& 后续开发展望 &/div>&div>到此为止,《是男人就坚持20秒》的基础功能已经全部实现了,有兴趣的读者可以进一步完善这款游戏。 &/div>&div>1. 重力感应&/div>&div>相信职业手机游戏开发者一定都玩过平衡球游戏,这类游戏通过倾斜手机受重力面来操作滚球的前进,图9-8是平衡球游戏的截图。《是男人就坚持20秒》也可以引入重力控制,试想一下,通过倾斜手机来控制“飞机”躲避炮弹攻击,是不是很有意思。&/div>&div>&/div>&div>图9-8& 平衡球游戏截图 &/div>&div>Android手机内置了重力感应器,Android操作系统提供了相应的接口。注册重力感应器的代码如下: &/div>&div>private SensorManager mSensorM&/div>&div>...&/div>&div>mSensorManager=(SensorManager) getSystemService(Context.SENSOR_SERVICE);&/div>&div>mSensorManager.registerListener(this,SensorManager.SENSOR_ACCELEROMETER); &/div>&div>当手机转动的时候,将触发onSensorChanged函数: &/div>&div>@Override &/div>&div>public void onSensorChanged(int sensor, float[] values) { &/div>&div>&&& if (Sensor.TYPE_ACCELEROMETER==sensor) &/div>&div>&&& { &/div>&div>&&&&&&&& //处理重力感应 &/div>&div>&&& } &/div>&div>} &/div>&div>values[]的含义如下:&/div>&div>l&&&&&&&&& values[0]&0:飞机向右移动;&/div>&div>l&&&&&&&&& values[0]&0: 飞机向左移动;&/div>&div>l&&&&&&&&& values[1]&0:飞机向上移动;&/div>&div>l&&&&&&&&& valuse[1]&0: 飞机向下移动。&/div>&div>2. 丰富炮弹种类 &/div>&div>在这个游戏中,只设计了两种非常普通的炮弹:慢速炮弹和快速炮弹。这两种炮弹都是沿直线运动的,速度按一定的规律递增,没有制作运动特效。开发人员可以进一步丰富炮弹的种类,可以设计弧形的炮弹轨迹,也可以设计折线的轨迹和混合型的轨迹。此外,开发者也可以在炮弹的速度方面进行设计。每一类不同的炮弹都可以量身打造运动特效与爆炸特效。炮弹信息可以在关卡资源加载方法load_gate()中进行初始化。 &/div>&div>3. 丰富道具 &/div>&div>丰富的道具将提升游戏的不确定性,提供游戏挫折补偿感,扩充游戏的耐玩性。《是男人就坚持20秒》游戏可以设计一些游戏道具(例如表9-8所示),“飞机”在移动中“吃”到这些道具可以增强生存能力。例如:“飞机”吃到道具之后,状态会相应地改变;道具效果消失后,状态也将改变。因此,需要扩充“飞机”的状态,变量pMode的取值范围需要扩充,pMode的每一个取值将对应一个“飞机”状态。状态之间的转换关系依然要通过状态转换图来描述,pMode取值与状态之间的关系也应该写到游戏状态表(表9-8)中。道具效果的实现相对简单,读者可以尝试完成。&/div>&div>表9-8& 道 具 表 &/div>&div align=center>道 具 名 称&/div>&div align=center>道 具 效 果&/div>&div>缩小&/div>&div>在5秒内缩小飞机的碰撞面积75%&/div>&div>无敌&/div>&div>在3秒内使飞机免疫炮弹攻击&/div>&div>加速&/div>&div>在5秒内增加“飞机”的飞行速度50%&/div>&div>减速&/div>&div>在5秒内减慢“炮弹”的移动速度30%&/div>&div>削弱&/div>&div>下一波产生的炮弹数量减少50%&/div>&div>加载道具资源的代码段仍然是放在关卡资源加载方法load_gate()中。 &/div>&div>4. 扩充关卡 &/div>&div>多关卡设计是教学版游戏向商业版游戏转变的分水岭。进一步扩展《是男人就坚持20秒》游戏,可以整合不同种类的炮弹与道具,设计难度逐渐提升的关卡系统。 &/div>&div>当然,引入多关卡设计并不能明显提升《是男人就坚持20秒》的游戏性,这里只是提供一种扩展思路。 &/div>&div>5. 加入排行榜 &/div>&div>你可以为这个游戏加入排行榜的功能,使得每次坚挺时间为前三甲的可以将自己的名字显示到排行榜中,这样可以让在这个手机上玩这个游戏的用户看到自己的水平,进而努力坚挺更长的时间。 &/div>&div>6. 加入互联网功能 &/div>&div>你可以给这个游戏加入互联网的功能,比如可以动态下载一副皮肤,改变飞机的样子或者子弹爆炸的效果图片,使用户有着不一样的体验。也可以将用户的坚挺记录发布到互联网上,进而可以看到全部玩家的坚挺排行情况,会让用户觉得更有意思。 &/div>&div>7. 扩展玩法 &/div>&div>在《是男人就坚持20秒》游戏中,玩家扮演“飞机”驾驶员,努力躲避炮弹的攻击;系统发射炮弹,扮演攻击“飞机”的角色。如果交换双方角色,将带来全新的游戏体验,将是高挑战性的编程体验。游戏玩法可以描述为:玩家单击屏幕的某一点,所有新产生的炮弹将瞄准该点发射;系统控制“飞机”尽力躲避炮弹袭击;玩家击毁“飞机”的耗时越短越好。&/div>&div>屏幕触摸感应、炮弹的生成和炮弹的聚焦都比较简单,参考之前的代码很容易实现。这种玩法只有一个难点,同时也是亮点,即飞机躲避炮弹的AI。 &/div>&div>&&/div>&div>&&/div>&div>&&/div>&div>l&&&&&&&&& 简单的游戏AI:简单AI是最容易被想到的,也是几乎没有技术门槛的。程序员预先设想出所有可能出现的情况,并为每一种情况设计相应的处理行为。简单AI几乎不涉及算法,可以描述成一幅状态转移图,代码由大量的嵌套判断语句组成。简单的AI存在两个硬伤:①对于一款商业化的游戏,程序员几乎不可能预料到所有的情况,无论如何划分,可能出现的情况都是片面和机械的;②对于每一种情况,处理方式是固定且唯一的,一旦程序的行为方式被玩家掌握,游戏的耐玩性就消失。&/div>&div>l&&&&&&&&& 高级的游戏AI:高级的游戏AI往往涉及到复杂的数学理论,要用到人工智能中的算法以及其他复杂算法。相比简单AI,高级AI的时间开销和空间开销往往更大。然而这并不影响高级AI在Android游戏中的应用,量身裁剪的复杂AI是能够适应资源相对稀缺的Android平台的。 &/div>&div>&&/div>&div>游戏AI的深层本质都可以归结为数学博弈问题,由于“飞机”AI的解空间过于庞大,直接通过博弈的方法并不能在可接受时间内求解问题。最佳的解决方法是混合应用遗传算法和博弈方法,如图9-9所示。遗传算法可以设计为: &/div>&div>编码设计:一个基因表示一帧时间内飞机的移动方向。基因长度限制在m=60以内,对于每秒跑30帧的游戏,在m/30=2秒内,飞机的运动轨迹都将是精确模拟的。&/div>&div>初始种群:样本容量限制在n=100以内。采用之前的遗传结果优化初始种群。 &/div>&div>双亲选择策略:使用基于排序选择的策略,急剧加速收敛。 &/div>&div>适应度评估阶段一:精确模拟2秒内飞机的移动,判断飞机的生存概率。 &/div>&div>适应度评估阶段二:粗评估2秒之后飞机的生存概率。 &/div>&div>子代生成与优化:采用常规策略。 &/div>&div>遗传代数:限制在z=20代以内。 &/div>&div>&/div>&div>图9-9& 排序选择策略示意图 &/div>&div>在上述的遗传算法中,每调用一次遗传算法,都将获得一组容量为n的“超级”样本,这些样本可以进行移位与补位操作,然后作为下一轮遗传操作的初始种群。通过这样的优化,遗传算法的全局最优性将得到明显提高。此外,游戏并不需要刻意地追求完美的解,近似最优解往往是最满足商业效率的选择。 &/div>&div>该遗传算法的计算瓶颈将在适应度评估阶段,如果屏幕上有k(根据策划案,k不会超过4000)颗炮弹,那么适应度评估的时间复杂度将是:n*m*k。引入多级适应度评估策划,并对炮弹数据做一定的预处理,适应度评估阶段的时间复杂度将远低于:n*m。所以,执行一次遗传算法的总体时间复杂度将是:o(n*m*z),代码如果写得足够漂亮,需要执行的代码行数将远远低于n*m*z=120000行。十几万行的代码量对于Android系统而言是可以承受的。此外,执行遗传算法所需的内存开销很小,不会受到内存资源不足的限制,也不会引发大规模的内存释放。 &/div>&div>通过上述的分析可以看出:使用高级AI实现该游戏中飞机的AI具备可行性,至少在代码层面是完全可行的。Android游戏开发者争论的焦点不再是“将高级AI写入Android游戏是否可行?”,大家关注的焦点转变为“如何高效地量身定做复杂的AI?”&/div>&div>9.3.6& 开发流程总结 &/div>&div>从前面的知识可知,整个游戏的开发流程是:首先进行游戏策划,接下来进行程序和美术同步开工。游戏开发总流程图如图9-10所示。&/div>&div>&/div>&div>图9-10& 开发总流程图&/div>&div>实际开发过程中,开发流程往往要复杂得多,不同的游戏开发团队有不同的游戏开发流程;不同类型的游戏,其开发流程也有区别。图9-11给出了一个详细的开发流程供大家参考。&/div>&div>&/div>&div>图9-11& 详细的开发流程参考图 &/div>&div>&&/div>&div>&&/div>
-----}

我要回帖

更多关于 自适应屏幕代码 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信