如何使用Java实现经典游戏推箱子

如何使用Java实现经典游戏推箱子

这篇文章将为大家详细讲解有关如何使用Java实现经典游戏推箱子,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

    主要设计

    1、游戏面板生成显示

    2、地图生成算法

    3、人物移动算法

    4、播放背景音乐

    5、箱子移动算法

    6、全部箱子移动到指定位置,才算游戏过关

    功能截图

    游戏开始

    移动效果

    游戏过关

    代码实现

    核心类

    publicclassGameFrameextendsJFrameimplementsActionListener,MouseListener,KeyListener{//实现动作事件监听器、鼠标事件监听器、键盘事件监听器//当前的关卡数,默认为第一关,从1开始计数privateintgrade=1;//row,column记载人的位置,分别表示二维数组中的行号和列号,即map[row][column]确定人的位置privateintrow=7,column=7;//leftX,leftY记载左上角图片的位置,避免图片从(0,0)坐标开始,因为是图片填充,从(0,0)开始不行privateintleftX=50,leftY=50;//记载地图的总共有多少行、多少列privateintmapRow=0,mapColumn=0;//记载屏幕窗口的宽度和高度privateintwidth=0,height=0;privatebooleanacceptKey=true;//程序所需要用到的图片privateImagepics[]=null;//图片数据privatebyte[][]map=null;//地图数据privateArrayListlist=newArrayList();privateSoundPlayerUtilsoundPlayer;//播放声音工具类/*常量,即游戏中的资源*/privatefinalstaticintWALL=1;//墙privatefinalstaticintBOX=2;//箱子privatefinalstaticintBOX_ON_END=3;//放到目的地的箱子privatefinalstaticintEND=4;//目的地privatefinalstaticintMAN_DOWN=5;//向下的人privatefinalstaticintMAN_LEFT=6;//向左的人privatefinalstaticintMAN_RIGHT=7;//向右的人privatefinalstaticintMAN_UP=8;//向上的人privatefinalstaticintGRASS=9;//通道privatefinalstaticintMAN_DOWN_ON_END=10;//站在目的地向下的人privatefinalstaticintMAN_LEFT_ON_END=11;//站在目的地向左的人privatefinalstaticintMAN_RIGHT_ON_END=12;//站在目的地向右的人privatefinalstaticintMAN_UP_ON_END=13;//站在目的地向上的人privatefinalstaticintMOVE_PIXEL=30;//表示每次移动30像素/***在构造方法GameFrame0中,调用initMap()法来初始化本关grade游戏地图,清空悔棋信*息列表list,同时播放MIDI背景音乐。*/publicGameFrame(){//游戏窗口的一些基本设置setTitle("推箱子游戏");setSize(600,600);setVisible(true);setLocation(300,20);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);ContainercontentPane=getContentPane();contentPane.setLayout(null);contentPane.setBackground(Color.black);//其他设置,初始化窗口的宽度和高度赋值给width和heightthis.width=getWidth();this.height=getHeight();//初始化图片资源getPics();//初始化地图数据initMap();//注册事件监听器setFocusable(true);addKeyListener(this);addMouseListener(this);//播放音乐initSound();}/***initMap()方法的作用是初始化本关grade游戏地图,清空悔棋信息列表list。调用*getMapSizeAndPosition(方法获取游戏区域大小及显示游戏的左上角位置(leftX,leftY)。*/publicvoidinitMap(){//获取当前关卡的地图数据map=MapFactory.getMap(grade);//清除上一关保存的回退地图数据,即清空list集合的内容list.clear();//初始化地图行列数和左上角起始坐标位置getMapSizeAndPosition();//获取角色的坐标位置getManPosition();}/***getManPosition()方法的作用是获取工人的当前位置(row,column)。*/publicvoidgetManPosition(){//即遍历地图数组map中存在那个值等于MANXXX(MAN_DOWN表示向下的人;MAN_UP表示向上的人)的情况,即表示该位置是人站立的位置,这个由地图数据扫描得出for(inti=0;i<map.length;i++){for(intj=0;j<map[0].length;j++){if(map[i][j]==MAN_DOWN||map[i][j]==MAN_DOWN_ON_END||map[i][j]==MAN_UP||map[i][j]==MAN_UP_ON_END||map[i][j]==MAN_LEFT||map[i][j]==MAN_LEFT_ON_END||map[i][j]==MAN_RIGHT||map[i][j]==MAN_RIGHT){//保存人的位置,i表示第几行,j表示第几列,而且是从0开始的this.row=i;this.column=j;break;}}}}/***getMapSizeAndPosition()方法用来获取游戏区域大小及显示游戏的左上角位置(lefX,leftY)。*/privatevoidgetMapSizeAndPosition(){//初始化mapRow和mapColumn,表示地图的行列数this.mapRow=map.length;this.mapColumn=map[0].length;//初始化leftX和leftY,即计算左上角的位置,this.leftX=(width-map[0].length*MOVE_PIXEL)/2;this.leftY=(height-map.length*MOVE_PIXEL)/2;}/***getPics()方法用来加载要显示的图片*/publicvoidgetPics(){//创建长度为13的数组,即有十三张图片pics=newImage[13];//然后循环将每张图片读取保存到pics数组中for(inti=0;i<13;i++){pics[i]=Toolkit.getDefaultToolkit().getImage("src\\images\\pic_"+(i+1)+".png");}}/***初始化播放的音乐*/publicvoidinitSound(){//调用SoundPlayerUtil类中的方法播放音乐soundPlayer=newSoundPlayerUtil();soundPlayer.loadSound("src\\sounds\\music.wav");soundPlayer.playSound(true);//循环播放}/***grassOrEnd()方法判断人所在的位置是通道GRASS还是目的地END**@paramman*@return*/publicbytegrassOrEnd(byteman){if(man==MAN_DOWN_ON_END||man==MAN_LEFT_ON_END||man==MAN_RIGHT_ON_END||man==MAN_UP_ON_END){returnEND;}returnGRASS;}/***人物向上移动*/privatevoidmoveUp(){//如果上一位是WALL,则不能移动if(map[row-1][column]==WALL){return;}//如果上一位是BOX或BOX_ON_END,需要考虑上一位的上一位是什么情况if(map[row-1][column]==BOX||map[row-1][column]==BOX_ON_END){//那么就需要考虑上一位的上一位情况,若上上一位是END或GRASS,则向上一步,其他情况不用处理if(map[row-2][column]==END||map[row-2][column]==GRASS){//要保留当前信息,以便回退上一步MapcurrMap=newMap(row,column,map);list.add(currMap);byteboxTemp=(byte)((byte)map[row-2][column]==END?BOX_ON_END:BOX);bytemanTemp=(byte)(map[row-1][column]==BOX?MAN_UP:MAN_UP_ON_END);//箱子变成temp,箱子往前移动一步map[row-2][column]=boxTemp;//人变成MAN_UP,往上走一步map[row-1][column]=manTemp;//将人刚才站的地方变成GRASS或者ENDmap[row][column]=grassOrEnd(map[row][column]);//人离开后修改人的坐标row--;}}else{//上一位为GRASS或END,无需考虑上上一步,其他情况不用处理if(map[row-1][column]==GRASS||map[row-1][column]==END){//保留当前这一步的信息,以便回退上一步MapcurrMap=newMap(row,column,map);list.add(currMap);bytetemp=(byte)(map[row-1][column]==END?MAN_UP_ON_END:MAN_UP);//人变成temp,人往上走一步map[row-1][column]=temp;//人刚才站的地方变成GRASS或者ENDmap[row][column]=grassOrEnd(map[row][column]);//人离开后修改人的坐标row--;}}}/***人物向下移动,其中(row,column)是当前角色站的位置,而map[row,column]表示当前角色*/privatevoidmoveDown(){//如果下一位是WALL,则不能移动//所以map[row+1][column]表示当前角色的下一步,也就是下一关图像块if(map[row+1][column]==WALL){return;}//如果下一位是箱子BOX或放到目的地的箱子BOX_ON_END(即如果下一位是箱子,而不管是什么类型的箱子,都可以推动箱子),需要考虑下位的下一位是什么情况if(map[row+1][column]==BOX||map[row+1][column]==BOX_ON_END){//那么就需要考虑下一位的下一位情况(即箱子的下一位是什么,决定着箱子是否可以向前移动),若下下一位是目的地END或通道GRASS,则表示箱子可以向下移动一步,其他情况不用处理//map[row+2][column]表示箱子的下一步是什么if(map[row+2][column]==END||map[row+2][column]==GRASS){//下面的代码就是箱子向前移动一步,人移动原来箱子的位置//要保留当前人和地图信息,以便回退下一步MapcurrMap=newMap(row,column,map);list.add(currMap);//判断箱子的下一步是否是目的地,如果是目的地,那么箱子的下一步就应该变成BOX_ON_END(放在目的地的箱子),如果不是目的地那应该还只是普通箱子BOXbyteboxTemp=(byte)((byte)map[row+2][column]==END?BOX_ON_END:BOX);//判断人的下一步是否是箱子bytemanTemp=(byte)(map[row+1][column]==BOX?MAN_DOWN:MAN_DOWN_ON_END);//箱子变成temp,箱子往下移动一步map[row+2][column]=boxTemp;//人变成MAN_UP,往下走一步map[row+1][column]=manTemp;//将人刚才站的地方变成GRASS或者ENDmap[row][column]=grassOrEnd(map[row][column]);//人离开后修改人的坐标row++;}}else{//执行到这里,表示人的下一步不是箱子,那么要么是通道要么是终点//下一位为GRASS或END,无需考虑下下一步,其他情况不用处理if(map[row+1][column]==GRASS||map[row+1][column]==END){//保留当前这一步的信息,以便回退下一步MapcurrMap=newMap(row,column,map);list.add(currMap);bytetemp=(byte)(map[row+1][column]==END?MAN_DOWN_ON_END:MAN_DOWN);//人变成temp,人往下走一步map[row+1][column]=temp;//人刚才站的地方变成GRASS或者ENDmap[row][column]=grassOrEnd(map[row][column]);//人离开后修改人的坐标row++;}}}/***人物向左移动*/privatevoidmoveLeft(){//如果左一位是WALL,则不能移动if(map[row][column-1]==WALL){return;}//如果左一位是BOX或BOX_ON_END,需要考虑左一位的左一位是什么情况if(map[row][column-1]==BOX||map[row][column-1]==BOX_ON_END){//那么就需要考虑左一位的左一位情况,若左左一位是END或GRASS,则向左一步,其他情况不用处理if(map[row][column-2]==END||map[row][column-2]==GRASS){//要保留当前信息,以便回退左一步MapcurrMap=newMap(row,column,map);list.add(currMap);byteboxTemp=(byte)((byte)map[row][column-2]==END?BOX_ON_END:BOX);bytemanTemp=(byte)(map[row][column-1]==BOX?MAN_LEFT:MAN_LEFT_ON_END);//箱子变成temp,箱子往前移动一步map[row][column-2]=boxTemp;//人变成MAN_UP,往左走一步map[row][column-1]=manTemp;//将人刚才站的地方变成GRASS或者ENDmap[row][column]=grassOrEnd(map[row][column]);//人离开后修改人的坐标column--;}}else{//左一位为GRASS或END,无需考虑左左一步,其他情况不用处理if(map[row][column-1]==GRASS||map[row][column-1]==END){//保留当前这一步的信息,以便回退左一步MapcurrMap=newMap(row,column,map);list.add(currMap);bytetemp=(byte)(map[row][column-1]==END?MAN_LEFT_ON_END:MAN_LEFT);//人变成temp,人往左走一步map[row][column-1]=temp;//人刚才站的地方变成GRASS或者ENDmap[row][column]=grassOrEnd(map[row][column]);//人离开后修改人的坐标column--;}}}/***人物向右移动*/privatevoidmoveRight(){//如果右一位是WALL,则不能移动if(map[row][column+1]==WALL){return;}//如果右一位是BOX或BOX_ON_END,需要考虑右位的右一位是什么情况if(map[row][column+1]==BOX||map[row][column+1]==BOX_ON_END){//那么就需要考虑右一位的右一位情况,若右右一位是END或GRASS,则向右一步,其他情况不用处理if(map[row][column+2]==END||map[row][column+2]==GRASS){//要保留当前信息,以便回退右一步MapcurrMap=newMap(row,column,map);list.add(currMap);byteboxTemp=(byte)((byte)map[row][column+2]==END?BOX_ON_END:BOX);bytemanTemp=(byte)(map[row][column+1]==BOX?MAN_RIGHT:MAN_RIGHT_ON_END);//箱子变成temp,箱子往右移动一步map[row][column+2]=boxTemp;//人变成MAN_UP,往右走一步map[row][column+1]=manTemp;//将人刚才站的地方变成GRASS或者ENDmap[row][column]=grassOrEnd(map[row][column]);//人离开后修改人的坐标column++;}}else{//右一位为GRASS或END,无需考虑右右一步,其他情况不用处理if(map[row][column+1]==GRASS||map[row][column+1]==END){//保留当前这一步的信息,以便回退右一步MapcurrMap=newMap(row,column,map);list.add(currMap);bytetemp=(byte)(map[row][column+1]==END?MAN_RIGHT_ON_END:MAN_RIGHT);//人变成temp,人往右走一步map[row][column+1]=temp;//人刚才站的地方变成GRASS或者ENDmap[row][column]=grassOrEnd(map[row][column]);//人离开后修改人的坐标column++;}}}/***验证玩家是否过关,如果有目的地END值或人直接站在目的地则没有成功**@return如果已经通关则返回true,否则返回false*/publicbooleanisFinished(){for(inti=0;i<mapRow;i++){for(intj=0;j<mapColumn;j++){if(map[i][j]==END||map[i][j]==MAN_DOWN_ON_END||map[i][j]==MAN_UP_ON_END||map[i][j]==MAN_LEFT_ON_END||map[i][j]==MAN_RIGHT_ON_END){returnfalse;}}}returntrue;}//使用双缓冲技术解决动画闪烁问题privateImageiBuffer;privateGraphicsgBuffer;/***重写绘制整个游戏区域的图形**@paramg*/@Overridepublicvoidpaint(Graphicsg){if(iBuffer==null){iBuffer=createImage(width,height);gBuffer=iBuffer.getGraphics();}//清空屏幕原来的绘画gBuffer.setColor(getBackground());gBuffer.fillRect(0,0,width,height);for(inti=0;i<mapRow;i++){for(intj=0;j<mapColumn;j++){//画出地图,i表示行数,j表示列数if(map[i][j]!=0){//这里要减1是因为图片的名称序号不对应,应该从0开始,但是从1开始的gBuffer.drawImage(pics[map[i][j]-1],leftX+j*MOVE_PIXEL,leftY+i*MOVE_PIXEL,30,30,this);}}}gBuffer.setColor(Color.RED);gBuffer.setFont(newFont("楷体_2312",Font.BOLD,30));gBuffer.drawString("现在是第",150,140);gBuffer.drawString(String.valueOf(grade),310,140);gBuffer.drawString("关",360,140);g.drawImage(iBuffer,0,0,this);/*下面的代码是未使用双缓冲技术会导致动画闪烁的代码*//*g.clearRect(0,0,width,height);for(inti=0;i<mapRow;i++){for(intj=0;j<mapColumn;j++){//画出地图,i表示行数,j表示列数if(map[i][j]!=0){//这里要减1是因为图片的名称序号不对应,应该从0开始,但是从1开始的g.drawImage(pics[map[i][j]-1],leftX+j*MOVE_PIXEL,leftY+i*MOVE_PIXEL,30,30,this);}}}g.setColor(Color.RED);g.setFont(newFont("楷体_2312",Font.BOLD,30));g.drawString("现在是第",150,140);g.drawString(String.valueOf(grade),310,140);g.drawString("关",360,140);*/}@OverridepublicvoidactionPerformed(ActionEvente){}@OverridepublicvoidkeyTyped(KeyEvente){}@OverridepublicvoidkeyPressed(KeyEvente){//当按键盘上的按键时触发的事件switch(e.getKeyCode()){caseKeyEvent.VK_UP://上方向键moveUp();//向上移动break;caseKeyEvent.VK_DOWN://下方向键moveDown();//向下移动break;caseKeyEvent.VK_LEFT://左方向键moveLeft();//向左移动break;caseKeyEvent.VK_RIGHT://右方向键moveRight();//向右移动break;}//然后重新绘制界面repaint();//在移动完成后可能已经通关,所以需要判断是否通关if(isFinished()){//禁用按键acceptKey=false;//判断是否是最后一关,如果是则直接提示,如果不是则询问是否要进入下一关if(grade==MapFactory.getCount()){JOptionPane.showMessageDialog(this,"恭喜通过最后一关!");}else{//提示进入下一关Stringmsg="恭喜通过第"+grade+"关!!!\n是否要进入下一关?";intchoice=JOptionPane.showConfirmDialog(null,msg,"过关",JOptionPane.YES_NO_OPTION);if(choice==1){System.exit(0);}elseif(choice==0){//进入下一关acceptKey=true;nextGrade();}}}}@OverridepublicvoidkeyReleased(KeyEvente){}@OverridepublicvoidmouseClicked(MouseEvente){//MouseEvent.BUTTON3表示鼠标右键if(e.getButton()==MouseEvent.BUTTON3){undo();}}@OverridepublicvoidmousePressed(MouseEvente){}@OverridepublicvoidmouseReleased(MouseEvente){}@OverridepublicvoidmouseEntered(MouseEvente){}@OverridepublicvoidmouseExited(MouseEvente){}/***返回当前人的位置用getManX()方法和getManY()方法**@return*/publicintgetManX(){returnrow;}publicintgetManY(){returncolumn;}/***返回当前关卡数**@return*/publicintgetGrade(){returngrade;}/***返回当前关卡的地图信息**@return*/publicbyte[][]getMap(){returnMapFactory.getMap(grade);}/***显示提示信息对话框**@paramstr*/publicvoiddisplayToast(Stringstr){JOptionPane.showMessageDialog(null,str,"提示",JOptionPane.ERROR_MESSAGE);}/***撤销移动操作*/publicvoidundo(){if(acceptKey){if(list.size()>0){//如果要撤销,必须要走过//考虑用栈更合适MappriorMap=(Map)list.get(list.size()-1);this.map=priorMap.getMap();this.row=priorMap.getManX();this.column=priorMap.getManY();repaint();//重新画图list.remove(list.size()-1);}else{displayToast("不能再撤销了!");}}else{displayToast("此关已完成,不能撤销!");}}/***实现下一关的初始化,并且调用repaint()方法显示游戏界面*/publicvoidnextGrade(){//初始化下一关的数据if(grade>=MapFactory.getCount()){displayToast("恭喜你完成所有关卡!");acceptKey=false;}else{//关卡数加1grade++;//初始化下一关的地图数据initMap();//重新绘制画面repaint();acceptKey=true;}}/***实现上一关初始化并且调用repaint()发显示游戏界面*/publicvoidpriorGrade(){grade--;acceptKey=true;if(grade<0){grade=0;}initMap();repaint();}}

    声音播放类

    publicclassSoundPlayerUtil{publicFilefile;publicAudioInputStreamstream;publicAudioFormatformat;DataLine.Infoinfo;Clipclip;/***加载声音文件,支持wav、mp3等声音文件**@paramfilePath声音文件的路径*/publicvoidloadSound(StringfilePath){file=newFile(filePath);try{stream=AudioSystem.getAudioInputStream(file);}catch(UnsupportedAudioFileException|IOExceptione){e.printStackTrace();}format=stream.getFormat();}/***播放音乐**@paramisLoop表示是否循环播放音乐,如果传入的是true则表示循环播放*/publicvoidplaySound(booleanisLoop){info=newDataLine.Info(Clip.class,format);try{clip=(Clip)AudioSystem.getLine(info);clip.open(stream);}catch(LineUnavailableException|IOExceptione){e.printStackTrace();}if(isLoop){clip.loop(Clip.LOOP_CONTINUOUSLY);//添加该句代码可以循环播放}clip.start();}}

    关于“如何使用Java实现经典游戏推箱子”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

    发布于 2022-02-15 20:43:00
    收藏
    分享
    海报
    0 条评论
    63
    上一篇:怎么为WPF框架Prism注册Nlog日志服务 下一篇:.NET Core如何使用Worker Service创建服务
    目录

      0 条评论

      本站已关闭游客评论,请登录或者注册后再评论吧~

      忘记密码?

      图形验证码