一、系统描述
问题描述
本程序是一个利用Java应用软件制作的飞机大战小游戏。在游戏过程中,用户通过鼠标的移动来控制飞机的运行方向,用鼠标点击控制游戏开始,鼠标移出界面表示暂停;鼠标移入继续进行游戏;程序运行窗口的右上角显示用户在游戏过程中所得的分数和飞机的生命值。当飞机生命值为0时游戏结束。本程序界面美观,随机奖励的设定增加了游戏的趣味性。
总体设计
本项目一共用了一个软件包shoot.Day01包,6个类分别为Airplane(小敌机类)Bee(小蜜蜂敌机类)Bullet(子弹类)Flying(本机类)FlyingObject(本机父类,所有敌机父类)Game(总体框架实现类,主类),2个接口 Award(奖励接口)Enemy(敌机接口)。
功能流程图
二、程序和算法的介绍
FlyingObject(本机和所有敌机父类)
定义了所有飞行物的长宽,坐标,以及共同的移动属性,是否出界标准等
packageshoot.Day01; importjava.awt.image.BufferedImage; publicabstractclassFlyingObject{ protectedBufferedImageimage; protectedintwidth; protectedintheight; protectedintx; protectedinty; //共同的属性,行为protectedabstractvoidstep(); publicabstractbooleanoutOfBounds(); //判断子弹是否打中敌人,在一定区域范围就算打中。publicbooleanshootBy(Bulletbullet){ intx1=this.x; intx2=this.x+this.width; inty1=this.y; inty2=this.y+this.height; intx=bullet.x; inty=bullet.y; returnx>x1&&x<x2&&y>y1&&y<y2; } }
Airplane(小敌机类)
定义小敌机的属性,长宽,下落距离,随机产生小敌机,得分,判断是否出界等。
packageshoot.Day01; importjava.util.Random; publicclassAirplaneextendsFlyingObjectimplementsEnemy{ privateintspeed=3; //敌机的属性,长宽,下落距离,随机产生publicAirplane(){ image=Game.enemy; width=image.getWidth(); height=image.getHeight(); Randomrand=newRandom(); x=rand.nextInt(Game.WIDTH-this.width); y=-this.height; } //得分publicintgetScore(){ return5; } //下落距离publicvoidstep(){ y+=speed; } //判断是否出界publicbooleanoutOfBounds(){ returnthis.y>=Game.HEIGHT; } }
Bee(小蜜蜂敌机类)
定义了小蜜蜂移动的格数,奖励,获取奖励类型,判断是否出界等。
packageshoot.Day01; importjava.util.Random; publicclassBeeextendsFlyingObjectimplementsAward{ //设定小蜜蜂移动的格数,奖励privateintxSpeed=1; privateintySpeed=2; privateintawardType; //构造方法,自动调用publicBee(){ image=Game.bee; //自动获取图片长宽width=image.getWidth(); height=image.getHeight(); Randomrand=newRandom(); x=rand.nextInt(Game.WIDTH-this.width); y=-this.height; //奖励随机获取awardType=rand.nextInt(2); } //获取奖励类型publicintgetType(){ returnawardType; } //重写step方法publicvoidstep(){ x+=xSpeed; y+=ySpeed; //判断小蜜蜂是否到达边界,并决定是否返回if (x>=Game.WIDTH-this.width){ xSpeed=-1; } if (x<=0){ xSpeed=1; } } //判断是否出界publicbooleanoutOfBounds(){ returnthis.y>=Game.HEIGHT; } }
Bullet(子弹类)
定义了子弹基本的下落距离,子弹的移动方法,判断子弹是否出界等。
packageshoot.Day01; importjava.util.Random; publicclassBulletextendsFlyingObject{ privateintspeed=3; //子弹构造方法,传参随本机publicBullet(intx,inty){ image=Game.stack; width=image.getWidth(); height=image.getHeight(); this.x=x; this.y=y; } //子弹移动方法publicvoidstep(){ y-=speed; } //判断子弹是否出界publicbooleanoutOfBounds(){ returnthis.y<=-this.height; } }
Flying(本机类)
定义了本机的基本属性,生命值,火力值,状态,以及射击的方法,随鼠标移动的方法,生命值火力值增加减少的方法,判断是否撞上飞行物的方法。
packageshoot.Day01; importjava.awt.image.BufferedImage; importjava.util.Random; publicclassFlyingextendsFlyingObject { //本机属性,生命值,火力,状态privateintlife; privateintdoubleFire; privateBufferedImage[] images; privateintindex; //构造方法publicFlying() { image=Game.benji; width=image.getWidth(); height=image.getHeight(); x=150; y=400; life=3; doubleFire=0; images=newBufferedImage[]{ Game.benji, Game.benji1 }; index=0; } //本机状态的切换publicvoidstep() { //控制两种图片变换时间index++; inta=index/50; intb=a%2; image=images[b]; } //射击方法publicBullet[] shoot() { //出炮口位置intxStep=this.width/8; intyStep=30; //单倍火力和双倍火力切换条件if (doubleFire>0) { Bullet[] bs=newBullet[2]; bs[0] =newBullet(this.x+xStep, this.y-yStep); bs[1] =newBullet(this.x+3*xStep, this.y-yStep); doubleFire-=2; returnbs; } else { Bullet[] bs=newBullet[1]; bs[0] =newBullet(this.x+2*xStep, this.y-yStep); returnbs; } } publicvoidmoveTo(intx, inty) { this.x=x-this.width/2; this.y=y-this.height/2; } //随鼠标移动,但也要重写该方法publicbooleanoutOfBounds(){ returnfalse; } //生命值增加和减少publicvoidaddLife(){ life++; } publicintgetLife(){ returnlife; } publicvoidclearLife(){ life--; } //火力值增加和减少publicvoidaddDoubleFire(){ doubleFire+=40; } publicvoidclearDoubleFire(){ doubleFire=0; } //判断是否撞上敌人,在一定范围就算撞上publicbooleanhit(FlyingObjectobject ){ intx1=object.x-this.width/2; intx2=object.x+object.width+this.width/2; inty1=object.y-this.height/2; inty2=object.y+object.height+this.height/2; intx=this.x+this.width/2; inty=this.y+this.height/2; returnx>x1&&x<x2&&y>y1&&y<y2; } }
Enemy(敌机接口)和Award(奖励接口)
Enemy(敌机接口)
packageshoot.Day01; publicinterfaceEnemy { publicintgetScore(); }
Award(奖励接口)
packageshoot.Day01; publicinterfaceAward { publicintDouble_File=0; publicintLIFE=1; publicintgetType(); }
这两个接口没有设置太多的功能,大家可以根据自行需要去扩展。
Game(总体框架实现类,主类)
继承JPanel类,添加了图片,对各类进行初始化,并加入了计时器等操作。
packageshoot.Day01; importjava.awt.*; importjava.awt.event.MouseAdapter; importjava.awt.event.MouseListener; importjava.awt.event.MouseMotionAdapter; importjava.awt.image.BufferedImage; importjavax.imageio.ImageIO; importjavax.swing.JFrame; importjavax.swing.JPanel; importjava.util.Random; importjava.util.Timer; importjava.util.TimerTask; importjava.util.Arrays; importjava.awt.event.MouseAdapter; importjava.awt.event.MouseEvent; importjava.awt.Color; importjava.awt.Font; /*** Game(总体框架实现类)主类* 继承JPanel类,添加了图片,对各类进行初始化,并加入了计时器等操作。* @author ioik*///主程序publicclassGameextendsJPanel { //设定界面长,宽publicstaticfinalintWIDTH=400; publicstaticfinalintHEIGHT=654; publicstaticBufferedImagebackground; publicstaticBufferedImagestart; publicstaticBufferedImagejixu; publicstaticBufferedImagebenji; publicstaticBufferedImagebenji1; publicstaticBufferedImageend; publicstaticBufferedImagebee; publicstaticBufferedImagestack; publicstaticBufferedImageenemy; //设定三种状态publicstaticfinalintSTART=0; publicstaticfinalintRUNNING=1; publicstaticfinalintPAUSE=2; publicstaticfinalintGAME_OVER=3; privateintstate=START; //创建本机对象,敌人数组,子弹数组privateFlyingmyfly=newFlying(); privateFlyingObject[] flyings={}; privateBullet[] bullets={}; //文件流错误必须要异常处理static { try{ //插入图片background=ImageIO.read(Game.class.getResource("black.png")); start=ImageIO.read(Game.class.getResource("start.png")); jixu=ImageIO.read(Game.class.getResource("jixuyouxi.png")); end=ImageIO.read(Game.class.getResource("gameover.png")); benji=ImageIO.read(Game.class.getResource("benji.png")); benji1=ImageIO.read(Game.class.getResource("benji1.png")); enemy=ImageIO.read(Game.class.getResource("enemy.png")); bee=ImageIO.read(Game.class.getResource("bee.png")); stack=ImageIO.read(Game.class.getResource("stack.png")); }catch(Exceptione){ //抛出异常e.printStackTrace(); } } //随机数方法 随机产生敌人和小蜜蜂奖励publicFlyingObjectnextOne(){ Randomrand=newRandom(); inttype=rand.nextInt(20); if (type<5){ returnnewBee(); }else{ returnnewAirplane(); } } //进入函数(自动的)intflyEnteredTIndex=0; publicvoidenterAction(){ flyEnteredTIndex++; //400ms,调用敌人产生函数每0.4秒产生一次敌人if (flyEnteredTIndex%40==0){ FlyingObjectone=nextOne(); //敌人数组扩容,接收新产生敌人,并赋值于最后一个位置flyings=Arrays.copyOf(flyings,flyings.length+1); flyings[flyings.length-1]=one; } } //敌人,子弹动起来publicvoidstepAction(){ //调用本机动起来函数myfly.step(); //调用所有子弹,敌人动起来for (inti=0;i<flyings.length;i++){ flyings[i].step(); } for (inti=0;i<bullets.length;i++){ bullets[i].step(); } } //射击方法intshootIndex=0; publicvoidshootAction(){ shootIndex++; //控制射击频率if (shootIndex%30==0){ //存储子弹Bullet[] bs=myfly.shoot(); bullets=Arrays.copyOf(bullets,bullets.length+bs.length); //新产生的子弹放入数组System.arraycopy(bs,0,bullets,bullets.length-bs.length,bs.length); } } //确定是否越界publicvoidoutOfBoundsAction(){ intindex=0; //判断敌人对象是否越界FlyingObject[] flyingLives=newFlyingObject[flyings.length]; for (inti=0;i<flyings.length;i++){ FlyingObjectf=flyings[i]; //保留未出界对象,出界对象数组缩容除去if (!f.outOfBounds()){ flyingLives[index]=f; index++; } } flyings=Arrays.copyOf(flyingLives,index); intj=0; //判断子弹是否出界Bullet[] ba=newBullet[bullets.length]; for (inti=0;i<bullets.length;i++){ Bulletb=bullets[i]; if (!b.outOfBounds()){ ba[j]=b; j++; } } bullets=Arrays.copyOf(ba,j); } //子弹撞击publicvoidbangAction(){ for (inti=0;i<bullets.length;i++){ Bulletb=bullets[i]; bang(b); } } //子弹撞击方法intscore=0; publicvoidbang(Bulletb){ intindex=-1; for (inti=0;i<flyings.length;i++){ FlyingObjectf=flyings[i]; //成功撞击的子弹if (f.shootBy(b)){ index=i; break; } } //成功撞击的子弹确定撞击对象if (index!=-1){ FlyingObjecta=flyings[index]; if (ainstanceofEnemy ){ //向上转型,如果是普通敌人对象就得分Enemye=(Enemy) a; score+=e.getScore(); } if (ainstanceofAward){ //如果是小蜜蜂对象,就判断得哪一种奖励,本机加火力值或者加生命值Awardc=(Award) a; inttype=c.getType(); switch (type){ caseAward.Double_File: myfly.addDoubleFire(); break; caseAward.LIFE: myfly.addLife(); break; } } //成功撞击的敌人需要除去,故进行数组缩容,除去被撞敌人FlyingObjectt=flyings[index]; flyings[index]=flyings[flyings.length-1]; flyings[flyings.length-1]=t; flyings=Arrays.copyOf(flyings,flyings.length-1); } } //检查游戏是否结束publicvoidcheckGameOverAction(){ if (isGameOver()){ state=GAME_OVER; } } //判断游戏结束的条件publicbooleanisGameOver(){ for (inti=0;i<flyings.length;i++){ FlyingObjectf=flyings[i]; if (myfly.hit(f)){ myfly.clearLife(); myfly.clearDoubleFire(); //撞击后的敌机消失,数组缩容FlyingObjectt=flyings[i]; flyings[i]=flyings[flyings.length-1]; flyings[flyings.length-1]=t; flyings=Arrays.copyOf(flyings,flyings.length-1); } } returnmyfly.getLife() <=0; } //减少main函数的负担publicvoidaction(){ //鼠标点击,运行状态下本机随着鼠标移动MouseAdapterl=newMouseAdapter() { publicvoidmouseMoved(MouseEvente){ if (state==RUNNING) { intx=e.getX(); inty=e.getY(); myfly.moveTo(x, y); } } publicvoidmouseClicked(MouseEvente){ switch (state){ //鼠标点击,开始游戏caseSTART: state=RUNNING; break; //游戏结束后,敌人子弹消失,得分为0;再次点击,游戏重新开始caseGAME_OVER: score=0; myfly=newFlying(); flyings=newFlyingObject[0]; bullets=newBullet[0]; state=START; break; } } //移出鼠标,游戏暂停publicvoidmouseExited(MouseEvente){ if (state==RUNNING){ state=PAUSE; } } //移入鼠标,游戏暂停publicvoidmouseEntered(MouseEvente){ if (state==PAUSE){ state=RUNNING; } } }; this.addMouseListener(l); this.addMouseMotionListener(l); //添加计时器,并设定时间间隔为10毫秒Timertimer=newTimer(); intintervel=10; timer.schedule(newTimerTask() { publicvoidrun() { if (state==RUNNING) { enterAction(); stepAction(); shootAction(); outOfBoundsAction(); bangAction(); checkGameOverAction(); } repaint(); } }, intervel, intervel); } //画板,画背景,画敌机,画本机,画状态publicvoidpaint(Graphicsg){ g.drawImage(background,0,0,null); paintmyfly(g); paintBullets(g); paintFlyingObjects(g); paintSA(g); paintState(g); } //画本机publicvoidpaintmyfly(Graphicsg){ g.drawImage(myfly.image,myfly.x,myfly.y,null); } //画敌机,小蜜蜂,随机产生publicvoidpaintFlyingObjects(Graphicsg){ for (inti=0;i<flyings.length;i++){ FlyingObjectf=flyings[i]; g.drawImage(f.image,f.x,f.y,null); } } //画子弹publicvoidpaintBullets(Graphicsg){ for (inti=0;i<bullets.length;i++){ Bulletb=bullets[i]; g.drawImage(b.image,b.x,b.y,null); } } publicvoidpaintSA(Graphicsg){ g.setColor(newColor(0xFF0000)); g.setFont(newFont(Font.SANS_SERIF,Font.BOLD,24)); g.drawString("SCORE:"+score,10,25); g.drawString("LIFE:"+myfly.getLife(),10,45); } publicvoidpaintState(Graphicsg){ switch (state){ caseSTART: g.drawImage(start,100,250,null); break; casePAUSE: g.drawImage(jixu,80,250,null); break; caseGAME_OVER: g.drawImage(end,100,250,null); break; } } publicstaticvoidmain(String[] args){ JFrameframe=newJFrame("Shooting"); Gamegame=newGame(); frame.add(game); frame.setSize(WIDTH,HEIGHT); frame.setAlwaysOnTop(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLocationRelativeTo(null); frame.setVisible(true); game.action(); } }
三、界面设计
找好游戏时所需要的图片,界面美观是用户游戏时,眼球的愉悦感和游戏的吸引力之一。
游戏界面:背景,本机,小敌机,小蜜蜂敌机。
background=ImageIO.read(Game.class.getResource("black.png"));
在这里附上一张游戏运行时图片。
到这一步,这个小游戏就基本上完成了,大家可以根据自己的需求去增加不同的功能哈。如果有啥问题,欢迎大家指出,谢谢大家。