首頁 >Java >java教程 >Java版實作2048的遊戲功能實例

Java版實作2048的遊戲功能實例

零下一度
零下一度原創
2017-07-21 22:01:091782瀏覽

 功能需求:2048的基本介面,能夠實現2048的遊戲功能。

總想法:兩個類別:Game和GameListener。

  Game負責介面的實作和paint方法的重寫

  GameListener負責實作鍵盤和滑鼠事件的處理。移動方法,相加方法,輸贏判斷和隨機數的出現都要在鍵盤監聽的方法中實現。

實現分析:要實現2048遊戲,首先要考慮2048都有些什麼?

介面實作:

  2048的遊戲介面很簡單,就是一些方格和數字。要實現這樣的介面,我們可以考慮使用Java的繪圖功能。具體來說就是使用JFrame類別提供的Graphics物件進行繪圖。 2048介面由一個大的矩形背景和包含數字的許多小方塊組成。 Graphics物件的繪製矩形的方法就能實現背景和小方格的繪製。小方格內的數字則可以使用graphics的drawString方法來繪製。只需要在繪製的時候注意一下顏色就好。介面實作要擁到的類別主要是JFrame類別。

基本介面實作程式碼,不過是一些按鈕之類的,沒什麼好說的。

private void initUI() {
        setTitle("2048");
        setDefaultCloseOperation(3);
        setSize(600, 700);
        setLocationRelativeTo(null);this.setLayout(null);//添加分数jl2 = new JLabel("分数:0");
        jl2.setFont(new Font("黑体", Font.BOLD, 30));
        jl2.setBounds(20, 30, 200, 50);this.add(jl2);//添加开始按钮ImageIcon start=new ImageIcon("res/start.png");//开始游戏图标,随意替换就好startJB=new JButton(start);
        startJB.setBounds(280, 40, 120, 30);
        startJB.setFocusable(false);
        startJB.setBorderPainted(false);//设置按钮的边框为空startJB.setFocusPainted(false);
        startJB.setContentAreaFilled(false);//设置按钮的边框内填充颜色        //添加退一步按钮ImageIcon back=new ImageIcon("res/backicon.png");//游戏结束图标,随意替换就好backJB=new JButton(back);
        backJB.setBounds(430, 40, 120, 30);
        backJB.setFocusable(false);
        backJB.setBorderPainted(false);
        backJB.setFocusPainted(false);
        backJB.setContentAreaFilled(false);        this.add(startJB);this.add(backJB);
        setVisible(true);
        GameListener gl = new GameListener(this, arry, jl2,startJB,backJB);
        addKeyListener(gl);
        startJB.addActionListener(gl);
        backJB.addActionListener(gl);
    }

方格和數字的繪製:

  方格和數字的繪製同樣是使用JFrame的畫布物件的繪製矩形的方法實作。

public void buffPaint(Graphics g) {
        Image image = createImage(600, 600);
        Graphics g2 = image.getGraphics();
        g2.setColor(Color.LIGHT_GRAY);
        Graphics2D g2d = (Graphics2D) g2;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.fillRoundRect(0, 0, Draw2048.RD, Draw2048.RD, 20, 20);
        g2.setColor(Color.GRAY);for (int r = 0; r < arry.length; r++) {for (int c = 0; c < arry[r].length; c++) {
                g2.fillRect(20 + c * (Draw2048.D + Draw2048.space), 20 + r* (Draw2048.D + Draw2048.space), Draw2048.D, Draw2048.D);
            }
        }for (int r = 0; r < arry.length; r++) {for (int c = 0; c < arry[r].length; c++) {if (arry[r][c] != 0) {
                    g2.setColor(255, 250, 240);
                    g2.fillRect(20 + c * (Draw2048.D + Draw2048.space), 20 + r* (Draw2048.D + Draw2048.space), Draw2048.D,
                            Draw2048.D);
                    g2.setColor(new Color(0, 191, 255));
                    g2.setFont(new Font("楷体", Font.BOLD, Draw2048.FSIZE));
                    g2.drawString(arry[r][c] + "", 50 + c* (Draw2048.D + Draw2048.space), 90 + r* (Draw2048.D + Draw2048.space));
                }
            }
        }
        g.drawImage(image, 50, 150, this);
    }
Draw2048是一个接口,里面定义了关于方格绘制的相关常量,D方格边长,space方格间间隔。RD背景大矩形的边长。使用接口的好处就是使得界面的更改(如改成5*5的格子)可以更加方便的实现,提高程序的扩展性。另外界面需要不断的更新,所以要调用paint方法不断的重绘。如果直接把这里的绘制写在paint方法中,不断的重绘会使得界面一直在闪烁。这里的解决方案就是使用图片缓冲,先在图片中绘制好,在把图片一次性绘制出来,这样就不会出现界面的闪烁问题了。

移動實作:

  要想讓方格移動起來,可以考慮的方法就是使用畫板的重繪。方格內的數字則是使用一個二維數組來保存。每次移動就讓數組的值改變就行,然後根據數組的值把資料繪製到介面上。當然玩遊戲時總不能靠意念操控,我們需要有輸入設備,也就是鍵盤。所以需要給介面加上鍵盤監聽。當使用者按下不同的鍵則實現不同的移動演算法。值得注意的是,在監聽類別中編寫對應的移動演算法的時候要理清循環的使用和結束(尤其是break語句的使用),否則會出現各種各樣的bug。移動實作是需要用到keyListener類別。

下面是向上移動的實現,其他方向的移動都差不多,自己琢磨一下就好:

<span style="font-size: 16px"><span style="color: #0000ff"><span style="color: #339966">//向上移动的算法</span><br/>for</span> (<span style="color: #0000ff">int</span> r = 0; r < arry.length; r++<span style="color: #000000">) </span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> c = 0; c < arry[r].length; c++<span style="color: #000000">) {</span><span style="color: #0000ff">if</span> (arry[r][c] > max)<span style="color: #008000">//</span><span style="color: #008000">找出数组最大值,用于判断玩家的方块是否达到2048</span>max =<span style="color: #000000"> arry[r][c];</span><span style="color: #0000ff">if</span> (arry[r][c] == 0<span style="color: #000000">) {</span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> r1 = r + 1; r1 < arry[c].length; r1++<span style="color: #000000">) </span><span style="color: #0000ff">if</span> (arry[r1][c] != 0<span style="color: #000000">) {
                                arry[r][c] </span>=<span style="color: #000000"> arry[r1][c];
                                arry[r1][c] </span>= 0<span style="color: #000000">;
                                count</span>++<span style="color: #000000">;<span style="color: #339966">//判断是否发生移动,并且作为输赢判断的标准之一</span></span><span style="color: #0000ff">break</span><span style="color: #000000">;
                            }
                      }
                }</span>

透過雙層循環,循環每一個值,如果它為0,則可以往上移動。遍歷該值所在列,找出第一個不為0的值,移動這個值(即交換兩個數的值)。移動後退出內層循環,繼續遍歷下一個值。

數字的相加:

  使用獨立的演算法遍歷數組,在移動方向上的相鄰數兩個相加,然後一個置為0。這裡的演算法實作和移動的演算法十分相像,要注意的地方同樣也是break和迴圈的使用。還有一個要注意的是:數字的相加要放在數字移動之前來完成,否則會出現移動後的數字空格。

 

<span style="font-size: 16px"><span style="color: #0000ff"><span style="color: #339966">//向左的相加算法</span><br>for</span> (<span style="color: #0000ff">int</span> r = 0; r < arry.length; r++<span style="color: #000000">)</span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> c = 0; c < arry[r].length; c++<span style="color: #000000">)</span><span style="color: #0000ff">if</span> (arry[r][c] != 0<span style="color: #000000">)</span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> c1 = c + 1; c1 < arry[r].length; c1++<span style="color: #000000">)</span><span style="color: #0000ff">if</span> (arry[r][c] ==<span style="color: #000000"> arry[r][c1]) {
                    arry[r][c] </span>*= 2<span style="color: #000000">;
                    score </span>+=<span style="color: #000000"> arry[r][c];
                    arry[r][c1] </span>= 0<span style="color: #000000">;
                    count</span>++<span style="color: #000000">;<span style="color: #339966">//判断是否发生相加,并作为输赢判断条件之一。</span></span><span style="color: #0000ff">break</span><span style="color: #000000">;
                    } </span><span style="color: #0000ff">else</span> <span style="color: #0000ff">if</span> (arry[r][c1] != 0<span style="color: #000000">) {                      </span><span style="color: #0000ff">break</span><span style="color: #000000">;
                    }</span></span>

#同樣是遍歷數組的每一個值,如果這個值不為0,則找所在行的相鄰的相同的值相加,結束最內層循環。如果相鄰的兩個值不同,也結束最內層循環。兩個break語句的使用,避免了數字之間的跳躍相加。

輸贏的判斷:

  2048的贏的規則是,相加數字出現2048,所以只需要判斷數組中是否有一個數字等於2048就行,如果有,就輸出對應的獲勝資訊。

  2048的輸的規則是,介面已經滿(陣列已滿,且無可移動,相加的數字)。個人的判斷方法是全域變數count加上判斷陣列是否已滿。 count如果等於0,則表示無法移動和相加,配合數組已滿的條件就能判斷玩家已經輸了。

隨機數字的出現:

  隨機數字出現的條件是發生移動或相加,所以判斷然讓使用count。條件成立則調用對應演算法實作。

 

以上是Java版實作2048的遊戲功能實例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn