首先感謝08年MLDN出的這個培訓視頻,我把代碼和文檔整理了一下,發佈出來給需要學習Swing的朋友。
源碼地址:
https://gitee.com/indexman/gobang
一、知識點準備:
JFrame:窗體
JOptionPane:對話框
MouseListener:鼠標事件
Graphics:繪製二維圖像
Thread:線程類
二、五子棋遊戲的功能:
- 在點擊鼠標時,可以在相應的位置顯示棋子。
- 可以自動判斷遊戲是否結束,是否黑方或白方已經勝利
- 對遊戲時間進行設置,判斷是否超出規定時間
三、遊戲開發步驟
1.開發遊戲界面
計算棋盤中每一條線的間距:這裏用的是19X19的圍棋棋盤,
總寬度爲360px, 分成18份,每份是20px
2.在棋盤上的鼠標點擊位置,顯示一個棋子
黑子:一個實心的黑圓表示
白子:一個空心的黑圓+一個實心的白圓表示
repaint() 方法表示重新執行一次paint()方法。
3.保存之前下過的棋子
通過一個二維數組來保存之前下過的所有棋子。
4.判斷遊戲勝負
依據 五子棋 的基本遊戲規則,判斷是否有同一顏色的棋子連城5個。
完成五子棋遊戲的核心算法
這裏可以把核心算法總結成一個靈活的方法。
提示信息的保存
5.處理屏幕閃爍問題
使用雙緩衝技術
6.實現各個按鈕的功能
開始遊戲:重新開始遊戲
遊戲設置: 設置倒計時時間
遊戲說明:用來說明遊戲規則和操作
認輸:某一方放棄遊戲,投子認負
關於:顯示遊戲製作者
退出:退出程序
-----------------------------------------------------分割線------------------------------------------------------
四、代碼部分
1.啓動類
package com.laoxu.game;
import com.laoxu.game.view.GobangFrame;
import com.laoxu.game.view.MyFrame;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
GobangFrame frame = new GobangFrame();
//MyFrame myFrame = new MyFrame();
//JOptionPane
/*JOptionPane.showMessageDialog(myFrame,"我的信息");
int result = JOptionPane.showConfirmDialog(myFrame,"確定要開始遊戲嗎?","",0);
if(result==0){
JOptionPane.showMessageDialog(myFrame,"遊戲開始");
}
if(result==1){
JOptionPane.showMessageDialog(myFrame,"遊戲結束");
}
if(result==2){
JOptionPane.showMessageDialog(myFrame,"重新選擇");
}
String username = JOptionPane.showInputDialog("請輸入你的姓名:");
System.out.println(username);
JOptionPane.showMessageDialog(myFrame,"輸入的姓名爲:"+username);*/
//System.out.println(System.getProperty("user.dir")+"/src/image/background.jpg");
}
}
2.五子棋類
package com.laoxu.game.view;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
* @description: 五子棋主界面
* @author: luohanye
* @create: 2019-03-21
**/
public class GobangFrame extends JFrame implements MouseListener,Runnable {
//獲取當前平面分辨率,例如:1920x1080
int swidth = Toolkit.getDefaultToolkit().getScreenSize().width;
int sheight = Toolkit.getDefaultToolkit().getScreenSize().height;
BufferedImage image = null;
//保存座標值
int x = 0;
int y = 0;
//保存下過的棋子 0-沒有棋子 1-黑子 2-白子
int[][] allChess = new int[19][19];
// 標識當前應該是黑子還是白子
boolean isBlack = true;
// 控制遊戲是否可以玩
boolean canPlay = true;
// 保存遊戲信息
String message = "黑方先行";
// 保存最多擁有多少時間(秒)
int maxTime = 0;
// 做倒計時的線程類
Thread t = new Thread(this);
// 保存黑方與白方的剩餘時間
int blackTime = 0;
int whiteTime = 0;
// 保存雙方剩餘時間的顯示信息
String blackMessage = "無限制";
String whiteMessage = "無限制";
public GobangFrame() {
//設置標題
this.setTitle("五子棋");
int width = 500, height = 500;
this.setSize(width, height);
//設置窗體出現的位置
this.setLocation((swidth - width) / 2, (sheight - height) / 2);
//窗口大小不可變
this.setResizable(false);
//定義關閉動作
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//添加監聽器
this.addMouseListener(this);
//窗體可見
this.setVisible(true);
//加載背景圖片
try {
image = ImageIO.read(new File(System.getProperty("user.dir")+"/src/image/background.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
// 啓動線程
t.start();
t.suspend();
// 刷新屏幕,防止開始遊戲時出現無法顯示的情況.
this.repaint();
}
public void paint(Graphics g) {
//雙緩衝技術防止屏幕閃爍
BufferedImage bi = new BufferedImage(500,500,BufferedImage.TYPE_INT_RGB);
Graphics g2 = bi.createGraphics();
g2.setColor(Color.BLACK);
//繪製背景
g2.drawImage(image, 0, 20, this);
g2.setFont(new Font("黑體", Font.BOLD, 20));
g2.drawString("遊戲信息:"+message, 130, 60);
//輸出時間信息
g2.setFont(new Font("宋體", 0, 14));
g2.drawString("黑方時間:" + blackMessage, 30, 470);
g2.drawString("白方時間:" + whiteMessage, 260, 470);
//繪製棋盤
/**
* X=10 Y=70
* X=370 Y=430
* X=370 Y=70
* X=10 Y=430
*/
for (int i = 0; i < 19; i++) {
g2.drawLine(10, 70 + 20 * i, 370, 70 + 20 * i);
g2.drawLine(10 + 20 * i, 70, 10 + 20 * i, 430);
}
//標註點位
g2.fillOval(68, 128, 4, 4);
g2.fillOval(308, 128, 4, 4);
g2.fillOval(308, 368, 4, 4);
g2.fillOval(68, 368, 4, 4);
g2.fillOval(68, 248, 4, 4);
g2.fillOval(308, 248, 4, 4);
g2.fillOval(188, 128, 4, 4);
g2.fillOval(188, 368, 4, 4);
g2.fillOval(188, 248, 4, 4);
//繪製棋子
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
//黑子
if (allChess[i][j] == 1) {
int tempX = i * 20 + 10;
int tempY = j * 20 + 70;
g2.fillOval(tempX - 7, tempY - 7, 14, 14);
}
//白子
if (allChess[i][j] == 2) {
int tempX = i * 20 + 10;
int tempY = j * 20 + 70;
g2.setColor(Color.WHITE);
g2.fillOval(tempX - 7, tempY - 7, 14, 14);
g2.setColor(Color.BLACK);
g2.drawOval(tempX - 7, tempY - 7, 14, 14);
}
}
}
g.drawImage(bi,0,0,this);
}
//鼠標按鍵在組件上按下時調用。
@Override
public void mousePressed(MouseEvent e) {
System.out.println("X=" + e.getX() + " Y=" + e.getY());
if (canPlay) {
x = e.getX();
y = e.getY();
//判斷點擊是否在棋盤內
if (x >= 10 && x <= 370 && y >= 70 && y <= 430) {
x = (x - 10) / 20;
y = (y - 70) / 20;
if (allChess[x][y] == 0) {
if (isBlack) {
allChess[x][y] = 1;
isBlack = false;
message="輪到白方";
} else {
allChess[x][y] = 2;
isBlack = true;
message="輪到黑方";
}
//判斷遊戲是否結束
boolean isWin = this.checkWin();
if (isWin) {
JOptionPane.showMessageDialog(this, "遊戲結束,"
+ (allChess[x][y] == 1 ? "黑方" : "白方") + "獲勝!");
canPlay = false;
}
}
this.repaint();
}
}
// 點擊 【開始遊戲】 按鈕
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=70 && e.getY()<=100){
int result = JOptionPane.showConfirmDialog(this,"是否重新開始遊戲?","",0);
if(result==0){
//1.清空棋盤
allChess = new int[19][19];
//另一種方式:
/*for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
allChess[i][j] = 0;
}
}*/
//2.重置遊戲信息
message = "黑方先行";
//3.將下一步下棋的人改爲黑方
isBlack = true;
//4.可以遊戲標誌改爲true
canPlay = true;
//5.重置黑白雙方時間限制
blackTime = maxTime;
whiteTime = maxTime;
if (maxTime > 0) {
blackMessage = maxTime / 3600 + ":"
+ (maxTime / 60 - maxTime / 3600 * 60) + ":"
+ (maxTime - maxTime / 60 * 60);
whiteMessage = maxTime / 3600 + ":"
+ (maxTime / 60 - maxTime / 3600 * 60) + ":"
+ (maxTime - maxTime / 60 * 60);
t.resume();
} else {
blackMessage = "無限制";
whiteMessage = "無限制";
}
//6.重新繪製窗體
this.repaint();
}
}
//點擊 【遊戲設置】 按鈕
if (e.getX() >= 400 && e.getX() <= 470 && e.getY() >= 120
&& e.getY() <= 150) {
String input = JOptionPane
.showInputDialog("請輸入遊戲的最大時間(單位:分鐘),如果輸入0,表示沒有時間限制:");
try {
maxTime = Integer.parseInt(input) * 60;
if (maxTime < 0) {
JOptionPane.showMessageDialog(this, "請輸入正確信息,不允許輸入負數!");
}
if (maxTime == 0) {
int result = JOptionPane.showConfirmDialog(this,
"設置完成,是否重新開始遊戲?");
if (result == 0) {
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
allChess[i][j] = 0;
}
}
// 另一種方式 allChess = new int[19][19];
message = "黑方先行";
isBlack = true;
blackTime = maxTime;
whiteTime = maxTime;
blackMessage = "無限制";
whiteMessage = "無限制";
this.canPlay = true;
this.repaint();
}
}
if (maxTime > 0) {
int result = JOptionPane.showConfirmDialog(this,
"設置完成,是否重新開始遊戲?");
if (result == 0) {
for (int i = 0; i < 19; i++) {
for (int j = 0; j < 19; j++) {
allChess[i][j] = 0;
}
}
// 另一種方式 allChess = new int[19][19];
message = "黑方先行";
isBlack = true;
blackTime = maxTime;
whiteTime = maxTime;
blackMessage = maxTime / 3600 + ":"
+ (maxTime / 60 - maxTime / 3600 * 60) + ":"
+ (maxTime - maxTime / 60 * 60);
whiteMessage = maxTime / 3600 + ":"
+ (maxTime / 60 - maxTime / 3600 * 60) + ":"
+ (maxTime - maxTime / 60 * 60);
t.resume();
this.canPlay = true;
this.repaint();
}
}
} catch (NumberFormatException e1) {
JOptionPane.showMessageDialog(this, "請正確輸入信息!");
}
}
//點擊 【遊戲說明】 按鈕
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=170 && e.getY()<=200){
JOptionPane.showMessageDialog(this,"這是一個五子棋遊戲程序,黑白雙方輪流下棋,當某一方連到五子時遊戲結束。");
}
//點擊 【認輸】 按鈕
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=270 && e.getY()<=300){
int result = JOptionPane.showConfirmDialog(this,"是否確認認輸?","",0);
if(result==0){
if(isBlack){
JOptionPane.showMessageDialog(this,"黑方已經認輸,遊戲結束!");
}else{
JOptionPane.showMessageDialog(this,"白方已經認輸,遊戲結束!");
}
//停止遊戲
canPlay = false;
}
}
//點擊 【關於】 按鈕
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=320 && e.getY()<=350){
JOptionPane.showMessageDialog(this,"本遊戲由老徐製作,有問題請聯繫老徐。");
}
//點擊 【退出】 按鈕
if(e.getX() >=400 && e.getX()<=470 && e.getY()>=370 && e.getY()<=400){
JOptionPane.showMessageDialog(this,"遊戲結束!");
System.exit(0);
}
}
private boolean checkWin() {
boolean flag = false;
//統計相連棋子數
//橫向
int count = 1;
int color = allChess[x][y];
/*int i = 1;
while (color == allChess[x + i][y]) {
count++;
i++;
}
i = 1;
while (color == allChess[x - i][y]) {
count++;
i++;
}
if (count >= 5) {
flag = true;
}
//豎向
int count2 = 1;
int i2 = 1;
while (color == allChess[x][y+i2]) {
count2++;
i2++;
}
i2 = 1;
while (color == allChess[x][y-i2]) {
count2++;
i2++;
}
if (count2 >= 5) {
flag = true;
}
// 右上+左下
int count3 = 1;
int i3 = 1;
while (color == allChess[x+i3][y-i3]) {
count3++;
i3++;
}
i3 = 1;
while (color == allChess[x-i3][y+i3]) {
count3++;
i3++;
}
if (count3 >= 5) {
flag = true;
}
// 左上+右下
int count4 = 1;
int i4 = 1;
while (color == allChess[x-i4][y-i4]) {
count4++;
i4++;
}
i4 = 1;
while (color == allChess[x+i4][y+i4]) {
count4++;
i4++;
}
if (count4 >= 5) {
flag = true;
}
*/ //判斷橫向
count = this.checkCount(1,0,color);
if(count>=5){
flag = true;
}else {
//判斷縱向
count = this.checkCount(0,1,color);
if(count>=5){
flag = true;
}else {
//判斷右上、左下
count = this.checkCount(1, -1, color);
if (count >= 5) {
flag = true;
} else {
//判斷右下、左上
count = this.checkCount(1, 1, color);
if (count >= 5) {
flag = true;
}
}
}
}
return flag;
}
private int checkCount(int xChange, int yChange, int color){
int count = 1;
int tempX = xChange;
int tempY = yChange;
while ((x+xChange>=0 && x+xChange <= 18 && y+yChange>=0 && y+yChange <= 18 )&&color == allChess[x+xChange][y+yChange]){
count++;
if(xChange != 0){
xChange++;
}
if(yChange!=0){
if(yChange>0){
yChange++;
}else {
yChange--;
}
}
}
xChange = tempX;
yChange = tempY;
while ((x-xChange>=0 && x-xChange <= 18 && y-yChange>=0 && y-yChange <= 18 )&&color == allChess[x-xChange][y-yChange]){
count++;
if(xChange != 0){
xChange++;
}
if(yChange!=0){
if(yChange>0){
yChange++;
}else {
yChange--;
}
}
}
return count;
}
@Override
public void run() {
// 判斷是否有時間限制
if (maxTime > 0) {
while (true) {
if (isBlack) {
blackTime--;
if (blackTime == 0) {
JOptionPane.showMessageDialog(this, "黑方超時,遊戲結束!");
}
} else {
whiteTime--;
if (whiteTime == 0) {
JOptionPane.showMessageDialog(this, "白方超時,遊戲結束!");
}
}
blackMessage = blackTime / 3600 + ":"
+ (blackTime / 60 - blackTime / 3600 * 60) + ":"
+ (blackTime - blackTime / 60 * 60);
whiteMessage = whiteTime / 3600 + ":"
+ (whiteTime / 60 - whiteTime / 3600 * 60) + ":"
+ (whiteTime - whiteTime / 60 * 60);
this.repaint();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(blackTime + " -- " + whiteTime);
}
}
}
// 鼠標單擊事件
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}