摘 要
社会在进步,人们生活质量也在日益提高。高强度的压力也接踵而来。社会中急需出现新的有效方式来缓解人们的压力。此次设计符合了社会需求,Java推箱子游戏可以让人们在闲暇之余,体验游戏的乐趣。具有操作简单,易于上手的特点。
推箱子游戏的玩法十分简单——控制人物绕过障碍物,到达目的地。此次毕业设计基于Java语言。将游戏地图做成通用的关卡设计,关卡用二维数组实现,在二维数组中设置值域来表示不同对象,运用二维数组的遍历算法来加载对应图片实现了游戏图片初始化状态。同时,通过初始化多个不同二维数组实现了游戏地图关卡的设计,方法巧妙且简单易行,有效的解决了图片排布问题。充分运用类和方法的调用来实现游戏关卡的初始化。用读取键值的方法判断游戏人物移动、停止、到达目的地。舒缓的音乐配合有趣的步骤,相信能引起很多人的兴趣。作为一个具有悠久历史的游戏,也能够引起人的怀念和共鸣。
游戏总体结构和代码分析
界面框架与按钮设计
本游戏界面简单清晰,操作界面优美,有很强的带入性,游戏操作模块分为:“悔一步”,“重来”,“上一关”,“下一关”,“第1关”,“最终关”,“选关”“音乐关”并且各个按钮的名称与相关功能为:
“悔一步”:返回上一移动状态(可以连续返回多步);
“重来”:重新开始当前关;
“上一关”:返回到当前关的上一关;
“下一关”:跳转到当前关的下一关;
“第1关”:游戏系统默认开始关为第一关,此按钮让玩家可以方便从其它关卡直接跳转到第一关;
“最终关”:可以跳转到最后一关,即游戏系统默认的最后一个关。
mainFrame() { super("推箱子v2.0"); setSize(720,720); setVisible(true); setResizable(false); setLocation(300,20); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container cont=getContentPane(); cont.setLayout(null); cont.setBackground(Color.black); Menu choice=new Menu(" 选项"); choice.add(renew);choice.add(last);choice.add(next);choice.add(choose);choice.add(back); choice.addSeparator();choice.add(exit); renew.addActionListener(this); last.addActionListener(this); next.addActionListener(this); choose.addActionListener(this); exit.addActionListener(this); back.addActionListener(this); Menu setmuc=new Menu(" 设置音乐") setmuc.add(nor);setmuc.add(qin);setmuc.add(po);setmuc.add(guang);setmuc.add(eye); nor.addActionListener(this); qin.addActionListener(this); po.addActionListener(this); guang.addActionListener(this); eye.addActionListener(this); Menu help=new Menu(" 帮助"); help.add(about); about.addActionListener(this); MenuBar bar=new MenuBar(); bar.add(choice);bar.add(setmuc);bar.add(help); setMenuBar(bar); }
选关模块
“选关”:选择想要挑战的关卡。当游戏玩家想要自主选择关卡时,可以随意输入想要跳入的关卡,节约了时间,玩家同过选择的关卡可以有效地挑战自我,连接上次的游戏进度。
其中,这个部分的部分代码为:
void Tuixiangzi(int i) { Levelmap=new Readmap(i); Levelmaptmp=new Readmap(i); map=Levelmap.getmap(); manX=Levelmap.getmanX(); manY=Levelmap.getmanY(); maptmp=Levelmaptmp.getmap(); repaint(); } int maxlevel(){return max;} public void paint(Graphics g) { for(int i=0; i<20; i++) for(int j=0; j<20; j++) { g.drawImage(myImage[map[j][i]],i*len,j*len,this); } g.setColor(new Color(0,0,0)); g.setFont(new Font("楷体_2312",Font.BOLD,30)); g.drawString("现在是第",150,40); g.drawString(String.valueOf(level),310,40); g.drawString("关",360,40); }
地图的绘制形成
地图以二维数组的形式进行存储,不同的数家代表了不同的含义。在本小游戏的地图文件中,0~9这十个数字的定义如下所示:
编号0:代表未定义的区域;
编号1:代表障碍物(或者边界);
编号2:代表草地;
编号3:代表箱子(未到指定位置);
编号4:代表目的地;
编号5:代表小人向下移动方向;
编号6:代表小人向左移动方向;
编号7:代表小人向右移动方向;
编号8:代表小人向上移动方向;
编号9:代表到达指定位置时的箱子。
例如下图所示(左边是二维数组的地图文件,右边是相对应的小游戏界面):
推箱子小游戏的游戏性,其主要功能是给用户一个良好的游戏界面,游戏中包括50关的地图文件。地图文件是是先写好的,所有界面信息以数的形式存放在一个20*20的二维数组当中并且把这些地图文件统一放在一个地图文件中,便于程序调用地图文件。然后,每一关就会按照这些数组数据重新绘制地图,从而达到小游戏的可用性及其相应的目的。
部分代码如下所示:
private int[][] mymap=new int[20][20]; FileReader r; BufferedReader br; String bb=""; int[] x;int c=0; Readmap(int k) { level=k; String s; File f=new File("maps\\"+level+".map"); r=new FileReader(f); br=new BufferedReader(r); } while ((s=br.readLine())!=null) { bb=bb+s; } byte[] d=bb.getBytes(); int len=bb.length(); int[] x=new int[len]; for(int i=0;i<bb.length();i++)x[i]=d[i]-48; for(int i=0;i<20;i++) { for(int j=0;j<20;j++) { mymap[i][j]=x[c]; if(mymap[i][j]==5) { mx=j;my=i; } c++; } }
小人与箱子移动的算法
小人与箱子的移动中包括了正移动以及“悔一步”的负移动。
正移动:小人向上、下、左、右的移动是一个判断算法,其判断都是通过判断小人前面是否是草地或者是箱子或者是障碍物(或者边界)。如果是障碍物或者是边界,就不能够进行移动;如果是没有箱子或者是障碍物,就可以自由移动;又如果是有箱子,就要判断是否可以移动箱子,最后再讨论箱子被推过的位置,小人移动的位置,以及它们的原位置和被遮挡住的新位置的图形变化等等,需要运用算法使其重新绘制地图,填补空白。算法判断完毕后,传出数据并且将其记录在一个堆栈中,以备“悔一步”时使用。
负移动:通过记录在堆栈中的数据来判断,前一步小人的移动方向以及移动中使用过的算法,逆向将代码重新运行,同时绘制并刷新地图以达到前一步的状态。
其中,这个部分的部分代码为:
void moveup() { if(map[manY-1][manX]==2||map[manY-1][manX]==4) { if(maptmp[manY][manX]==4||maptmp[manY][manX]==9) map[manY][manX]=4; else map[manY][manX]=2; map[manY-1][manX]=8; repaint();manY--;mystack.push(10); } else if(map[manY-1][manX]==3) { if(map[manY-2][manX]==4) { if(maptmp[manY][manX]==4||maptmp[manY][manX]==9) map[manY][manX]=4; else map[manY][manX]=2; map[manY-1][manX]=8; map[manY-2][manX]=9; repaint();manY--;mystack.push(11); } else if(map[manY-2][manX]==2) { if(maptmp[manY][manX]==4||maptmp[manY][manX]==9) map[manY][manX]=4; else map[manY][manX]=2; map[manY-1][manX]=8; map[manY-2][manX]=3; repaint();manY--;mystack.push(11); } else {map[manY][manX]=8;repaint();} } else if(map[manY-1][manX]==9) { if(map[manY-2][manX]==4) { if(maptmp[manY][manX]==4||maptmp[manY][manX]==9) map[manY][manX]=4; else map[manY][manX]=2; map[manY-1][manX]=8; map[manY-2][manX]=9; repaint();manY--;mystack.push(11); } else if(map[manY-2][manX]==2) { if(maptmp[manY][manX]==4||maptmp[manY][manX]==9) map[manY][manX]=4; else map[manY][manX]=2; map[manY-1][manX]=8; map[manY-2][manX]=3; repaint();manY--;mystack.push(11); } else {map[manY][manX]=8;repaint();} } if(map[manY-1][manX]==1) { map[manY][manX]=8;repaint(); } }
小人上下移动模块
在本小游戏系统中,小人与箱子的移动应该是能带给玩家较为逼真的视觉感受,这样才能体现系统的有效性与娱乐性。玩家通过控制小人,推动箱子在草地上避过障碍物与边界死角来到达指定的终点位置。
其中,这个部分代码分为上下左右移动。其中代码原理相同,转向利用下一步位置的判断。
void moveleft(){ ……} void movedown(){ ……} void moveright(){ ……}
小人向后转的代码如下:
void backup(int t) { int n=t; if(n==10) { if(maptmp[manY][manX]==4||maptmp[manY][manX]==9) { map[manY][manX]=4; } else map[manY][manX]=2; } else if(n==11) { if(maptmp[manY][manX]==4||maptmp[manY][manX]==9) { map[manY][manX]=9; } else map[manY][manX]=3; if(maptmp[manY-1][manX]==4||maptmp[manY-1][manX]==9) { map[manY-1][manX]=4; } else map[manY-1][manX]=2; } map[manY+1][manX]=8; repaint();manY++; }
悔棋模块
在推箱子小游戏中,数据的存储是非常重要的,不然就无法进行“悔一步”的操作。本小游戏系统受用了堆栈的存储方式来存储并且控制小人与箱子的移动以及地图文件的变换。而“悔一步”的操作让玩家可以进行反悔这一步操作,可避免因一时失手走错而导致的阻塞书面,可以节省玩家重新开始的时间。
其中,这个部分的部分代码为:
else if(e.getSource()==btnback||e.getSource()==back) { if(panel.isMystackEmpty())JOptionPane.showMessageDialog(this, "您还未移动!!!"); else { switch(panel.back()) { case 10:panel.backup(10);break; case 11:panel.backup(11);break; case 20:panel.backdown(20);break; case 21:panel.backdown(21);break; case 30:panel.backleft(30);break; case 31:panel.backleft(31);break; case 40:panel.backright(40);break; case 41:panel.backright(41);break; } }
音乐控制
在推箱子小游戏中,音乐的选择是非常重要的,通过音乐选择可以提高游戏的娱乐性。让游戏玩家可以再轻松越快的环境中进行游戏,同时游戏的随关卡转换也是本次设计的一大特点。使用了Java MIDI技术中的Sequence类和Sequencer容器和其中的getSequence()方法等。
其中,这个部分的部分代码为:
public void itemStateChanged(ItemEvent ie) { int no=jc.getSelectedIndex(); switch(no) { case 0:sound.setMusic("nor.mid"); if(sound.isplay()) sound.mystop(); sound.loadSound(); btnmuc.setLabel("音乐关"); nor.setEnabled(false); qin.setEnabled(true); guang.setEnabled(true); eye.setEnabled(true); po.setEnabled(true); panel.requestFocus(); break; case 1:sound.setMusic("qin.mid"); if(sound.isplay()) sound.mystop(); sound.loadSound(); btnmuc.setLabel("音乐关"); nor.setEnabled(true); qin.setEnabled(false); guang.setEnabled(true); eye.setEnabled(true); po.setEnabled(true); panel.requestFocus(); break; case 2:…… case 3:…… case 4:sound.setMusic("eyes on me.mid"); if(sound.isplay()) sound.mystop(); sound.loadSound(); btnmuc.setLabel("音乐关"); nor.setEnabled(true); qin.setEnabled(true); guang.setEnabled(true); eye.setEnabled(false); po.setEnabled(true); panel.requestFocus();break; }
效果如图
游戏视频展示以及下载链接