?? mazegame.java
字號:
//在安裝了jdk的情況下,有以下兩種方式運行:
//1.打開開發軟件,新建一個類文件名為MazeGame.java,然后把源程序全部復制過去再編譯運行即可
//2.可用命令行直接執行字節碼文件MazeGame.class
import javax.swing.*;
import javax.swing.event.*;
import java.util.Random;
import java.awt.*;
import java.awt.event.*;
/**
* 一個2D迷宮游戲.
*
* @author 山
*
*/
public class MazeGame {// 主方法
public static void main(String[] args) {
JFrame frame = new JFrame("迷宮游戲");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MazeGamePanel());
frame.pack();
frame.setVisible(true);
}
}
class MazeGamePanel extends JPanel {// 主面板:顯示迷宮以及定義相關操作.
private final int DELAY = 10;// 定時器延遲時間
private final int checkSizeDefault = 40, widthDefault = 20,
heightDefault = 10, numberOfChasersDefault = 2,
tearDownAllowedDefault = 0;// 默認值
private final int panelWidth = 1000, panelHeight = 700;// 主面板寬,高
private int checkSize, width, height, numberOfChasers, tearDownAllowed;
// 迷宮的單位方格大小(以像素為單位),迷宮的寬度(方格數),迷宮的高度(方格數),追趕者數目,玩家可拆墻次數限制
private Maze maze;// 一個迷宮面板
private JButton tipButton,solve, newMaze, chase, newSure,newCancel;
// 解答開關按鍵,生成新迷宮按鍵,追趕者開關按鍵,制造新迷宮確定取消按鍵
private boolean tipControl,solveControl,chaseControl;//開關按鍵控制:true為目前開啟,false為目前關閉
private JLabel help, tip, tearDownAllowedTimes;// 幫助標簽,提示標簽
private Timer timer;// 定時器:每隔10毫秒就獲取當前游戲結果和當前提示.注意:迷宮開始,計時器開始;迷宮停止(指玩家輸或贏),計時器停止
// (由于玩家不動也有可能被追趕成功,因此不推薦在每次移動時作出判斷)
private JLabel checkSizeLabel, widthLabel, heightLabel,
numberOfChasersLabel, tearDownAllowedLabel;// 滑動條提示
private JSlider checkSizeSlider, widthSlider, heightSlider,
numberOfChasersSlider, tearDownAllowedSlider;// 滑動條
private JPanel mazeMaker;// 迷宮制造者:通過滑動條制造迷宮.僅當玩家點擊"制造新迷宮"或輸或贏時才可見
private JScrollPane mazeContainer;//存放迷宮面板的滾動窗格
public MazeGamePanel() {
checkSize = checkSizeDefault;// 初始化一個迷宮:方格邊長:20像素
width = widthDefault;// 寬35個方格
height = heightDefault;// 高25個方格.即迷宮面板大小為500*700
numberOfChasers = numberOfChasersDefault;// 默認兩個追趕者
tearDownAllowed = tearDownAllowedDefault;// 默認不能拆墻
maze = new Maze(checkSize, width, height, numberOfChasers,
tearDownAllowed);// 根據上面的設置來初始化迷宮
tipButton=new JButton("開啟/關閉 提示");
tipControl=false;
chase = new JButton("開啟/關閉 追趕者");
chaseControl=true;
solve = new JButton("開啟/關閉 答案");
solveControl=false;
newMaze = new JButton("制造新迷宮");
newSure = new JButton("確定");
newCancel = new JButton("取消");
help = new JLabel(maze.help()+ "請 按 方 向 建 移 動. 按 W,S,A,D 拆除 上,下,左,右 墻.");
//按鍵功能與Maze類無關
tip = new JLabel(maze.currentTip());
tearDownAllowedTimes = new JLabel("剩余可拆墻次數:"
+ maze.tearDownAllowedTimes());
timer = new Timer(DELAY, new TimerListener());
timer.start();// 注意:迷宮開始,計時器開始;迷宮停止(指玩家輸或贏),計時器停止
maze.addKeyListener(new KeyBoardListener());// 迷宮面板添加鍵盤監聽器
maze.setFocusable(true);
checkSizeSlider = new JSlider(JSlider.HORIZONTAL, 10, 50,
checkSizeDefault);
checkSizeSlider.setMajorTickSpacing(10);
checkSizeSlider.setMinorTickSpacing(2);
checkSizeSlider.setPaintTicks(true);
checkSizeSlider.setPaintLabels(true);
checkSizeSlider.setAlignmentX(Component.LEFT_ALIGNMENT);
widthSlider = new JSlider(JSlider.HORIZONTAL, 10, 50, widthDefault);
widthSlider.setMajorTickSpacing(10);
widthSlider.setMinorTickSpacing(2);
widthSlider.setPaintTicks(true);
widthSlider.setPaintLabels(true);
widthSlider.setAlignmentX(Component.LEFT_ALIGNMENT);
heightSlider = new JSlider(JSlider.HORIZONTAL, 10, 40, heightDefault);
heightSlider.setMajorTickSpacing(10);
heightSlider.setMinorTickSpacing(2);
heightSlider.setPaintTicks(true);
heightSlider.setPaintLabels(true);
heightSlider.setAlignmentX(Component.LEFT_ALIGNMENT);
numberOfChasersSlider = new JSlider(JSlider.HORIZONTAL, 0, 20,
numberOfChasersDefault);
numberOfChasersSlider.setMajorTickSpacing(1);
numberOfChasersSlider.setPaintTicks(true);
numberOfChasersSlider.setPaintLabels(true);
numberOfChasersSlider.setAlignmentX(Component.LEFT_ALIGNMENT);
tearDownAllowedSlider = new JSlider(JSlider.HORIZONTAL, -1, 50,
tearDownAllowedDefault);
tearDownAllowedSlider.setMajorTickSpacing(10);
tearDownAllowedSlider.setMinorTickSpacing(2);
tearDownAllowedSlider.setPaintTicks(true);
tearDownAllowedSlider.setPaintLabels(true);
tearDownAllowedSlider.setAlignmentX(Component.LEFT_ALIGNMENT);
maze.setPreferredSize(new Dimension(panelWidth,panelHeight));
//用setPreferredSize設置加入到滾動窗格的面板的大小才可以使滾動窗格有效
mazeContainer=new JScrollPane(maze);//迷宮面板放到滾動窗格中
mazeContainer.setEnabled(false);//這樣可以使JScrollPane不響應鍵盤,只響應鼠標.這樣走迷宮會好一點
SliderListener listener = new SliderListener();
checkSizeSlider.addChangeListener(listener);
widthSlider.addChangeListener(listener);
heightSlider.addChangeListener(listener);
numberOfChasersSlider.addChangeListener(listener);
tearDownAllowedSlider.addChangeListener(listener);
checkSizeLabel = new JLabel("小方格邊長(以像素為單位):" + checkSizeDefault);
widthLabel = new JLabel("寬度(方格數):" + widthDefault);
heightLabel = new JLabel("高度(方格數):" + heightDefault);
numberOfChasersLabel = new JLabel("追趕者數目(可設為0):"
+ numberOfChasersDefault);
tearDownAllowedLabel = new JLabel("可拆墻限制次數(設為-1則不限次數,設為0則不能拆墻):"
+ tearDownAllowedDefault);
ButtonListener bListener = new ButtonListener();
chase.addActionListener(bListener);
solve.addActionListener(bListener);
tipButton.addActionListener(bListener);
newMaze.addActionListener(bListener);
newSure.addActionListener(bListener);
newCancel.addActionListener(bListener);// 添加按鍵監聽器
JLabel title=new JLabel("迷 宮 制 造 者");
mazeMaker = new JPanel();
mazeMaker.setLayout(new BoxLayout(mazeMaker, BoxLayout.Y_AXIS));
mazeMaker.add(title);
mazeMaker.add(Box.createRigidArea(new Dimension(0, 50)));
mazeMaker.add(checkSizeLabel);
mazeMaker.add(checkSizeSlider);
mazeMaker.add(Box.createRigidArea(new Dimension(0, 30)));
mazeMaker.add(widthLabel);
mazeMaker.add(widthSlider);
mazeMaker.add(Box.createRigidArea(new Dimension(0, 30)));
mazeMaker.add(heightLabel);
mazeMaker.add(heightSlider);
mazeMaker.add(Box.createRigidArea(new Dimension(0, 30)));
mazeMaker.add(numberOfChasersLabel);
mazeMaker.add(numberOfChasersSlider);
mazeMaker.add(Box.createRigidArea(new Dimension(0, 30)));
mazeMaker.add(tearDownAllowedLabel);
mazeMaker.add(tearDownAllowedSlider);
JPanel controls = new JPanel();
controls.setLayout(new BoxLayout(controls, BoxLayout.X_AXIS));
controls.add(newSure);
controls.add(newCancel);
mazeMaker.add(Box.createRigidArea(new Dimension(0, 50)));
mazeMaker.add(controls);
mazeMaker.add(Box.createRigidArea(new Dimension(0, panelHeight)));
// 加上以上語句,在顯示mazeMaker的時候就不會顯示到迷宮,比較美觀
mazeMaker.setPreferredSize(new Dimension(panelWidth, panelHeight));
mazeMaker.setVisible(false);// 僅當玩家點擊"制造新迷宮"或輸或贏時才可見
JPanel labels = new JPanel();
labels.setLayout(new BoxLayout(labels, BoxLayout.Y_AXIS));
labels.add(tip);
labels.add(tearDownAllowedTimes);
labels.add(help);
JPanel controlPanel = new JPanel();// 控制面板
controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.X_AXIS));
controlPanel.add(tipButton);
controlPanel.add(chase);
controlPanel.add(solve);
controlPanel.add(newMaze);
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(mazeMaker);//迷宮制造者面板
add(Box.createRigidArea(new Dimension(0, 20)));
add(mazeContainer);//存放迷宮的滾動窗格
add(Box.createRigidArea(new Dimension(0, 20)));
add(labels);//所有標簽存放的面板
add(Box.createRigidArea(new Dimension(0, 20)));
add(controlPanel);//控制面板:追趕者開關,答案開關,新迷宮按鍵
setPreferredSize(new Dimension(panelWidth, panelHeight));
}
private class TimerListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if(tipControl==true)
tip.setText(maze.currentTip());// 提示和可拆墻剩余次數最好都隨時更新
else
tip.setText("提示已關閉");
tearDownAllowedTimes.setText("剩余可拆墻次數:"
+ maze.tearDownAllowedTimes());
// 以下為判斷當前玩家輸贏狀態
if (maze.currentResult() == 1) {// 玩家已經到達終點
timer.stop();
// 必須要在此停止,否則因為timer仍在計時且兩個if其中之一直成立
int another = JOptionPane.showConfirmDialog(null,
"恭喜到達終點! 現在要生成一個新迷宮并重新開始?");
if (another == JOptionPane.YES_OPTION) {
timer.stop();
mazeMaker.setVisible(true);// 玩家確認制造新迷宮,則把迷宮制造者設為可見
}
maze.requestFocusInWindow();// 迷宮面板必須要重獲焦點(不能把這句放在if語句外,因為timer定時執行,會使maze不斷獲得焦點)
} else if (maze.currentResult() == -1) {// 玩家輸了
timer.stop();
// 必須要在此停止,否則因為timer仍在計時且兩個if其中之一直成立
int another = JOptionPane.showConfirmDialog(null,
"你輸啦! 現在要生成一個新迷宮并重新開始?");
if (another == JOptionPane.YES_OPTION) {
timer.stop();
mazeMaker.setVisible(true);// 玩家確認制造新迷宮,則把迷宮制造者設為可見
}
maze.requestFocusInWindow();// 迷宮面板必須要重獲焦點(不能把這句放在if語句外,因為timer定時執行,會使maze不斷獲得焦點)
}
}
}
private class KeyBoardListener extends KeyAdapter {
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
maze.move(2);
break;
case KeyEvent.VK_DOWN:
maze.move(4);
break;
case KeyEvent.VK_LEFT:
maze.move(1);
break;
case KeyEvent.VK_RIGHT:
maze.move(3);
break;
case KeyEvent.VK_W: {
maze.tearDown(2);
tearDownAllowedTimes.setText("剩余可拆墻次數:"
+ maze.tearDownAllowedTimes());
}
break;
case KeyEvent.VK_S: {
maze.tearDown(4);
tearDownAllowedTimes.setText("剩余可拆墻次數:"
+ maze.tearDownAllowedTimes());
}
break;
case KeyEvent.VK_A: {
maze.tearDown(1);
tearDownAllowedTimes.setText("剩余可拆墻次數:"
+ maze.tearDownAllowedTimes());
}
break;
case KeyEvent.VK_D: {
maze.tearDown(3);
tearDownAllowedTimes.setText("剩余可拆墻次數:"
+ maze.tearDownAllowedTimes());
}
break;
case KeyEvent.VK_I://保護
maze.protectEnabled(true);
break;
case KeyEvent.VK_O://非保護
maze.protectEnabled(false);
break;
case KeyEvent.VK_P://可變范圍拆墻
maze.variableTearDown(3, 3);
break;
}
}
}
private class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == chase){
if(chaseControl==false){
maze.chaseOn();chaseControl=true;}
else
{maze.chaseOff();chaseControl=false;}
}
else if (e.getSource() == solve){
if(solveControl==false){
maze.solveOn();solveControl=true;}
else {
maze.solveOff();solveControl=false;}
}
else if (e.getSource() == tipButton){
if(tipControl==true)tipControl=false;
else
tipControl=true;//在timer監聽器中判斷即可
}
else if (e.getSource() == newSure) {
mazeMaker.setVisible(false);// 玩家能點擊到這個按鍵說明已被設為可見,現在要重設為不可見
maze.newMaze(checkSizeSlider.getValue(),
widthSlider.getValue(), heightSlider.getValue(),
numberOfChasersSlider.getValue(), tearDownAllowedSlider
.getValue());
timer.start();// 計時器與迷宮一起啟動
} else if (e.getSource() == newCancel)
mazeMaker.setVisible(false);// 玩家能點擊到這個按鍵說明已被設為可見,現在要重設為不可見
else {
timer.stop();
mazeMaker.setVisible(true);// 玩家點擊制造新迷宮,則把迷宮制造者設為可見
}
maze.requestFocusInWindow();// 在按了鍵后迷宮面板必須要重獲焦點
}
}
private class SliderListener implements ChangeListener {
public void stateChanged(ChangeEvent e) {// 注意:此方法若可執行,說明mazeMaker此時必定可見
checkSizeLabel.setText("小方格邊長(以像素為單位):"
+ checkSizeSlider.getValue());
widthLabel.setText("寬度(方格數):" + widthSlider.getValue());
heightLabel.setText("高度(方格數):" + heightSlider.getValue());
numberOfChasersLabel.setText("追趕者數目(可設為0):"
+ numberOfChasersSlider.getValue());
tearDownAllowedLabel.setText("可拆墻限制次數(設為-1則不限次數,設為0則不能拆墻):"
+ tearDownAllowedSlider.getValue());
}
}
}
/**
* 本類定義了一個迷宮(面板)以及和迷宮相關的一些操作.直接把迷宮加入程序主面板即可.
*
* @author 山
*
*/
class Maze extends JPanel {
private final int DELAY = 1000;// 追趕者走動延遲時間(注意:追趕者很聰明,因此走動延遲時間長一點)
private int checkSize, width, height;// (迷宮由單位方格構成)單位方格的邊長,迷宮的寬度,迷宮的高度
private Wall[][] horizontal;// 單位水平墻.例如:若迷宮長度為3*5,則horizontal為4*5數組
private Wall[][] vertical;// 單位豎直墻.例如:若迷宮長度為3*5,則horizontal為3*6數組
private Check[][] checks;// 單位方格的狀態(在遍歷迷宮時需要).例如:若迷宮長度為3*5,則checks為3*5數組
private int playerRow, playerColumn;// 玩家目前所在行,目前所在列
private boolean[][] notMarked;// 單位方格是否有標記過(在隨機生成迷宮時需要)
private boolean solve;// 控制是否打印結果
private Timer timer;// 定時器:追趕者每隔一段時間就會在迷宮中走動,玩家碰到追趕者就輸.
private boolean chase;// 追趕者開關器
private Chaser[] chasers;// 追趕者們
private int tearDownAllowed;// 玩家可拆墻次數限制
private int currentResult;// 當前玩家的輸贏情況:-1表示玩家輸,0表示仍在進行,1表示玩家贏
private enum Check {// 遍歷迷宮時,單位方格的狀態:notFoot--未踏足過;notMain--踏足過但不在主路徑上;
// main--踏足過且在主路徑上(主路徑即從起點到終點的路徑)
notFoot, notMain, main;
}
private enum Wall {// 單位墻的存在狀態: notExist--不存在;exist--存在
notExist, exist;
}
/**
* 一個新的隨機生成的迷宮(面板).墻壁為黑色,玩家為藍色,追趕者為紅色. 建議本迷宮面板背景設置為白色. 玩家初始在左上角起點.右下角為終點.
* 這是新生成迷宮的提示:這是一個迷宮游戲.玩家是藍點,走到右下角就贏.若途中碰到兩個紅點追趕者就輸.請按鍵盤的方向鍵移動
* (建議新生成迷宮時給予玩家提示)
*
* @param checkSize
* (迷宮由單位方格構成)單位方格的邊長,以像素輸入
* @param width
* 迷宮的寬度.例如:寬度若為3即寬度為3個單位方格
* @param height
* 迷宮的高度.例如:高度若為3即高度為3個單位方格
* @param numberOfChasers
* 追趕者的數量(不要追趕者則為0)
* @param tearDownAllowed
* 玩家可拆墻次數限制(不可拆墻則為0,可拆無數次則為-1)
*/
public Maze(int checkSize, int width, int height, int numberOfChasers,
int tearDownAllowed) {
this.checkSize = checkSize;
this.width = width;
this.height = height;
this.tearDownAllowed = tearDownAllowed;
this.playerRow = 0;
this.playerColumn = 0;// 玩家初始在左上角的方格:起點
this.solve = false;// 不打印結果
this.timer = new Timer(DELAY, new TimerListener());// 追趕者移動定時器
if (numberOfChasers < 1){
this.chase = false;// 玩家設定追趕者為"關"
chasers=new Chaser[0];}
else {
this.chase=true;
chasers = new Chaser[numberOfChasers];// 初始化追趕者
for (int i = 0; i < numberOfChasers; i++) {
Random gen = new Random();
int ranRow, ranColumn;
ranRow = gen.nextInt(this.height);
ranColumn = gen.nextInt(this.width);
chasers[i] = new Chaser(ranRow, ranColumn);
}
chaseOn();// 開啟追趕者,使其移動
}
this.currentResult = 0;// 游戲進行中
this.checks = new Check[this.height][this.width];// 迷宮為height*width個方格
this.horizontal = new Wall[this.height + 1][this.width];
// 單位水平墻.horizontalWalls有height*width-1個
this.vertical = new Wall[this.height][this.width + 1];
// 單位豎直墻.horizontalWalls有(height-1)*width個
this.notMarked = new boolean[this.height][this.width];
// 單位方格是否有標記過(在隨機生成迷宮時需要)
// 以下為迷宮具體初始化:
// 全部方格狀態初始化為"未踏足過"
for (int i = 0; i < this.height; i++)
for (int j = 0; j < this.width; j++)
checks[i][j] = Check.notFoot;
// 全部方格初始化為"未標記"
for (int i = 0; i < this.height; i++)
for (int j = 0; j < this.width; j++)
notMarked[i][j] = true;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -