本文实例为大家分享了java实现2048游戏功能的具体代码,供大家参考,具体内容如下
功能要求:2048的基本界面,能够实现2048的游戏功能。
总思路:两个类:Game和GameListener。
Game负责界面的实现和paint方法的重写
GameListener负责实现键盘和鼠标事件的处理。移动方法,相加方法,输赢判断和随机数的出现都要在键盘监听的方法中实现。
实现分析:要实现2048游戏,首先需要考虑2048都有些什么?
界面实现:
2048的游戏界面很简单,就是一些方格和数字。要实现这样的界面,我们可以考虑一下使用Java的绘图功能。具体来说就是使用JFrame类提供的Graphics对象进行绘图。2048界面由一个大的矩形背景和包含数字的许多小方块组成。Graphics对象的绘制矩形的方法就能实现背景和小方格的绘制。小方格内的数字则可以使用graphics的drawString方法来绘制。只需要在绘制的时候注意一下颜色就好。界面实现要拥到的类主要是JFrame类。
基本界面实现代码,不过是一些按钮之类的,没什么好说的。
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 ); //添加分数 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的画布对象的绘制矩形的方法实现。
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是一个接口,里面定义了关于方格绘制的相关常量,D方格边长,space方格间间隔。RD背景大矩形的边长。使用接口的好处就是使得界面的更改(如改成5*5的格子)可以更加方便的实现,提高程序的扩展性。另外界面需要不断的更新,所以要调用paint方法不断的重绘。如果直接把这里的绘制写在paint方法中,不断的重绘会使得界面一直在闪烁。这里的解决方案就是使用图片缓冲,先在图片中绘制好,在把图片一次性绘制出来,这样就不会出现界面的闪烁问题了。
移动实现:
要想让方格移动起来,可以考虑的方法就是使用画板的重绘。方格内的数字则使用一个二维数组来保存。每次移动就让数组的值发生变化就行,然后根据数组的值把数据绘制到界面上。当然玩游戏时总不能靠意念操控,我们需要有输入设备,也就是键盘。所以需要给界面加上键盘监听。当用户按下不同的键则实现不同的移动算法。值得注意的是,在监听类中编写相应的移动算法的时候要理清循环的使用和结束(尤其是break语句的使用),否则会出现各种各样的bug。移动实现是需要用到keyListener类。
下面是向上移动的实现,其他方向的移动都差不多,自己琢磨一下就好:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//向上移动的算法 for ( int r = 0 ; r < arry.length; r++) for ( int c = 0 ; c < arry[r].length; c++) { if (arry[r][c] > max) //找出数组最大值,用于判断玩家的方块是否达到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++; //判断是否发生移动,并且作为输赢判断的标准之一 break ; } } } |
通过双层循环,循环每一个值,如果它为0,则可以往上移动。遍历该值所在列,找到第一个不为0的值,移动这个值(即交换两个数的值)。移动后退出内层循环,继续遍历下一个值。
数字的相加:
使用独立的算法遍历数组,在移动方向上的相邻数两个相加,然后一个置为0。这里的算法实现和移动的算法十分相像,需要注意的地方同样也是break和循环的使用。还有一个要注意的就是:数字的相加要放在数字移动之前来完成,否则会出现移动后的数字空格。
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++; //判断是否发生相加,并作为输赢判断条件之一。 break ; } else if (arry[r][c1] != 0 ) { break ; } |
同样是遍历数组的每一个值,如果这个值不为0,则找所在行的相邻的相同的值相加,结束最内层循环。如果相邻的两个值不同,也结束最内层循环。两个break语句的使用,避免了数字之间的跳跃相加。
输赢的判断:
2048的赢的规则是,相加数字出现2048,所以只需要判断数组中是否有一个数字等于2048就行,如果有,就输出相应的获胜信息。
2048的输的规则是,界面已经满(数组已满,且无可移动,相加的数字)。个人的判断方法是全局变量count加上判断数组是否已满。count如果等于0,则表示无法移动和相加,配合数组已满的条件就能判断玩家已经输了。
随机数字的出现:
随机数字出现的条件是发生移动或者相加,所以判断然让使用count。条件成立则调用相应算法实现。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。