閱讀這篇博客需要一點的編程基礎,一點即可。關於本項目請看博主博客。本教程採用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.大佬若發現有錯誤,煩請指正。