用了策略模式。com.cz.tank.strategies的DrawTankStrategy类。
3.4.8 调停者模式
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。com.cz.tank 类 Game Mediator使用了调停者模式。
3.4.9 门面模式
外部与一个子系统的通信必须通过一个统一的门面(Facade)对象进行,这就是门面模式。
3.4.10 PNG格式
PNG具体格式由PNG Specification Version 1.0定义的。PNG格式提供透明背景的图像,这对绘制游戏画面和被操纵主角极有帮助。坦克之间或与障碍物碰撞时就不会因为背景有特定的颜色,显示出的效果像贴上的图片而缺乏真实感,物体之间轻微重叠时最上层图片也不会覆盖超过其有效象素外的部分。
PNG格式图片中包含许多定义其图片特性的冗余部分(Chunks)。这些代码包含在每一个单独的PNG格式图像中,然而如果将多个PNG图像合并在一张幅面稍大一些的整图中,多个chunks就可以得到精简,图片的大小可以得到控制。使用Image类中的create Image函数可从整图中分割出所需要的元素。在Game包中的Tiled Layer和Sprite类都整合了这样的功能。本程序中的地图元素都集成在一张MAP.png图片中,实现了方便的管理和程序体积的精简。
3.4.11 AWT绘制的基本原理
AWT的绘制与界面更新使用了一个单独的线程,称为AWT线程。paint、repaint、update 三个方法关系如图3.5所示 :
11
图3.5 双缓冲原理示意图
3.4.12 双缓冲
进行游戏绘图一般需要手动编程使用双缓冲。需要在paint()方法内所想要画的图形画在一张预先准备好的背景,等所有绘图操作都完成后再将背景的数据拷贝到实际的屏幕上。Image类提供了一个建立背景的静态方法createImage(int width, int )。
当敌人坦克完全死亡时(enemyNum为0),需调用System类的currentTimeMillis()赋值给结果的时间。接着调用setCurrent()显示统计分数的画面,为了进入下一关,统计画面只是停留四秒,就重新转回BattleCanvas画面。当然,如果当前已是最后一关,就不会再转回。进入下一关时,许多变量需要重新被初始化,如地图的绘制、敌人出现位置的重置、敌人的数量、玩家坦克的当前位置。
如果游戏未结束,则需判断屏上坦克是否已小于还剩坦克的总数,如果是这样,就需要再提供一辆坦克。提供新坦克之前,在屏幕上设置了一个专用指示的闪光符号,它继承了Sprite,运行在单独的线程中。以在两秒钟内反复闪现两次为一个生命周期。当它闪光完毕后,敌人就会从闪光位置出现。这样可提示玩家具体敌人将在什么时刻出现,以便做好准备。闪光位置设置了三处坐标,由于敌人不能同时出现,便设置了enemyOutDelay的倒数计时,每次屏幕刷新会减少一次计数,直到为0时就准备一辆坦克。本程序设置的两次坦克出现的最小间隔为2秒。
如果玩家已经死亡,就需要使用LayerManager的insert()将gameover字样插入到最上层,以免被其他物体覆盖。
12
在检测用户输入的input()函数中,当按方向键时,玩家坦克就将向不同的方向运行,这调用了UserSprite的go()函数;当开炮时,就调用其fire()函数,作出相应的行为。
在出现正式画面前设置了一个loading state*字样的单独屏幕,调用了loadinglevel()函数,并停滞了1500毫秒,提示用户做好准备。
在绘图的render()过程中,除了要重绘坦克、地图、子弹外,还会在右边空白处绘出一个生命统计栏。并反复使用Graphics的drawLine()、drawImage()绘画出一个三维的效果,增强视觉感。该三维栏的上方为白色,下方为黑色,就创造了立体感。在每次刷新绘图页面时,应使用GameCanvas的flushGraphics()将屏幕后台的缓冲区内的图象刷新到前台来。
在允许敌人出现前,需要检测给即将出现的敌人分配一个数组序号。在程序中调用了getNullEnemyIndex()进行测试,当返回为-1时说明没有序号可以分配,否则,将返回空的序号。
4.2 坦克的共同行为
在TankSprite中定义了所有坦克(包括敌方坦克和玩家坦克)的共同行为和属性。EnemeySprite和UserSprite都继承了该类以简化结构。在transformDirection[]中定义了坦克四个方向分别应将原始图片旋转的角度,分别为
TRANS_NONE,TRANS_ROT90,TRANS_ROT180,TRANS_ROT270,以便在后来的setTransform()中将这些常量代入。构造函数中创建了每个坦克必须拥有的一颗子弹,这些子弹就将只跟随自己的坦克调动。
为了能提前预测碰撞,调用了defineCollisionRectangle()将碰撞矩形向前设置了一个象素,具体原理见第二章。
在setBulletDirection()中,将根据坦克当前的方向确定子弹出膛后的方向,其中setRefPixelPosition()将子弹的参考点设置在其未变形状态的底部,setXY()将其放置到炮口的位置,setTransform()将其图片方向转到需要使用的位置。
canPass()函数将返回坦克是否能够向前前进,考虑到的因素有边界、障碍物。它返回一个boolean值,提供给go()函数,做进一步的判断。getTileIndex()将检测传递来的象素处是什么类型的障碍物,它将象素除以8(即障碍物的象素宽度),取整,
13
再通过getCell()得到。在得到障碍物属性后,判断其序号是否与草相同,或是否为空(序号为0)。因为所有的障碍物中只有草不会阻碍坦克的向前运行。
4.3 玩家坦克的功能属性
构造函数中需要将坦克方向设置为向上,因为刚出现时就是这样的状态。当开炮时,调用BulletSprite的setLayerManager()将子弹与layerManager联系起来。需要联系的还有自身坦克、地图。这些都由坦克传递给子弹。因为子弹是属于坦克的,它的属性需要跟当前的坦克保持一致。接着使用append()将子弹贴到layerManager上显示出来。最终调用其start()开始子弹自己的线程。子弹一旦开始运行,就脱离了当前坦克的控制,直到其生命周期终止。
无论子弹是属于敌人还是玩家的,它都必须记录自己的来源和攻击的对象。在玩家坦克发射的子弹中,就必须将攻击对象设置为所有的敌人。这样,它才能有扫描的目标。在setShootCheck()的参数中,传给子弹的是敌人的数组,子弹的对象就被确定了。
die()、resetPosition()、getLife()都是很简短的函数,但却提供必不可少的功能。他们可被外部调用,以取得生命值、死亡记数、重置位置。
在go()函数,每个方向在走前都须用if (canPass(UP)&&!collidesEnemy()) 检测是否可以行走。canPass()检测是否有障碍物及是否到边界。collidesEnenmy检测是否前方有坦克阻碍行动。当可以行走时,就在当前方向的坐标上增加或减少一个象素。
在collidesEnemy()函数中,将有一个for循环按照敌人数组的序号依次检测6次。有一点非常重要:在检测前,需要将敌人的检测矩形区域设置为与原来图片一样大小。否则,当玩家向上走,而有敌人从左方向右走,并且已经碰撞到玩家坦克时,玩家坦克会因为被判定已与敌人发生碰撞而不允许前进。事实上,敌人坦克此时并没有阻碍玩家前进。这样的判断必须排除在考虑范围外。当然,在设置完成后,必须将将检测区域设置回原先的状态,否则敌人在往后自己的检测中将发生错误。
14
4.4 敌人坦克的功能属性
由于和UserSrite同属于一个TankSprite的继承类,其功能就与UserSprite有很大的相似之处,但也有其自身的特别属性。其主要功能流程图如图4.2所示。
首先,EnemySprite继承了Runable接口。因为敌人的运行是自动的,需要有设定的程序让它可自己控制,而不像UserSprite完全通过每次输入的键盘信号来做出反映。因此,它可以运行在单独的线程中。
setEnemyShootCheck()函数与UserSprite中的一样,设置了攻击的对象,并且此函数将继续把参数传递给自身的子弹,以便子弹可以识别攻击对象。此函数由BattleCanvas调用。
getRandomDirection()以当前系统时间作为种子,调用了Random类的nextInt()产生一个随机的整数,此整数取除4的余数的绝对值作为随机的方向。
Random random=new Random(System.currentTimeMillis()); return Math.abs(random.nextInt())%4+1;
此时返回的值的范围就确定在1~4之间,正好对应四个方向。将他们代入需要使用方向的函数中就可以使用了。
getRandomStep()的原理类似:
Random random=new Random(System.currentTimeMillis()); return (Math.abs(random.nextInt())%4)*50;
只是需要乘以每秒会刷新的屏幕的次数。这样就相当于允许在某一个方向运行0~3秒钟的时间。
每个敌人还需要拥有一个内部的所有敌人的数组元素。这样,它们才可以自动检测自己是否与同伴发生了碰撞,以便采取躲避、转向等行动。
collidesWithOtherTank()将检测是否与其他坦克(包括敌人和玩家)。一个循环将依据敌人的序号查找5次。if(i==number)break ;语句将避免检测到自己,永远返回真。
collidesInOtherTank()虽与上面的函数很相似,但仍有一些细微不同,那就是不需要在检测前设置被检测方的矩形区域。因为不需要进行预先检测。此函数用来检测是否在刚出现时就与其他坦克发生碰撞的。如果一出现,出口就被堵死,显然,
15
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库(最新版)基于JAVA的游戏毕业设计论文(3)在线全文阅读。
相关推荐: