游戲2048——那些年我們追過的臉萌
這是一款意外躥紅的小游戲,簡單、益智,符合當代年輕人的氣息,無須學習復雜的規則便可以得心應手,并且愈玩愈烈。簡單介紹后,接下來就分析一下該游戲的界面實現和功能實現。
在界面的實現方面,我們需要考慮會用到哪些 API 類,分別如下:
- 布局:FlowLayout,BorderLayout;
- 組件:JFrame,JPanel,JButton,JTextField;
- 繪圖:Graphics,Color,Image;
- 事件接口:KeyListener,KeyEvent,ActionListener,ActionEvent;
- 組件大小:Dimension;
- 菜單:JMenuBar,JMenu,JMenuItem。
首先,我們需要創建一個窗體,并且設置大小、題目、位置、可見等。然后整個界面應采用邊框布局,分別是北邊、南邊和中間,而北邊面板應采用流式布局來安排得分和最高分。北邊面板需添加兩個文本框以顯示分數,并設置成不可更改的形式。
南邊面板添加一個開始按鈕使得游戲失敗后還可以繼續玩。中間面板需要一個四乘四的格子,我采用畫矩形的方法,先畫一個背景深粉色矩形,再在上面 通過畫筆類用雙層循環畫出 16 個小矩形,然后需要在格子上繪制圖片,我們采用 Image 抽象類來添加并顯示圖片。接著我們可以添加一個簡單的菜單以及子菜單。最后我們需要在事件源文本框、按鈕、中間面板分別添加監聽器,使得可以監聽動作的發 生和鍵盤的按下或釋放。這樣子一個 2048 游戲界面就大概實現了。順便為自己提些注意事項:
- 第一,界面 Game2048 類繼承了 JPanel 類,所以在寫北邊和南邊面板方法的時候應該將 JFrame 作為參數。ps:其實我還是沒懂為什么要繼承 JPanel 類,有沒有大神回復一下;
- 第二,JTextField 應該定義為全局變量,以便在設置分數的時候可以傳遞參數到功能實現的類里邊;
- 第三,創建畫筆對象應該在窗體可見之后;
- 第四,由于游戲主界面是表格形式,所以圖片的存放應該設置為二維數組;
- 第五,繪制畫圖板的時候,應該調用父類的 paint ()方法;
- 第六,為了讓鍵盤監聽中間面板得以更好的實現,應該將文本框及按鈕的焦點設置為 false。
import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.Image; import java.awt.LayoutManager; import java.awt.Rectangle; import java.awt.Shape; import java.awt.image.ImageObserver; import java.text.AttributedCharacterIterator; import java.util.Random; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.JTextField;public class Game2048 extends JPanel { int array[][]=new int[4][4]; private Random rand=new Random (); private int score; private JTextField jte=new JTextField ("得分:"); private JTextField jte1=new JTextField ("最高分:"); private JButton jbu=new JButton ("START"); private boolean flag=true;
public static void main (String[] args) { Game2048 game =new Game2048(); game.initUI (); } public void initUI (){ JFrame jf=new JFrame (); jf.setTitle ("那些年我們追過的臉萌"); jf.setSize (435, 560); jf.setDefaultCloseOperation (3); jf.setLocationRelativeTo (null); jf.setLayout (new BorderLayout ()); initNorth (jf); initSouth (jf); initMenu (jf); this.setBackground (Color.WHITE); jf.add (this,BorderLayout.CENTER); jf.setVisible (true); this.setFocusable (true); Graphics g=this.getGraphics (); GameListener lis=new GameListener (this,g,array,jte,jte1); this.addKeyListener (lis); jbu.addActionListener (lis); } private void drawBackground (Graphics g){ g.setColor (new Color (255,155,255)); g.fillRoundRect (5, 5,410,410, 15, 15); g.setColor (new Color (255,181,255)); for(int i=0;i<4;i++) for(int j=0;j<4;j++) g.fillRoundRect (15+j*100,15+i*100,90,90, 15, 15); } private void drawRC (){ int name1=(rand.nextInt (2) +1)*2; int name2=(rand.nextInt (2) +1)*2; int r1=rand.nextInt (4); int c1=rand.nextInt (4); int r2=rand.nextInt (4); int c2=rand.nextInt (4); while(r1==r2&&c1==c2){ r2=rand.nextInt (4); c2=rand.nextInt (4); } array[r1][c1]=name1; array[r2][c2]=name2; System.out.println (r1 + " " + c1); System.out.println (r2 + " " + c2); } private void drawImage (Graphics g){ for(int r=0;r<4;r++){ for(int c=0;c<4;c++){ if(array[r][c]!=0){ Image image=new ImageIcon (this.getClass () .getResource (array[r][c]+".jpg")) .getImage (); g.drawImage (image,15+c*100, 15+r*100,90,90, null); } } } } public void paint (Graphics g){ super.paint (g); drawBackground (g); if(flag){ drawRC (); flag=false; } drawImage (g); } public void setFlag (boolean flag){ this.flag=flag; } public void initNorth (JFrame jf){ JPanel jpanel=new JPanel (); jpanel.setBackground (new Color (255,155,255)); jte.setPreferredSize (new Dimension (100, 30)); jte.setEditable (false); jpanel.add (jte); jte.setFocusable (false); jte1.setPreferredSize (new Dimension (100, 30)); jte1.setEditable (false); jpanel.add (jte1); jte1.setFocusable (false); jf.add (jpanel,BorderLayout.NORTH); } public void initSouth (JFrame jf){ JPanel jpanel=new JPanel (); jpanel.setBackground (new Color (255,155,255)); jpanel.add (jbu); jbu.setFocusable (false); jf.add (jpanel,BorderLayout.SOUTH); } public void initMenu (JFrame jf){ JMenuBar jme=new JMenuBar (); String [] array={"文件","編輯","查看","幫助"}; String [][] arrayItem={{"新建","打開","保存"},{"撤銷","重復","剪切"},{"工具箱","顏料盒"},{"幫助主題","關于 2048"}}; for (int i=0;i<array.length;i++){ JMenu jm=new JMenu (array[i]); for(int j=0;j<arrayItem[i].length;j++){ if(!arrayItem[i][j].equals ("")){ JMenuItem jmenu=new JMenuItem (arrayItem[i][j]); jm.add (jmenu);} else{ JSeparator separ = new JSeparator (); jme.add (separ); } } jme.add (jm); } jf.setJMenuBar (jme); }
}</pre>
在功能的實現方面,我們需要考慮事件的監聽及游戲實現的算法。事件里的事件源是你的動作發生所在的組件上,事件監聽器的添加方法是 add**Listener (**Listener l),事件處理類是接口的子類,因為接口不能創建對象,并且要實現事件處理類的所有方法,其中需要重寫動作監聽方法,從而獲取開始按鈕信息并設置開始,以 及設置分數、顯示分數情況。根據簡單的游戲規則,也就是1、開始時隨機出現兩個分數為 2 或 4 的圖片2、按下鍵盤的上下左右鍵所有的圖片隨之移動,并且碰到相鄰且相同的圖片可以相加形成相應分數的圖片,判斷如果有相加或者移動就再隨機生成一張分數 為 2 或 4 的圖片3、相加后要設置分數的改變4、判斷如果出現分數為 2048 的圖片則跳出文本框提示玩家已經勝利了5、判斷如果所有格子都已經滿了,且相鄰的所有格子都無法相加,則跳出文本框提示玩家已經輸了,可以重新開始。
移動和相加部分是該游戲的精髓,所以進簡單分析一下:如果是先移動再相加就會出現一個問題,就是相加完之后還需要再移動一次,才能保證上下左右鍵正常工作,所以我們采取先相加再移動的策略。
先判斷當前格子是否不為0,如果不為 0 則判斷是否相同再相加,不過這會出現一個問題,就是相鄰的格子為0,但是再下一個格子與之相同,所以應該先判斷相鄰是否為空,如果為空則循環繼續,如果不 為空則判斷是否相同,如果相同便可以相加,如果不同則跳出循環;然后是移動問題,移動主要根據上下左右鍵,通過循環判斷相應的下一個格子是否為空,如果是 空的格子則可以移動,也就是交換格子的內容。最后再提幾個注意點:
- 第一,應該寫一個構造函數,從而可以傳遞界面的一些參數;
- 第二,可以設置標志位 boolean flag = false 來判斷是否有移動或者相加情況,從而選擇是否繼續執行一些方法;
- 第三,判斷是否失敗的方法中,需要判斷兩種情況,也就是格子是否都滿了和是否不能再移動了,第一種情況中可以通過循環判斷每個格子是否都不為0,然后再用一個計數器來累積判斷次數,如果有 16 次也就是符合情況了,第二種情況中可以判斷左上角的 9 個格子中與右邊及下面相鄰的格子是否有相同的,再判斷第四列與下面相鄰的格子中是否有相同的和第四行中與下面相鄰的格子中是否有相同的,同樣給一個計數器,如果最總計數結果為 0 次,則已經不能再移動,如果兩種情況同時成立,則輸了(這是顧大神教我的)。
import java.awt.Graphics; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.Random; import javax.swing.ImageIcon; import javax.swing.JOptionPane; import javax.swing.JTextField;public class GameListener implements KeyListener, ActionListener { private Graphics g; private int[][] array; private Game2048 a; private boolean flag = false; private JTextField jte,jte1; public int count=0; private int max_score; private Random rand = new Random ();
public GameListener (Game2048 a, Graphics g, int[][] array, JTextField jte,JTextField jte1) { this.a = a; this.g = g; this.array = array; this.jte = jte; this.jte1 = jte1; } public void actionPerformed (ActionEvent e) { if (e.getActionCommand () .equals ("START")) { // e.getSource ()返回的是一個對象(事件源) start (); jte.setText ("得分:"+count); } } public void start () { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { array[i][j] = 0; } } a.setFlag (true); a.paint (g); } public void keyTyped (KeyEvent e) { } public void keyPressed (KeyEvent e) { } private void randomAC () { Random rand = new Random (); int name = (rand.nextInt (2) + 1) * 2; int r = rand.nextInt (4); int c = rand.nextInt (4); while (array[r][c] != 0) { r = rand.nextInt (4); c = rand.nextInt (4); } array[r][c] = name; } public void stop (){ int t=0; int p=0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if(array[i][j]!=0){ t++; } } } for(int r=0;r<3;r++){ for(int c=0;c<3;c++){ if(array[r][c]==array[r][c+1]&&array[r][c]!=0){ p++;} if(array[r][c]==array[r+1][c]&&array[r][c]!=0){ p++ ;} } } for(int r=0;r<3;r++){ if(array[r][3]==array[r+1][3]&&array[r][3]!=0) { p++;} } for(int c=0;c<3;c++){ if(array[3][c]==array[3][c+1]&&array[3][c]!=0){ p++;} } if(t==16&&p==0){ JOptionPane.showMessageDialog (a, "您輸了"); if(count>max_score){ max_score=count; count=0; jte1.setText ("最高分"+max_score); }else if(count<max_score){ count=0; } for (int r = 0; r < 4; r++) { for (int c = 0; c < 4; c++) { array[r][c] = 0; } } } } public void keyReleased (KeyEvent e) { stop (); int keyCode = e.getKeyCode (); switch (keyCode) { case 37:// 左鍵 relLeft (); int t=0; if (flag) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if(array[i][j]!=0){ t++; } } } if(t!=16){ randomAC (); a.repaint (); } jte.setText ("得分:"+count); } break; case 38:// 上鍵 relUp (); int b=0; if (flag) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if(array[i][j]!=0){ b++; } } } if(b!=16){ randomAC (); a.repaint (); } jte.setText ("得分:"+count); } break; case 39:// 右鍵 relRight (); int p=0; if (flag) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if(array[i][j]!=0){ p++; } } } if(p!=16){ randomAC (); a.repaint (); } jte.setText ("得分:"+count); } break; case 40:// 下鍵 relDown (); int q=0; if (flag) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if(array[i][j]!=0){ q++; } } } if(q!=16){ randomAC (); a.repaint (); } jte.setText ("得分:"+count); } break; } if(count==2048){ JOptionPane.showMessageDialog (a, "您的分數達到 2048,您贏了"); } } public void relLeft () { for (int r = 0; r < 4; r++) { for (int c = 0; c < 4; c++) { if (array[r][c] != 0) { for (int c1 = c + 1; c1 < 4; c1++) { if (array[r][c1] == 0) { continue; } else if (array[r][c] == array[r][c1]) { array[r][c] += array[r][c1]; array[r][c1] = 0; count+=array[r][c]; flag = true; } else if (array[r][c] != array[r][c1]) { break; } } } } } for (int r = 0; r < 4; r++) { for (int c = 0; c < 4; c++) { if (array[r][c] == 0) { for (int c1 = c + 1; c1 < 4; c1++) { if (array[r][c1] != 0) { array[r][c] = array[r][c1]; array[r][c1] = 0; c++; flag = true; } } } } } } public void relRight () { for (int r = 0; r < 4; r++) { for (int c = 3; c >=0; c--) { if (array[r][c] != 0) { for (int c1 = c - 1; c1 >=0; c1--) { if (array[r][c1] == 0) { continue; } else if (array[r][c] == array[r][c1]) { array[r][c] += array[r][c1]; array[r][c1] = 0; count+=array[r][c]; flag = true; } else if (array[r][c] != array[r][c1]) { break; } } } } } for (int r = 3; r >= 0; r--) { for (int c = 3; c >= 0; c--) { if (array[r][c] == 0) { for (int c1 = c - 1; c1 >= 0; c1--) { if (array[r][c1] != 0) { array[r][c] = array[r][c1]; array[r][c1] = 0; c--; flag = true; } } } } } } public void relDown () { for (int c = 0; c <4; c++) { for (int r = 3; r >=0; r--) { if (array[r][c] != 0) { for (int r1 = r - 1; r1 >=0; r1--) { if (array[r1][c] == 0) { continue; } else if (array[r][c] == array[r1][c]) { array[r][c] += array[r1][c]; array[r1][c] = 0; count+=array[r][c]; flag = true; } else if (array[r][c] != array[r1][c]) { break; } } } } } for (int c = 3; c >= 0; c--) { for (int r = 3; r >= 0; r--) { if (array[r][c] == 0) { for (int r1 = r - 1; r1 >= 0; r1--) { if (array[r1][c] != 0) { array[r][c] = array[r1][c]; array[r1][c] = 0; r--; flag = true; } } } } } } public void relUp () { for (int c = 0; c <4; c++) { for (int r = 0; r <4; r++) { if (array[r][c] != 0) { for (int r1 = r + 1; r1 <4; r1++) { if (array[r1][c] == 0) { continue; } else if (array[r][c] == array[r1][c]) { array[r][c] += array[r1][c]; array[r1][c] = 0; count+=array[r][c]; flag = true; } else if (array[r][c] != array[r1][c]) { break; } } } } } for (int c = 0; c < 4; c++) { for (int r = 0; r < 4; r++) { if (array[r][c] == 0) { for (int r1 = r + 1; r1 < 4; r1++) { if (array[r1][c] != 0) { array[r][c] = array[r1][c]; array[r1][c] = 0; r++; flag = true; } } } } } }
}</pre>
來自: www.iteye.com
本文由用戶 n6xb 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!