推箱子游戏的基本思路:
1、添加背景,创建人物和箱子,以及障碍物,目标;
2、利用二维数组布置障碍物;
3、添加键盘监听,控制人物移动;
4、碰撞检测;
5、通关判定。

程序源代码:

1、添加背景,创建人物和箱子,以及障碍物,目标

//添加背景
	private void backgroundInit() {
		//添加背景图片
		ImageIcon icon = new ImageIcon("background.png");
		JLabel lab_bg = new JLabel(icon);
		lab_bg.setBounds(0, 0, 800, 600);
		this.add(lab_bg);
	}
	
	JLabel lab_man;
	JLabel lab_box1;
	JLabel lab_box2;
	JLabel lab_box3;
	JLabel target1;
	JLabel target2;
	JLabel target3;
	
	// 新建一个数组存放JLabel组件
	JLabel[][] boxs = new JLabel[12][16];
	
	//8代表目标
	private void targetInit() {
		ImageIcon icon = new ImageIcon("8.png");
		target1 = new JLabel(icon);
		target1.setBounds(600, 300, 50, 50);
		this.add(target1);
		datas[6][12] = 8;

		target2 = new JLabel(icon);
		target2.setBounds(600, 250, 50, 50);
		this.add(target2);
		datas[5][12] = 8;

		target3 = new JLabel(icon);
		target3.setBounds(600, 200, 50, 50);
		this.add(target3);
		datas[4][12] = 8;
	}
	// 4代表箱子
	private void boxInit() {
		ImageIcon icon1 = new ImageIcon("4.png");
		lab_box1 = new JLabel(icon1);
		lab_box1.setBounds(6 * 50, 6 * 50, 50, 50);
		this.add(lab_box1);
		// 修改箱子对应位置上的数据为4(数据初始化)
		datas[6][6] = 4;
		// 把JLabel组件放入boxs中
		boxs[6][6] = lab_box1;

		ImageIcon icon2 = new ImageIcon("4.png");
		lab_box2 = new JLabel(icon2);
		lab_box2.setBounds(8 * 50, 6 * 50, 50, 50);
		this.add(lab_box2);
		//现实中的行列跟游戏里的行列恰恰相反
		datas[6][8] = 4;
		boxs[6][8] = lab_box2;

		ImageIcon icon3 = new ImageIcon("4.png");
		lab_box3 = new JLabel(icon3);
		lab_box3.setBounds(6 * 50, 8 * 50, 50, 50);
		this.add(lab_box3);
		datas[8][6] = 4;
		boxs[8][6] = lab_box3;
	}
	//人物的坐标
	int mx;
	int my;
	//人物
	private void manInit() {
		//人物的初始坐标(行列)
		mx = 1;
		my = 1;
		ImageIcon icon = new ImageIcon("-10.png");
		lab_man = new JLabel(icon);
		//人物在窗体中的实际坐标=人物所在行列 x 人物图片尺寸
		lab_man.setBounds(mx * 50, my * 50, 50, 50);
		this.add(lab_man);
	}
	//1代表障碍
	private void treeInit() {
		ImageIcon imageIcon = new ImageIcon("1.png");
		for (int i = 0; i < datas.length; i++) {
			for (int j = 0; j < datas[i].length; j++) {
				if (datas[i][j] == 1) {
					// 创建障碍物
					JLabel lab_tree = new JLabel(imageIcon);
					lab_tree.setBounds(50 * j, 50 * i, 50, 50);
					this.add(lab_tree);
				}
			}
		}
	}

2、利用二维数组布置障碍物

根据背景图片的大小,把背景分成12行16列的50 x 50大小的组件,每个组件都填充一张图片(人物、箱子、障碍物、目标),并用一个数字标识(1是障碍;4是箱子;8是目标;12是有箱子的目标);因此,推箱子游戏的本质是数字之间的移动和判定。

// 使用二维数组模拟地图场景数据;
int[][] datas = { 
			{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
			{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, 
			{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
			{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, 
			{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
			{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, 
			{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
			{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
			{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
			{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, 
			{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
			{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };

3、添加键盘监听,控制人物移动; + 碰撞检测;

人物跟箱子、跟障碍、跟目标之间的关系一共有13种(笨办法就像以下一样一一列出):
-----人物 障碍
-----人物 箱子 障碍
-----人物 箱子 箱子
-----人物 箱子 目标箱子
-----人物 目标箱子 障碍
-----人物 目标箱子 箱子
-----人物 目标箱子 目标箱子
-----人物 空地
-----人物 空目标
-----人物 箱子 空地
-----人物 箱子 空目标
-----人物 目标箱子 空地
-----人物 目标箱子 空目标

public void keyReleased(KeyEvent e) {
		// 获取按键号
		int key = e.getKeyCode();

		// 向上移动
		if (key == 38) {
			// 人 空
			if (datas[my - 1][mx] == 0) {
				//人物移动一格(行列)
				my = my - 1;
				//获取人物当前位置坐标
				int x = (int) lab_man.getLocation().getX();
				int y = (int) lab_man.getLocation().getY();
				//人物坐标移动50
				lab_man.setLocation(x, y - 50);
				//插入人物向上移动时的图片
				ImageIcon icon = new ImageIcon("10.png");
				lab_man.setIcon(icon);
				return;
			}
			// 人   障碍
			if (datas[my - 1][mx] == 1) {
			//此种情况下,什么事都不做,直接返回
				return;
			}
			// 人  箱  障碍
			if (datas[my - 1][mx] == 4 && datas[my - 2][mx] == 1) {
			//此种情况下,什么事都不做,直接返回
				return;
			}
			// 人  箱  箱
			if (datas[my - 1][mx] == 4 && datas[my - 2][mx] == 4) {
			//此种情况下,什么事都不做,直接返回
				return;
			}
			// 人  箱  箱笼
			if (datas[my - 1][mx] == 4 && datas[my - 2][mx] == 12) {
			//此种情况下,什么事都不做,直接返回
				return;
			}
			// 人  箱笼  障碍
			if (datas[my - 1][mx] == 12 && datas[my - 2][mx] == 1) {
			//此种情况下,什么事都不做,直接返回
				return;
			}
			// 人  箱笼  箱
			if (datas[my - 1][mx] == 12 && datas[my - 2][mx] == 4) {
			//此种情况下,什么事都不做,直接返回
				return;
			}
			// 人  箱笼  箱笼
			if (datas[my - 1][mx] == 12 && datas[my - 2][mx] == 12) {
			//此种情况下,什么事都不做,直接返回
				return;
			}
			// 人  笼
			if (datas[my - 1][mx] == 8) {
				//人物移动一格
				my = my - 1;
				//获取人物当前位置坐标
				int x = (int) lab_man.getLocation().getX();
				int y = (int) lab_man.getLocation().getY();
				//人物坐标移动50
				lab_man.setLocation(x, y - 50);
				//插入此状态图片
				ImageIcon icon = new ImageIcon("10.png");
				lab_man.setIcon(icon);
				return;
			}
			// 人  箱  空地   4-0   0-4
			if (datas[my - 1][mx] == 4 && datas[my - 2][mx] == 0) {
			//此种情况下,箱子标识与空地互换
				datas[my - 1][mx] = 0;
				datas[my - 2][mx] = 4;

			}
			// 人  箱    笼    4-0  8-12
			if (datas[my - 1][mx] == 4 && datas[my - 2][mx] == 8) {
			//此种情况下,箱子原来位置的标识变成0,箱子移动后的位置标识变成12(箱子到达目标)
				datas[my - 1][mx] = 0;
				datas[my - 2][mx] = 12;
				//箱子到达目标的个数加1
				num++;
			}
			// 人  箱笼   空     12-8 0-4
			if (datas[my - 1][mx] == 12 && datas[my - 2][mx] == 0) {
			//此种情况下,箱子原来位置的标识变成8,箱子移动后的位置标识变成4
				datas[my - 1][mx] = 8;
				datas[my - 2][mx] = 4;
				num--;
			}
			// 人 箱笼 空笼 12-8 8-12
			if (datas[my - 1][mx] == 12 && datas[my - 2][mx] == 8) {
			//此种情况下,箱子原来位置的标识变成8,箱子移动后的位置标识变成12
				datas[my - 1][mx] = 8;
				datas[my - 2][mx] = 12;
			}
			//箱子的坐标改变
			boxs[my - 1][mx].setLocation(mx * 50, my * 50 - 100);
			boxs[my - 2][mx] = boxs[my - 1][mx];
			boxs[my - 1][mx] = null;
			//人物移动一格
			my = my - 1;
			int x = (int) lab_man.getLocation().getX();
			int y = (int) lab_man.getLocation().getY();
			//人物坐标移动50
			lab_man.setLocation(x, y - 50);
			ImageIcon icon = new ImageIcon("10.png");
			lab_man.setIcon(icon);
			//每移动一下就通关判定一次
			victory();
			return;

		}

键盘监听的其他几个方向为:
上:38;下:40;左:37;右:39;

因为都是重复的,此处就不再赘述。

5、通关判定

int num = 0;
	int total = 3;
	private void victory() {
		//箱子全部到达目标即为胜利
		if (num == total) {
			System.out.println("胜利");
		}
	}

只要每按一下按键,就调用一次 victory() 即可。

项目总结:

这个项目一开始看觉得不难,但是中间仍然出现了一些小问题,比如num老是没有进行相应的加减,后来检查了几遍才发现是目标忘记添加标识了,导致通关判定一直没有成功(还是粗心了啊!)。总的来说,这个项目的新点难点在于障碍物的设置,初次运用二维数组来对地图配置,这种思维的亮点在于对地图进行分割成一个个小组件,然后对每个组件填充不同的内容,进而配置地图。只要通过初始设定数组的内容,然后遍历数组,就可以很方便地对地图进行修改。

最后,写程序一定要一步一个脚印,否则思路乱了就会容易出错。