本文實(shí)例為大家分享了java實(shí)現(xiàn)2048游戲功能的具體代碼,供大家參考,具體內(nèi)容如下
功能要求:2048的基本界面,能夠?qū)崿F(xiàn)2048的游戲功能。
總思路:兩個(gè)類:Game和GameListener。
Game負(fù)責(zé)界面的實(shí)現(xiàn)和paint方法的重寫
GameListener負(fù)責(zé)實(shí)現(xiàn)鍵盤和鼠標(biāo)事件的處理。移動(dòng)方法,相加方法,輸贏判斷和隨機(jī)數(shù)的出現(xiàn)都要在鍵盤監(jiān)聽的方法中實(shí)現(xiàn)。
實(shí)現(xiàn)分析:要實(shí)現(xiàn)2048游戲,首先需要考慮2048都有些什么?
界面實(shí)現(xiàn):
2048的游戲界面很簡(jiǎn)單,就是一些方格和數(shù)字。要實(shí)現(xiàn)這樣的界面,我們可以考慮一下使用Java的繪圖功能。具體來(lái)說(shuō)就是使用JFrame類提供的Graphics對(duì)象進(jìn)行繪圖。2048界面由一個(gè)大的矩形背景和包含數(shù)字的許多小方塊組成。Graphics對(duì)象的繪制矩形的方法就能實(shí)現(xiàn)背景和小方格的繪制。小方格內(nèi)的數(shù)字則可以使用graphics的drawString方法來(lái)繪制。只需要在繪制的時(shí)候注意一下顏色就好。界面實(shí)現(xiàn)要擁到的類主要是JFrame類。
基本界面實(shí)現(xiàn)代碼,不過是一些按鈕之類的,沒什么好說(shuō)的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
private void initUI() { setTitle( "2048" ); setDefaultCloseOperation( 3 ); setSize( 600 , 700 ); setLocationRelativeTo( null ); this .setLayout( null ); //添加分?jǐn)?shù) jl2 = new JLabel( "分?jǐn)?shù):0" ); jl2.setFont( new Font( "黑體" , Font.BOLD, 30 )); jl2.setBounds( 20 , 30 , 200 , 50 ); this .add(jl2); //添加開始按鈕 ImageIcon start= new ImageIcon( "res/start.png" ); //開始游戲圖標(biāo),隨意替換就好 startJB= new JButton(start); startJB.setBounds( 280 , 40 , 120 , 30 ); startJB.setFocusable( false ); startJB.setBorderPainted( false ); //設(shè)置按鈕的邊框?yàn)榭?/code> startJB.setFocusPainted( false ); startJB.setContentAreaFilled( false ); //設(shè)置按鈕的邊框內(nèi)填充顏色 //添加退一步按鈕 ImageIcon back= new ImageIcon( "res/backicon.png" ); //游戲結(jié)束圖標(biāo),隨意替換就好 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); } |
方格和數(shù)字的繪制:
方格和數(shù)字的繪制同樣是使用JFrame的畫布對(duì)象的繪制矩形的方法實(shí)現(xiàn)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
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是一個(gè)接口,里面定義了關(guān)于方格繪制的相關(guān)常量,D方格邊長(zhǎng),space方格間間隔。RD背景大矩形的邊長(zhǎng)。使用接口的好處就是使得界面的更改(如改成5*5的格子)可以更加方便的實(shí)現(xiàn),提高程序的擴(kuò)展性。另外界面需要不斷的更新,所以要調(diào)用paint方法不斷的重繪。如果直接把這里的繪制寫在paint方法中,不斷的重繪會(huì)使得界面一直在閃爍。這里的解決方案就是使用圖片緩沖,先在圖片中繪制好,在把圖片一次性繪制出來(lái),這樣就不會(huì)出現(xiàn)界面的閃爍問題了。
移動(dòng)實(shí)現(xiàn):
要想讓方格移動(dòng)起來(lái),可以考慮的方法就是使用畫板的重繪。方格內(nèi)的數(shù)字則使用一個(gè)二維數(shù)組來(lái)保存。每次移動(dòng)就讓數(shù)組的值發(fā)生變化就行,然后根據(jù)數(shù)組的值把數(shù)據(jù)繪制到界面上。當(dāng)然玩游戲時(shí)總不能靠意念操控,我們需要有輸入設(shè)備,也就是鍵盤。所以需要給界面加上鍵盤監(jiān)聽。當(dāng)用戶按下不同的鍵則實(shí)現(xiàn)不同的移動(dòng)算法。值得注意的是,在監(jiān)聽類中編寫相應(yīng)的移動(dòng)算法的時(shí)候要理清循環(huán)的使用和結(jié)束(尤其是break語(yǔ)句的使用),否則會(huì)出現(xiàn)各種各樣的bug。移動(dòng)實(shí)現(xiàn)是需要用到keyListener類。
下面是向上移動(dòng)的實(shí)現(xiàn),其他方向的移動(dòng)都差不多,自己琢磨一下就好:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//向上移動(dòng)的算法 for ( int r = 0 ; r < arry.length; r++) for ( int c = 0 ; c < arry[r].length; c++) { if (arry[r][c] > max) //找出數(shù)組最大值,用于判斷玩家的方塊是否達(dá)到2048 max = arry[r][c]; if (arry[r][c] == 0 ) { for ( int r1 = r + 1 ; r1 < arry[c].length; r1++) if (arry[r1][c] != 0 ) { arry[r][c] = arry[r1][c]; arry[r1][c] = 0 ; count++; //判斷是否發(fā)生移動(dòng),并且作為輸贏判斷的標(biāo)準(zhǔn)之一 break ; } } } |
通過雙層循環(huán),循環(huán)每一個(gè)值,如果它為0,則可以往上移動(dòng)。遍歷該值所在列,找到第一個(gè)不為0的值,移動(dòng)這個(gè)值(即交換兩個(gè)數(shù)的值)。移動(dòng)后退出內(nèi)層循環(huán),繼續(xù)遍歷下一個(gè)值。
數(shù)字的相加:
使用獨(dú)立的算法遍歷數(shù)組,在移動(dòng)方向上的相鄰數(shù)兩個(gè)相加,然后一個(gè)置為0。這里的算法實(shí)現(xiàn)和移動(dòng)的算法十分相像,需要注意的地方同樣也是break和循環(huán)的使用。還有一個(gè)要注意的就是:數(shù)字的相加要放在數(shù)字移動(dòng)之前來(lái)完成,否則會(huì)出現(xiàn)移動(dòng)后的數(shù)字空格。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//向左的相加算法 for ( int r = 0 ; r < arry.length; r++) for ( int c = 0 ; c < arry[r].length; c++) if (arry[r][c] != 0 ) for ( int c1 = c + 1 ; c1 < arry[r].length; c1++) if (arry[r][c] == arry[r][c1]) { arry[r][c] *= 2 ; score += arry[r][c]; arry[r][c1] = 0 ; count++; //判斷是否發(fā)生相加,并作為輸贏判斷條件之一。 break ; } else if (arry[r][c1] != 0 ) { break ; } |
同樣是遍歷數(shù)組的每一個(gè)值,如果這個(gè)值不為0,則找所在行的相鄰的相同的值相加,結(jié)束最內(nèi)層循環(huán)。如果相鄰的兩個(gè)值不同,也結(jié)束最內(nèi)層循環(huán)。兩個(gè)break語(yǔ)句的使用,避免了數(shù)字之間的跳躍相加。
輸贏的判斷:
2048的贏的規(guī)則是,相加數(shù)字出現(xiàn)2048,所以只需要判斷數(shù)組中是否有一個(gè)數(shù)字等于2048就行,如果有,就輸出相應(yīng)的獲勝信息。
2048的輸?shù)囊?guī)則是,界面已經(jīng)滿(數(shù)組已滿,且無(wú)可移動(dòng),相加的數(shù)字)。個(gè)人的判斷方法是全局變量count加上判斷數(shù)組是否已滿。count如果等于0,則表示無(wú)法移動(dòng)和相加,配合數(shù)組已滿的條件就能判斷玩家已經(jīng)輸了。
隨機(jī)數(shù)字的出現(xiàn):
隨機(jī)數(shù)字出現(xiàn)的條件是發(fā)生移動(dòng)或者相加,所以判斷然讓使用count。條件成立則調(diào)用相應(yīng)算法實(shí)現(xiàn)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。