推箱子

总结了很久以前 2014年10月份 的面向对象程序设计大作业,重点实践了 swing 组件,对 JFrame 容器有了更高层次的认识,了解了 Java 事件处理机制。也体现当时的中二哈哈哈。

概述

游戏介绍

经典的推箱子是一个来自日本的古老游戏,目的是在训练你的逻辑思考能力。在一个狭小的仓库中,要求把木箱放到指定的位置,稍不小心就会出现箱子无法移动或者通道被堵住的情况,所以需要巧妙的利用有限的空间和通道,合理安排移动的次序和位置,才能顺利的完成任务。
注:因剧情需要,本游戏将围墙用宝箱来表示、所推的箱子用水晶来表示、目的地为法阵;即玩家被宝箱包围着,必须将所有水晶移动到达法阵才算过关。

游戏背景

游戏的主人公李逍遥之子李剑灵,在其姐姐李忆如被抓后,为了回到过去改变一切,勤加修炼回魂仙梦。不料有一次法术出错,落入幻梦之中。幻梦一共有五十关,每一关他必须将所有水晶置于法阵中才能过关;游戏由此展开。同时,在游戏中伴着不同的背景音乐,忆起陈年往事……

主要功能

游戏中的控制对象是李剑灵 、包围在其周围的是幻梦中的宝箱(但打不开,也隐喻了李剑灵的一生被宿命束缚着,虽珍惜眼前的一切甚至到最后逆天而行,却也无法保护好其身边所珍惜的……)、人可以任意行走的草地、几个可以移动的水晶和水晶放置的法阵。玩家通过按上下左右控制主人公移动水晶,当所有的水晶都移动到法阵后出现过关信息并选择是否进行下一关,否的话退出游戏,是的话显示下一关。直至 50 关全过出现通关信息。

概要设计

类、方法及其功能

图片资源

0:宝箱之外的区域;1:将可移动区域包围起来的宝箱;2:可移动区域——草地;
3:被移动的水晶;4:目的地——法阵;5:向上的人;6:向左的人;
7:向右的人;8:向下的人;9:水晶在法阵上;background:主界面的背景图。

详细设计

主要界面类(Sokoban.java)

该类主要继承 swing 包中的 JFrame 组件并实现 ActionListener,ItemListener 接口。其中需要声明并创建各种 JLabel 标签、JButton 按钮、JComboBox 组合框以及 JMenuItem 菜单栏。同时也兼任把背景、游戏界面和音乐控件加进整个主要界面中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Sokoban extends JFrame implements ActionListener,ItemListener {
public JLabel ……
public JButton ……
GamePanel panel;
Sound sound;
ImageIcon icon;
Image img;
JComboBox transed =new JComboBox();
JMenuItem ……
//Sokoban()是对声明或声明创建后的控件进行布局定位
Sokoban(){……}
// void actionPerformed(ActionEvent e) 这个方法是对大部分控件进行监听及事件处理
public void actionPerformed(ActionEvent e) {……}
// void itemStateChanged(ItemEvent ie) 是对组合框控件的监听及事件处理
public void itemStateChanged(ItemEvent ie) {……}
}

背景图片类(CInstead.java)

该类主要继承 swing 包中的 JPanel 组件。读取并绘制背景图片。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.awt.*;
import javax.swing.*;

public class CInstead extends JPanel {//此类用于添加背景图片
ImageIcon icon;
Image img;
public CInstead() {//用来读取图片
icon=new ImageIcon( Toolkit.getDefaultToolkit().getImage("image\\background.jpg" ));
img=icon.getImage();
}
public void paintComponent(Graphics g) {//绘制图片
super.paintComponent(g);
g.drawImage(img,0,0,null );
}
}

地图读取类(Readmap.java)

该类用用 FileReader 具体地读取地图内容,每一张地图都把其对应的矩阵放在一个 txt 文档中。一个 txt 代表一张地图,当关卡切换时便读取对应的 txt 并转化为二维数组,最终将数组给予游戏主程序类对其显示在游戏界面上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.io.*;
class Readmap {
private int level,mx,my;
private int[][] mymap=new int[20][20];
FileReader read; BufferedReader nr;String tr="";
int[] x;int c=0;
Readmap(int k){
//读图主程序
}
//获取器
int[][] getmap(){return mymap;}
int getmanX(){return mx;}
int getmanY(){return my;}
}

游戏主程序类(GamePanel.java)

该类为整个程序的主体,包含了对游戏的整个逻辑判断、接收地图读取类的数组将地图显示出来、监听键盘事件改变游戏视图以及提供各种方法给主要界面类的控件进行事件响应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.awt.*;
import java.awt.event.*;
import java.util.Stack;
import javax.swing.*;

class GamePanel extends JPanel implements KeyListener {
//最多关卡数为50关
int max=50,manX,manY,boxnum; int[][] map,maptmp;
Image[] myImage;
Readmap Levelmap; Readmap Levelmaptmp;
int len=30;
public int level=1;//关卡数为第一关
Stack mystack=new Stack();
GamePanel(){
//游戏窗口在面板上的定位
}
void Sokoban(int i){
//变量赋值
}
int maxlevel(){return max;}
public void paint(Graphics g){
//游戏窗口内文字的介绍
}
public void keyPressed(KeyEvent e){
if(e.getKeyCode()==KeyEvent.VK_UP){moveup();}//键盘监听到向上走
if(e.getKeyCode()==KeyEvent.VK_DOWN){movedown();}//键盘监听到向下走
if(e.getKeyCode()==KeyEvent.VK_LEFT){moveleft();}//键盘监听到向左走
if(e.getKeyCode()==KeyEvent.VK_RIGHT){moveright();}//键盘监听到向右走
if(iswin()){
//胜利弹出窗口
}
}
boolean isMystackEmpty(){return mystack.isEmpty();}//指出变量是否已经初始化
int back(){return (Integer)mystack.pop();}//出栈的操作(后退次数)
void remove(){mystack.removeAllElements();}//换关卡或重新开始
void moveup(){……}//向上走
void movedown(){……}//向下走
void moveleft() {……}//向左走
void moveright() {……}//向右走
//后退
void backup(int t) {……}
void backdown(int t) {……}
void backleft(int t) {……}
void backright(int t) {……}
boolean iswin() {
//是否胜利
}
}

背景音乐类(Sound.java)

该类用专门的 javax.sound.midi 包来处理个播放 MIDI 音乐,其中包括了与 MIDI 相关的各个类及其方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.io.File;
import javax.sound.midi.*;

public class Sound {
String path=new String("music\\");
String file=new String("1.mid");
Sequence seq;
Sequencer midi;
boolean sign;
void loadSound() {
try {
seq=MidiSystem.getSequence(new File(path+file));
midi=MidiSystem.getSequencer();
midi.open();
midi.setSequence(seq);
midi.start();
midi.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
} catch (Exception ex) {ex.printStackTrace();}
sign=true;
}
void mystop(){midi.stop();midi.close();sign=false;}//停止播放音乐
boolean isplay(){return sign;}//是否正在播放
void setMusic(String e){file=e;}//更换音乐
}

部分展示

总结

程序的优点

  1. 除了推箱子这个主体游戏,还加入了游戏的背景以及作品信息,为这个游戏注入灵魂。
  2. 完善了推箱子这个游戏的各种功能,如【首关】【上一关】【选关】【下一关】【末关】【退一步】【重来】
  3. 该游戏不仅有背景音乐,还可以对背景音乐进行切换。
  4. 能够将上述功能完整地揉和在一起,成为一个拥有自己特色的游戏。

程序的不足

  1. 程序中仍有一些代码不够简洁。
  2. 程序运行时仍可能有少许BUG存在。

遇到的困难及解决的问题

  1. 背景音乐只能支持 mid 格式,虽然成功独立构建支持 wav 格式音乐类及其附加功能,但因为对 java.applet.AudioClip 不了解,无法完美地揉和进程序中,最终决定依旧使用支持 mid 格式的类。
  2. 一些类或方法如什么是 Containerclear()removeAllElements() 的区别、什么时候用 ItemStateChanged 什么时候用 actionperformed 等等,因未曾经常使用这些类或方法导致知识的遗忘或缺失。编写本程序可巩固知识,温故而知新。

收获

  1. 了解了 swing 组件的使用方法,对 JFrame 容器有了更高层次的认识。
  2. 了解了 Java 事件处理机制以及实现了多个处理过程。
  3. 使用了抽象思维将游戏地图抽象为二维数组,游戏过程就是数组内数据的改变过程。
  4. 写代码不仅仅只是为了完成一个大作业,而是要投入感情去完成一个游戏。程序本身便是程序员孕育出来的孩子。

具体代码见:https://coding.net/u/gcusky/p/Sokoban/git

文章目录
  1. 1. 概述
    1. 1.1. 游戏介绍
    2. 1.2. 游戏背景
    3. 1.3. 主要功能
  2. 2. 概要设计
  3. 3. 详细设计
    1. 3.1. 主要界面类(Sokoban.java)
    2. 3.2. 背景图片类(CInstead.java)
    3. 3.3. 地图读取类(Readmap.java)
    4. 3.4. 游戏主程序类(GamePanel.java)
    5. 3.5. 背景音乐类(Sound.java)
  4. 4. 部分展示
  5. 5. 总结
    1. 5.1. 程序的优点
    2. 5.2. 程序的不足
    3. 5.3. 遇到的困难及解决的问题
    4. 5.4. 收获

20170129-project-4/

本页二维码