游戏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); } }
在功能的实现方面,我们需要考虑事件的监听及游戏实现的算法。事件里的事件源是你的动作发生所
在的组件上,事件监听器的添加方法是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; } } } } } } }