阅读这篇博客需要一点的编程基础,一点即可。关于本项目请看博主博客。本教程采用JAVA + Eclipse编写,不懂Eclipse请点击→传送门
本篇博客目录
-
地图编辑器的接口 MapConfig.java
我们要先设置地图编辑器的大小,地图编辑器上每个资源的大小,即每个格子的大小,通过简单的6个图片,构成一个完整的地图
本人默认设置地图为800*800,每个素材50*50。即每行每列都是18个素材
地图数组中,数字带边素材。0墙 1地板 2空箱子 3 箱子 4箱子点 5出生点
package cn.edu.caztc.sokobangame;
import javax.swing.ImageIcon;
public interface MapConfig {
/**素材宽度*/
int SOUREC_WIDTH = 50;
/**素材高度*/
int SOUREC_HEIGHT = 50;
/**地图宽度*/
int MAP_WIDTH = 800;
/**地图高度*/
int MAP_HEIGHT= 800;
/**地图保存的位置 */
String PATH = "D:\\推箱子";
ImageIcon icon101 = new ImageIcon("images/墙.png");
ImageIcon icon102 = new ImageIcon("images/地板.png");
ImageIcon icon103 = new ImageIcon("images/空箱子.png");
ImageIcon icon104 = new ImageIcon("images/箱子.png");
ImageIcon icon105 = new ImageIcon("images/箱子点.png");
ImageIcon icon106 = new ImageIcon("images/player.png");
//将所有的图片素材对象放入一个数组中,便于窗体上的下拉列表添加所有的图片素材
ImageIcon[] allicons = {icon101,icon102,icon103,icon104,icon105,icon106};
}
-
地图编辑器界面 CreatMap.java
CreatMap()构造方法,用于创造界面及控件
void setBox(JComboBox<ImageIcon> box) 将接口里面的allicons中6个素材添加到box中,box为下拉列表
class PanelListenner 面板监听类,鼠标点击某位置,给位置对应的数组赋值
class MySetPanel 临时地图面板类,显示地图
class Buttonlistenner 按键监听类,点击保存地图按钮则保存地图文件
1.界面创建
设想中界面大致是这样的,左侧为显示界面,用于地图显示,jpanel实现。右侧配置,编辑框+按钮实现配置确认等。保存为保存按钮。其中有一个下拉图片框用于选择素材。
代码:
package cn.edu.caztc.sokobangame;
import java.awt.Dimension;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
public class CreatMap extends JFrame implements MapConfig {
private JPanel contentPane;
private JTextField tf_level;
private JPanel panel;
// 关卡变量
int level = 1;
// 用来选择素材的下拉表
private JComboBox<ImageIcon> box;
/**
* 创建界面
*/
public CreatMap() {
// 设置标题
setTitle("推箱子地图编辑器");
// 设置关闭方式
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// 设置大小位置
setBounds(100, 100, 1180, 735);
// 面板
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
// 滚动面板
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(14, 13, 800, 653);
contentPane.add(scrollPane);
// 地图面板
panel = new JPanel();
panel.setPreferredSize(new Dimension(800, 800));
scrollPane.setViewportView(panel);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
// 标签
JLabel lb_1 = new JLabel("关卡");
lb_1.setBounds(874, 160, 111, 36);
contentPane.add(lb_1);
// 编辑框
tf_level = new JTextField();
// 设置默认关卡
tf_level.setText(String.valueOf(level));
tf_level.setColumns(10);
tf_level.setBounds(1003, 158, 117, 40);
contentPane.add(tf_level);
// 下拉菜单
box = new JComboBox<ImageIcon>();
box.setBounds(939, 312, 123, 99);
contentPane.add(box);
// 界面居中
setLocationRelativeTo(null);
// 显示界面
setVisible(true);
}
}
2.下拉列表框显示图片
顾名思义,显示图片好选择啊。修改下面红框内容即可。
代码:
//省略***********************************
//下拉菜单
box = new JComboBox<ImageIcon>();
setBox(box);
box.setSelectedIndex(0);
box.setBounds(939, 312, 123, 99);
contentPane.add(box);
//省略
//省略*****************************************
//省略
// 设置地图中的素材下拉表
public void setBox(JComboBox<ImageIcon> box) {
for (int i = 0; i < allicons.length; i++) {
box.addItem(allicons[i]);
}
}
3.显示界面显示地图
做了前面的准备我们当然要显示地图了啊。思路呢,就是JPanel的paint画出来。我们定义一个int型3维数组,用于保存地图。其中第一二位代表地图坐标,第三位代表地图图片所对应的数字。我默认为0墙 1地板 2空箱子 3 箱子 4箱子点 5出生点。一张地图必须且仅有一个5.再定义一个ImageIcon2维数组,用于保存所对应的图片。
package cn.edu.caztc.sokobangame;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
public class CreatMap extends JFrame implements MapConfig {
private JPanel contentPane;
private JTextField tf_level;
private JPanel panel;
// 关卡变量
int level = 1;
// 用来选择素材的下拉表
private JComboBox<ImageIcon> box;
// 地图数组
static int[][][] map1 = new int[MAP_WIDTH / SOUREC_WIDTH][MAP_HEIGHT / SOUREC_HEIGHT][1];
// 图片数组,用于显示地图
static ImageIcon[][] icons = new ImageIcon[MAP_WIDTH / SOUREC_WIDTH][MAP_HEIGHT / SOUREC_HEIGHT];
/**
* 创建界面
*/
public CreatMap() {
// 设置标题
setTitle("推箱子地图编辑器");
// 设置关闭方式
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// 设置大小位置
setBounds(100, 100, 1180, 735);
// 面板
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
// 滚动面板
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(14, 13, 800, 653);
contentPane.add(scrollPane);
// 地图面板
panel = new MySetPanel();
panel.setPreferredSize(new Dimension(800, 800));
scrollPane.setViewportView(panel);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
// 标签
JLabel lb_1 = new JLabel("关卡");
lb_1.setBounds(874, 160, 111, 36);
contentPane.add(lb_1);
// 编辑框
tf_level = new JTextField();
// 设置默认关卡
tf_level.setText(String.valueOf(level));
tf_level.setColumns(10);
tf_level.setBounds(1003, 158, 117, 40);
contentPane.add(tf_level);
// 下拉菜单
box = new JComboBox<ImageIcon>();
setBox(box);
box.setSelectedIndex(0);
box.setBounds(939, 312, 123, 99);
contentPane.add(box);
// 保存地图按钮
JButton btn_save = new JButton("保存地图");
btn_save.setBounds(945, 578, 117, 40);
contentPane.add(btn_save);
// 给面板安装鼠标监听器
PanelListenner plis = new PanelListenner();
panel.addMouseListener(plis);
// 界面居中
setLocationRelativeTo(null);
// 显示界面
setVisible(true);
}
// 设置地图中的素材下拉表
public void setBox(JComboBox<ImageIcon> box) {
for (int i = 0; i < allicons.length; i++) {
box.addItem(allicons[i]);
}
}
/**
* 面板监听类
*
* @author 莫言情难忘
*/
class PanelListenner extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
int num = 0;
// 得到该位置对应的数组下标
int j = e.getX() / SOUREC_WIDTH;
int i = e.getY() / SOUREC_HEIGHT;
System.out.println(i + "<>" + j);
// 得到选择框中的图片
ImageIcon icon = (ImageIcon) box.getSelectedItem();
// 0墙 1地板 2空箱子 3 箱子 4箱子点 5出生点
int index = box.getSelectedIndex();
if (index > 5) {
index = 0;
icons[i][j] = icon101;
} else {
map1[i][j][0] = index;
icons[i][j] = icon;
}
panel.repaint();
}
}
/**
* 临时地图面板类
*
* @author 莫言情难忘
*
*/
class MySetPanel extends JPanel {
@Override
public void paint(Graphics g) {
super.paint(g);
for (int i = 0; i < MAP_HEIGHT / SOUREC_HEIGHT; i++) {
for (int j = 0; j < MAP_WIDTH / SOUREC_WIDTH; j++) {
if (icons[i][j] != null) {
g.drawImage(icons[i][j].getImage(), getDrawX(j), getDrawY(i), SOUREC_WIDTH, SOUREC_HEIGHT,
null);
}
}
}
}
// 将数组下标转化成对应的图片左上角坐标
public int getDrawX(int j) {
int x = j * 50;
return x;
}
// 将数组下标转化成对应的图片左上角坐标
public int getDrawY(int i) {
int y = i * 50;
return y;
}
}
}
4.保存地图
最后一步,保存地图啊。因为我们有个int型3维数组,所以我们把这个数组依次保存即可。有一点注意,我们一个个把所有素材点出来很浪费时间,所以我们把0设置为墙的作用体现出来了。不用做墙默认也是墙。其中的一些方法,在下方工具类中包含。
package cn.edu.caztc.sokobangame;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
public class CreatMap extends JFrame implements MapConfig {
private JPanel contentPane;
private JTextField tf_level;
private JPanel panel;
// 关卡变量
int level = 1;
// 用来选择素材的下拉表
private JComboBox<ImageIcon> box;
// 地图数组
static int[][][] map1 = new int[MAP_WIDTH / SOUREC_WIDTH][MAP_HEIGHT / SOUREC_HEIGHT][1];
// 图片数组,用于显示地图
static ImageIcon[][] icons = new ImageIcon[MAP_WIDTH / SOUREC_WIDTH][MAP_HEIGHT / SOUREC_HEIGHT];
/**
* 创建界面
*/
public CreatMap() {
// 设置标题
setTitle("推箱子地图编辑器");
// 设置关闭方式
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// 设置大小位置
setBounds(100, 100, 1180, 735);
// 面板
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
// 滚动面板
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(14, 13, 800, 653);
contentPane.add(scrollPane);
// 地图面板
panel = new MySetPanel();
panel.setPreferredSize(new Dimension(800, 800));
scrollPane.setViewportView(panel);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
// 标签
JLabel lb_1 = new JLabel("关卡");
lb_1.setBounds(874, 160, 111, 36);
contentPane.add(lb_1);
// 编辑框
tf_level = new JTextField();
// 设置默认关卡
tf_level.setText(String.valueOf(level));
tf_level.setColumns(10);
tf_level.setBounds(1003, 158, 117, 40);
contentPane.add(tf_level);
// 下拉菜单
box = new JComboBox<ImageIcon>();
setBox(box);
box.setSelectedIndex(0);
box.setBounds(939, 312, 123, 99);
contentPane.add(box);
// 保存地图按钮
JButton btn_save = new JButton("保存地图");
btn_save.setBounds(945, 578, 117, 40);
contentPane.add(btn_save);
// 给面板安装鼠标监听器
PanelListenner plis = new PanelListenner();
panel.addMouseListener(plis);
// 给按钮安装事件监听器
Buttonlistenner blis = new Buttonlistenner();
btn_save.addActionListener(blis);
// 界面居中
setLocationRelativeTo(null);
// 显示界面
setVisible(true);
}
// 设置地图中的素材下拉表
public void setBox(JComboBox<ImageIcon> box) {
for (int i = 0; i < allicons.length; i++) {
box.addItem(allicons[i]);
}
}
/**
* 面板监听类
*
* @author 莫言情难忘
*/
class PanelListenner extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
int num = 0;
// 得到该位置对应的数组下标
int j = e.getX() / SOUREC_WIDTH;
int i = e.getY() / SOUREC_HEIGHT;
System.out.println(i + "<>" + j);
// 得到选择框中的图片
ImageIcon icon = (ImageIcon) box.getSelectedItem();
// 0墙 1地板 2空箱子 3 箱子 4箱子点 5出生点
int index = box.getSelectedIndex();
if (index > 5) {
index = 0;
icons[i][j] = icon101;
} else {
map1[i][j][0] = index;
icons[i][j] = icon;
}
panel.repaint();
}
}
/**
* 临时地图面板类
*
* @author 莫言情难忘
*
*/
class MySetPanel extends JPanel {
@Override
public void paint(Graphics g) {
super.paint(g);
for (int i = 0; i < MAP_HEIGHT / SOUREC_HEIGHT; i++) {
for (int j = 0; j < MAP_WIDTH / SOUREC_WIDTH; j++) {
if (icons[i][j] != null) {
g.drawImage(icons[i][j].getImage(), getDrawX(j), getDrawY(i), SOUREC_WIDTH, SOUREC_HEIGHT,
null);
}
}
}
}
// 将数组下标转化成对应的图片左上角坐标
public int getDrawX(int j) {
int x = j * 50;
return x;
}
// 将数组下标转化成对应的图片左上角坐标
public int getDrawY(int i) {
int y = i * 50;
return y;
}
}
/**
* 按键监听类
*
* @author 莫言情难忘
*
*/
class Buttonlistenner implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
// 如果按下了创建按钮,就保存地图
if (e.getActionCommand().equals("保存地图")) {
level = Integer.parseInt(tf_level.getText());
if (Utils.IsExistence(PATH + "\\diy" + level + ".map")) {
int n = JOptionPane.showConfirmDialog(null, "地图已存在,是否覆盖?", "警告", JOptionPane.YES_NO_OPTION);// 0确定
// 1取消
if (n == 0) {
// 确定即保存
CreatMapTxt();
}
} else {
// 不存在文件则创建文件
CreatMapTxt();
}
}
}
}
/**
* 创建地图文件
*/
void CreatMapTxt() {
try {
// 得到文件输出流
FileOutputStream fos = new FileOutputStream(PATH + "\\diy" + level + ".map");
// 将文件输出流包装成基本数据输出流
DataOutputStream dos = new DataOutputStream(fos);
// 从配置的接口中得到二维数组的大小(由于本类已经实现了上面的Mapconfig接口,所以可以直接用里面的数据)
int i = MAP_HEIGHT / SOUREC_HEIGHT;
int j = MAP_WIDTH / SOUREC_WIDTH;
// 先数组的大小写入文件
dos.writeInt(i);
dos.writeInt(j);
// 按顺序将三维数组写入文件,后面游戏读取地图的时候也要按这种顺序读回来
for (int ii = 0; ii < i; ii++) {
for (int jj = 0; jj < j; jj++) {
dos.writeInt(map1[ii][jj][0]);
}
}
// 强制流中的数据完全输出完
dos.flush();
// 关闭输出流
dos.close();
} catch (Exception ef) {
ef.printStackTrace();
}
System.out.println("保存成功");
}
}
5.代码优化
其实,代码中有许多地方可以优化,我并未优化。因为可能涉及到后面的东西。比如,我们完全可以不需要imageicon数组保存各个坐标的图片,完全可以用数字替代图片,当paint到坐标时,从int数组中取出数字,然后通过一个getimage胡奥渠道其所对应的图片。再比如,没有必要设置一个按键监听类。因为他就一个按键。。。
-
工具类 Utils.java
当然,工具类必不可少啊,所用的工具类
package cn.edu.caztc.sokobangame;
import java.io.File;
public class Utils{
/**
* 判断是否存在文件file
*
* @param file 如:D:\\推箱子\\1.map
* @return 存在即为true
*/
static boolean IsExistence(String file) {
File fileuser = new File(file);
if (!fileuser.exists()) {
return false;
}
return true;
}
}
-
测试类test.java
这样地图编辑器就做好了,我们简单创建一个test类测试一下
最终效果
若有问题,可联系QQ1179307527.大佬若发现有错误,烦请指正。