import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Random;
import java.util.Stack;
import javax.swing.*;
class Lattice {
static final int INTREE = 1;
static final int NOTINTREE = 0;
private int x = -1; // 格子的位置,在第几行
private int y = -1; // 第几列
private int flag = NOTINTREE; // flag,标识格子是否已加入树中
private Lattice father = null; // 格子的父亲节点
public Lattice(int xx, int yy) {
x = xx;
y = yy;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getFlag() {
return flag;
}
public Lattice getFather() {
return father;
}
public void setFather(Lattice f) {
father = f;
}
public void setFlag(int f) {
flag = f;
}
public String toString() {
return new String("(" + x + "," + y + ")\n");
}
}
class Maze extends JPanel {
//private static final long serialVersionUID = -8300339045454852626L;
private final int NUM;//num是数组和地图的边长,
private final int width;//width 每个格子的宽度和高度;
private final int padding;//地图面板的大小;
private final Lattice[][] maze;//lattic类的数组maze,用于保存每个格子;
private int ballX, ballY;//起始位置坐标;
private boolean drawPath = false; // flag,标识是否画出路径
Maze(int m, int wi, int p) {
NUM = m;
width = wi;
padding = p;
maze = new Lattice[NUM][NUM];
for (int i = 0; i <= NUM - 1; i++)
for (int j = 0; j <= NUM - 1; j++)
maze[i][j] = new Lattice(i, j);
createMaze();
setKeyListener();
this.setFocusable(true);
}
// 初始化游戏,重开一局时使用
private void init() {
for (int i = 0; i <= NUM - 1; i++)
for (int j = 0; j <= NUM - 1; j++) {
maze[i][j].setFather(null);
maze[i][j].setFlag(Lattice.NOTINTREE);
}
ballX = 0;
ballY = 0;
drawPath = false;
createMaze();
// setKeyListener();
this.setFocusable(true);
repaint();
}
// 由格子的行数,得到格子中心点的像素X座标
public int getCenterX(int x) {
return padding + x * width + width / 2;
}
// 由格子的列数,得到格子中心点的像素Y座标
public int getCenterY(int y) {
return padding + y * width + width / 2;
}
public int getCenterX(Lattice p) {
return padding + p.getY() * width + width / 2;
}
public int getCenterY(Lattice p) {
return padding + p.getX() * width + width / 2;
}
// 检查是否到达最后一个格子,若是则走出了迷宫,重开一局游戏
private void checkIsWin() {
if (ballX == NUM - 1 && ballY == NUM - 1) {
JOptionPane.showMessageDialog(null, "你赢了 !", "你走出了迷宫。",
JOptionPane.PLAIN_MESSAGE);
init();
}
}
// 移动小球,c为按键码
synchronized private void move(int c) {
int tx = ballX, ty = ballY;
// System.out.println(c);
switch (c) {
case KeyEvent.VK_LEFT :
ty--;
break;
case KeyEvent.VK_RIGHT :
ty++;
break;
case KeyEvent.VK_UP :
tx--;
break;
case KeyEvent.VK_DOWN :
tx++;
break;
case KeyEvent.VK_SPACE :
drawPath = !drawPath;
break;
default :
}
// 若移动后未出界且格子之间有路径,则进行移动,更新小球位置,否则移动非法
if (!isOutOfBorder(tx, ty)
&& (maze[tx][ty].getFather() == maze[ballX][ballY]
|| maze[ballX][ballY].getFather() == maze[tx][ty])) {
ballX = tx;
ballY = ty;
}
}
private void setKeyListener() {
this.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
int c = e.getKeyCode();
move(c);
repaint();
checkIsWin();
}
});
}
// 是否出界
private boolean isOutOfBorder(Lattice p) {
return isOutOfBorder(p.getX(), p.getY());
}
private boolean isOutOfBorder(int x, int y) {
return x > NUM - 1 || y > NUM - 1 || x < 0 || y < 0;
}
// 获取格子的邻居格子
private Lattice[] getNeis(Lattice p) {
final int[] adds = {-1, 0, 1, 0, -1};
if (isOutOfBorder(p)) {
return null;
}
Lattice[] ps = new Lattice[4]; // 四个邻居格子,顺序为上右下左,出界的邻居为null
int xt;
int yt;
for (int i = 0; i <= 3; i++) {
xt = p.getX() + adds[i];
yt = p.getY() + adds[i + 1];
if (isOutOfBorder(xt, yt))
continue;
ps[i] = maze[xt][yt];
}
return ps;
}
// 构建随机树,创建迷宫
private void createMaze() {
// 随机选一个格子作为树的根
Random random = new Random();
int rx = NUM-1;
int ry = NUM-1;
// 深度优先遍历
Stack<Lattice> s = new Stack<Lattice>();
Lattice p = maze[rx][ry];
Lattice[] neis = null;
s.push(p);//根节点压入栈中;
while (!s.isEmpty()) {//如果栈不是空;
p = s.pop();//推出栈顶;
p.setFlag(Lattice.INTREE);//标志在栈内。
neis = getNeis(p);//遍历周围的格子;放进数组;
int ran = Math.abs(random.nextInt());//随机生成一个数;
for (int a = 0; a <= 3; a++) {//生成树;
ran++;
ran %= 4;//ran的值永远是0,1,2,3。
assert neis != null;
if (neis[ran] == null || neis[ran].getFlag() == Lattice.INTREE)//节点附近的节点有出界或者在树内;
continue;
s.push(neis[ran]);//否则压入栈;
neis[ran].setFather(p);//记录父节点;
}
}
}
// 抹掉两个格子之间的边
private void clearFence(int i, int j, int fx, int fy, Graphics g) {
int sx = padding + ((Math.max(j, fy)) * width),
sy = padding + ((Math.max(i, fx)) * width),
dx = (i == fx ? sx : sx + width),
dy = (i == fx ? sy + width : sy);
if (sx != dx) {
sx++;
dx--;
} else {
sy++;
dy--;
}
g.drawLine(sx, sy, dx, dy);
}
protected void paintComponent(Graphics g) {//画网格
super.paintComponent(g);
g.setColor(Color.blue);
for (int i = 0; i <= NUM; i++) {//画横线
g.drawLine(padding + i * width, padding, padding + i * width,
padding + NUM * width);
}
for (int j = 0; j <= NUM; j++) {//画竖线
g.drawLine(padding, padding + j * width, padding + NUM * width,
padding + j * width);
}
g.setColor(this.getBackground());//用背景色覆盖
for (int i = NUM - 1; i >= 0; i--) {
for (int j = NUM - 1; j >= 0; j--) {
Lattice f = maze[i][j].getFather();
if (f != null) {
int fx = f.getX(), fy = f.getY();
clearFence(i, j, fx, fy, g);
}
}
}
g.drawLine(padding, padding + 1, padding, padding + width - 1);
int last = padding + NUM * width;
g.drawLine(last, last - 1, last, last - width + 1);
g.setColor(Color.RED);
g.fillOval(getCenterX(ballY) - width / 3, getCenterY(ballX) - width / 3,
width / 2, width / 2);
if (drawPath) drawPath(g);
}
private void drawPath(Graphics g) {
if (drawPath)
g.setColor(Color.RED);
Lattice p = maze[0][0];//入口格子为对象
while (p.getFather() != null) {
g.drawLine(getCenterX(p), getCenterY(p), getCenterX(p.getFather()),
getCenterY(p.getFather()));
p = p.getFather();
}
g.fillOval(getCenterX(p) - width / 3, getCenterY(p) - width / 3,
width / 2, width / 2);//设置末尾圆点位置;
p = maze[NUM - 1][NUM - 1];//出口格子为对象
while (p.getFather() != null) {
if (p.getFlag() == 3)
break;
g.drawLine(getCenterX(p), getCenterY(p), getCenterX(p.getFather()),
getCenterY(p.getFather()));//画出父子节点之间的通路;
p = p.getFather();
}
}
}
class Countdown extends JFrame {
private static final long serialVersionUID = 1L;
private JLabel countdownLabel; // 倒计时标签
private Timer countdownTimer; // 倒计时计时器
private JPanel panel; // 面板
public Countdown(int seconds) {
setTitle("Countdown"); // 设置窗口标题
setSize(300, 200); // 设置窗口大小
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel(); // 初始化面板
getContentPane().add(panel); // 把面板添加到内容面板
countdownLabel = new JLabel("" + seconds, JLabel.CENTER); // 初始化倒计时标签
countdownLabel.setFont(countdownLabel.getFont().deriveFont(64.0f)); // 设置标签字体大小
panel.add(countdownLabel); // 把倒计时标签添加到面板中心
countdownTimer = new Timer(1000, new ActionListener() {
private int remainingSeconds = seconds;
public void actionPerformed(ActionEvent e) {
remainingSeconds--;
countdownLabel.setText("" + remainingSeconds); // 更新倒计时标签的文本
if (remainingSeconds <= 0) {
((Timer)e.getSource()).stop(); // 停止计时器
JOptionPane.showMessageDialog(null, "倒计时结束"); // 显示消息提示框
dispose(); // 关闭窗口
}
}
});
countdownTimer.start(); // 启动计时器
setVisible(true); // 显示窗口
}
}
class Cal extends JFrame implements ActionListener{
public static void main(String[] args) {
final int LX1 = 800, LY1 = 300;
JPanel pan = new JPanel();//创建容器;
pan.setBounds(20,40,240,280);
JButton pan4=new JButton("请选择难度等级");
JButton pan1=new JButton("简单");//按钮显示;
JButton pan2=new JButton("中等");
JButton pan3=new JButton("困难");
GridLayout grid2 = new GridLayout(4, 1);
pan.setLayout(grid2);//容器分割为四行,用于存放按钮。
pan.add(pan4);
pan.add(pan1);//按钮加入容器中。
pan.add(pan2);
pan.add(pan3);
Cal frame1 = new Cal();//创建Cal类的对象frame1.
frame1.setTitle("Welcome");//设置框架标题。
frame1.add(pan);//pan容器加入框架;
frame1.setLayout(null);//设置容器与框架边界有间隔。
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//默认关闭;
frame1.setSize(300, 400 );
frame1.setLocation(LX1, LY1);//frame1在屏幕的显示位置;
frame1.setVisible(true);//框架可视化;
frame1.setResizable(false);//大小不可更改;
pan1.addActionListener(frame1);
pan2.addActionListener(frame1);//按钮加入侦听;
pan3.addActionListener(frame1);
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equals("简单")) {
final int n = 20, width = 600, padding = 30, LX = 700, LY = 100;
JPanel p = new Maze(n, (width - padding - padding) / n, padding);
JFrame frame = new JFrame("欢迎来到迷宫游戏 简单模式 提示:(按空格键显示或隐藏路径)");
new Countdown(100);
frame.getContentPane().add(p);
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
frame.setSize(width + padding, width + padding + padding);
frame.setLocation(LX, LY);
frame.setVisible(true);
frame.setResizable(false);
}
if(e.getActionCommand().equals("中等")) {
final int n = 30, width = 600, padding = 25, LX = 700, LY = 100;//设定大小;
JPanel p = new Maze(n, (width - padding - padding) / n, padding);//生成迷宫;
JFrame frame = new JFrame("欢迎来到迷宫游戏 中等模式 提示:(按空格键显示或隐藏路径)");
new Countdown(150);
frame.getContentPane().add(p);//迷宫容器加入frame.
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
frame.setSize(width + padding, width + padding + padding);//设置frame的大小。
frame.setLocation(LX, LY);//屏幕上的显示位置;
frame.setVisible(true);//可视化;
frame.setResizable(false);
}
if(e.getActionCommand().equals("困难")) {
final int n = 50, width = 800, padding = 20, LX = 400, LY = 50;
JPanel p = new Maze(n, (width - padding - padding) / n, padding);
JFrame frame = new JFrame("欢迎来到迷宫游戏 困哪模式 提示:(按空格键显示或隐藏路径)");
new Countdown(200);
frame.getContentPane().add(p);
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
frame.setSize(width + padding, width + padding + padding);
frame.setLocation(LX, LY);
frame.setVisible(true);
frame.setResizable(false);
}
}
}
迷宫添加功能
最新推荐文章于 2024-10-17 19:11:48 发布