main
张锟 2 weeks ago
parent 28cc6d0cad
commit b93c0eef91

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Sokoban</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=GBK

@ -1,2 +1,4 @@
# work
推箱子游戏
![alt text](屏幕截图(202)-1.png) ![alt text](屏幕截图(201)-1.png) ![alt text](屏幕截图(200)-1.png) ![alt text](屏幕截图(199)-1.png)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000011100000000000
00000014100000000000
00000010100000000000
00000010100000000000
00000010111111000000
00011112020041000000
00014002311111000000
00011111210000000000
00000001010000000000
00000001010000000000
00000001410000000000
00000001110000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000011111110000000
00000010344410000000
00000010001111000000
00000111200001000000
00000100012101000000
00000102010001000000
00000100011111000000
00000111110000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000011111000000000
00000013001000000000
00000010221011100000
00000010201014100000
00000011101114100000
00000001000004100000
00000001001000100000
00000001001111100000
00000001111000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000011111000000
00000001110001000000
00000011000121000000
00000010011001000000
00000010200001000000
00000013011201000000
00000011114441000000
00000000011111000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000111110000000
00000000130410000000
00000000121010000000
00000000100010000000
00000011101010000000
00000010020410000000
00000010240010000000
00000011111110000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000111100000000
00000001100110000000
00000001020010000000
00000001002010000000
00000001114010000000
00000000014010000000
00000000014110000000
00000001114010000000
00000001020310000000
00000001002010000000
00000001000110000000
00000001111100000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000111100000000
00000001144100000000
00000011444100000000
00000011020100000000
00000010202100000000
00000010120110000000
00000010102010000000
00000010000010000000
00000011131110000000
00000000111000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,7 @@
import com.mr.view.MainFrame;
public class Start {
public static void main(String[] args) {
new MainFrame().setVisible(true);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000011100000000000
00000014100000000000
00000010100000000000
00000010100000000000
00000010111111000000
00011112020041000000
00014002311111000000
00011111210000000000
00000001010000000000
00000001010000000000
00000001410000000000
00000001110000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000011111110000000
00000010344410000000
00000010001111000000
00000111200001000000
00000100012101000000
00000102010001000000
00000100011111000000
00000111110000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000011111000000000
00000013001000000000
00000010221011100000
00000010201014100000
00000011101114100000
00000001000004100000
00000001001000100000
00000001001111100000
00000001111000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000011111000000
00000001110001000000
00000011000121000000
00000010011001000000
00000010200001000000
00000013011201000000
00000011114441000000
00000000011111000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000111110000000
00000000130410000000
00000000121010000000
00000000100010000000
00000011101010000000
00000010020410000000
00000010240010000000
00000011111110000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000111100000000
00000001100110000000
00000001020010000000
00000001002010000000
00000001114010000000
00000000014010000000
00000000014110000000
00000001114010000000
00000001020310000000
00000001002010000000
00000001000110000000
00000001111100000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

@ -0,0 +1,20 @@
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000111100000000
00000001144100000000
00000011444100000000
00000011020100000000
00000010202100000000
00000010120110000000
00000010102010000000
00000010000010000000
00000011131110000000
00000000111000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000

@ -0,0 +1,36 @@
package com.mr.model;
import com.mr.util.GameImageUtil;
/**
*
* @author mingrisoft
*
*/
public class Box extends RigidBody {
private boolean arrived = false;// 是否到达目的地
public Box() {
super(GameImageUtil.boxImage1);
}
public Box(int x, int y) {
super(x,y);
setImage(GameImageUtil.boxImage1);
}
public void arrive() {// 到达
setImage(GameImageUtil.boxImage2);
arrived = true;
}
public void leave() {// 离开
setImage(GameImageUtil.boxImage1);
arrived = false;
}
public boolean isArrived() {
return arrived;
}
}

@ -0,0 +1,17 @@
package com.mr.model;
import com.mr.util.GameImageUtil;
/**
*
*
* @author mingrisoft
*
*/
public class Destination extends RigidBody {
public Destination() {
super(GameImageUtil.destinationImage);
}
}

@ -0,0 +1,67 @@
package com.mr.model;
import java.util.ArrayList;
/**
*
*
* @author mingrisoft
*
*/
public class Map {
private RigidBody matrix[][];// 地图数据数组
private Player player;// 地图中的玩家对象
private ArrayList<Box> boxs;// 地图中包含的箱子列表
public Map(RigidBody matrix[][]) {
this.matrix = matrix;
player = new Player();
boxs = new ArrayList<>();
init();
}
/**
*
*
* @return
*/
public RigidBody[][] getMapData() {
return matrix;
}
/**
* ,
*/
private void init() {
// 遍历地图数组
for (int i = 0, ilength = matrix.length; i < ilength; i++) {
for (int j = 0, jlength = matrix[i].length; j < jlength; j++) {
RigidBody rb = matrix[i][j];// 读出刚体对象
if (rb instanceof Player) {// 如果是玩家
player.x = i;// 记录用户横坐标索引
player.y = j;// 记录用户纵坐标索引
matrix[i][j] = null;// 将该索引下的刚体对象清除
} else if (rb instanceof Box) {
Box box = new Box(i, j);// 创建对应该横纵坐标索引的箱子对象
boxs.add(box);// 箱子列表保存此箱子
matrix[i][j] = null;// 将该索引下的刚体对象清除
}
}
}
}
/**
*
*
* @return
*/
public Player getPlayer() {
return player;
}
/**
*
*
* @return
*/
public ArrayList<Box> getBoxs() {
return boxs;
}
}

@ -0,0 +1,14 @@
package com.mr.model;
import com.mr.util.GameImageUtil;
/**
* Íæ¼Ò
* @author mingrisoft
*
*/
public class Player extends RigidBody{
public Player() {
super(GameImageUtil.playerImage);
}
}

@ -0,0 +1,51 @@
package com.mr.model;
import java.awt.Image;
/**
*
*
* @author mingrisoft
*
*/
public abstract class RigidBody {
public int x;// 横坐标索引
public int y;// 纵坐标索引
private Image image;// 图片
public RigidBody(int x, int y) {
this.x = x;
this.y = y;
}
public RigidBody(Image image) {
this.image = image;
}
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
}
/**
* equals()xy
*/
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RigidBody other = (RigidBody) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}

@ -0,0 +1,17 @@
package com.mr.model;
import com.mr.util.GameImageUtil;
/**
* ǽ¿é
*
* @author mingrisoft
*
*/
public class Wall extends RigidBody {
public Wall() {
super(GameImageUtil.wallImage);
}
}

@ -0,0 +1,37 @@
package com.mr.util;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.File;
import javax.imageio.ImageIO;
/**
*
*
* @author mingrisoft
*/
public class GameImageUtil {
private static final String IMAGE_PATH = "src/com/mr/image";// 图片存放的路径
public static BufferedImage playerImage;// 玩家图片
public static BufferedImage boxImage1;// 箱子未到达目的地图片
public static BufferedImage boxImage2;// 箱子已到达目的地图片
public static BufferedImage wallImage;// 墙图片
public static BufferedImage destinationImage;// 目的地图片
public static BufferedImage backgroundImage;// 背景图片
static {// 对图片进行初始化
try {
playerImage = ImageIO.read(new File(IMAGE_PATH, "player.png"));
boxImage1 = ImageIO.read(new File(IMAGE_PATH, "box1.png"));
boxImage2 = ImageIO.read(new File(IMAGE_PATH, "box2.png"));
wallImage = ImageIO.read(new File(IMAGE_PATH, "wall.png"));
destinationImage = ImageIO
.read(new File(IMAGE_PATH, "destination.png"));
backgroundImage = ImageIO
.read(new File(IMAGE_PATH, "background.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
}

@ -0,0 +1,170 @@
package com.mr.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import com.mr.model.Box;
import com.mr.model.Destination;
import com.mr.model.Map;
import com.mr.model.Player;
import com.mr.model.RigidBody;
import com.mr.model.Wall;
/**
*
*
* @author mingrisoft
*/
public class GameMapUtil {
private static final char NULL_CODE = '0';// 空白区域使用的占位符
private static final char WALL_CODE = '1';// 墙使用的占位符
private static final char BOX_CODE = '2';// 箱子使用的占位符
private static final char PLAYER_CODE = '3';// 玩家使用的占位符
private static final char DESTINATION_CODE = '4';// 目的地使用的占位符
private static final String MAP_PATH = "src/com/mr/map";// 地图存放的路径
public static final String CUSTOM_MAP_NAME = "custom";// 自定义地图名称
/**
*
*
* @param arr
* -
* @param mapName
* -
*/
static public void createMap(RigidBody[][] arr, String mapName) {
StringBuilder data = new StringBuilder();// 地图文件将要写入的内容
for (int i = 0, ilength = arr.length; i < ilength; i++) {
for (int j = 0, jlength = arr[i].length; j < jlength; j++) {
RigidBody rb = arr[i][j];// 获取地图数据中的刚体对象
if (rb == null) {// 如果是空对象
data.append(NULL_CODE);// 拼接空白区域的占位符
} else if (rb instanceof Wall) {// 如果是墙
data.append(WALL_CODE);// 拼接墙的占位符
} else if (rb instanceof Player) {// 如果是玩家
data.append(PLAYER_CODE);// 拼接玩家的占位符
} else if (rb instanceof Box) {// 如果是箱子
data.append(BOX_CODE);// 拼接箱子的占位符
} else if (rb instanceof Destination) {// 如果是目的地
data.append(DESTINATION_CODE);// 拼接目的地的占位符
}
}
data.append("\n");// 拼接换行
}
File mapFile = new File(MAP_PATH, mapName);// 创建地图文件对象
// 开始文件输出流
try (FileOutputStream fos = new FileOutputStream(mapFile);) {
fos.write(data.toString().getBytes());// 将字符串写入到文件
fos.flush();// 刷新输出流
} catch (IOException e) {
e.printStackTrace();
}
}
/**
*
*
* @return
*/
public static Map readCustomMap() {
File f = new File(MAP_PATH, CUSTOM_MAP_NAME);// 创建定义地图文件对象
if (!f.exists()) {// 如果文件不存在
return null;
}
Map map = readMap(CUSTOM_MAP_NAME);// 读取自定义文件中的数据
return map;// 返回自定义地图对象
}
/**
*
*/
public static void clearCustomMap() {
File f = new File(MAP_PATH, CUSTOM_MAP_NAME);// 创建定义地图文件对象
if (f.exists()) {// 如果文件存在
f.delete();// 删除
}
}
/**
*
*
* @param mapNum
* -
* @return
*/
public static Map readMap(int mapNum) {
// 将数字转为字符串,调用重载方法
return readMap(String.valueOf(mapNum));
}
/**
*
*
* @param mapName
* -
* @return
*/
public static Map readMap(String mapName) {
File f = new File(MAP_PATH, mapName);// 获取地图文件对象
if (!f.exists()) {// 如果文件不存在
System.err.println("地图不存在:" + mapName);
return null;
}
RigidBody[][] data = new RigidBody[20][20];// 地图数据数组
// 开启缓冲输入流
try (FileInputStream fis = new FileInputStream(f);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr)) {
String tmp = null;// 读取一行数据的临时字符串
int row = 0;// 当前读取的行数
while ((tmp = br.readLine()) != null) {// 循环读出一行有内容的字符串
char codes[] = tmp.toCharArray();// 读出的字符串拆分成字符数组
// 循环字符数组并保证读取的行数不超过20
for (int i = 0; i < codes.length && row < 20; i++) {
RigidBody rb = null;// 准备保存到地图数组中的刚体对象
switch (codes[i]) {// 判断读出的字符
case WALL_CODE :// 如果是墙的占位符
rb = new Wall();// 刚体以墙的形式实例化
break;
case BOX_CODE :// 如果是箱子的占位符
rb = new Box();// 刚体以箱子的形式实例化
break;
case PLAYER_CODE :// 如果是玩家的占位符
rb = new Player();// 刚体以玩家的形式实例化
break;
case DESTINATION_CODE :// 如果是目的地的占位符
rb = new Destination();// 刚体以目的地的形式实例化
break;
}
data[row][i] = rb;// 刚体对象保存到地图数组中
}
row++;// 读取的行数递增
}
} catch (IOException e) {
e.printStackTrace();
}
return new Map(data);// 返回包含此地图数据的地图对象
}
/**
*
*
* @return
*/
public static int getLevelCount() {
File dir = new File(MAP_PATH);// 读取地图存放路径
if (dir.exists()) {// 如果该路径是文件夹
File maps[] = dir.listFiles();// 获取该文件夹下的所有文件
for (File f : maps) {// 遍历这些文件
if (CUSTOM_MAP_NAME.equals(f.getName())) {// 如果存在自定义地图
return maps.length - 1;// 总关卡数 = 文件数 -1
}
}
return maps.length;// 总关卡数 = 文件数
}
return 0;// 没有关卡
}
}

@ -0,0 +1,211 @@
package com.mr.view;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import com.mr.model.Box;
import com.mr.model.Destination;
import com.mr.model.Map;
import com.mr.model.Player;
import com.mr.model.RigidBody;
import com.mr.model.Wall;
import com.mr.util.GameMapUtil;
/**
*
*
* @author mingrisoft
*
*/
public class GamePanel extends JPanel implements KeyListener {
private MainFrame frame;// 主窗体
private int level;// 关卡编号
private RigidBody[][] data = new RigidBody[20][20];// 地图数据
private BufferedImage image;// 主图片
private Graphics2D g2;// 主图片的绘图对象
private Player player;// 玩家
private ArrayList<Box> boxs;// 箱子列表
/**
* 1
*
* @param frame
* -
* @param level
* -
*/
public GamePanel(MainFrame frame, int level) {
this.frame = frame;
this.level = level;
Map map;// 游戏地图对象
if (level < 1) {// 如果关卡数小于1
map = GameMapUtil.readCustomMap();// 读取自定义地图对象
this.level = 0;// 关卡数设为0方便定义下一关
if (map == null) {// 如果没有自定义地图文件
this.level = 1;// 关卡设为第一关
map = GameMapUtil.readMap(1);// 开始第一关
}
} else {// 如果关卡数大于1
map = GameMapUtil.readMap(level);// 读取对应关卡数的地图
}
data = map.getMapData();// 获取地图里的地图数据数组
player = map.getPlayer();// 获取地图中玩家对象
boxs = map.getBoxs();// 获取地图中箱子列表
// 主图片为600*600的彩图
image = new BufferedImage(600, 600, BufferedImage.TYPE_INT_RGB);
g2 = image.createGraphics();// 获取主图片绘图对象
this.frame.addKeyListener(this);// 主窗体添加键盘监听
this.frame.setFocusable(true);// 主窗体获取焦点
this.frame.setTitle("推箱子(按esc重新开始)");// 修改主窗体标题
}
/**
*
*/
@Override
public void paint(Graphics g) {
paintImage();// 绘制主图片
g.drawImage(image, 0, 0, this);// 将主图片绘制到面板中
boolean finish = true;// 用于判断游戏是否结束的标志
for (Box box : boxs) {// 遍历箱子列表
finish &= box.isArrived();// 结束标志与箱子到达状态做与运算
}
if (finish && boxs.size() > 0) {// 如果所有箱子都到达目的地且箱子的个数大于0
gotoAnotherLevel(level + 1);// 进入下一关
}
}
/**
*
*/
private void paintImage() {
g2.setColor(Color.WHITE);// 使用白色
g2.fillRect(0, 0, getWidth(), getHeight());// 绘制一个矩形填充整个图片
for (int i = 0, ilength = data.length; i < ilength; i++) {// 遍历地图数据数组
for (int j = 0, jlength = data[i].length; j < jlength; j++) {
RigidBody rb = data[i][j];// 获取刚体对象
if (rb != null) {
Image image = rb.getImage();// 获取刚体图片
g2.drawImage(image, i * 30, j * 30, 30, 30, this);// 在对应位置绘制刚体图片
}
}
}
for (Box box : boxs) {// 遍历箱子列表
// 在对应位置绘制箱子图片
g2.drawImage(box.getImage(), box.x * 30, box.y * 30, 30, 30, this);
}
// 绘制玩家图片
g2.drawImage(player.getImage(), player.x * 30, player.y * 30, 30, 30,
this);
}
/**
*
*
* @param level
* -
*/
private void gotoAnotherLevel(int level) {
frame.removeKeyListener(this);// 主窗体删除本类实现的键盘事件
// 创建线程创建Runnable接口的匿名类
Thread t = new Thread(() -> {
try {
Thread.sleep(500);// 0.5秒之后
} catch (Exception e) {
e.printStackTrace();
}
if (level > GameMapUtil.getLevelCount()) {// 如果传入的关卡数大于最大关卡数
frame.setPanel(new StarPanel(frame));// 进入开始面板
JOptionPane.showMessageDialog(frame, "通关啦!");// 弹出通关对话框
} else {
frame.setPanel(new GamePanel(frame, level));// 进入对应关卡
}
});
t.start();// 启动线程
}
/**
*
*/
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();// 获取按下的按键值
int x = player.x;// 记录玩家横坐标索引
int y = player.y;// 记录玩家纵坐标索引
switch (key) {// 判断按键值
case KeyEvent.VK_UP :// 如果按下的是上箭头按键
moveThePlayer(x, y - 1, x, y - 2);
break;
case KeyEvent.VK_DOWN :// 如果按下的是下箭头按键
moveThePlayer(x, y + 1, x, y + 2);
break;
case KeyEvent.VK_LEFT :// 如果按下的是左箭头按键
moveThePlayer(x - 1, y, x - 2, y);
break;
case KeyEvent.VK_RIGHT :// 如果按下的是右箭头按键
moveThePlayer(x + 1, y, x + 2, y);
break;
case KeyEvent.VK_ESCAPE :// 如果按Esc键
gotoAnotherLevel(level);// 重新开始本局游戏
break;
}
repaint();// 重绘面板
}
/**
*
*
* @param xNext1
* -
* @param yNext1
* -
* @param xNext2
* -
* @param yNext2
* -
*/
private void moveThePlayer(int xNext1, int yNext1, int xNext2, int yNext2) {
if (data[xNext1][yNext1] instanceof Wall) {// 如果玩家前方是墙
return;// 什么都不做
}
Box box = new Box(xNext1, yNext1);// 在用户前方位置创建箱子对象
if (boxs.contains(box)) {// 如果这个箱子在箱子列表中是存在的
int index = boxs.indexOf(box);// 获取该箱子在列表中的索引
box = boxs.get(index);// 取出列表中该箱子的对象
if (data[xNext2][yNext2] instanceof Wall) {// 如果箱子的前方是墙
return;// 什么都不做
}
if (boxs.contains(new Box(xNext2, yNext2))) {// 如果箱子的前方还有其他箱子
return;// 什么都不做
}
if (data[xNext2][yNext2] instanceof Destination) {// 如果箱子的前方是目的地
box.arrive();// 箱子到达
} else if (box.isArrived()) {// 如果箱子就在目的地上
box.leave();// 箱子离开
}
box.x = xNext2;// 箱子被玩家推到了新位置
box.y = yNext2;
}
player.x = xNext1;// 玩家移动到新位置
player.y = yNext1;
}
@Override
public void keyReleased(KeyEvent e) {
// 不实现此方法
}
@Override
public void keyTyped(KeyEvent e) {
// 不实现此方法
}
}

@ -0,0 +1,68 @@
package com.mr.view;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import com.mr.util.GameMapUtil;
/**
*
*
* @author mingrisoft
*
*/
public class MainFrame extends JFrame {
private int width = 605;// 不可调整大小时的宽度可调整大小宽度选择616
private int height = 627;// 不可调整大小时的高度可调整大小高度选择638
public MainFrame() {
setTitle("推箱子");// 设置标题
setResizable(false);// 不可调整大小
setSize(width, height);// 设置宽高
Toolkit tool = Toolkit.getDefaultToolkit(); // 创建系统该默认组件工具包
Dimension d = tool.getScreenSize(); // 获取屏幕尺寸,赋给一个二维坐标对象
// 让主窗体在屏幕中间显示
setLocation((d.width - getWidth()) / 2, (d.height - getHeight()) / 2);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);// 点击关闭窗体不做任何操作
addListener();// 添加监听
setPanel(new StarPanel(this));// 载入开始面板
GameMapUtil.clearCustomMap();// 删除自定义地图
}
/**
*
*
* @param panel
* -
*/
public void setPanel(JPanel panel) {
Container c = getContentPane();// 获取主容器对象
c.removeAll();// 删除容器中所有组件
c.add(panel, BorderLayout.CENTER);// 容器添加面板
c.validate();// 容器重新验证所有组件
c.repaint();
}
/**
*
*/
private void addListener() {
addWindowListener(new WindowAdapter() {// 添加窗体事件监听
public void windowClosing(WindowEvent e) {// 窗体关闭时
int closeCode = JOptionPane.showConfirmDialog(MainFrame.this,
"是否退出游戏?", "提示!", JOptionPane.YES_NO_OPTION);// 弹出选择对话框,并记录用户选择
if (closeCode == JOptionPane.YES_OPTION) {// 如果用户选择确定
System.exit(0);// 关闭程序
}
}
});
}
}

@ -0,0 +1,255 @@
package com.mr.view;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import com.mr.model.Box;
import com.mr.model.Destination;
import com.mr.model.Player;
import com.mr.model.RigidBody;
import com.mr.model.Wall;
import com.mr.util.GameMapUtil;
/**
*
*
* @author mingrisoft
*
*/
public class MapEditPanel extends JPanel {
private MainFrame frame;// 主窗体绘图面板
private BufferedImage image;// 绘制地图的主图片
private Graphics2D g2;// 地图图片的绘图对象
private DrawMapPanel editPanel;// 绘制面板
private JToggleButton wall;// 墙按钮,可显示被选中状态
private JToggleButton player;// 玩家按钮,可显示被选中状态
private JToggleButton box;// 箱子按钮,可显示被选中状态
private JToggleButton destination;// 目的地按钮,可显示被选中状态
private JButton save, clear, back;// 保存按钮,清除按钮,返回按钮
private RigidBody data[][];// 地图数据数组
private int offsetX = 100, offsetY = 80;// 地图图片在绘制面板的横纵坐标偏移量
/**
*
*/
public MapEditPanel(MainFrame frame) {
this.frame = frame;
this.frame.setTitle("地图编辑器(鼠标左键绘图,鼠标右键擦除)");
init();// 组件初始化
addListener();// 添加组件事件监听
}
/**
*
*/
private void init() {
// 使用400*400的彩色图片,游戏画面为600*600此处为游戏画面的缩放版
image = new BufferedImage(400, 400, BufferedImage.TYPE_INT_RGB);
g2 = image.createGraphics();// 获取图片绘图对象
editPanel = new DrawMapPanel();// 实例化绘制面板
data = new RigidBody[20][20];// 地图数组与游戏中行列数保持一致
setLayout(new BorderLayout());// 使用边界布局
save = new JButton("保存");// 实例化按钮对象
clear = new JButton("清空");
back = new JButton("返回");
wall = new JToggleButton("墙块");
player = new JToggleButton("玩家");
box = new JToggleButton("箱子");
destination = new JToggleButton("目的地");
wall.setSelected(true);// 墙按钮默认选中
ButtonGroup group = new ButtonGroup();// 按钮组
group.add(wall);// 按钮组添加墙按钮
group.add(player);// 按钮组添加玩家按钮
group.add(box);// 按钮组添加箱子按钮
group.add(destination);// 按钮组添加目的地按钮
FlowLayout flow = new FlowLayout();// 流布局
flow.setHgap(20);// 水平间隔20像素
JPanel buttonPanel = new JPanel(flow);// 创建按钮面板,采用流布局
buttonPanel.add(wall);// 按钮面板依次添加按钮
buttonPanel.add(player);
buttonPanel.add(box);
buttonPanel.add(destination);
buttonPanel.add(clear);
buttonPanel.add(save);
buttonPanel.add(back);
add(editPanel, BorderLayout.CENTER);// 绘图面板放在中央位置
add(buttonPanel, BorderLayout.SOUTH);// 按钮面板放在南部
}
/**
*
*/
void paintImage() {
g2.setColor(Color.WHITE);// 使用白色
g2.fillRect(0, 0, image.getWidth(), image.getHeight());// 填充一个覆盖整个图片的白色矩形
for (int i = 0, ilength = data.length; i < ilength; i++) {// 遍历地图数据数组
for (int j = 0, jlength = data[i].length; j < jlength; j++) {
RigidBody rb = data[i][j];// 取出刚体对象
if (rb != null) {// 如果不是null
Image image = rb.getImage();// 获取此刚体的图片
g2.drawImage(image, i * 20, j * 20, 20, 20, this);// 会制在主图片中
}
}
}
}
/**
*
*/
private void addListener() {
frame.addMouseListener(editPanel);// 主窗体添加绘图面板中的鼠标事件
frame.addMouseMotionListener(editPanel);// 主窗体添加绘图面板中的鼠标拖曳事件
clear.addActionListener(e -> { // 点击清除按钮时
data = new RigidBody[20][20];// 地图数据数组重新赋值
repaint();// 重绘组件
});
save.addActionListener(e -> {// 点击保存按钮时
String name = GameMapUtil.CUSTOM_MAP_NAME;// 地图名称为自定义地图名称
// while (true) {
// name = JOptionPane.showInputDialog(MapEditPanel.this,
// "请输入地图名称");// 弹出输入对话框,并记录用户输入的值
// if (name == null) {// 如果用户选择取消
// return;// 结束方法
// } else if ("".equals(name.trim())) {// 如果用户未输入任何有效值
// continue;// 重新执行循环
// } else {// 如果用户输入有效值
// break;// 退出循环
// }
// }
GameMapUtil.createMap(data, name);// 保存此地图的数据文件
JOptionPane.showMessageDialog(MapEditPanel.this,
"自定义地图创建成功!请直接开始游戏。");// 弹出对话框提示创建成功
back.doClick();// 触发返回按钮的点击事件
});
back.addActionListener(e -> {// 当点击返回按钮时
frame.removeMouseListener(editPanel);// 主窗体删除绘图面板中的鼠标事件
frame.removeMouseMotionListener(editPanel);// 主窗体删除绘图面板中的鼠标拖曳事件
frame.setPanel(new StarPanel(frame));// 主窗体载入开始面板
});
}
/**
*
*
*
*/
class DrawMapPanel extends JPanel
implements
MouseListener,
MouseMotionListener {
boolean paintFlag = false;// 绘制墙体标志
int clickButton;// 鼠标点击的按键
/**
*
*/
@Override
public void paint(Graphics g) {
paintImage();// 绘制主图片
g.setColor(Color.gray);// 使用灰色
g.fillRect(0, 0, getWidth(), getHeight());// 绘制一个矩形填充整个面板
g.drawImage(image, offsetX, offsetY, this);// 将主图片绘制到面板中
}
/**
*
*
* @param e
* -
*/
private void drawRigid(MouseEvent e) {
RigidBody rb;// 创建刚体对象
// 创建鼠标是否在地图范围内标志,由于窗体压缩了绘图面板,所以有些数值需要微调
boolean inMap = e.getX() > offsetX && e.getX() < 400 + offsetX
&& e.getY() > offsetY + 20 && e.getY() < 400 + offsetY + 20;
if (inMap && paintFlag) {// 如果鼠标在地图内的标志和绘制墙体标志都为true
int x = (e.getX() - offsetX) / 20;// 计算鼠标所在区域的刚体的横坐标索引
int y = (e.getY() - offsetY - 20) / 20;// 计算鼠标所在区域的刚体的纵坐标索引
if (wall.isSelected()) {// 如果墙按钮被选中
rb = new Wall();// 刚体按照墙进行实例化
} else if (player.isSelected()) {// 如果玩家按钮被选中
rb = new Player();// 刚体按照玩家进行实例化
} else if (box.isSelected()) {// 如果箱子钮被选中
rb = new Box();// 刚体按照箱子进行实例化
} else {// 如果目的地按钮被选中
rb = new Destination();// 刚体按照目的地进行实例化
}
if (clickButton == MouseEvent.BUTTON1) {// 如果按下的是鼠标左键
if (data[x][y] == null) {// 选中的位置没有任何刚体
data[x][y] = rb;// 填充选中按钮对应的刚体
}
} else if (clickButton == MouseEvent.BUTTON3) {// 如果按下的是鼠标右键
data[x][y] = null;// 将选中的位置的对象清空
}
repaint();// 重绘组件
}
}
/**
*
*/
@Override
public void mouseDragged(MouseEvent e) {
drawRigid(e);// 绘制墙块
}
/**
*
*/
@Override
public void mousePressed(MouseEvent e) {
paintFlag = true;// 绘制墙体标志为true
clickButton = e.getButton();// 记录当前按下的鼠标按键
drawRigid(e);// 绘制墙块
}
/**
*
*/
@Override
public void mouseReleased(MouseEvent e) {
paintFlag = false;// 绘制墙体标志为false
}
@Override
public void mouseMoved(MouseEvent e) {
// 不实现此方法
}
@Override
public void mouseClicked(MouseEvent e) {
// 不实现此方法
}
@Override
public void mouseEntered(MouseEvent e) {
// 不实现此方法
}
@Override
public void mouseExited(MouseEvent e) {
// 不实现此方法
}
}
}

@ -0,0 +1,97 @@
package com.mr.view;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
import com.mr.util.GameImageUtil;
/**
*
*
* @author mingrisoft
*
*/
public class StarPanel extends JPanel implements KeyListener {
BufferedImage image;// 面板中显示的图片
Graphics2D g2;// 图片绘图对象
MainFrame frame;// 主窗体
int x = 160;// 图标的横坐标
int y;// 图标的纵坐标
final int y1 = 320;// 第一个选项的纵坐标
final int y2 = 420;// 第二个选项的纵坐标
public StarPanel(MainFrame frame) {
this.frame = frame;
this.frame.addKeyListener(this);
this.frame.setFocusable(true);
// 图片使用600*600的彩图
image = new BufferedImage(600, 600, BufferedImage.TYPE_INT_RGB);
g2 = image.createGraphics();
this.frame.setTitle("推箱子");
y = y1;// 默认选择第一个选项
}
@Override
public void paint(Graphics g) {
paintImage();// 绘制图片
g.drawImage(image, 0, 0, this);// 将图片绘制面板中
}
/**
*
*/
private void paintImage() {
g2.drawImage(GameImageUtil.backgroundImage, 0, 0, this);
g2.setColor(Color.BLACK);// 使用黑色
g2.setFont(new Font("黑体", Font.BOLD, 40));// 字体
g2.drawString("开始游戏", 230, y1 + 30);// 绘制第一个选项的文字
g2.drawString("地图编辑器", 230, y2 + 30);// 绘制第二个选项的文字
g2.drawImage(GameImageUtil.playerImage, x, y, this);// 将玩家图片作为选择图标
}
/**
*
*/
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();// 获取按键的编码
switch (key) {// 判断按键
case KeyEvent.VK_ENTER :// 如果是回车键
switch (y) {// 判断图标的坐标
case y1 :// 如果选中第一个选项
frame.removeKeyListener(this);// 删除当前键盘事件
frame.setPanel(new GamePanel(frame, 0));// 进入游戏面板
break;
case y2 :// 如果选中第二个选项
frame.removeKeyListener(this);// 删除当前键盘事件
frame.setPanel(new MapEditPanel(frame));// 进入地图编辑器面板
break;
}
break;
case KeyEvent.VK_UP :// 如果是上箭头键,采用下箭头一样的逻辑
case KeyEvent.VK_DOWN :// 如果是下箭头键
if (y == y1) {// 如果图标选中第一个选项
y = y2;// 更换选中第二个选项
} else {
y = y1;// 更换险种第一个选项
}
break;
}
repaint();// 重绘面板
}
@Override
public void keyReleased(KeyEvent e) {
// 不实现此方法
}
@Override
public void keyTyped(KeyEvent e) {
// 不实现此方法
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 KiB

Loading…
Cancel
Save