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