Java游戏开发——华容道

游戏介绍:

“华容道”是一款比较古老的游戏,其源于三国时期著名的历史故事。华容道作为一个经典游戏,各部分的设计都恰到好处,非常巧妙,因此成为世界游戏界的三大不可思议。

“华容道”游戏初始时曹操被围在华容道最里层,玩家需要移动其他角色,使曹操顺利到达出口。玩家先选择需要移动的角色,然后拖动鼠标,被选中的角色就会向鼠标拖动的方向移动。最后,当成功地将曹操移动到出口时,游戏结束。

本次开发的“华容道”运行效果如下图所示:

使用到的素材文件夹:

素材及完整源码链接: https://pan.baidu.com/s/1_vOhAYk07h9dXBaez_lW3g 提取码: sni6

游戏设计思路:

“华容道”整体可以看成5*4的网格,其中张飞、赵云、关羽、马超、黄忠各占两个格子,兵占一个格子,曹操最大占4个格子。在这里玩家鼠标的操作可以看成对带图标的JButton的拖动。声明变量W表示一个格子的边长,自定义数据结构Node类,用于保存每个关卡按钮的初始位置,使用继承自JButton的Person类用于每个带人物图标按钮的显示。

在这里我约定Node的x,y下标等同于图片按钮左上角对应的二维数组的x,y下标,id表示每个按钮的人物ID,direction为true时横放,direction为false时竖放。比如Node(0,0,0,true),表示曹操按钮位于左上角。

Node类:

package 华容道;

public class Node {
	private int id;
	private boolean direction;//true为横放,false为竖放
	private int x;//左上角数组x下标
	private int y;//左上角数组y下标
	
	public Node(int id,int x,int y,boolean direction){
		this.id = id;
		this.x = x;
		this.y = y;
		this.direction = direction;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public boolean getDirection() {
		return direction;
	}

	public void setDirection(boolean direction) {
		this.direction = direction;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}
	
	
}

Person类是带人物ID的JButton

Person类:

package 华容道;

import java.awt.Color;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;

import javax.swing.JButton;

public class Person extends JButton{

	int id;//编号
	String name;

	
	public Person(int id,String str){
		super("");
		name = str;
		this.id = id;
	}
	
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
	
	
}

 MapFactory是存储游戏关卡数据的工厂类,MapFactory同时也支持历史记录的读写

MapFactory类:

package 华容道;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;

public class MapFactory {
		
	//ID编号对应关系:0曹操,1赵云,2张飞,3关羽,4马超,5黄忠,6小兵,7小兵,8小兵,9小兵
	//Node(id,x,y,direction),id对应每个角色,x,y对应左上角数组下标,direction为true时,横放,false时,竖放
	static String[] array;
	public static Node[][] map = new Node[][]{

		{//七步成诗
			new Node(0,2,2,true),new Node(1,2,0,false),new Node(2,0,1,false),
			new Node(3,0,3,false),new Node(4,0,0,false),new Node(5,2,1,false),
			new Node(6,4,0,true),new Node(7,4,1,true),new Node(8,4,2,true),new Node(9,4,3,true)
		},

		{//横刀立马
			new Node(0,0,1,true),new Node(1,0,3,false),new Node(2,2,3,false),
			new Node(3,2,1,true),new Node(4,0,0,false),new Node(5,2,0,false),
			new Node(6,4,0,true),new Node(7,3,1,true),new Node(8,3,2,true),new Node(9,4,3,true)
		},
		
		{//屯兵东路
			new Node(0,0,0,true),new Node(1,0,3,false),new Node(2,0,2,false),
			new Node(3,2,0,true),new Node(4,3,0,false),new Node(5,3,1,false),
			new Node(6,2,2,true),new Node(7,2,3,true),new Node(8,3,2,true),new Node(9,3,3,true)
		},
		{//插翅难飞
			new Node(0,0,1,true),new Node(1,0,0,false),new Node(2,4,2,true),
			new Node(3,4,0,true),new Node(4,2,1,false),new Node(5,0,3,false),
			new Node(6,2,0,true),new Node(7,3,0,true),new Node(8,2,3,true),new Node(9,3,3,true)
		},
		{//巧过五关
			new Node(0,0,1,true),new Node(1,2,2,true),new Node(2,3,2,true),
			new Node(3,4,1,true),new Node(4,3,0,true),new Node(5,2,0,true),
			new Node(6,0,0,true),new Node(7,1,0,true),new Node(8,0,3,true),new Node(9,1,3,true)
		},
		{//层层设防
			new Node(0,0,1,true),new Node(1,1,3,false),new Node(2,1,0,false),
			new Node(3,4,1,true),new Node(4,2,1,true),new Node(5,3,1,true),
			new Node(6,0,0,true),new Node(7,3,0,true),new Node(8,0,3,true),new Node(9,3,3,true)
		},
		{//近在咫尺
			new Node(0,3,2,true),new Node(1,0,1,false),new Node(2,3,0,true),
			new Node(3,2,0,true),new Node(4,0,3,false),new Node(5,0,2,false),
			new Node(6,0,0,true),new Node(7,1,0,true),new Node(8,2,2,true),new Node(9,2,3,true)
		},
		{//兵临曹营
			new Node(0,0,1,true),new Node(1,3,1,false),new Node(2,3,2,false),
			new Node(3,2,1,true),new Node(4,2,0,false),new Node(5,2,3,false),
			new Node(6,0,0,true),new Node(7,1,0,true),new Node(8,0,3,true),new Node(9,1,3,true)
		},
		{//众志成城
			new Node(0,1,1,true),new Node(1,3,1,false),new Node(2,3,3,false),
			new Node(3,0,2,true),new Node(4,0,0,false),new Node(5,2,0,false),
			new Node(6,0,1,true),new Node(7,1,3,true),new Node(8,2,3,true),new Node(9,4,2,true)
		},
		{//佳人梳妆
			new Node(0,1,0,true),new Node(1,3,1,false),new Node(2,3,2,false),
			new Node(3,0,2,true),new Node(4,1,2,false),new Node(5,2,3,false),
			new Node(6,3,0,true),new Node(7,4,0,true),new Node(8,1,3,true),new Node(9,4,3,true)
		}
	};
	
	
	
	//返回指定关卡布局信息的拷贝(避免直接引用)
	public static Node[] getMap(int level){
		
		Node[] temp = new Node[10];
		for(int i=0;i<10;i++){
			temp[i] = map[level-1][i];
		}
		
		return temp;
	}
	
	public static int getRecord(int level){
		
		FileOutputStream fos = null;
		DataOutputStream dos = null;
		FileInputStream fis = null;
		DataInputStream dis = null;
		
		File file = new File("D://GameRecordAboutSwing");
		
		if(!file.exists()){
			file.mkdirs();
		}
		File record = new File("D://GameRecordAboutSwing/recordKlotskiGame.txt");

		try{
		if(!record.exists()){//如果不存在,新建文本
			record.createNewFile();
			fos = new FileOutputStream(record);
			dos = new DataOutputStream(fos);
			String s = "9999,9999,9999,9999,9999,9999,9999,9999,9999,9999";
			dos.writeBytes(s);
			System.out.println(record.isFile());;
		}
		//读取记录
		fis = new FileInputStream(record);
		dis = new DataInputStream(fis);
		String str = dis.readLine();
		array = str.split(",");
		
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			 try {
					if(fis!=null)
					 fis.close();
					if(dis!=null)
					 dis.close();			
					if(fos!=null)
			    	 fos.close();
					if(dos!=null)
					 dos.close();				
			     } catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
			
		return Integer.parseInt(array[level-1]);
		
	}
	
	public static void writeRecord(int level,int step){
		
		FileOutputStream fos = null;
		DataOutputStream dos = null;
		File record = new File("D://GameRecordAboutSwing/recordKlotskiGame.txt");
		
		try {
			//清空原有记录
			FileWriter fileWriter =new FileWriter(record);
	        fileWriter.write("");
			fileWriter.flush();
			fileWriter.close();
	        //重新写入文本
			fos = new FileOutputStream(record);
			dos = new DataOutputStream(fos);
			array[level-1] = step+"";
			StringBuilder s = new StringBuilder();
			s.append(array[0]);
			for(int i=1;i<array.length;i++){
				s.append(","+array[i]);
			}System.out.println(s.toString());
			dos.writeBytes(s.toString());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
		     try {
				if(fos!=null)
		    	 fos.close();
				if(dos!=null)
				 dos.close();				
		     } catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		       
		}
        

		
	}
	
	
	public static void hello(){
		
	}
	
	
}

MapUtil是地图工具类,方法直接调用工厂类方法

MapUtil类:

package 华容道;

public class MapUtil {

	public MapUtil(){
		
	}
	
	public Node[] getMap(int level){
		return MapFactory.getMap(level);
	}
	
	//获取关卡历史记录
	public int getRecord(int level){	
		return MapFactory.getRecord(level);
	}
	
	//更新指定关卡记录
	public void updateRecode(int level,int record){
		MapFactory.writeRecord(level, record);
	}
	
	
}

GamePanel是继承自JPanel类的游戏面板类,内部包含游戏的主要逻辑:

①图片的获取

	
	//获取图片
	private void getIcons() {
				
		for(int i=0;i<15;i++){
			if(i==0){
				pics[i] = Toolkit.getDefaultToolkit().getImage("D:/Game/KlotskiGame/pic"+i+".png").getScaledInstance(2*W, 2*W,Image.SCALE_SMOOTH);
			}else if(i==1||i==2||i==3||i==4||i==5){
				pics[i] = Toolkit.getDefaultToolkit().getImage("D:/Game/KlotskiGame/pic"+i+".png").getScaledInstance(2*W, W,Image.SCALE_SMOOTH);
			}else if(i==6||i==7||i==8||i==9){
				pics[i] = Toolkit.getDefaultToolkit().getImage("D:/Game/KlotskiGame/pic"+i+".png").getScaledInstance(W, W,Image.SCALE_SMOOTH);
			}else{
				pics[i] = Toolkit.getDefaultToolkit().getImage("D:/Game/KlotskiGame/pic"+i+".png").getScaledInstance(W,2*W,Image.SCALE_SMOOTH);				
			}
			icons[i] = new ImageIcon(pics[i]);

			
		}
		repaint();
	}

②按钮图片显示和按钮位置的初始化

遍历地图按钮位置信息数组map,然后根据map数组元素对应的图标ID,对相应的人物图标按钮数组persons依次进行图标和位置的初始化。在这里只有黄忠、赵云、关羽、马超、张飞存在横放竖放的选择,竖放的图标ID = 横放的图标ID+9

//对游戏布局进行初始化
	private void initialize() {
		
		for(int i=0;i<10;i++){
			switch(map[i].getId()){
			case 0://曹操,占四格
				persons[i] = new Person(0,"曹操");
				persons[i].setIcon(icons[0]);
				persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, 2*W, 2*W);
				break;
			case 1://赵云,占两格
				persons[i] = new Person(1,"赵云");
				if(map[i].getDirection()){//横放
					persons[i].setIcon(icons[1]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, 2*W, W);					
				}else{//竖放
					persons[i].setIcon(icons[10]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, 2*W);
				}
				break;
			case 2://张飞,占两格
				persons[i] = new Person(2,"张飞");
				if(map[i].getDirection()){//横放
					persons[i].setIcon(icons[2]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, 2*W, W);					
				}else{//竖放
					persons[i].setIcon(icons[11]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, 2*W);
				}
				break;
			case 3://关羽,占两格
				persons[i] = new Person(3,"关羽");
				if(map[i].getDirection()){//横放
					persons[i].setIcon(icons[3]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, 2*W, W);					
				}else{//竖放
					persons[i].setIcon(icons[12]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, 2*W);
				}
				break;
			case 4://马超,占两格
				persons[i] = new Person(4,"马超");
				if(map[i].getDirection()){//横放
					persons[i].setIcon(icons[4]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, 2*W, W);					
				}else{//竖放
					persons[i].setIcon(icons[13]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, 2*W);
				}
				break;
			case 5://黄忠,占两格
				persons[i] = new Person(5,"黄忠");
				if(map[i].getDirection()){//横放
					persons[i].setIcon(icons[5]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, 2*W, W);					
				}else{//竖放
					persons[i].setIcon(icons[14]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, 2*W);
				}
				break;
			case 6://小兵,占一格
				persons[i] = new Person(6,"小兵");
				persons[i].setIcon(icons[6]);
				persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, W);									
				break;
			case 7://小兵,占一格
				persons[i] = new Person(7,"小兵");
				persons[i].setIcon(icons[7]);
				persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, W);													
				break;
			case 8://小兵,占一格
				persons[i] = new Person(8,"小兵");
				persons[i].setIcon(icons[8]);
				persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, W);									
				break;
			case 9://小兵,占一格
				persons[i] = new Person(9,"小兵");
				persons[i].setIcon(icons[9]);
				persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, W);									
				break;		
			}
			persons[i].addMouseListener(this);
			persons[i].addKeyListener(this);
			this.add(persons[i]);
		}		
	}

③判断鼠标拖拽方向

记录下鼠标按下的像素点p1,再记录鼠标松开时的像素点p2,用p2去减去p1,得到x的位移量和y的位移量,如果x的位移量大于y的位移量,说明是水平方向拖拽,然后再判断p2.x-p1.x是正数还是负数,如果是正数,说明是水平向右拖拽,否则是水平向左拖拽。

	private String getDirection(){
		int dx,dy;
		String direction;
		dx = p2.x - p1.x;
		dy = p2.y - p1.y;
		if(dx==0&&dy==0){
			return "no move";
		}
		if(Math.abs(dx)>Math.abs(dy)){//水平方向的偏移大于竖直方向的偏移,即水平方向运动
			if(dx>0){//释放点在按压点右边
				direction =  "right";
			}else{
				direction =  "left";				
			}
			
		}else{//竖直方向的偏移大于水平方向的偏移,即竖直方向运动
			if(dy>0){//释放点在按压点下边
				direction =  "down";
			}else{
				direction =  "up";				
			}
			
		}
		return direction;
	}
	

	@Override
	public void mousePressed(MouseEvent e) {
		if(e.getSource()==null)//没有选中任何按钮,直接返回
			return ;
		p1.x = e.getX();
		p1.y = e.getY();
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		if(e.getSource()==null)
			return ;
		Person man = (Person) e.getSource();
		p2.x = e.getX();
		p2.y = e.getY();
		String direction = getDirection();
		boolean canMoveFlag;
		if(direction.equals("up")){
			canMoveFlag = getCanMoveFlag(man,UP);
			if(canMoveFlag)
				move(man,UP);
		}else if(direction.equals("down")){
			canMoveFlag = getCanMoveFlag(man,DOWN);
			if(canMoveFlag)
				move(man,DOWN);			
		}else if(direction.equals("left")){
			canMoveFlag = getCanMoveFlag(man,LEFT);
			if(canMoveFlag)
				move(man,LEFT);
		}else if(direction.equals("right")){
			canMoveFlag = getCanMoveFlag(man,RIGHT);
			if(canMoveFlag)
				move(man,RIGHT);			
		}
	}

④判断是否可以向某个方向拖拽

getCanMoveFlag(Person man,int direction)根据传入的JButton对象和拖拽方向,获取是否可以移动的标记。

先获取传入的按钮对应的矩形对象,然后根据不同方向对该矩形移动一个单位格,然后对每个人物按钮对应的矩形对象判断是否相交,如果跟每个按钮对应的矩形都不相交,再跟边界的四个矩形进行是否相交的判断,最后返回canMoveFlag;

	Rectangle leftBoundary = new Rectangle(leftX-10, leftY, 10, 5*W);
	Rectangle rightBoundary = new Rectangle(leftX+4*W, leftY, 10, 5*W);
	Rectangle upBoundary = new Rectangle(leftX, leftY-10, 4*W, 10);
	Rectangle downBoundary = new Rectangle(leftX, leftY+5*W, 4*W, 10);

private boolean getCanMoveFlag(Person man, int direction) {

		boolean canMoveFlag = true;
		Rectangle manRect = man.getBounds();//返回点击的人物按钮对应的矩形对象
		int x = manRect.x;
		int y = manRect.y;		
		if(direction ==UP){
			y -= W;
		}else if(direction == DOWN){
			y += W;
		}else if(direction == LEFT){
			x -= W;
		}else if(direction == RIGHT){
			x += W;
		}
		manRect.setLocation(x, y);//矩形进行了移动
		for(int i=0;i<10;i++){
			if(persons[i].getId()!=man.getId()){//位移后的矩形与其他人物图块对应的矩形进行碰撞检测
				Rectangle personRect = persons[i].getBounds();
				if(personRect.intersects(manRect)){//如果两矩形相交,说明不能移动
					canMoveFlag = false;
				}
			}
		}
		
		//检测是否超出游戏区域
		if(manRect.intersects(upBoundary)||manRect.intersects(downBoundary)||manRect.intersects(leftBoundary)||manRect.intersects(rightBoundary)){
			canMoveFlag = false;
		}
		
		
		return canMoveFlag;
	}

⑤移动按钮并判断是否过关 

如果可以向某个方向移动按钮,则改变根据方向改变按钮的位置,step++,然后判断曹操是否位于终点

private void move(Person man, int direction) {
		switch(direction){
		case UP:man.setLocation(man.getX(), man.getY()-W);break;
		case DOWN:man.setLocation(man.getX(), man.getY()+W);break;
		case LEFT:man.setLocation(man.getX()-W, man.getY());break;
		case RIGHT:man.setLocation(man.getX()+W, man.getY());break;		
		}
		step++;
		GameClient.helpPanel.nowStep.setText(""+step);
		if(isWin(man)){
			if(level==10){JOptionPane.showMessageDialog(this, "恭喜通过最后一关");}
			else{	
				String msg;
				if(step<mapUtil.getRecord(level)){
					msg = "恭喜你通过第"+level+"关!!!\n通关步数是"+step+"\n刷新了历史记录"+mapUtil.getRecord(level)+"步\n是否要进入下一关?";
					mapUtil.updateRecode(level, step);
				}else{
					msg = "恭喜你通过第"+level+"关!!!\n通关步数是"+step+"\n"+"是否要进入下一关?";				
				}
				int type = JOptionPane.YES_NO_OPTION;
				String title = "过关";
				int choice = 0;
				choice = JOptionPane.showConfirmDialog(null, msg,title,type);
				if(choice==1){
					System.exit(0);
				}else if(choice == 0){
					level++;
					step = 0;
					for(int i=0;i<10;i++){
						this.remove(persons[i]);
					}
					repaint();
					map = mapUtil.getMap(level);
					GameClient.panel.remove(GameClient.helpPanel);
					GameClient.helpPanel = new HelpPanel(level);
					GameClient.panel.add(GameClient.helpPanel,BorderLayout.EAST);
					GameClient.helpPanel.validate();
					GameClient.gamePanel.validate();
					GameClient.panel.validate();
					initialize();
					setVisible(true);
					this.requestFocus();
				}
			}
		}
	}


	private boolean isWin(Person man) {
		if(man.getId()==0&&man.getX()==leftX+W&&man.getY()==leftY+3*W){//如果移动的图块是曹操,并且曹操位于终点
			return true;
		}
		
		return false;
	}

GamePanel类

package 华容道;

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class GamePanel extends JPanel implements ActionListener,MouseListener,KeyListener{
	int leftX=50,leftY=50;
	int level,W=100;
	int step = 0;//存储当前步数
	int record;//存储当前关卡记录
	Icon[] icons;
	Image[] pics;
	Node[] map;
	Person[] persons;
	Point p1 = new Point(0,0);//鼠标按下时点击的像素坐标
	Point p2 = new Point(0,0);//鼠标释放时点击的像素坐标
	Rectangle leftBoundary = new Rectangle(leftX-10, leftY, 10, 5*W);
	Rectangle rightBoundary = new Rectangle(leftX+4*W, leftY, 10, 5*W);
	Rectangle upBoundary = new Rectangle(leftX, leftY-10, 4*W, 10);
	Rectangle downBoundary = new Rectangle(leftX, leftY+5*W, 4*W, 10);

	public static final int UP=1,DOWN=2,LEFT=3,RIGHT=4;

	MapUtil mapUtil = new MapUtil();
	
	public GamePanel(int level){
		this.level = level;
		map	= mapUtil.getMap(level);
		persons = new Person[10];
		icons = new Icon[15];
		pics = new Image[15];
		setLayout(null);
		setSize(400, 500);
		getIcons();
		initialize();
		HelpPanel.restart.addMouseListener(new MouseAdapter(){
			public void mouseClicked(MouseEvent e){
				step = 0;
				for(int i=0;i<10;i++){
					GameClient.gamePanel.remove(persons[i]);
				}
				repaint();
				map = mapUtil.getMap(level);
				GameClient.panel.remove(GameClient.helpPanel);
				GameClient.helpPanel = new HelpPanel(level);
				GameClient.panel.add(GameClient.helpPanel,BorderLayout.EAST);
				GameClient.helpPanel.validate();
				GameClient.gamePanel.validate();
				GameClient.panel.validate();
				initialize();
				setVisible(true);
				GameClient.gamePanel.requestFocus();
				repaint();
			}
		});;
	}

	
	public void paint(Graphics g){
		setLayout(null);
		g.clearRect(0, 0, getWidth(), getHeight());
		for(int i=0;i<10;i++){
			persons[i].requestFocus();
			persons[i].paintComponents(g);
		}
	}
	
	
	//获取图片
	private void getIcons() {
				
		for(int i=0;i<15;i++){
			if(i==0){
				pics[i] = Toolkit.getDefaultToolkit().getImage("D:/Game/KlotskiGame/pic"+i+".png").getScaledInstance(2*W, 2*W,Image.SCALE_SMOOTH);
			}else if(i==1||i==2||i==3||i==4||i==5){
				pics[i] = Toolkit.getDefaultToolkit().getImage("D:/Game/KlotskiGame/pic"+i+".png").getScaledInstance(2*W, W,Image.SCALE_SMOOTH);
			}else if(i==6||i==7||i==8||i==9){
				pics[i] = Toolkit.getDefaultToolkit().getImage("D:/Game/KlotskiGame/pic"+i+".png").getScaledInstance(W, W,Image.SCALE_SMOOTH);
			}else{
				pics[i] = Toolkit.getDefaultToolkit().getImage("D:/Game/KlotskiGame/pic"+i+".png").getScaledInstance(W,2*W,Image.SCALE_SMOOTH);				
			}
			icons[i] = new ImageIcon(pics[i]);

			
		}
		repaint();
	}


	
	//对游戏布局进行初始化
	private void initialize() {
		
		for(int i=0;i<10;i++){
			switch(map[i].getId()){
			case 0://曹操,占四格
				persons[i] = new Person(0,"曹操");
				persons[i].setIcon(icons[0]);
				persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, 2*W, 2*W);
				break;
			case 1://赵云,占两格
				persons[i] = new Person(1,"赵云");
				if(map[i].getDirection()){//横放
					persons[i].setIcon(icons[1]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, 2*W, W);					
				}else{//竖放
					persons[i].setIcon(icons[10]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, 2*W);
				}
				break;
			case 2://张飞,占两格
				persons[i] = new Person(2,"张飞");
				if(map[i].getDirection()){//横放
					persons[i].setIcon(icons[2]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, 2*W, W);					
				}else{//竖放
					persons[i].setIcon(icons[11]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, 2*W);
				}
				break;
			case 3://关羽,占两格
				persons[i] = new Person(3,"关羽");
				if(map[i].getDirection()){//横放
					persons[i].setIcon(icons[3]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, 2*W, W);					
				}else{//竖放
					persons[i].setIcon(icons[12]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, 2*W);
				}
				break;
			case 4://马超,占两格
				persons[i] = new Person(4,"马超");
				if(map[i].getDirection()){//横放
					persons[i].setIcon(icons[4]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, 2*W, W);					
				}else{//竖放
					persons[i].setIcon(icons[13]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, 2*W);
				}
				break;
			case 5://黄忠,占两格
				persons[i] = new Person(5,"黄忠");
				if(map[i].getDirection()){//横放
					persons[i].setIcon(icons[5]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, 2*W, W);					
				}else{//竖放
					persons[i].setIcon(icons[14]);
					persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, 2*W);
				}
				break;
			case 6://小兵,占一格
				persons[i] = new Person(6,"小兵");
				persons[i].setIcon(icons[6]);
				persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, W);									
				break;
			case 7://小兵,占一格
				persons[i] = new Person(7,"小兵");
				persons[i].setIcon(icons[7]);
				persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, W);													
				break;
			case 8://小兵,占一格
				persons[i] = new Person(8,"小兵");
				persons[i].setIcon(icons[8]);
				persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, W);									
				break;
			case 9://小兵,占一格
				persons[i] = new Person(9,"小兵");
				persons[i].setIcon(icons[9]);
				persons[i].setBounds(leftX+map[i].getY()*W, leftY+map[i].getX()*W, W, W);									
				break;		
			}
			persons[i].addMouseListener(this);
			persons[i].addKeyListener(this);
			this.add(persons[i]);
		}		
	}
	
	private String getDirection(){
		int dx,dy;
		String direction;
		dx = p2.x - p1.x;
		dy = p2.y - p1.y;
		if(dx==0&&dy==0){
			return "no move";
		}
		if(Math.abs(dx)>Math.abs(dy)){//水平方向的偏移大于竖直方向的偏移,即水平方向运动
			if(dx>0){//释放点在按压点右边
				direction =  "right";
			}else{
				direction =  "left";				
			}
			
		}else{//竖直方向的偏移大于水平方向的偏移,即竖直方向运动
			if(dy>0){//释放点在按压点下边
				direction =  "down";
			}else{
				direction =  "up";				
			}
			
		}
		return direction;
	}
	

	@Override
	public void mousePressed(MouseEvent e) {
		if(e.getSource()==null)//没有选中任何按钮,直接返回
			return ;
		p1.x = e.getX();
		p1.y = e.getY();
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		if(e.getSource()==null)
			return ;
		Person man = (Person) e.getSource();
		p2.x = e.getX();
		p2.y = e.getY();
		String direction = getDirection();
		boolean canMoveFlag;
		if(direction.equals("up")){
			canMoveFlag = getCanMoveFlag(man,UP);
			if(canMoveFlag)
				move(man,UP);
		}else if(direction.equals("down")){
			canMoveFlag = getCanMoveFlag(man,DOWN);
			if(canMoveFlag)
				move(man,DOWN);			
		}else if(direction.equals("left")){
			canMoveFlag = getCanMoveFlag(man,LEFT);
			if(canMoveFlag)
				move(man,LEFT);
		}else if(direction.equals("right")){
			canMoveFlag = getCanMoveFlag(man,RIGHT);
			if(canMoveFlag)
				move(man,RIGHT);			
		}
	}
	


	private void move(Person man, int direction) {
		switch(direction){
		case UP:man.setLocation(man.getX(), man.getY()-W);break;
		case DOWN:man.setLocation(man.getX(), man.getY()+W);break;
		case LEFT:man.setLocation(man.getX()-W, man.getY());break;
		case RIGHT:man.setLocation(man.getX()+W, man.getY());break;		
		}
		step++;
		GameClient.helpPanel.nowStep.setText(""+step);
		if(isWin(man)){
			if(level==10){JOptionPane.showMessageDialog(this, "恭喜通过最后一关");}
			else{	
				String msg;
				if(step<mapUtil.getRecord(level)){
					msg = "恭喜你通过第"+level+"关!!!\n通关步数是"+step+"\n刷新了历史记录"+mapUtil.getRecord(level)+"步\n是否要进入下一关?";
					mapUtil.updateRecode(level, step);
				}else{
					msg = "恭喜你通过第"+level+"关!!!\n通关步数是"+step+"\n"+"是否要进入下一关?";				
				}
				int type = JOptionPane.YES_NO_OPTION;
				String title = "过关";
				int choice = 0;
				choice = JOptionPane.showConfirmDialog(null, msg,title,type);
				if(choice==1){
					System.exit(0);
				}else if(choice == 0){
					level++;
					step = 0;
					for(int i=0;i<10;i++){
						this.remove(persons[i]);
					}
					repaint();
					map = mapUtil.getMap(level);
					GameClient.panel.remove(GameClient.helpPanel);
					GameClient.helpPanel = new HelpPanel(level);
					GameClient.panel.add(GameClient.helpPanel,BorderLayout.EAST);
					GameClient.helpPanel.validate();
					GameClient.gamePanel.validate();
					GameClient.panel.validate();
					initialize();
					setVisible(true);
					this.requestFocus();
				}
			}
		}
	}

	
	private boolean isWin(Person man) {
		if(man.getId()==0&&man.getX()==leftX+W&&man.getY()==leftY+3*W){//如果移动的图块是曹操,并且曹操位于终点
			return true;
		}
		
		return false;
	}

	private boolean getCanMoveFlag(Person man, int direction) {

		boolean canMoveFlag = true;
		Rectangle manRect = man.getBounds();//返回点击的人物按钮对应的矩形对象
		int x = manRect.x;
		int y = manRect.y;		
		if(direction ==UP){
			y -= W;
		}else if(direction == DOWN){
			y += W;
		}else if(direction == LEFT){
			x -= W;
		}else if(direction == RIGHT){
			x += W;
		}
		manRect.setLocation(x, y);//矩形进行了移动
		for(int i=0;i<10;i++){
			if(persons[i].getId()!=man.getId()){//位移后的矩形与其他人物图块对应的矩形进行碰撞检测
				Rectangle personRect = persons[i].getBounds();
				if(personRect.intersects(manRect)){//如果两矩形相交,说明不能移动
					canMoveFlag = false;
				}
			}
		}
		
		//检测是否超出游戏区域
		if(manRect.intersects(upBoundary)||manRect.intersects(downBoundary)||manRect.intersects(leftBoundary)||manRect.intersects(rightBoundary)){
			canMoveFlag = false;
		}
		
		
		return canMoveFlag;
	}

	@Override
	public void keyPressed(KeyEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void keyReleased(KeyEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void keyTyped(KeyEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseClicked(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}



	@Override
	public void actionPerformed(ActionEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
	
	
	
}

HelpPanel类是继承自JPanel的辅助面板类,负责显示关卡名字和步数

HelpPanel类:

package 华容道;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class HelpPanel extends JPanel{

	String[] names = new String[]{"七步成诗","横刀立马","屯兵东路","插翅难飞","巧过五关","层层设防","近在咫尺","兵临曹营","众志成城","佳人梳妆"};//十个关卡的名字
	int level;
	JPanel panelNorth = new JPanel(new GridLayout(4,2,10,30));
	static JLabel nowStep = new JLabel("0");
	JLabel nowName = new JLabel("");
	JLabel record = new JLabel("9999");
	static	JButton restart = new JButton("重置");
                 	
	public HelpPanel(int level){
		this.level = level;
		this.setVisible(true);
		this.setLayout(new BorderLayout());//当前面板
		panelNorth.add(new JLabel("关卡名字:"));
		panelNorth.add(nowName);
		panelNorth.add(new JLabel("当前步数:"));
		panelNorth.add(nowStep);
		panelNorth.add(new JLabel("历史记录:"));
		panelNorth.add(record);//历史记录
		panelNorth.add(restart);
		
		this.add(panelNorth,BorderLayout.NORTH);
		initialize();
	}

	public void setLevel(int level){
		this.level = level;
		initialize();
	}
	
	private void initialize() {
//		System.out.println("level is "+level);
//		System.out.println(names[level-1]);
		nowName.setText(names[level-1]+"");
		nowStep.setText("0");
		record.setText(""+MapFactory.getRecord(level));	
	}
	
}

游戏窗口类GameClient类用于装载游戏面板和辅助面板。

GameClient类:

package 华容道;
import java.awt.BorderLayout;
import java.awt.Container;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class GameClient extends JFrame{

	static GamePanel gamePanel;
	static HelpPanel helpPanel;
	static Container panel;//窗口容器
	
	public GameClient(){

		helpPanel = new HelpPanel(1);
		gamePanel = new GamePanel(1);//设置关卡
		gamePanel.setLayout(new BorderLayout());
		panel = this.getContentPane();
		panel.setLayout(new BorderLayout());
		panel.add(helpPanel,BorderLayout.EAST);
		panel.add(gamePanel,BorderLayout.CENTER);
		this.setSize(650,650);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setTitle("华容道");
		this.setVisible(true);
		this.setResizable(false);	
	}
	
	
	public static void main(String[] args) {
			new GameClient();
	}

}

这次游戏开发中途遇到了一些显示问题的bug(第二关开始最后一个人物按钮变成全屏),导致出现了很多复杂的静态变量引用,不过目前已经解决,实现略繁琐,就不细说了。

ps:根据关卡名字均可以在百度上找到解密过程,(#^.^#)

素材及完整源码链接: https://pan.baidu.com/s/1_vOhAYk07h9dXBaez_lW3g 提取码: sni6

猜你喜欢

转载自blog.csdn.net/A1344714150/article/details/85225068