preface
In 2014, Gabriele Cirulli spent the weekend writing the program of 2048, which was just fun. He wants to create a different visual effect from his own animation.
The game is implemented in java language, using swing technology for interface processing, and the design idea is object-oriented.
Main demand
Every time you control all blocks to move in the same direction, two blocks with the same number collide and merge into their sum. After each operation, a 2 or 4 will be randomly generated in the blank square. Finally, you will win if you get a "2048" block. If all 16 squares are filled and the adjacent squares are different, that is, they cannot be moved, the game will end.
Main design
1. Game panel generation display
2. Block design
3. Keyboard monitoring, direction keys to control the movement of numbers
4. Digital mobile logic algorithm processing
5. The number adds up to 2048 and the game wins
Function screenshot
The game begins
Movement effect
code implementation
Interface layout class
public class Game2048View implements ActionListener{ Block[] blocks; //block JPanel myJPanel; //Main panel JPanel jp1,jp2; //SubPanel // int moveFlag; // Number of cumulative moves boolean numFlag; // Used to determine whether new numbers can be added JLabel scroeValue; //Show score public Game2048View(JFrame myJFrame){ blocks=new Block[16]; // moveFlag=0; numFlag=true; this.myJPanel=(JPanel)myJFrame.getContentPane();///Get content panel setJp1(); myJPanel.add(jp1,BorderLayout.NORTH); setJp2(); myJPanel.add(jp2, BorderLayout.CENTER); myJFrame.addKeyListener(new Game2048Logic(this,myJFrame,blocks,numFlag,scroeValue)); } public void addc(JPanel jp1,Component component, GridBagConstraints gbc,int gridwidth,int gridheight, int weightx,int weighty,int gridx,int gridy) { //This method is used to add a control to the container gbc.gridwidth=gridwidth; //This method is to set the number of grids occupied by the component level. If it is 0, it means that the component is the last one in the line gbc.gridheight=gridheight; //This method is to set the number of grids occupied by the vertical component gbc.weightx=weightx; //This method sets the horizontal stretching range of the component. If it is 0, it means no stretching. If it is not 0, it will stretch as the window increases, between 0 and 1 gbc.weighty=weighty; //This method sets the vertical stretching range of the component. If it is 0, it means no stretching. If it is not 0, it will stretch as the window increases, between 0 and 1 gbc.gridx=gridx; gbc.gridy=gridy; gbc.fill=GridBagConstraints.BOTH; jp1.add(component,gbc); } public void setJp1() { GridBagLayout gbLayout=new GridBagLayout(); jp1=new JPanel(gbLayout); JPanel Jtitle=new JPanel(); JLabel title=new JLabel("2048"); title.setFont(new Font("font", Font.PLAIN, 45));//Type, style, size title.setHorizontalAlignment(JLabel.LEFT);//jLabel text left-right alignment attribute setting method and alignment Jtitle.add(title); jp1.add(Jtitle); JPanel Jscroe=new JPanel(new GridLayout(2, 1));//new GridLayout(2, 1) is the grid layout style. The parameters "2" and "1" are "rows" and "columns" of the grid respectively. JLabel scroe=new JLabel("Scroe"); scroe.setFont(new Font("font", Font.PLAIN, 16)); scroe.setHorizontalAlignment(JLabel.CENTER); scroeValue=new JLabel("0"); scroeValue.setFont(new Font("font", Font.PLAIN, 16)); scroeValue.setHorizontalAlignment(JLabel.CENTER); Jscroe.add(scroe); Jscroe.add(scroeValue); jp1.add(Jscroe); JPanel Jnoite=new JPanel(); JLabel noite=new JLabel("Move the direction key and accumulate the number to 2048"); noite.setFont(new Font("font", Font.PLAIN, 14)); noite.setHorizontalAlignment(JLabel.LEFT); Jnoite.add(noite); jp1.add(Jnoite); JPanel JnewGame=new JPanel(); JButton newGame=new JButton("New Game"); newGame.setHorizontalAlignment(JButton.CENTER); newGame.addActionListener(this); JnewGame.add(newGame); jp1.add(JnewGame); GridBagConstraints gbc=new GridBagConstraints(); addc(jp1, Jtitle, gbc, 3, 2, 60, 60, 0, 0); addc(jp1, Jscroe, gbc, 0, 2, 40, 60, 3, 0); addc(jp1, Jnoite, gbc, 3, 1, 60, 40, 0, 2); addc(jp1, JnewGame, gbc, 0, 1, 40, 40, 3, 2); } public void setJp2() { addBlock(); initBlock(); initBlock(); } /** * Add box */ public void addBlock(){ jp2=new JPanel(); /* * setLayout Is to set the current component as a streaming layout Components are arranged from left to right in the form. If they are arranged to the end of the line, they are arranged in a new line * GridLayout(int rows, int cols, int hgap, int vgap) Creates a grid layout with a specified number of rows and columns. rows - The rows has a value representing any number of rows cols - The cols has a value representing any number of columns hgap - Horizontal spacing vgap - Vertical spacing */ jp2.setLayout(new GridLayout(4, 4, 5, 5)); for (int i = 0; i < blocks.length; i++) { blocks[i]=new Block(); blocks[i].setHorizontalAlignment(JLabel.CENTER);// Opaque label blocks[i].setOpaque(true);// Set control opacity jp2.add(blocks[i]); } } /** * Initialization block */ public void initBlock(){ while (numFlag) { int index=(int) (Math.random()*16); if (blocks[index].getText().trim().equals("")) { blocks[index].setValue("2"); break; } else { continue; } } } /** * Get the height of the first sub panel */ public int getMyJPanelHeidth() { return jp1.getSize().height; } @Override public void actionPerformed(ActionEvent e) { // Method stub automatically generated by TODO newGame(); } /** * Restart the game */ public void newGame() { for (int i = 0; i < blocks.length; i++) { blocks[i].setValue(""); } numFlag=true; scroeValue.setText("0"); initBlock(); initBlock(); } }
Business logic class
public class Game2048Logic implements KeyListener{ Block[] blocks; boolean numFlag; // Used to determine whether new numbers can be added JLabel scroeValue; //Show score int blocksarr[]=new int[4]; //Save values in a row / column box JFrame myJFrame; int scroe=0; Game2048View game2048View; //Initialize keyboard events public Game2048Logic(Game2048View game2048View, JFrame myJFrame, Block[] blocks,boolean numFlag,JLabel scroeValue) { // Constructor stub automatically generated by TODO this.blocks=blocks; this.numFlag=numFlag; this.scroeValue=scroeValue; this.myJFrame=myJFrame; this.game2048View=game2048View; } //Initialize button event public Game2048Logic() { // Constructor stub automatically generated by TODO } public boolean getnumFlag() { return numFlag; } @Override public void keyPressed(KeyEvent e) { // Method stub automatically generated by TODO int[] blocksarr=getBlock(); switch (e.getKeyCode()) { case KeyEvent.VK_UP: colBlock("up"); hasEmptyBlock(); if (Arrays.equals(blocksarr, getBlock())) { } else { refershBlock(); } isGameFail("up"); break; case KeyEvent.VK_DOWN: colBlock("down"); hasEmptyBlock(); if (Arrays.equals(blocksarr, getBlock())) { } else { refershBlock(); } isGameFail("down"); break; case KeyEvent.VK_LEFT: rowBlock("left"); hasEmptyBlock(); if (Arrays.equals(blocksarr, getBlock())) { } else { refershBlock(); } isGameFail("left"); break; case KeyEvent.VK_RIGHT: rowBlock("right"); hasEmptyBlock(); if (Arrays.equals(blocksarr, getBlock())) { } else { refershBlock(); } isGameFail("right"); break; default: break; } scroeValue.setText(""+scroe); win(); } /** * Vertical square movement processing function */ public void colBlock(String direction){ int tmp1=0; int tmp2=0; int index=0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (blocks[tmp1].getText().trim().equals("")) { tmp1+=4; if (tmp1>=16) { break; } else { continue; } } else { blocksarr[index]=Integer.parseInt(blocks[tmp1].getText().trim()); index+=1; tmp1+=4; if (tmp1>=16 || index>=4) { break; } else { continue; } } } switch (direction) { case "up": blocksarr=handleBlocksarr(blocksarr); break; case "down": blocksarr=reverseArr(handleBlocksarr(reverseArr(blocksarr))); break; default: break; } for (int n = 0; n < blocksarr.length; n++) { if (blocksarr[n]==0) { blocks[tmp2].setText(""); blocks[tmp2].setBackground(Color.gray); } else { blocks[tmp2].setValue(blocksarr[n]+""); } tmp2+=4; } index=0; tmp1=i+1; tmp2=i+1; //Empty array blockarr for (int n = 0; n < blocksarr.length; n++) { blocksarr[n]=0; } } } /** * Horizontal square movement processing function */ public void rowBlock(String direction) { int tmp1=0; int tmp2=0; int index=0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (blocks[tmp1].getText().trim().equals("")) { tmp1+=1; if (tmp1>=16) { break; } else { continue; } } else { blocksarr[index]=Integer.parseInt(blocks[tmp1].getText().trim()); index+=1; tmp1+=1; if (tmp1>=16 || index>=4) { break; } else { continue; } } } switch (direction) { case "left": blocksarr=handleBlocksarr(blocksarr); break; case "right": blocksarr=reverseArr(handleBlocksarr(reverseArr(blocksarr))); break; default: break; } for (int n = 0; n < blocksarr.length; n++) { if (blocksarr[n]==0) { blocks[tmp2].setText(""); blocks[tmp2].setBackground(Color.gray); } else { blocks[tmp2].setValue(blocksarr[n]+""); } tmp2+=1; } index=0; //Empty array blockarr for (int n = 0; n < blocksarr.length; n++) { blocksarr[n]=0; } } } /** * Process and return an array */ public int[] handleBlocksarr(int[] blocksarr) { int index=0; int[] result=new int[4]; for (int i = 0; i < blocksarr.length; i++) { //sort if (blocksarr[i]!=0) { result[index]=blocksarr[i]; index++; } } if (index==0 || index==1) { for (int i = index; i < result.length; i++) { result[i]=0; } } else { for (int i = 0; i < blocksarr.length; i++) { blocksarr[i]=0; } switch (index) { case 2: if (result[0]==result[1]) { blocksarr[0]=result[0]+result[1]; scroe+=result[0]*2; } else { blocksarr=result; } break; case 3: if (result[0]==result[1]) { blocksarr[0]=result[0]+result[1]; scroe+=result[0]*2; blocksarr[1]=result[2]; } else { if (result[1]==result[2]) { blocksarr[0]=result[0]; blocksarr[1]=result[1]+result[2]; scroe+=result[1]*2; } else { blocksarr=result; } } break; case 4: if (result[0]==result[1]) { blocksarr[0]=result[0]+result[1]; scroe+=result[0]*2; if (result[2]==result[3]) { blocksarr[1]=result[2]+result[3]; scroe+=result[2]*2; } else { blocksarr[1]=result[2]; blocksarr[2]=result[3]; } } else { if (result[1]==result[2]) { blocksarr[0]=result[0]; blocksarr[1]=result[1]+result[2]; scroe+=result[1]*2; blocksarr[2]=result[3]; } else { blocksarr[0]=result[0]; blocksarr[1]=result[1]; if (result[2]==result[3]) { blocksarr[2]=result[2]+result[3]; scroe+=result[2]*2; } else { blocksarr=result; } } } break; default: break; } result=blocksarr; } return result; } /** * Invert array eg: 45000 -- > 00054 */ public int[] reverseArr(int[] arr) { int[] tmp=new int[arr.length]; int index=arr.length-1; for (int i = 0; i < arr.length; i++) { tmp[index]=arr[i]; index--; } return tmp; } /** * Refresh the box and add a new 2 or 4 */ public void refershBlock(){ if (numFlag==false) { } while (numFlag) { int index=(int) (Math.random()*16); if (blocks[index].getText().trim().equals("")) { if (Math.random()<0.8) { blocks[index].setValue("2"); } else { blocks[index].setValue("4"); } break; } else { continue; } } } /** * Determine whether there are empty squares */ public void hasEmptyBlock() { for (int i = 0; i < blocks.length; i++) { if (blocks[i].getText().trim().equals("")) { this.numFlag=true; break; } else { this.numFlag=false; } } } /** * Judge whether the game fails */ public void isGameFail(String direction) { boolean result=true; //true means failure and false means no failure int tmp=0; if (numFlag == false) { // Indicates that there are no empty squares switch (direction) { case "up": case "down": for (int i = 0; i < 4; i++) { tmp=i*4; for (int j = 0; j < 3; j++) { if (blocks[tmp].getText().trim().equals(blocks[tmp+1].getText().trim())) { result = false; //The game did not fail break; } else { tmp++; } } if (result==false) { break; } } break; case "left": case "right": for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { if (blocks[tmp].getText().trim().equals(blocks[tmp+4].getText().trim())) { result = false; //The game did not fail break; } else { tmp+=4; if (tmp>=16) { break; } else { continue; } } } tmp=i+1; if (result==false) { break; } } break; default: break; } } else { result=false; } if (result==true) { JOptionPane.showMessageDialog( null , "Game Over",null , JOptionPane.ERROR_MESSAGE) ; game2048View.newGame(); } else { } } /** * Judge whether the game is successful, that is, the success is accumulated to 2048 */ public void win() { for (int i = 0; i < blocks.length; i++) { if (blocks[i].getText().trim().equals("2048")) { JOptionPane.showMessageDialog( null , "YOU ARE WIN",null , JOptionPane.ERROR_MESSAGE) ; game2048View.newGame(); break; } } } /** * Get all the box contents for comparison */ public int[] getBlock() { int[] blocksarr=new int[16]; for (int i = 0; i < blocks.length; i++) { if (blocks[i].getText().trim().equals("")) { blocksarr[i]=0; } else { blocksarr[i]=Integer.parseInt(blocks[i].getText().trim()); } } return blocksarr; } @Override public void keyReleased(KeyEvent e) { // Method stub automatically generated by TODO } @Override public void keyTyped(KeyEvent e) { // Method stub automatically generated by TODO } }
summary
Through the implementation of the 2048 game, I have a further understanding of the relevant knowledge of swing and a deeper understanding of the language java than before.
Some basic syntax of java, such as data types, operators, program flow control and arrays, are better understood. The core of java is the idea of object-oriented. I finally realized something about this concept.
Source code acquisition
Download address of source code: portal ---- >
Like it. After following the blogger, chat privately and get it for free
Today is the 14th / 100th day of continuous writing.
You can follow me, praise me, comment on me and collect me.