pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cnblogs</groupId>
<artifactId>wanson</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>12</source>
<target>12</target>
</configuration>
</plugin>
</plugins>
</build>
<packaging>jar</packaging>
</project>
PlaySound.java
package com.cnblogs.wanson;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
//播放聲音的線程
public class PlaySound extends Thread {
private String filename;
public PlaySound(String wavfile) {
filename = "" + wavfile;
}
public void run() {
File soundFile = new File(filename);
AudioInputStream audioInputStream = null;
try {
//獲得音頻輸入流
audioInputStream = AudioSystem.getAudioInputStream(soundFile);
} catch (Exception e1) {
e1.printStackTrace();
return;
}
//指定聲音流中特定數據安排
AudioFormat format = audioInputStream.getFormat();
SourceDataLine auline = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
try {
//從混頻器獲得源數據行
auline = (SourceDataLine) AudioSystem.getLine(info);
//打開具有指定格式的行,這樣可使行獲得所有所需的系統資源並變得可操作。
auline.open(format);
} catch (Exception e) {
e.printStackTrace();
return;
}
//允許數據行執行數據 I/O
auline.start();
int nBytesRead = 0;
// 這是緩沖
byte[] abData = new byte[512];
try {
while (nBytesRead != -1) {
//從音頻流讀取指定的最大數量的數據字節,並將其放入給定的字節數組中
nBytesRead = audioInputStream.read(abData, 0, abData.length);
if (nBytesRead >= 0)
//通過此源數據行將音頻數據寫入混頻器
auline.write(abData, 0, nBytesRead);
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
auline.drain();
auline.close();
}
}
}
ComponentListener.java
package com.cnblogs.wanson;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Arrays;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
public class ComponentListener extends KeyAdapter implements ActionListener {
private GameStart UI;// 界面對象
private int Numbers[][];// 存放數據的數組
private Random rand = new Random();
private int BackUp[][] = new int[4][4];//用於備份數組,供回退時使用
private int BackUp2[][] = new int[4][4];//用於備份數組,供起死回生時使用
public JLabel lb;
int score = 0;
int tempscore, tempscore2;//記錄回退的分數值
public JButton bt, about, back;
public JCheckBox isSoundBox;
//是否勝利,true:勝利,false:失敗
private boolean isWin = false;
//是否復活,true:使用復活,false:不使用復活
private boolean relive = false;
//是否可以回退,true:不可回退,false:可以回退 (是否已經進行過一次回退了)
private boolean hasBack = false;
//是否播放音樂,true:播放音效,false:不播放音效
private boolean isSound = true;
public ComponentListener(GameStart UI, int Numbers[][], JLabel lb, JButton bt, JButton about, JButton back, JCheckBox isSoundBox) {
this.UI = UI;
this.Numbers = Numbers;
this.lb = lb;
this.bt = bt;
this.about = about;
this.back = back;
this.isSoundBox = isSoundBox;
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == bt) {
//游戲開始
isWin = false;
//各個小格賦初值0
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
Numbers[i][j] = 0;
//游戲開始,分數為0
score = 0;
lb.setText("分數:" + score);
//生成4個0-3之間的隨機數
int r1 = rand.nextInt(4);
int r2 = rand.nextInt(4);
int c1 = rand.nextInt(4);
int c2 = rand.nextInt(4);
//由r1,c1;r2,c2組成兩個初始值,所以初始值的坐標不能重復
while (r1 == r2 && c1 == c2) {
r2 = rand.nextInt(4);
c2 = rand.nextInt(4);
}
// 生成初始數字(2或者4)
int value1 = rand.nextInt(2) * 2 + 2;
int value2 = rand.nextInt(2) * 2 + 2;
// 把數字存進對應的位置
Numbers[r1][c1] = value1;
Numbers[r2][c2] = value2;
//數字更改,重新繪制圖形,為此組件創建圖形上下文
UI.paint(UI.getGraphics());
} else if (e.getSource() == about) {
//點擊了關於標簽
JOptionPane.showMessageDialog(UI, "游戲規則:\n"
+ "1、開始時棋盤內隨機出現兩個數字,出現的數字僅可能為2或4\n"
+ "2、玩家可以選擇上下左右四個方向,若棋盤內的數字出現位移或合並,視為有效移動\n"
+ "3、玩家選擇的方向上若有相同的數字則合並,每次有效移動可以同時合並,但不可以連續合並\n"
+ "4、合並所得的所有新生成數字相加即為該步的有效得分\n"
+ "5、玩家選擇的方向行或列前方有空格則出現位移\n"
+ "6、每有效移動一步,棋盤的空位(無數字處)隨機出現一個數字(依然可能為2或4)\n"
+ "7、棋盤被數字填滿,無法進行有效移動,判負,游戲結束\n"
+ "8、棋盤上出現2048,判勝,游戲結束。\n"
);
} else if (e.getSource() == back && hasBack == false) {
System.out.println("回退");
//點擊了回退一步標簽,而且只能回退一次,只有再執行一次上下左右操作才可以再次回退
hasBack = true;
//判斷本次回退是回退上一步,還是復活,回退上上步
if (relive == false) {
//替換上一步的分數
score = tempscore;
lb.setText("分數:" + score);
for (int i = 0; i < BackUp.length; i++) {
Numbers[i] = Arrays.copyOf(BackUp[i], BackUp[i].length);
}
} else {
//選擇了起死回生
score = tempscore2;
lb.setText("分數:" + score);
for (int i = 0; i < BackUp2.length; i++) {
Numbers[i] = Arrays.copyOf(BackUp2[i], BackUp2[i].length);
}
//再給一次復活的機會
relive = false;
}
//重新繪制
UI.paint(UI.getGraphics());
} else if (e.getSource().equals(isSoundBox)) {
//是否選中靜音復選框
if (isSoundBox.isSelected()) {
isSound = false;
} else {
isSound = true;
}
}
}
// 鍵盤監聽,監聽游戲焦點的←,↑,→,↓;方向鍵鍵值:左:37上:38右:39下:40
public void keyPressed(KeyEvent event) {
int Counter = 0;// 記錄數字有效移動位數,判斷是否移動了
int NumCounter = 0;// 記錄當前有數字的小方格數量,判斷是否已滿
int NumNearCounter = 0;// 記錄相鄰格子數字相同的對數
hasBack = false;
//每次進行真正的移位合並操作之前,記錄前一步
//記錄上上步
if (BackUp != null || BackUp.length != 0) {
tempscore2 = tempscore;// 先把分數備份好
// 下面的for循環調用java.util.Arrays.copyOf()方法復制數組,實現備份
for (int i = 0; i < BackUp.length; i++) {
BackUp2[i] = Arrays.copyOf(BackUp[i], BackUp[i].length);
}
}
//記錄上步
tempscore = score;// 先把分數備份好
// 下面的for循環調用java.util.Arrays.copyOf()方法復制數組,實現備份
for (int i = 0; i < Numbers.length; i++) {
BackUp[i] = Arrays.copyOf(Numbers[i], Numbers[i].length);
}
if (isWin == false) {
switch (event.getKeyCode()) {
case 37:
// 向左移動
if (isSound == true) {
new PlaySound("D:\\java_project\\HandleJavaWithMysql\\src\\main\\resources\\res\\move.wav").start();// 播放移位音樂
}
//經過這個循環,把每行有值的格子,都被搬到最左邊了,同一行右側有值的格子,覆蓋左側值為0的格子
for (int h = 0; h < 4; h++)
for (int l = 0; l < 4; l++)
if (Numbers[h][l] != 0) {
int temp = Numbers[h][l];
int pre = l - 1;
while (pre >= 0 && Numbers[h][pre] == 0) {
Numbers[h][pre] = temp;
Numbers[h][pre + 1] = 0;
pre--;
Counter++;
}
}
//表盤當前左側相鄰相等的值會相加,造成左邊值為【和】,相鄰右邊值為【0】
for (int h = 0; h < 4; h++)
for (int l = 0; l < 4; l++)
if (l + 1 < 4
&& (Numbers[h][l] == Numbers[h][l + 1])
&& (Numbers[h][l] != 0 || Numbers[h][l + 1] != 0)) {
if (isSound == true)
new PlaySound("D:\\java_project\\HandleJavaWithMysql\\src\\main\\resources\\res\\merge.wav").start();
Numbers[h][l] = Numbers[h][l] + Numbers[h][l + 1];
Numbers[h][l + 1] = 0;
Counter++;
score += Numbers[h][l];
if (Numbers[h][l] == 2048) {
isWin = true;
}
}
//經過這個循環,把每行有值的格子,都被搬到最左邊了,同一行右側有值的格子,覆蓋左側值為0的格子
for (int h = 0; h < 4; h++)
for (int l = 0; l < 4; l++)
if (Numbers[h][l] != 0) {
int temp = Numbers[h][l];
int pre = l - 1;
while (pre >= 0 && Numbers[h][pre] == 0) {
Numbers[h][pre] = temp;
Numbers[h][pre + 1] = 0;
pre--;
Counter++;
}
}
break;
case 39:// 向右移動
if (isSound == true)
new PlaySound("D:\\java_project\\HandleJavaWithMysql\\src\\main\\resources\\res\\move.wav").start();
for (int h = 3; h >= 0; h--)
for (int l = 3; l >= 0; l--)
if (Numbers[h][l] != 0) {
int temp = Numbers[h][l];
int pre = l + 1;
while (pre <= 3 && Numbers[h][pre] == 0) {
Numbers[h][pre] = temp;
Numbers[h][pre - 1] = 0;
pre++;
Counter++;
}
}
for (int h = 3; h >= 0; h--)
for (int l = 3; l >= 0; l--)
if (l + 1 < 4
&& (Numbers[h][l] == Numbers[h][l + 1])
&& (Numbers[h][l] != 0 || Numbers[h][l + 1] != 0)) {
if (isSound == true)
new PlaySound("D:\\java_project\\HandleJavaWithMysql\\src\\main\\resources\\res\\merge.wav").start();
Numbers[h][l + 1] = Numbers[h][l]
+ Numbers[h][l + 1];
Numbers[h][l] = 0;
Counter++;
score += Numbers[h][l + 1];
if (Numbers[h][l + 1] == 2048) {
isWin = true;
}
}
for (int h = 3; h >= 0; h--)
for (int l = 3; l >= 0; l--)
if (Numbers[h][l] != 0) {
int temp = Numbers[h][l];
int pre = l + 1;
while (pre <= 3 && Numbers[h][pre] == 0) {
Numbers[h][pre] = temp;
Numbers[h][pre - 1] = 0;
pre++;
Counter++;
}
}
break;
case 38:
// 向上移動
if (isSound == true)
new PlaySound("D:\\java_project\\HandleJavaWithMysql\\src\\main\\resources\\res\\move.wav").start();
for (int l = 0; l < 4; l++)
for (int h = 0; h < 4; h++)
if (Numbers[h][l] != 0) {
int temp = Numbers[h][l];
int pre = h - 1;
while (pre >= 0 && Numbers[pre][l] == 0) {
Numbers[pre][l] = temp;
Numbers[pre + 1][l] = 0;
pre--;
Counter++;
}
}
for (int l = 0; l < 4; l++)
for (int h = 0; h < 4; h++)
if (h + 1 < 4
&& (Numbers[h][l] == Numbers[h + 1][l])
&& (Numbers[h][l] != 0 || Numbers[h + 1][l] != 0)) {
if (isSound == true)
new PlaySound("D:\\java_project\\HandleJavaWithMysql\\src\\main\\resources\\res\\merge.wav").start();
Numbers[h][l] = Numbers[h][l] + Numbers[h + 1][l];
Numbers[h + 1][l] = 0;
Counter++;
score += Numbers[h][l];
if (Numbers[h][l] == 2048) {
isWin = true;
}
}
for (int l = 0; l < 4; l++)
for (int h = 0; h < 4; h++)
if (Numbers[h][l] != 0) {
int temp = Numbers[h][l];
int pre = h - 1;
while (pre >= 0 && Numbers[pre][l] == 0) {
Numbers[pre][l] = temp;
Numbers[pre + 1][l] = 0;
pre--;
Counter++;
}
}
break;
case 40:
// 向下移動
if (isSound == true)
new PlaySound("D:\\java_project\\HandleJavaWithMysql\\src\\main\\resources\\res\\move.wav").start();
for (int l = 3; l >= 0; l--)
for (int h = 3; h >= 0; h--)
if (Numbers[h][l] != 0) {
int temp = Numbers[h][l];
int pre = h + 1;
while (pre <= 3 && Numbers[pre][l] == 0) {
Numbers[pre][l] = temp;
Numbers[pre - 1][l] = 0;
pre++;
Counter++;
}
}
for (int l = 3; l >= 0; l--)
for (int h = 3; h >= 0; h--)
if (h + 1 < 4
&& (Numbers[h][l] == Numbers[h + 1][l])
&& (Numbers[h][l] != 0 || Numbers[h + 1][l] != 0)) {
if (isSound == true)
new PlaySound("D:\\java_project\\HandleJavaWithMysql\\src\\main\\resources\\res\\merge.wav").start();
Numbers[h + 1][l] = Numbers[h][l]
+ Numbers[h + 1][l];
Numbers[h][l] = 0;
Counter++;
score += Numbers[h + 1][l];
if (Numbers[h + 1][l] == 2048) {
isWin = true;
}
}
for (int l = 3; l >= 0; l--)
for (int h = 3; h >= 0; h--)
if (Numbers[h][l] != 0) {
int temp = Numbers[h][l];
int pre = h + 1;
while (pre <= 3 && Numbers[pre][l] == 0) {
Numbers[pre][l] = temp;
Numbers[pre - 1][l] = 0;
pre++;
Counter++;
}
}
break;
}
//移位,合並,移位完成后,判斷是否有可重復值
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (Numbers[i][j] == Numbers[i][j + 1]
&& Numbers[i][j] != 0) {
NumNearCounter++;
}
if (Numbers[i][j] == Numbers[i + 1][j]
&& Numbers[i][j] != 0) {
NumNearCounter++;
}
if (Numbers[3][j] == Numbers[3][j + 1]//第四行只需要判斷是否與右邊有重復
&& Numbers[3][j] != 0) {
NumNearCounter++;
}
if (Numbers[i][3] == Numbers[i + 1][3]//第四列只需要判斷與下邊是否有重復
&& Numbers[i][3] != 0) {
NumNearCounter++;
}
}
}
//判斷不為0的空余格式數
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (Numbers[i][j] != 0) {
NumCounter++;
}
}
}
System.out.println(Counter);
//有效移位數>0,則補充一個新的2或者4
if (Counter > 0) {
lb.setText("分數:" + score);
int r1 = rand.nextInt(4);
int c1 = rand.nextInt(4);
while (Numbers[r1][c1] != 0) {
r1 = rand.nextInt(4);
c1 = rand.nextInt(4);
}
int value1 = rand.nextInt(2) * 2 + 2;
Numbers[r1][c1] = value1;
}
if (isWin == true) {
UI.paint(UI.getGraphics());
JOptionPane.showMessageDialog(UI, "恭喜你贏了!\n您的最終得分為:" + score);
}
if (NumCounter == 16 && NumNearCounter == 0) {
//移動后滿格並且沒有可合並的小格子,游戲結束relive:復活一次
relive = true;
JOptionPane.showMessageDialog(UI, "沒地方可以合並咯!!"
+ "\n很遺憾,您輸了~>_<~" + "\n悄悄告訴你,游戲有起死回生功能哦,不信你“退一步”試試?"
+ "\n說不定能扭轉乾坤捏 (^_~)");
}
UI.paint(UI.getGraphics());
}
}
}
GameStart.java
package com.cnblogs.wanson;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
//本類繼承自JFrame,創建游戲窗口,只需要new本類對象
public class GameStart extends JFrame {
private static final long serialVersionUID = 1L;
/* public static void main(String[] args) {
GameStart view = new GameStart();
view.init();
}*/
//用於存放數據的二維數組,構成4*4網格的游戲界面數值,數組中的值就是其對應位置方格的值,0代表無值
private int Numbers[][] = new int[4][4];
public void init() {
this.setTitle("2048游戲");
this.setLocation(450, 100);
this.setSize(400, 500);
this.setLayout(null);
// 開始游戲按鈕
ImageIcon imgicon = new ImageIcon("D:\\java_project\\HandleJavaWithMysql\\src\\main\\resources\\res\\start.png");
JButton start = new JButton(imgicon);
start.setFocusable(false);//設置此按鈕不可獲取焦點
start.setBorderPainted(false);//設置此按鈕沒有邊框
start.setFocusPainted(false);//設置不繪制邊框,設置 paintFocus屬性,對於要繪制的焦點狀態,該屬性必須為 true。paintFocus 屬性的默認值為 true。一些外觀沒有繪制焦點狀態;它們將忽略此屬性
start.setContentAreaFilled(false);//設置不繪制邊框,設置 contentAreaFilled 屬性。如果該屬性為 true,則按鈕將繪制內容區域。如果希望有一個透明的按鈕,比如只是一個圖標的按鈕,那么應該將此屬性設置為 false。不要調用 setOpaque(false)。contentAreaFilled 屬性的默認值為 true。
start.setBounds(5, 10, 120, 30);// 設置按鈕的x,y坐標位置和寬度與高度
this.add(start);
//后退一步按鈕
ImageIcon backicon = new ImageIcon("D:\\java_project\\HandleJavaWithMysql\\src\\main\\resources\\res\\backicon.png");
JButton back = new JButton(backicon);
back.setFocusable(false);
back.setBorderPainted(false);
back.setFocusPainted(false);
back.setContentAreaFilled(false);
back.setBounds(270, 10, 120, 30);// 設置按鈕的x,y坐標位置和寬度與高度
this.add(back);
// 關於按鈕
ImageIcon imgicon2 = new ImageIcon("res/about.png");
JButton about = new JButton(imgicon2);
about.setFocusable(false);
about.setBorderPainted(false);
about.setFocusPainted(false);
about.setContentAreaFilled(false);
about.setBounds(160, 10, 70, 30);
this.add(about);
// 分數顯示
JLabel scoreLabel = new JLabel("分數:0");
scoreLabel.setBounds(40, 45, 120, 30);
scoreLabel.setFont(new Font("幼圓", Font.CENTER_BASELINE, 18));
scoreLabel.setForeground(new Color(0x000000));
this.add(scoreLabel);
JCheckBox isSoundBox = new JCheckBox("靜音");
isSoundBox.setBounds(290, 45, 120, 30);
isSoundBox.setFont(new Font("幼圓", Font.CENTER_BASELINE, 18));
isSoundBox.setFocusable(false);
isSoundBox.setBorderPainted(false);
isSoundBox.setFocusPainted(false);
isSoundBox.setContentAreaFilled(false);
this.add(isSoundBox);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setResizable(false);
this.setVisible(true);// 顯示界面
// 創建事件處理類
ComponentListener cl = new ComponentListener(this, Numbers, scoreLabel, start, about, back, isSoundBox);
start.addActionListener(cl);
about.addActionListener(cl);
back.addActionListener(cl);
isSoundBox.addActionListener(cl);
this.addKeyListener(cl);
}
// 重寫窗體
@Override
public void paint(Graphics g) {
super.paint(g);
//設置畫筆顏色
g.setColor(new Color(0xBBADA0));
//填充整個4*4圓角矩形區域,使用當前顏色填充指定的圓角矩形
g.fillRoundRect(15, 110, 370, 370, 15, 15);// 大矩形框
g.setColor(new Color(0xCDC1B4));
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
//填充每一個4*4小方格區域
g.fillRoundRect(25 + i * 90, 120 + j * 90, 80, 80, 15, 15);// 小矩形框
}
}
// 調整數字的位置並上色
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
//如果小方格上數字不為0,則說明有值,進行繪制背景色與數字
if (Numbers[j][i] != 0) {
int FontSize = 30;
int MoveX = 0, MoveY = 0;
switch (Numbers[j][i]) {
case 2:
g.setColor(new Color(0xeee4da));
FontSize = 30;
MoveX = 0;
MoveY = 0;
break;
case 4:
g.setColor(new Color(0xede0c8));
FontSize = 30;
MoveX = 0;
MoveY = 0;
break;
case 8:
g.setColor(new Color(0xf2b179));
FontSize = 30;
MoveX = 0;
MoveY = 0;
break;
case 16:
g.setColor(new Color(0xf59563));
FontSize = 29;
MoveX = -5;
MoveY = 0;
break;
case 32:
g.setColor(new Color(0xf67c5f));
FontSize = 29;
MoveX = -5;
MoveY = 0;
break;
case 64:
g.setColor(new Color(0xf65e3b));
FontSize = 29;
MoveX = -5;
MoveY = 0;
break;
case 128:
g.setColor(new Color(0xedcf72));
FontSize = 28;
MoveX = -10;
MoveY = 0;
break;
case 256:
g.setColor(new Color(0xedcc61));
FontSize = 28;
MoveX = -10;
MoveY = 0;
break;
case 512:
g.setColor(new Color(0xedc850));
FontSize = 28;
MoveX = -10;
MoveY = 0;
break;
case 1024:
g.setColor(new Color(0xedc53f));
FontSize = 27;
MoveX = -15;
MoveY = 0;
break;
case 2048:
g.setColor(new Color(0xedc22e));
FontSize = 27;
MoveX = -15;
MoveY = 0;
break;
default:
g.setColor(new Color(0x000000));
break;
}
//數字不為0的小方格覆蓋原色,根據不同的值上不同的色
g.fillRoundRect(25 + i * 90, 120 + j * 90, 80, 80, 15, 15);// 小矩形框覆蓋上色
g.setColor(new Color(0x000000));
g.setFont(new Font("Kristen ITC", Font.PLAIN, FontSize));
//繪制字符串,參數分別為:要繪制的字符串,字符串繪制的x坐標,y坐標
g.drawString(Numbers[j][i] + "", 25 + i * 90 + 30 + MoveX,
120 + j * 90 + 50 + MoveY);
}
}
}
}
}
主窗口測試
package com.cnblogs.wanson.test;
import com.cnblogs.wanson.GameStart;
public class HandleJavaWithMysqlTest {
public static void main(String[] args) {
GameStart view = new GameStart();
view.init();
}
}