1、Click to edit Master text styles,Second level,Third level,Fourth level,Fifth level,*,能力目标,学习情境,4,MIDP2.0游戏开发,能力目标,学会使用,GameCvanvas,类的按键响应方法;,会熟练构造精灵类;,学会使用精灵实现动画的自动播放;,能熟练设置精灵出现的位置、显示帧、切换帧和绘制帧的方法。,学会使用,Vector,类创建子弹、移动子弹和绘制子弹;,学会自定义对象类的方法,构造一个游戏对象;,学会使用变量来模拟计数器功能,实现游戏对象以不同的频率运动;,熟练掌握使用,setTransform,(
2、),方法,实现精灵对象的旋转;,熟练掌握使用精灵类提供的方法进行碰撞的检测;,认知目标,认识游戏的屏幕,GameCanvas,类;,了解,GameCanvas,类和,Canvas,类的区别;,了解,GameCvanvas,类的自动双缓冲区和脱屏画笔;,学习精灵类中常用方法的语法格式,并理解其中参数的含义。,了解,Vector,类中的常用方法,并理解各参数的含义;,理解计数器在游戏程序中的使用方法和重要性;,理解,setTransform,(),方法中参数,rotating,的,8,个取值的含义,认知,collidesWith,()方法的语法格式,并理解其参数的含义;,理解区域级和像素级碰撞区别
3、理解播放帧序列中的帧号码和帧序号的区别;,4.1,学习情景描述,除棋牌类游戏之外,一款手机游戏,通常在一个特定的场景下展开,这个特定的场景,就是游戏的背景,根据需要,游戏背景还可以滚动。在游戏背景下,通常有若干个游戏的对象。其中可以由玩家通过按键来控制的游戏对象,称为游戏的,“,主角,”,,其余不能由玩家来控制的角色称为,“,NPC,”,,即非玩家控制角色,或系统控制角色。这些游戏对象可以进行移动、跳跃、投掷、发射子弹、攻击等各种动作。并通过对这些游戏对象和动作进行,HP,(生命值)、,MP,(经验值)、攻击力等等的数值设计,来判断游戏的胜利或者失败。,本学习情境分为五个学习单元来完成。单
4、元一,游戏主角设计;单元二,子弹设计;单元三,,NPC,(非玩家控制角色)设计;单元四,游戏背景设计;单元五,滚屏设计。,游戏主角是由玩家来控制的游戏对象,可以是一架飞机、一辆坦克、一个人物等。玩家通过手机键盘上的按键,可以控制游戏主角在手机屏幕上进行移动、跳跃、投掷、格斗等各种动作。在本学习单元中,首先,通过控制一架飞机在屏幕上的移动,来认识,GameCanvas,,并学习在,GameCanvas,中的键盘处理;然后,通过在移动时,改变飞机的形状,初识精灵类;最后,通过控制一个主角人物在屏幕上的行走,来深入地学习精灵类的使用。,4.2,游戏主角设计,4.2.2,知识准备:了解,GameCan
5、vas,类,MIDP,有,1.0,和,2.0,版本之分。在,MIDP2.0,中,新增加了一个,javax.microedition.lcdui.game,包,这个包中定义了专门用于游戏开发的,5,个新类:,GameCanvas,、,Layer,、,LayerManager,、,Sprite,和,TiledLayer,。这些类主要针对游戏开发的特点,提供了用于提高游戏性能的独立功能。,其中,,GameCanvas,类继承自,MIDP1.0,中的,Canvas,类,为游戏提供了基本的,“,屏幕,”,功能。除了完全具备,Canvas,类的功能外,这个类还提供了游戏专用的功能,这些功能简化了游戏开发并
6、提高了运行性能。比如:提供双缓冲绘制机制,能直接获得设备键盘的物理状态等等。,4.2.3,任务一:,控制主角飞机在屏幕上移动,任务准备,(,1,)声明变量,(,2,)在构造方法中实现初始化,(,3,)绘制屏幕,(,4,)响应键盘,(,5,)启动线程,(,6,)控制线程,(,7,)测试运行,(,1,)新建,MIDlet,工程,工程名为“,Ch4_Unit1”,。,(,2,)新建,MIDlet,类,类名为“,Ch4_Unit1_MIDlet”,。,(,3,)新建,GameCanvas,类。,(,4,)将主角飞机图片资源文件,plane.png,,拷贝到工程的“,res,”,文件夹下。,实现过程,知
7、识提炼,:,MIDP2.0,中的键盘处理,在,MIDP2.0,中,不需要等待键盘事件发生后才能获取键盘状态信息,通过调用,GameCanvas,类上定义的的,getKeyStates,(),方法,可以随时获取键盘状态信息,并且可以获取多个按键状态信息。,getKeyStates,(),方法的语法如下:,public,int,getKeyStates,(),该方法返回一个,int,型数据,其中每一位代表一个按键的状态。如果某个按键被按下,则对应的数据位为,1,,否则,数据位为,0,。,0,0,0,0,1,0,0,0,0,GAME D,GAME,C,GAME B,GAME A,FIRE,DOWN,
8、UP,RIGHT,LEFT,MIDP2.0,在,GameCanvas,类上定义了九个静态属性常量来表示游戏按键状态码,分别为:,UP_PRESSED,上功能键或数字,2,键,DOWN_PRESSED,下功能键或数字,8,键,LEFT_PRESSED,左功能键或数字,4,键,RIGHT_PRESSED,右功能键或数字,6,键,FIRE_PRESSED,开火功能键或数字,5,键,GAME_A_PRESSED,数字,1,键,GAME_B_PRESSED,数字,3,键,GAME_C_PRESSED,数字,7,键,GAME_D_PRESSED,数字,9,键,检测按键状态方法,int,keyCode,=,
9、this.getKeyStates,();/,获取键盘状态,if(,keyCode,=,GameCanvas.UP_PRESSED,)/,假如是按上键,else if(,keyCode,=,GameCanvas.DOWN_PRESSED,)/,假如是按下键,else if(,keyCode,=,GameCanvas.LEFT_PRESSED,)/,假如是按左键,else if(,keyCode,=,GameCanvas.RIGHT_PRESSED,)/,假如是按右键,当然,也可以使用位,“,与,”,的方法进行判断:,int keyCode=this.getKeyStates();/,获取键盘状
10、态,if(keyCode&,GameCanvas,.UP_PRESSED)!=0)/,假如是按向上键,else if(keyCode&,GameCanvas,.DOWN_PRESSED)!=0)/,假如是按下键,else if(keyCode&GameCanvas.LEFT_PRESSED)!=0)/,假如是按向左键,else if(keyCode&GameCanvas.RIGHT_PRESSED)!=0)/,假如是按向右键,使用上述两种方法,,都,只能判断一个按键被按下的情况,无法判断多个按键被同时按下的情况。原因是,比如,当同时按下,“,上,”,和,“,右,”,按键的时候,得到的值既不是,
11、UP_PRESSED,也不是,RIGHT_PRESSED,。,改进方法如下:,if(keyCode&,GameCanvas,.UP_PRESSED)!=0),&(keyCode&,GameCanvas,.LEFT_PRESSED)!=0)/,左、上两个按键被同时按下,else if(keyCode&,GameCanvas,.UP_PRESSED)!=0),&(keyCode&GameCanvas.RIGHT_PRESSED)!=0)/,右、上两个按键被同时按下,else if(keyCode&GameCanvas.LEFT_PRESSED)!=0)/,左键被按下,else if(keyCode
12、GameCanvas.RIGHT_PRESSED)!=0)/,右键被按下,小提示,:由于使用的是,else if,判断,因此对多按键被同时按下的判断应放在单按键被按下的判断之前。比如,当同时按下左、上按键时,会有如下三个条件同时成立:,(keyCode&,GameCanvas,.UP_PRESSED)!=0,(keyCode&,GameCanvas,.LEFT_PRESSED)!=0,(keyCode&,GameCanvas,.UP_PRESSED)!=0)&(keyCode&,GameCanvas,.LEFT_PRESSED)!=0),如果单按键被按下的判断放在前面,那么后面的多按键被按下
13、的情况就不再判断了。,protected,GameCanvas(boolean,suppressKeyEvents,),参数,suppressKeyEvents,用于指定是否支持,MIDP1.0,中的键盘响应事件。当参数取值为,true,时,指明不支持键盘响应事件。当用户操作键盘时,不会引起,keyPressed,(),、,keyReleased,(),和,keyRepeated,(),这三个事件方法被调用,这样可以提高速度和性能。如果参数取值为,false,,那么一旦按键被按下,就会调用上述三个传统的键盘事件处理方法。,在,GameCanvas,类中定义的九个表示按键状态码的静态属性常量中,
14、并不包含键盘左上角的“左软键”和键盘右上角的“右软键”。而这两个按键通常被用来响应“返回”、“暂停”、“退出”、“确定”等与用户交互的功能按钮。因此,在,MIDP2.0,中保留了对,MIDP1.0,中的按键响应机制,通常用于对“左软键”和“右软键”的响应。,要点提示,1,:,GameCanvas,类的构造方法,要点提示,1,:绘制双缓冲区,1,、在,MIDP2.0,中,,GameCanvas,类为每个实例对象提供唯一的图形缓冲区,缓冲区的大小与屏幕大小一样。每一个,GameCanvas,对象所拥有的缓冲区都是独立的,因此最好在游戏中仅创建一个,GameCanvas,类对象,并且重复利用。,2,
15、调用,GameCanvas,类的,getGraphics,(),方法,可以获得绘制缓冲区的,Graphics,对象。,3,、调用,GameCanvas,类的,flushGraphics,(),方法,可以将缓冲区中的内容绘制到屏幕上。,4.2.4,任务二:飞机移动时,实现形状的改变,知识准备:了解精灵类,精灵类(,Sprite,)被定义在,javax.microedition.lcdui.game,包中,专门用来代表游戏中的动画角色,可以显示一帧或多帧的连续图像。但所有的帧都必须大小相同,并且由一个,Image,对象提供。,比如:飞机在飞行时有三种不同的形状,正向飞行,向左侧飞行和向右侧飞行:
16、将这三个形状不同,但大小相同(,2524,)的小图片合成到一个,Image,对象中。每个小图片为一帧,帧的序号从,0,开始,并且按从左到右,从上到下的顺序依次排列。通过精灵类(,Sprite,)对象,可以任意显示其中某一帧的静态图像,或者连续地、循环显示任意顺序的某几帧,以实现动画效果。精灵类(,Sprite,)还提供了许多变换模式(翻转和旋转),以及碰撞检测的方法,能大大简化游戏逻辑的实现。,知识提炼:精灵类构造方法,(,1,),public Sprite,(,Image,image,),说明:通过图像创建一个只含有一帧图像的非动画,Sprite,类对象,即只能显示一幅静态图像。利用该方法
17、创建精灵类对象的目的,主要是利用,Sprite,类提供的方法,实现图像各种角度的翻转、碰撞检测等功能。,参数:,image,为只含有一帧的图像。,(,2,),public Sprite,(,Image,image,,,int,frameWidth,,,int,frameHeight,),说明:通过图像创建一个指定每帧图像大小的动画精灵类对象,该方法是最常用的。,参数:,image,为包含多帧的图像。,frameWidth,和,frameHeight,分别为每一帧的宽度和高度。,(,3,),public Sprite,(,Sprite,sprite,),说明:通过一个精灵类对象创建一个新的精灵类
18、对象。,参数:,sprite,是一个已创建好的精灵类。,要点提示,(,1,)单帧图像处理,public void,setFrame(int,index),其中,参数,index,为当前精灵类对象某一帧的序号。,(,2,)设置精灵位置并绘制精灵,public void,setPosition,(,int,x,,,int,y),说明:将精灵类对象定位到屏幕上的任意位置。,参数:,x,为横坐标,,y,为纵坐标。,public void,paint(Graphics,g),说明:绘制精灵类对象的当前帧。,参数:,g,为绘制上下文。,4.2.5,任务三:设计主角人物在屏幕上行走,任务准备,(,1,)声明
19、变量,(,2,)在构造方法中实现初始化,(,3,)绘制屏幕,(,4,)响应键盘,(,5,)启动线程,(,6,)控制线程,(,7,)测试运行,(,1,)新建,MIDlet,类,类名为,“,Ch4_Unit1_MIDlet2,”,。,(,2,)新建,GameCanvas,类,类名为,“,Ch4_Unit1_GameCanvas2,”,(,3,)将主角人物图片资源文件,man.png,,拷贝到工程的,“,res,”,文件夹下。,实现过程,要点提示,(,1,)多帧动画处理,public void,setFrameSequence(int,sequence),public void nextFrame(
20、),public void prevFrame(),小提示,:,使用,getFrame(),方法可以获取动画播放帧序列中当前正在播放的图像帧序号。在实际应用中,可以使用该方法判断某一次动画的播放帧是否已经播放完毕,比如:爆炸效果。,(,2,)精灵的移动,public void move(int dx,int dy),小提示:,当多次使用,move(),方法移动精灵后,其在屏幕上的确切位置就有些模糊了。此时可以使用精灵类提供的,getX(),和,getY(),方法,来获取精灵的当前坐标位置,以此判断精灵是否移出了屏幕,即进行边界的处理。,要点提示,int,sequence=6,7,8,0,1,2
21、3,4,5,9,10,11 ;/,上、下、左、右播放帧序列,int,direction=-1;/,行走方向,0,:上,1,:下,2,:左,3,:右,-1,:无方向,4.2.6,延伸任务:控制主角人物,按键行走,释放站立,private void,controlFrame(int,direction),if(,frameIndex,sequencedirection.length,-1),frameCounter,-;,if(,frameCounter,=0),frameIndex,+;,frameCounter,=3;,else,frameIndex,=1;/,跳过,0,帧(站立帧),spM
22、an.setFrame(sequencedirectionframeIndex,);/,设置帧,4.3,子弹设计,在射击类游戏中,除主角和,NPC,之外,子弹是一个很重要的游戏元素。他可以自动地、源源不断地发射,或者按键触发后发射。,其实,子弹只是一个特殊的游戏元素,使用与设计子弹同样的方法,可以设计任何形状的、与游戏角色相关的游戏元素,比如:潜水艇扔下的水雷、武士发出的暗器、天上飘落的雪花、大富翁游戏中掉落的金子等等。,在本学习单元中,首先,通过,Vector,类进行子弹的设计;然后,通过自定义类的方法设计子弹,并指出两种设计方法在实现手段和执行效率及执行效果上的差别;最后,通过实现散弹,进
23、一步学习精灵类对象在运行过程,进行图片的动态转换和对象的旋转。,4.3.2,使用,Vector,类设计子弹,知识,准备:了解,Vector,类,Vector,类位于,java.util,包中。在,J2ME MIDP2.0,中,保留了,J2SE,中的,Vector,类。该类对用户隐藏了内存分配的细节问题,使用,Vector,可以作为需要动态生成对象的容器。而游戏中的子弹则是动态生成的最好例子。,Vector,有,“,矢量、媒介、载体,”,的含义,很多书上称之为,“,向量,”,。但把他看成一个,“,容器,”,也许更形象一些,因为他能够像容器一样存放各种类型的对象。而且这个,“,容器,”,随着存放对
24、象的多少,其长度可以动态地变化。从这个角度来说,,Vector,就是一个能够存放任意类型对象的动态数组。,任务准备,(,1,)声明子弹相关变量,(,2,)在构造方法中加载图片,(,3,),自定义创建子弹的方,(,4,),自定义移动子弹的方法,(,5,),自定义绘制子弹的方,(,6,),在绘制屏幕的,show(),方法中,添加对上述三个方法的调用语句,实现对子弹的绘制。,(,7,)测试运行,(,1,),新建,MIDlet,工程,工程名为“,Ch4_Unit2,”,。,(,2,),新建,MIDlet,类,类名为“,Ch4_Unit2_MIDlet,”,。,(,3,),新建,GameCanvas,类
25、类名为“,Ch4_Unit2_GameCanvas,”,。,(,4,),将飞机图片文件,plane2.png,,和子弹图片文件,bullet.png,,拷贝到工程的“,res,”,文件夹下。,实现过程,知识提炼:,Vector,类的常用方法,(,1,)构造方法,public Vector(int initialCapacity,int capacityIncrement),说明:通过参数构造一个,Vector,的动态数组。能设置动态数组的初始容量和递增增量。,参数:,initialCapacity为Vector动态数组的初始容量。capacityIncrement为Vector动态数的递增增
26、量。,public Vector(int initialCapacity),说明:通过参数构造一个Vector的动态数组。只能设置动态数组的初始容量。一般是增长目前大小的一倍。,参数:initialCapacity为Vector动态数组的初始容量。,public Vector(),说明:构造一个Vector动态数组。,参数:无。,(,2)size方法,语法:,public int size(),说明:返回当前,Vector,动态数组中的元素个数。,参数:无。,(,3)addElement方法:,语法:,public void addElement(Object obj),说明:把组件加到向量尾
27、部,同时大小加,1,,向量容量比以前大,1,。,参数:,Object obj,是向动态数组中添加的对象。,(,4)removeElementAt方法:,语法:,public void removeElementAt(int i),说明:把第,i,个元素移出动态数组。同时,动态数组的大小减,1,。,参数:,int i,为要移除的动态数组的第,i,个元素。,(,5,),ElementAt,方法,:,语法:,public Object ElementAt(int i),说明:返回,Vector,动态数组中的第,i,个对象。,参数:,int i,为返回动态数组的第,i,个元素。,返回值:返回,Vect
28、or,动态数组中的对象。注意,返回的对象要强制转换为自己所需要的类型。,要点提示,private void,createBullet,(),if(-distance=0),Sprite,spBullet,=new,Sprite(imgBullet,);,spBullet.setPosition(spPlane.getX,()+10,spPlane.getY,();,vecBullet.addElement(spBullet,);,distance=6;,4.3.3,任务五:使用自定义子弹(,Bullet,)类的方法设计子弹,任务四和任务五,采用不同的方法,实现了同样的效果。但要注意区别两者在实
29、现方法和执行效率及执行效果上的差别。,Vector,类使用方便,但其对内存的频繁动态分配,在一定程度上会影响执行效率。并且放入,Vector,中的对象,一般都具有相同的属性和行为,比如,都按同一轨迹运行。而自定义子弹类的方法,则比较灵活。可以通过给定不同的子弹图片,定义各种不同形状的子弹,可以设计多种运行轨迹,使得不同的子弹可以按不同的轨迹运行。,需要注意的是,,Bullet,类只是一个普通的,JAVA,类,并没有继承自精灵类。主要原因是精灵类占用内存空间比较大一些,而且运行起来比较慢。因此,在本任务中只声明了一个子弹精灵,spBullet,,而不是十五个。并该子弹精灵看成是一个“精灵图章”,
30、当需要显示,15,颗子弹中的某一颗时,只需在相应的位置上“盖”上对应的“精灵图章”即可,这样既节省了内存,又提高了运行效率。,4.3.4,延伸任务:实现散弹,知识提炼,(,2,)精灵对象的旋转,使用,setTransform(),方法可以实现精灵对象的旋转,语法格式如下:,public viod setTransform(int rotating),其中,参数,Rotating,是由,8,个静态属性常量来表示。,TRANS_NONE,:保持图像的原始位置。,TRANS_ROT90,:顺时针旋转,90,度。,TRANS_ROT180,:顺时针旋转,180,度。,TRANS_ROT270,:顺时针
31、旋转,270,度。,TRANS_MIRROR,:沿着图片垂直,Y,轴翻转。,TRANS_MIRROR_ROT90,:沿着图片垂直,Y,轴翻转后,再顺时针旋转,90,度。,TRANS_MIRROR_ROT180,:沿着图片垂直,Y,轴翻转后,再顺时针旋转,180,度。,TRANS_MIRROR_ROT270,:沿着图片垂直,Y,轴翻转后,再顺时针旋转,270,度。,(,1,)精灵图片的动态转换,使用,setImage(),方法可以在运行时,实现精灵图片的动态转换,语法格式如下:,public void setImage(Image img,int frameWidth,int frameHeig
32、ht),其中,参数,img,表示要动态改变的图片对象,,frameWidth,和,frameHeight,是图片帧的宽度和高度。,4.4 NPC,(非玩家控制的角色)设计,NPC,,即非玩家控制的角色,比如:射击类游戏中的敌机。,NPC,的设计通常包括:形象设计、出现的时机、出现方式、运行轨迹、碰撞检测、生命值和得分等简单的数值设计、以及简单的,AI,设计等等。,在本学习单元中,以射击类游戏中的敌机为,NCP,的特例。首先,对其的出现方式和运行轨迹进行设计;然后,通过添加时间轴,对其的出现时机进行了统一的安排和设计;接着,进行主角子弹和敌机的碰撞检测;最后,通过出现,BOSS,,进行简单的数值
33、设计和,AI,设计。,4.4.2,任务六:出现敌机(队列),要点提示:敌机队形的设计,在本任务中,设计了四种队形,但实现方法都是类似的。要点是首先定位第一架敌机,后面的敌机位置分别是前一架敌机在,x,位置或,y,位置上间隔一定的距离。,比如,斜线排列的队形。首先,定位第一架敌机的位置是(,18,-10,);然后,后面的敌机分别在前一架敌机的右上侧,,x,方向上距离,36,像素,,y,方向上距离,35,像素。代码如下:,spNpc10.setPosition(18,-10);/,设置队列中第一架敌机出现的位置,for(,int,i=1;i spNpc1.length;i+),spNpc1i.se
34、tPosition(spNpc1i-1.getX()+36,spNpc1i-1.getY()-35);,水平排列和垂直排列的方法与此类似。根据这个方法还可以设计出很多其他的队形。,4.4.3,延伸任务:添加时间轴,安排游戏情节,timing+;,if(timing=20*1|timing=20*7),npcPlane.setNpc0Position(spNpc0,screenWidth,);,else if(timing=20*3|timing=20*9),npcPlane.setNpcQueue1(spNpc1);,else if(timing=20*4|timing=20*14),npcP
35、lane.setNpcQueue2(spNpc2,screenWidth,screenHeight,);,else if(timing=20*5|timing=20*15),npcPlane.setNpcQueue3(spNpc3,screenWidth,);,else if(timing=20*6|timing=20*18),npcPlane.setNpcQueue4(spNpc4);,时间,出现,NPC,1,秒和,7,秒,出现一架敌机,3,秒和,9,秒,出现敌机队列,1,4,秒和,14,秒,出现敌机队列,2,5,秒和,15,秒,出现敌机队列,3,6,秒和,20,秒,出现敌机队列,4,要点提
36、示:时间轴,在射击类游戏中,敌机的出现都应该是事先安排好的,在什么时刻出现什么敌机,什么时刻出现什么状态。例如,在本任务中,游戏开始,1,秒钟后,出现第一架敌机,过,3,秒钟后,出现敌机队列,1,,过,4,秒钟后,出现敌机队列,2,,,。这类似于游戏的脚本,从游戏的开始到结束,都贯穿着时间轴的概念。,在时间轴的设计中,使用了记录时间的一个长整型变量,timing,,在每个线程周期中,该变量值就会增加,1,。因为每个线程周期为,50,毫秒,当,timing,的值为,20,1,时,就意味着过了,50,20,1,毫秒,即,1,秒钟。依次类推,当,tming,的值为,20,2,时,就意味着游戏开始后,
37、2,秒钟,,。,时间轴的实现方法,可以使用如任务中的,if,else if,结构,当然,也可以使用,swtich,结构。,不管是什么游戏,都会有条,“,主线,”,,例如:对于射击游戏,主线就是时间。对于,RPG,(角色扮演类)游戏,主线可能就是游戏的故事情节。而对于益智类游戏来说,主线则显得不是很明朗,例如五子棋游戏,那么他的主线就是根据玩家下的每一步棋子,来判断下一步应该怎么操作。,4.4.4,任务七:实现主角子弹和敌机的碰撞检测,要点提示:关于爆炸效果,程序中,每一架敌机都对应地定义了一个爆炸精灵,这是因为爆炸帧的播放需要一个过程,因此两架甚至多架敌机的爆炸效果可能会同时出现在屏幕上,所以
38、爆炸精灵一般不共用。,爆炸效果的绘制是一个逐帧绘制的过程,当最后一帧绘制完毕的时候,就应该停止。否则,爆炸效果会反复出现。精灵类提供的,getFrame,(),方法,可以获取当前正在播放的帧索引号。当帧索引号再次变为,0,的时候,表示帧序列已经播放完毕,此时,应该再次设置当前的爆炸精灵为不可见。代码如下:,if(,spExplosion.getFrame,()=0)/,如果帧序列播放完毕(重新开始下一循环序列了),spExplosion.setVisible(false,);,知识提炼,public final,boolean,collidesWith,(,Sprite,s,boolean,p
39、ixelLevel,),public final,boolean,collidesWith,(,Image,image,int,x,int,y,boolean,pixelLevel,),public final,boolean,collidesWith,(,TiledLayer,t,boolean,pixelLevel,),每种碰撞的检测都存在两种级别的检测模式:区域级模式和像素级模式。,4.4.5,任务八:出现,BOSS,要点提示,(,1,)数值设计,在本任务中,设计,Boss,的初始生命值为,100,,被子弹击中一次,生命值就减,2,,直至为,0,,,Boss,爆炸。,在玩家得分的设计上,
40、击中一架敌机,得分加,10,。当,Boss,被击爆时,得分加,100,。,(,2,),Boss,的,AI,设计,在,Boss,完全移入屏幕之前,子弹击中,Boss,,其生命值是不减的。当,Boss,作水平移动时,碰到左右边界,会自动折返。当被子弹击中时,显示闪烁效果,并且移动速度变慢。,4.5,游戏,背景设计,游戏背景设计是游戏的一个亮点所在,当拿到一款游戏的时候,这款游戏是否吸引人,由两个方面所决定,游戏的控制和游戏的背景设计。,在,MIDP1.0,中没有提供专门负责背景设计的类,而在,MIDP2.0,中提供了一个,TiledLayer,类,该类专门负责对游戏背景进行管理和操作。由于移动设备
41、的存储资源有限,因此不可能装载过大的图片来作为游戏的背景。,本学习单元中,首先,通过一个图块(瓷片),“,铺设,”,单一的游戏背景;然后,使用由一块一块图块(瓷片)合成的图片,来,“,铺设,”,丰富的游戏背景;最后,学习使用地图编辑器,进行大型地图的设计,并显示在手机屏幕上。,4.5.2,任务九:使用一个图块,“铺设”单一的游戏背景,知识,提炼,TiledLayer,类被定义在,javax.microedition.lcdui.game,包中。该类代表一个背景图层,将背景所占区域分成大小相等的一系列单元格,每个单元格可被一个图块填充,最终构成一个组合成的大图块背景。,(,1,)背景的初步规划,
42、根据手机屏幕的大小,以及图块的宽、高,设计使用该图块铺满整个屏幕,横着需要多少个图块,,竖着需要多少个图块。使用,TiledLayer,类的构造方法可以进行游戏背景的初步规划。,TiledLayer,类的构造方法语法如下:,public,TiledLayer(int,columns,int,rows,Image,image,int,tileWidth,int,tileHeight,),(,2,)背景的铺设,为了形成背景,必须将图块填充到单元格中,通过调用,setCell,()方法可以为单元格设置引用的图块。语法如下:,public void,setCell(int,col,int,row,in
43、t,tileIndex,),(,3,)背景的绘制,背景形成后,需要通过,paint,(,Graphics g,)方法绘制出,TiledLayer,实例。,要点提示,在本任务中,,water.png,文件中只有一个图块,该图块的大小为,3030,,整个屏幕都使用同一个图块进行填充,类似这种使用同一个图块填充一片连续区域的情况,也可以使用,fillCells(),方法来完成。语法如下:,public void fillCells(int col,int row,int numCols,int numRows,int tileIndex),因此,程序中如下的相关代码:,for(,int,i=0;i,
44、backCols,;i+),for(,int,j=0;j,backRows,;j+),tlgBackground.setCell(i,j,1);,可以使用如下这条语句进行替代:,tlgBackground.fillCells(0,0,backCols,backRows,1);,4.5.3,任务十:使用合成图块,“铺设”丰富的游戏背景,要点提示,本任务中,使用了由一个一个图块合成的图片文件进行地图的铺设,这些图块必须拥有相同的宽度和高度。每个图块按从左到右,从上到下的顺序分配一个索引号,索引号从,1,开始。,小提示,:如果使用索引为,0,去填充单元格,则表示该区域不填充、绘制任何东西。,4.,5
45、4,任务十一:使用地图编辑器,设计大型地图,对于飞行射击类、角色扮演类等手机游戏,在设计游戏背景的时候,通常比手机屏幕要大很多,并且在游戏过程中,背景会自动地滚动,或者随着游戏主角的移动而移动,以增加游戏的动感。这样大的一个游戏背景,全凭想象来设计二维的地图数据,显然是不可行的。因此,在本任务中,通过借助于第三方开发的地图编辑器,来设计大型地图,将大大简化工作量和工作难度。,比较常用的手机游戏地图编辑器有,Mappy,(,mapwin,)、,Tiled,、,TILE STUDIO,等等,这些地图编辑工具可以到相关网站免费下载。,(,2,)在手机屏幕上显示地图,首先,将导出并修改的,“,m
46、ap1.png,”,文件拷贝到,“,Ch4_Unit4,”,工程的,“,res,”,文件夹下。,在,“,Ch4_Unit4,”,工程中新建类,类名为,“,Map1,”,。,打开,“,Map1,”,类,声明一个二维数组,并把,map1.txt,文件中的二维数组,“,map1_map0,”,的值拷贝过来。,将原先声明的二维数组,“,mapData,”,删除,,,并将代码中所有引用该二维数组的地方,,,全部用,“,Map1.map1Data,”,替代,(,1,)使用,mapwin,编辑地图,新建地图,导入图块,铺设地图,保存地图文件,导出地图数据,实现过程,小提示,:单击菜单“,MapTools”“
47、Remove Unused or Duplicate”,,可以在导出的图块文件中移除那些在编辑地图过程中没有用到的图块。,4.5.5,延伸任务:铺设多层地图,4.6,滚,屏设计,4.5.4,的任务十一和,4.5.5,的延伸任务中,铺设的地图都比手机屏幕大许多。默认情况下,手机屏幕上显示的仅仅是地图的左上角部分,其余部分由于位于屏幕之外,而无法显示。如果能让屏幕“滚动”起来,不仅能逐渐展示完整的地图,丰富玩家的视觉效果,而且还可以根据游戏情节需要,切换游戏场景,增加游戏的动感。,在本学习单元中,首先,实现游戏屏幕的自动滚动,了解实现滚屏的基本原理;然后,与任务八中完成的成果相结合,实现一个简单的
48、滚屏飞行射击游戏;最后,实现,RPG,游戏中的滚屏效果,即屏幕随着游戏主角的移动而移动。,4.6.2,知识准备:了解层管理器,事实上,,Sprite,类和,TiledLayer,类的父类均是定义在,javax.microedition.lcdui.game,包中的,Layer,类。,Layer,类是一个抽象类,表示游戏中的一个可视元素,图层。图层是可以在游戏画布中单独绘制并能够移动的对象。每个,Layer,类实例都有一定的位置、宽度、高度和是否可见的状态。,Layer,类的子类必须实现一个,piant(Graphics,),方法,使得它们能够被绘制在屏幕上。,游戏中不止一个图层存在,每个加入,
49、LayerManager,中的图层都会被分配一个索引值,索引值从,0,开始顺序分配。就想排队一样,先加入的排在前面,后加入的排在后面。但需要注意的是,图层的绘制过程却是相反的,排后面的(即索引值大的)先绘制,排前面的(即索引值小的)后绘制。显然,后绘制的图层将覆盖先绘制的图层。,另外,,LayerManager,中的图层索引号永远是连续的,如果一个图层从,LayerManager,中被移除,那么后面图层的索引号都将相应地作调整,以便使得图层索引号保持连续性。,4.6.3,任务十二:实现游戏屏幕的自动滚动,实现滚屏的原理:只需要在线程周期中不断改变可视窗口的,x,坐标(实现横向滚动),或者改变,
50、y,坐标(实现纵向滚动)。然后,将改变坐标位置后的可视窗口重新绘制在手机屏幕上即可。,X,轴,(,0,0,),Y,轴,(0,viewY,),手机屏幕,可视窗口,(,0,0,),screenHeight,screenWidth,setViewWindow(0,viewY,screenWidth,screenHeight,),paint(g,0,0),知识提炼:,:,LayerManager,类其他常用方法,(,1,),LayerManager,类构造方法,(,2,),append,方法,(,3,),remove,方法,(,4,),getLayerAt,方法,(,5,),getSize,方法,小提






