您好,登錄后才能下訂單哦!
本篇內容介紹了“怎么用Java實現(xiàn)中國象棋游戲”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
下棋的棋盤首先要準備好,這就是一個合適大小合適比例合適位置的界面,然后在窗體上畫上(沒錯drawLine的那種畫上)n條直線和斜線,具體數(shù)值根據(jù)你的界面大小設置。這樣畫出的界面整齊好看~
public void paint(Graphics g){ super.paint(g);//重寫畫圖函數(shù) Font f=new Font("微軟雅黑",Font.BOLD,30); g.setFont(f); g.drawRect(60, 50, 500, 560);//外圈 g.drawRect(70, 60, 480, 540);//內圈 //橫線部分 int length=60; for(int i=0;i<9;i++){ g.drawLine(70, length, 550, length); length+=60; } //中間漢字 g.drawString("楚河", 160, 340); g.drawString("漢界", 400, 340); //豎線部分 length=130; for(int i=0;i<7;i++){ //上半部分豎線 g.drawLine( length,60, length,300); //下半部分豎線 g.drawLine( length,360, length,600); length+=60; } //上半部分九宮格斜線 g.drawLine(250, 60, 370, 180); g.drawLine(370, 60, 250, 180); //下半部分九宮格斜線 g.drawLine(250, 480, 370, 600); g.drawLine(250, 600, 370, 480); }
畫好棋盤之后加上功能按鈕,這個時候的功能暫時不考慮實現(xiàn),可以根據(jù)喜好隨意添加。這里推薦將按鈕類型設置成數(shù)組,便于及時增刪。
//添加到面板上 String[] type = {"開始游戲","重新開始","認 輸","悔 棋"}; for(int i=0;i<type.length;i++){ Button btn = new Button(type[i]); btn.setPreferredSize(new Dimension(150,50)); anniu.add(btn); }
這個時候你會發(fā)現(xiàn)在加按鈕的地方貼的十分緊湊,我的解決辦法是在這一塊面板上再加一個面板設置為白色覆蓋在上面,這樣根據(jù)面板的流式布局按鈕就會下移,調整空白面板的寬度可以改變按鈕的位置。
將找到的棋子圖片加到棋盤交叉的位置上才是給棋盤注入靈魂,將所有的十四類圖片加到package中以便程序之后可以在其他電腦上運行(這里推薦png格式,jpg格式會有方形邊框)。
接下來分三步走
1.創(chuàng)建一個10行9列的整數(shù)數(shù)組,用來存儲每個位置的數(shù)據(jù);
2.創(chuàng)建一個長度為14的Image數(shù)組,用來與棋子類型對應;
3.遍歷整數(shù)數(shù)組畫出對應的棋子;
這是棋盤的直觀圖,也就是我們的整數(shù)數(shù)組的初始值:
將Image與棋子圖片對應:
//初始化給每個chess定義 for(int k=0; k<14; k++){ chess[k] = new ImageIcon(this.getClass().getResource((k+1)+".png")).getImage(); }
遍歷畫圖:
//根據(jù)棋盤布局 for(int i=0;i<place.length;i++){ for(int j=0;j<place[0].length;j++){ if(place[i][j] >0){ bg.drawImage(chess[place[i][j]-1], chessX+60*j, chessY+60*i, 50, 50, null); } } }
通過函數(shù)獲得鼠標拖動前后兩個點所代表的棋盤上的位置,并將這兩個位置的二維數(shù)組的值交換,然后重新繪圖實現(xiàn)棋子的移動。
int x1, y1, x2, y2; public void mousePressed(MouseEvent e) { x1 = e.getX(); y1 = e.getY(); x1 = getj(x1); y1 = geti(y1); } public void mouseReleased(MouseEvent e) { x2 = e.getX(); y2 = e.getY(); x2 = getj(x2); y2 = geti(y2); } //根據(jù)點的坐標得到其代表的位置,具體參數(shù)可以微調,我的格子是60x60大小 public int getj(int x){ return (x-50)/60; } public int geti(int y){ return (y-40)/60; }
這個時候遇到的狀況就是你每次移動一次之后,整個界面都要重繪一次,而畫面是直接畫在窗體上的,數(shù)據(jù)會直接傳到電腦硬件,這樣一來畫圖速度就慢了,所以會出現(xiàn)每走一步界面就閃爍一次的情況,這種情況下,我們可以將畫面先存在緩存中,就不經(jīng)過硬件直接畫出來,這樣效率就可以明顯提高。
BufferedImage buffer = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics bg = buffer.getGraphics(); //這個中間寫的是你畫界面的方法,也就是上面提到的paint方法內部 //...... //繪制緩存到窗體上 g.drawImage(buffer, 0, 0, null);
率先吃掉對方帥或將的隊伍獲勝,寫一個函數(shù)判斷誰勝誰負顯示勝局,同時將數(shù)據(jù)初始化為0,準備再來一局:
(showMessageDialog方法可以直接跳出一個框)
//判斷游戲結束并顯示勝局 public void isWine() { System.out.println(place[y1][x1]+" "+place[y2][x2]); if (place[y2][x2]==7&&place[y1][x1]!=0) { place[y2][x2] = place[y1][x1]; place[y1][x1] = 0; UI.repaint(); JOptionPane.showMessageDialog(null, "黑方 勝利!"); again(); } else if(place[y2][x2]==14&&place[y1][x1]!=0) { place[y2][x2] = place[y1][x1]; place[y1][x1] = 0; UI.repaint(); JOptionPane.showMessageDialog(null, "紅方 勝利!"); again(); } } //游戲結束時要重繪 public void again(){ for(int i=0; i<place.length; i++){ for(int j=0; j<place[0].length; j++){ place[i][j] = 0; } } }
加動作監(jiān)聽器
public void actionPerformed(ActionEvent e) { type = e.getActionCommand(); if("開始游戲".equals(type)||"重新開始".equals(type)){ x=0; count = 1;//這里要把每次的走棋方刷新,認輸時也需要刷新 init(); UI.repaint(); } } //初始化place坐標 public void init(){ /*紅兵 1.png *紅炮 2.png *紅車 3.png *紅馬 4.png *紅相 5.png *紅仕 6.png *紅帥 7.png *黑卒 8.png *黑炮 9.png *黑車 10.png *黑馬 11.png *黑象 12.png *黑士 13.png *黑將 14.png */ for(int i=0;i<place.length;i++){ for(int j=0;j<place[0].length;j++){ place[i][j] = 0; } } place[0][0] = 10; place[9][0] = 3; place[0][1] = 11; place[9][1] = 4; place[0][2] = 12; place[9][2] = 5; place[0][3] = 13; place[9][3] = 6; place[0][4] = 14; place[9][4] = 7; place[0][5] = 13; place[9][5] = 6; place[0][6] = 12; place[9][6] = 5; place[0][7] = 11; place[9][7] = 4; place[0][8] = 10; place[9][8] = 3; place[2][1] = 9; place[7][1] = 2; place[2][7] = 9; place[7][7] = 2; place[3][0] = 8; place[6][0] = 1; place[3][2] = 8; place[6][2] = 1; place[3][4] = 8; place[6][4] = 1; place[3][6] = 8; place[6][6] = 1; place[3][8] = 8; place[6][8] = 1; }
這里的init函數(shù)是給整數(shù)二維數(shù)組初始化為開局后遍歷可以加上棋子的狀態(tài)。
//規(guī)定各個棋子的移動規(guī)則 public boolean rule(int gi, int gj,int si, int sj){ int x = place[gi][gj]; int y = place[si][sj]; int start, end; //判斷為何種棋子 //車:只能走直線 if(x == 3||x == 10){ if(gi != si&&gj != sj) return false; else if(gi == si){ start = Math.min(gj, sj); end = Math.max(gj, sj); for(int m = 1; m < end - start; m++){ if(place[gi][start+m] != 0) return false; } return true; } else if(gj == sj){ start = Math.min(gi, si); end = Math.max(gi, si); for(int m = 1; m < end - start; m++){ if(place[start+m][gj] != 0) return false; } return true; } else return true; } //馬:走日,且某個位置不可以有棋子 else if(x == 4||x == 11){ //下 if(si - gi == 2&&Math.abs(gj-sj) == 1&&place[gi+1][gj] == 0) return true; //上 else if(gi - si == 2&&Math.abs(gj-sj) == 1&&place[gi-1][gj] == 0) return true; //右 else if(sj - gj == 2&&Math.abs(gi-si) == 1&&place[gi][gj+1] == 0) return true; //左 else if(gj - sj == 2&&Math.abs(gi-si) == 1&&place[gi][gj-1] == 0) return true; //否則不可以走 else return false; } //相:走田,且不能過河 else if(x == 5||x == 12){ //左上 if(gi - si == 2&&gj - sj == 2&&place[gi-1][gj-1] == 0){ if((x == 5&&si >= 5)||(x == 12&&si < 5)) return true; else return false; } //右上 else if(gi - si == 2&&sj - gj == 2&&place[gi-1][gj+1] == 0){ if((x == 5&&si >= 5)||(x == 12&&si < 5)) return true; else return false; } //左下 else if(si - gi == 2&&gj - sj == 2&&place[gi+1][gj-1] == 0){ if((x == 5&&si >= 5)||(x == 12&&si < 5)) return true; else return false; } //右下 else if(si - gi == 2&&sj - gj == 2&&place[gi+1][gj+1] == 0){ if((x == 5&&si >= 5)||(x == 12&&si < 5)) return true; else return false; } else return false; } //士:斜著走不能出田字格 else if(x == 6||x == 13){ if(Math.abs(gj-sj)==1&&Math.abs(gi-si)==1){ if(x == 6&&si >= 7&&sj >= 3&&sj <= 5) return true; else if(x == 13&&si <= 2&&sj >= 3&&sj <= 5) return true; else return false; } else return false; } //將:不能出田字格且不能會面 else if(x == 7||x == 14){ if((Math.abs(gj-sj)==1&&gi - si ==0)||(gj - sj ==0&&Math.abs(gi-si)==1)){ if(x == 7&&si >= 7&&sj >= 3&&sj <= 5) return true; else if(x == 14&&si <= 2&&sj >= 3&&sj <= 5) return true; else return false; } else return false; } //炮:隔一個 else if(x == 2||x == 9){ //若要吃棋子,必須中間有且只有一枚棋子 if(x*y!=0){ int t = 0; if(gi == si){ for(int m = Math.min(gj, sj); m <= Math.max(gj, sj); m++){ if(place[gi][m] != 0) t++; } } else if(gj == sj){ for(int m = Math.min(gi, si); m <= Math.max(gi, si); m++){ if(place[m][gj] != 0) t++; } } if(t == 3) return true; } //若為不吃棋子的情況,中間不可以有其他棋子,且只能走直線 else { int t = 0; if(gi == si){ for(int m = Math.min(gj, sj); m <= Math.max(gj, sj); m++){ if(place[gi][m] != 0) t++; } } else if(gj == sj){ for(int m = Math.min(gi, si); m <= Math.max(gi, si); m++){ if(place[m][gj] != 0) t++; } } if(t == 1) return true; else return false; } } //兵:不能后退,且過了河才可以左右移動 else if(x == 1||x == 8){ //判斷是否過河 if(x == 1){ if(gi >=5){ if(gi - si == 1&&gj == sj) return true; else return false; } else if((gi - si == 1&&sj - gj == 0)||(gi - si == 0&&Math.abs(sj-gj) == 1)) return true; else return false; } else if(x == 8){ if(gi <5){ if(si - gi == 1&&gj == sj) return true; else return false; } else if(((si - gi == 1&&sj - gj == 0))||(gi - si == 0&&Math.abs(sj-gj) == 1)) return true; else return false; } else return false; } return false; }
長長的一大串,這里對于炮和將需單獨考慮,炮有直行和隔一個兩種走法,需分開考慮,而將就更是麻煩
//判斷將是否會面 public boolean meet(){ int jiangi=0, jiangj=0, shuaii=0, shuaij=0, temp=0; for(int i=0; i<10; i++){ for(int j=0; j<9; j++){ if(place[i][j]==7){ shuaii = i; shuaij = j; } else if(place[i][j]==14){ jiangi = i; jiangj = j; } } } if(shuaij == jiangj){ for(int i=jiangi+1; i<shuaii; i++){ if(place[i][shuaij] != 0) temp++; } } else return false;//沒會面 if(temp != 0) return false;//沒會面 else return true;//會面了 }
紅黑輪流下棋
我單獨寫了一個方法判斷將是否會面,因為導致將會面的不僅是將自身的移動導致,還可能是其他棋子的移動,所以也是一個boolean函數(shù),只有同時滿足前一個函數(shù)以及這個函數(shù)返回的是不會面,才可以移動,移動時我定義了一個參數(shù)x記錄局數(shù),根據(jù)他的奇偶判斷輪到哪一方走。這樣就實現(xiàn)了象棋的規(guī)則。
難免會有失誤,加上悔棋功能更合適。
我們在交換兩個點的值時(或者吃子的情況)需記錄下之前的值,然后當動作監(jiān)聽器監(jiān)聽到點擊悔棋時又交換回來。
一次只能悔棋一次,且剛開始時棋子沒有移動不能悔棋。
加上自己挑的背景,并為了方便下棋,標注輪到哪一方。
加背景可以用到畫棋子同樣的方法,所以要畫在棋盤前面,防止被覆蓋住。
這樣加上去又有一個很明顯的問題,就是你每操作一次右邊的按鈕都會消失,被你的背景圖覆蓋了,這怎么辦呢?
于是乎,又總結出了三種方法:
1.重寫按鈕的paint方法;
2.將按鈕以菜單的形式加在左上角;
3.將按鈕直接p在背景圖上(截圖),再畫就可以了;
此外你還可以在背景圖上加上“當前下棋方”的字樣,在邊上顯示當前下棋方將領的圖片。
這里借用了count參數(shù)并將其傳到了監(jiān)聽器中,重寫了構造函數(shù)。
int count=1; if(listener.count==1){ //畫帥 bg.drawImage(chess[6], 708, 322, 50, 50, null); }else if(listener.count==-1){ //畫將 bg.drawImage(chess[13], 708, 322, 50, 50, null); }
每下一子,count×(-1),以此標記是哪一方并畫圖。
附一張成果圖:
“怎么用Java實現(xiàn)中國象棋游戲”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注億速云網(wǎng)站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。