JavaFx-----五子棋(单机版和对战版)
生活随笔
收集整理的這篇文章主要介紹了
JavaFx-----五子棋(单机版和对战版)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
一.項目介紹
使用 JavaFx + MySql + MyBatis 實現(xiàn)單機和網(wǎng)絡(luò)版五子棋對戰(zhàn).
二.功能介紹
1. 登錄
-- 使用MyBatis和JDBC連接數(shù)據(jù)庫, 實現(xiàn)登錄功能
-- 使用I/O流,實現(xiàn)本地文件記住密碼功能
2.注冊
--使用MyBatis和JDBC連接數(shù)據(jù)庫, 實現(xiàn)注冊功能
-- 注冊完密碼后,返回登錄界面,自動填充注冊的用戶名和密碼
3.網(wǎng)絡(luò)模式選擇
4.單機版
-- 對戰(zhàn)
-- 新局
-- 悔棋
-- 保存棋譜
-- 打開棋譜
5.網(wǎng)絡(luò)版
-- 顯示在線用戶
-- 連接對戰(zhàn)
三. 項目源碼
package com.wn.main;
import java.net.InetAddress;
import java.net.UnknownHostException;
import com.wn.pojo.Global;
import com.wn.ui.LoginStage;
import javafx.application.Application;
import javafx.stage.Stage;
public class MainApplication extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
//獲取當前用戶的本機IP地址
InetAddress inetAddress = null;
try {
inetAddress = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
//將本機ip存儲到全局變量中
Global.myIp = inetAddress.getHostAddress();
new LoginStage().show();
}
}
package com.wn.ui;
import com.wn.CommonUtils.DAOUtils;
import com.wn.CommonUtils.DataHanding;
import com.wn.dao.UserDAO;
import com.wn.dao.UserStatusDAO;
import com.wn.pojo.Global;
import com.wn.pojo.User;
import com.wn.pojo.UserStatus;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class LoginStage extends Stage implements EventHandler<ActionEvent> {
private Button loginBtn;
private Button registerBtn;
private TextField account;
private PasswordField password;
private CheckBox checkBox;
public LoginStage() {
setTitle("五子棋-登錄");
Pane pane = new Pane();
Scene scene = new Scene(pane, 400, 250);
setScene(scene);
getIcons().add(new Image("Imgs/icon-2.jpg"));
pane.setBackground(new Background(new BackgroundFill(Color.GAINSBORO, null, null)));
// 設(shè)置窗口不可變
setResizable(false);
// 標題:用戶登錄
Label title = new Label("用戶登錄");
title.setFont(new Font("KaiTi", 30));
title.setLayoutX(150);
title.setLayoutY(30);
// 賬戶標簽
Label accountLabel = new Label("賬戶 : ");
accountLabel.setFont(new Font("KaiTi", 20));
accountLabel.setLayoutX(80);
accountLabel.setLayoutY(90);
// 賬戶輸入框
account = new TextField();
account.setLayoutX(150);
account.setLayoutY(90);
// 密碼標簽
Label passwordLabel = new Label("密碼 : ");
passwordLabel.setFont(new Font("KaiTi", 20));
passwordLabel.setLayoutX(80);
passwordLabel.setLayoutY(140);
// 密碼輸入框
password = new PasswordField();
password.setLayoutX(150);
password.setLayoutY(140);
// 記住密碼
checkBox = new CheckBox();
checkBox.setLayoutX(80);
checkBox.setLayoutY(180);
Text text = new Text(100, 195,"記住密碼");
text.setFont(new Font("KaiTi",15));
// 登錄按鈕
loginBtn = new Button("登 錄");
loginBtn.setLayoutX(80);
loginBtn.setLayoutY(210);
loginBtn.setDefaultButton(true);
loginBtn.setOnAction(this);
// 注冊按鈕
registerBtn = new Button("注 冊");
registerBtn.setLayoutX(250);
registerBtn.setLayoutY(210);
registerBtn.setOnAction(this);
// 把元素添加到面板
pane.getChildren().addAll(title, accountLabel, account, passwordLabel,
password, loginBtn, registerBtn,checkBox,text);
// 如果用戶剛注冊,則在登錄界面讀取用戶注冊的賬戶和密碼
String content = null;
if (Global.account != null && Global.password != null) {
this.account.setText(Global.account);
this.password.setText(Global.password);
//讀取用戶上一次在本機登錄的賬戶和密碼
}else if ((content=DataHanding.readLastAccount())!=null) {
String[] strings = content.split(",");
if (strings[strings.length-1].equals(Global.myIp)) {
account.setText(strings[0]);
password.setText(strings[1]);
}
}
}
public void login() {
String accountText = account.getText();
String passwordText = password.getText();
Global.account = accountText;
//用戶名或密碼不能為空
if (accountText.equals("")||passwordText.equals("")) {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("用戶名或密碼不能為空");
alert.show();
return;
}
//獲取用戶當前登錄狀態(tài),如果已登錄,不能重復(fù)登錄
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
UserStatus status = statusDAO.selectInfoByAccount(accountText);
if (status.getStatus() == 1) {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("當前用戶已在線,不能重復(fù)登錄");
alert.show();
return;
}
// 獲取UserDAO的實現(xiàn)類
UserDAO userDAO = DAOUtils.getMapper(UserDAO.class);
// 根據(jù)用戶輸入的account獲得User對象
User user = userDAO.getByAccount(accountText);
if (user != null && user.getPassword().equals(passwordText)) {
//登錄成功,判斷用戶是否選擇記住密碼
if (checkBox.isSelected()) {
//用戶選擇記住密碼,調(diào)用記住密碼方法
DataHanding.rememberLastAccount(accountText, passwordText,Global.myIp);
}
//更新用戶狀態(tài)為在線狀態(tài)
statusDAO.updateStatusByAccount(accountText,Global.myIp, 1);
// 已設(shè)置自動提交事務(wù)
// 打開游戲模式選擇界面
new ModeChooseStage().show();
this.close();
} else {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("用戶名或密碼錯誤 ,登錄失敗");
alert.showAndWait();
}
}
@Override
public void handle(ActionEvent event) {
//獲取用戶點擊的按鈕
Button btn = (Button) event.getSource();
String text = btn.getText();
switch (text) {
case "登 錄":
login();
break;
case "注 冊":
new RegisterStage().show();
this.close();
break;
}
}
}
package com.wn.ui;
import com.wn.CommonUtils.DAOUtils;
import com.wn.CommonUtils.DataHanding;
import com.wn.dao.UserDAO;
import com.wn.dao.UserStatusDAO;
import com.wn.pojo.Global;
import com.wn.pojo.User;
import com.wn.pojo.UserStatus;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class LoginStage extends Stage implements EventHandler<ActionEvent> {
private Button loginBtn;
private Button registerBtn;
private TextField account;
private PasswordField password;
private CheckBox checkBox;
public LoginStage() {
setTitle("五子棋-登錄");
Pane pane = new Pane();
Scene scene = new Scene(pane, 400, 250);
setScene(scene);
getIcons().add(new Image("Imgs/icon-2.jpg"));
pane.setBackground(new Background(new BackgroundFill(Color.GAINSBORO, null, null)));
// 設(shè)置窗口不可變
setResizable(false);
// 標題:用戶登錄
Label title = new Label("用戶登錄");
title.setFont(new Font("KaiTi", 30));
title.setLayoutX(150);
title.setLayoutY(30);
// 賬戶標簽
Label accountLabel = new Label("賬戶 : ");
accountLabel.setFont(new Font("KaiTi", 20));
accountLabel.setLayoutX(80);
accountLabel.setLayoutY(90);
// 賬戶輸入框
account = new TextField();
account.setLayoutX(150);
account.setLayoutY(90);
// 密碼標簽
Label passwordLabel = new Label("密碼 : ");
passwordLabel.setFont(new Font("KaiTi", 20));
passwordLabel.setLayoutX(80);
passwordLabel.setLayoutY(140);
// 密碼輸入框
password = new PasswordField();
password.setLayoutX(150);
password.setLayoutY(140);
// 記住密碼
checkBox = new CheckBox();
checkBox.setLayoutX(80);
checkBox.setLayoutY(180);
Text text = new Text(100, 195,"記住密碼");
text.setFont(new Font("KaiTi",15));
// 登錄按鈕
loginBtn = new Button("登 錄");
loginBtn.setLayoutX(80);
loginBtn.setLayoutY(210);
loginBtn.setDefaultButton(true);
loginBtn.setOnAction(this);
// 注冊按鈕
registerBtn = new Button("注 冊");
registerBtn.setLayoutX(250);
registerBtn.setLayoutY(210);
registerBtn.setOnAction(this);
// 把元素添加到面板
pane.getChildren().addAll(title, accountLabel, account, passwordLabel,
password, loginBtn, registerBtn,checkBox,text);
// 如果用戶剛注冊,則在登錄界面讀取用戶注冊的賬戶和密碼
String content = null;
if (Global.account != null && Global.password != null) {
this.account.setText(Global.account);
this.password.setText(Global.password);
//讀取用戶上一次在本機登錄的賬戶和密碼
}else if ((content=DataHanding.readLastAccount())!=null) {
String[] strings = content.split(",");
if (strings[strings.length-1].equals(Global.myIp)) {
account.setText(strings[0]);
password.setText(strings[1]);
}
}
}
public void login() {
String accountText = account.getText();
String passwordText = password.getText();
Global.account = accountText;
//用戶名或密碼不能為空
if (accountText.equals("")||passwordText.equals("")) {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("用戶名或密碼不能為空");
alert.show();
return;
}
//獲取用戶當前登錄狀態(tài),如果已登錄,不能重復(fù)登錄
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
UserStatus status = statusDAO.selectInfoByAccount(accountText);
if (status.getStatus() == 1) {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("當前用戶已在線,不能重復(fù)登錄");
alert.show();
return;
}
// 獲取UserDAO的實現(xiàn)類
UserDAO userDAO = DAOUtils.getMapper(UserDAO.class);
// 根據(jù)用戶輸入的account獲得User對象
User user = userDAO.getByAccount(accountText);
if (user != null && user.getPassword().equals(passwordText)) {
//登錄成功,判斷用戶是否選擇記住密碼
if (checkBox.isSelected()) {
//用戶選擇記住密碼,調(diào)用記住密碼方法
DataHanding.rememberLastAccount(accountText, passwordText,Global.myIp);
}
//更新用戶狀態(tài)為在線狀態(tài)
statusDAO.updateStatusByAccount(accountText,Global.myIp, 1);
// 已設(shè)置自動提交事務(wù)
// 打開游戲模式選擇界面
new ModeChooseStage().show();
this.close();
} else {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("用戶名或密碼錯誤 ,登錄失敗");
alert.showAndWait();
}
}
@Override
public void handle(ActionEvent event) {
//獲取用戶點擊的按鈕
Button btn = (Button) event.getSource();
String text = btn.getText();
switch (text) {
case "登 錄":
login();
break;
case "注 冊":
new RegisterStage().show();
this.close();
break;
}
}
}
package com.wn.ui;
import com.wn.CommonUtils.DAOUtils;
import com.wn.dao.UserStatusDAO;
import com.wn.pojo.ChessMode;
import com.wn.pojo.Global;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
public class ModeChooseStage extends Stage implements EventHandler<ActionEvent> {
Pane pane;
public ModeChooseStage() {
setTitle("五林大會");
pane = new Pane();
Scene scene = new Scene(pane,460,260);
setScene(scene);
getIcons().add(new Image("Imgs/icon-2.jpg"));
Image image = new Image("Imgs/Gobang.jpg",480,280,false,true,true);
ImageView imageView = new ImageView(image);
pane.getChildren().add(imageView);
pane.setBackground(new Background(
new BackgroundFill(Color.DARKOLIVEGREEN, null, null)));
// 設(shè)置窗口不可變
setResizable(false);
setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
statusDAO.setStatusByAccount(Global.account, 0);
}
});
Label title = new Label("選擇游戲模式");
title.setFont(new Font("KaiTi", 30));
title.setLayoutX(155);
title.setLayoutY(80);
//添加單機版和網(wǎng)絡(luò)部按鈕
Button buttonOnline = new Button("網(wǎng)絡(luò)版");
buttonOnline.setFont(new Font("KaiTi",20));
buttonOnline.setPrefSize(100, 60);
buttonOnline.setLayoutX(280);
buttonOnline.setLayoutY(140);
buttonOnline.setOnAction(this);
Button buttonSingle = new Button("單機版");
buttonSingle.setFont(new Font("KaiTi",20));
buttonSingle.setPrefSize(100, 60);
buttonSingle.setLayoutX(80);
buttonSingle.setLayoutY(140);
buttonSingle.setOnAction(this);
//將按鈕添加進面板
pane.getChildren().addAll(title,buttonOnline,buttonSingle);
}
/**
* 在線模式
* @author Dracarys
*/
public void playOnineVersion() {
new OnlineConfigStage().show();
this.close();
Global.mode = ChessMode.NETWORK;
}
/**
* 單機版按鈕及單機版游戲事件
* @author Dracarys
* @param pane
*/
public void playSingleVersion() {
try {
new GameStage().show();
Global.mode = ChessMode.SINGLE;
this.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void handle(ActionEvent event) {
Button btn = (Button)event.getSource();
switch (btn.getText()) {
case "單機版":
playSingleVersion();
break;
case "網(wǎng)絡(luò)版":
playOnineVersion();
break;
}
}
}
package com.wn.ui;
import java.util.List;
import com.wn.CommonUtils.DAOUtils;
import com.wn.CommonUtils.NetUtils;
import com.wn.CommonUtils.ReceiveThread;
import com.wn.dao.UserStatusDAO;
import com.wn.pojo.ConnectionMessage;
import com.wn.pojo.Global;
import com.wn.pojo.UserStatus;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
public class OnlineConfigStage extends Stage implements EventHandler<ActionEvent> {
private Pane pane; //舞臺面板
private TextField myPort; //本機端口
private TextField conIp; //連接ip
private TextField conPort; //連接端口
private List<UserStatus> list; //在線用戶列表
public OnlineConfigStage(){
setTitle("網(wǎng)絡(luò)連接設(shè)置");
pane = new Pane();
Scene scene = new Scene(pane,600,400);
setScene(scene);
getIcons().add(new Image("Imgs/icon-2.jpg"));
pane.setBackground(new Background(new BackgroundFill(Color.GAINSBORO, null, null)));
// 設(shè)置窗口不可變
setResizable(false);
addElement();
getOnlineUser();
//當用戶退出程序時,將用戶的狀態(tài)設(shè)置為下線
setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
statusDAO.setStatusByAccount(Global.account, 0);
}
});
}
private void addTipsInfo() {
//在線玩家標題提示
Label title = new Label("當前在線玩家");
title.setFont(new Font("KaiTi", 30));
title.setLayoutX(350);
title.setLayoutY(30);
pane.getChildren().add(title);
//在線用戶信息行
String[] tipsInfo = {"用戶名","連接IP","連接PORT"};
for (int i = 0; i < tipsInfo.length; i++) {
Label Lable = new Label(tipsInfo[i]);
Lable.setFont(new Font("KaiTi", 20));
Lable.setLayoutX(280+100*i);
Lable.setLayoutY(80);
pane.getChildren().add(Lable);
}
}
private void addElement() {
//網(wǎng)絡(luò)連接設(shè)置標題提示
Label title = new Label("網(wǎng)絡(luò)連接設(shè)置");
title.setFont(new Font("KaiTi", 30));
title.setLayoutX(50);
title.setLayoutY(30);
//連接地址文本提示
Label myPortLable = new Label("本機端口");
myPortLable.setFont(new Font("KaiTi", 20));
myPortLable.setLayoutX(60);
myPortLable.setLayoutY(90);
//添加本機端口輸入框
myPort = new TextField();
myPort.setLayoutX(60);
myPort.setLayoutY(120);
myPort.setText("6666"); //測試port
//連接端口文本提示
Label conIPLable = new Label("連接IP");
conIPLable.setFont(new Font("KaiTi", 20));
conIPLable.setLayoutX(60);
conIPLable.setLayoutY(160);
//添加連接IP輸入框
conIp = new TextField();
conIp.setLayoutX(60);
conIp.setLayoutY(190);
conIp.setText("192.172.4.8"); //測試ip
//連接端口文本提示
Label conPortLable = new Label("連接端口");
conPortLable.setFont(new Font("KaiTi", 20));
conPortLable.setLayoutX(60);
conPortLable.setLayoutY(230);
//添加連接PORT輸入框
conPort = new TextField();
conPort.setLayoutX(60);
conPort.setLayoutY(260);
conPort.setText("6666"); //測試port
//添加確定和取消按鈕
Button conBtn = new Button("連 接");
conBtn.setPrefSize(150, 30);
conBtn.setLayoutX(60);
conBtn.setLayoutY(320);
conBtn.setOnAction(this);
Button canBtn = new Button("取 消");
canBtn.setPrefSize(150, 25);
canBtn.setLayoutX(60);
canBtn.setLayoutY(360);
canBtn.setOnAction(this);
pane.getChildren().addAll(title,myPortLable,myPort,conIPLable,conIp,conPortLable,conPort,conBtn,canBtn);
}
private void getOnlineUser() {
//獲取UserStatusDAO實現(xiàn)類
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
//獲取在線用戶列表
list = statusDAO.selectInfoByStatus(1);
if (list.size()<=1) {
return;
}
addTipsInfo();
int i = 0;
for (UserStatus userStatus : list) {
if (!userStatus.getAccount().equals(Global.account)) {
//添加用戶名信息
Label accountLable = new Label(userStatus.getAccount());
accountLable.setFont(new Font("KaiTi", 20));
accountLable.setLayoutX(290);
accountLable.setLayoutY(120+60*i);
//添加ip信息
Label ipLable = new Label(userStatus.getIp());
ipLable.setFont(new Font("KaiTi", 20));
ipLable.setLayoutX(360);
ipLable.setLayoutY(120+60*i);
//添加port信息
Label portLable = new Label(userStatus.getPort()+"");
portLable.setFont(new Font("KaiTi", 20));
portLable.setLayoutX(500);
portLable.setLayoutY(120+60*i);
pane.getChildren().addAll(accountLable,ipLable,portLable);
i++;
}
}
}
public void connect(){
//當網(wǎng)絡(luò)連接信息未填寫時,彈窗提醒
if (myPort.getText().equals("") || conIp.getText().equals("") || conPort.getText().equals("")) {
Alert alert = new Alert(AlertType.INFORMATION,"網(wǎng)絡(luò)連接信息不能為空");
alert.initOwner(this);
alert.showAndWait();
return;
}
//如果沒有在線用戶時,無法正常連接
if (list.size()<=1) {
Alert alert = new Alert(AlertType.INFORMATION,"當前無在線用戶,無法進行連接");
alert.initOwner(this);
alert.showAndWait();
return;
}
//嘗試將用戶輸入的端口信息轉(zhuǎn)為int型
try {
Global.myPort = Integer.parseInt(myPort.getText());
Global.oppoIp = conIp.getText();
Global.oppoPort = Integer.parseInt(conPort.getText());
} catch (NumberFormatException e1) {
e1.printStackTrace();
return;
}
try {
//啟動線程,接收客戶端連接
ReceiveThread receiveThread = new ReceiveThread();
Thread thread = new Thread(receiveThread);
thread.start();
new GameStage().show();
//將用戶輸入的本機端口保存到數(shù)據(jù)庫
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
statusDAO.updatePortByAccount(Global.account, Global.myPort);
//向服務(wù)端發(fā)送連接請求信息,連接信息中包含客戶端的用戶
NetUtils.sendMessage(new ConnectionMessage(Global.account));
} catch (InterruptedException e) {
e.printStackTrace();
}
this.close();
}
@Override
public void handle(ActionEvent event) {
Button btn = (Button)event.getSource();
switch (btn.getText()) {
case "連 接":
connect();
break;
case "取 消":
new ModeChooseStage().show();
this.close();
break;
}
}
}
package com.wn.ui;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import com.wn.CommonUtils.DAOUtils;
import com.wn.CommonUtils.NetUtils;
import com.wn.dao.UserStatusDAO;
import com.wn.pojo.ButtonMessage;
import com.wn.pojo.ChessMessage;
import com.wn.pojo.ChessMode;
import com.wn.pojo.ConnectionMessage;
import com.wn.pojo.Global;
import com.wn.pojo.Message;
import com.wn.pojo.MessageTimes;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
public class GameStage extends Stage implements EventHandler<ActionEvent> {
private List<Circle> chessList = new ArrayList<Circle>();
private boolean isBlack = true; //判斷棋子是否是黑色
private boolean gameOver = false; //判斷游戲是否結(jié)束 true游戲結(jié)束,false代表可以下棋
private int topMargin = 50;//上間距
private int leftMargin = 350;//上間距
private int gap = 50; //內(nèi)間距
private int chessWidth = 1400; //棋盤寬
private int chessHeight = 830; //棋盤高
private int size = 15; //棋盤線條數(shù)
private int buttonWidth = 80; //按鈕寬
private int buttonLength = 40; //按鈕高
private int cicreRadius = 18; //棋子半徑
private int i = 0; //打開棋譜記錄當前是第幾手棋
private boolean canPlay = false; //定義網(wǎng)絡(luò)模式下,當前是否能下棋
Pane pane;
public GameStage() throws InterruptedException {
//設(shè)置窗口不可改變
// setResizable(false);
//創(chuàng)建面板對象
pane = new Pane();
//創(chuàng)建場景對象
Scene scene = new Scene(pane,chessWidth,chessHeight);
//將場景放進舞臺
setScene(scene);
getIcons().add(new Image("Imgs/icon-2.jpg"));
Image image = new Image("Imgs/background.jpg",1400,830,false,true,true);
ImageView imageView = new ImageView(image);
pane.getChildren().add(imageView);
setTitle("五子棋");
drawLine();
addButton();
Global.gameStage = this;
playChess();
//當用戶退出程序時,將用戶的狀態(tài)設(shè)置為下線
setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
exitGame();
}
});
}
/**
* 根據(jù)接收到的Message對象,更新棋子信息
* @author Dracarys
*/
public void updateUI(Message message) {
if (message instanceof ConnectionMessage) {
ConnectionMessage conMessage = (ConnectionMessage)message;
if (conMessage.getClientUser()!=null) {
//有客戶端連接時,彈窗提醒
Alert alert = new Alert(AlertType.CONFIRMATION,conMessage.getClientUser()+"已進入戰(zhàn)斗!!");
alert.initOwner(this);
alert.show();
}
//接收到的消息為棋子信息消息
}else if (message instanceof ChessMessage) {
ChessMessage chessMessage = (ChessMessage)message;
double x = chessMessage.getX();
double y = chessMessage.getY();
double pieceX = x * gap + leftMargin;
double pieceY = y * gap + topMargin;
dropPiece(pieceX, pieceY);
//接收到消息之后將標簽改完false
canPlay = false;
return;
//接收到的消息為按鈕信息消息
}else if (message instanceof ButtonMessage) {
ButtonMessage btnMessage = (ButtonMessage)message;
String btnName = btnMessage.getBtnName();
switch (btnName) {
case "新 局":
//接收到點擊新局按鈕的第一次消息
if (btnMessage.getTimes().equals(MessageTimes.FIRST)) {
//彈窗提醒,讓接收方選擇是否同意開啟新局
Alert alert = new Alert(AlertType.CONFIRMATION,"對方想開啟新局");
alert.initOwner(this);
alert.showAndWait();
//如果用戶選擇同意開啟新局,則開啟新局
if (alert.getResult() == ButtonType.OK) {
startGame();
//然后用戶不同意開啟新局,將按鈕消息的同意情況更改為false
}else {
//不同意,更改isAgree為false
btnMessage.setAgree(false);
}
//向新局發(fā)起方發(fā)送消息
btnMessage.setTimes(MessageTimes.SECOND);
NetUtils.sendMessage(btnMessage);
}else if (btnMessage.getTimes().equals(MessageTimes.SECOND)) {
Alert alert = new Alert(AlertType.INFORMATION);
//對方不同意開啟新局
if (!btnMessage.isAgree()) {
alert.setContentText("對方不同意開啟新局");
alert.initOwner(this);
alert.showAndWait();
}else {
alert.setContentText("對方同意開啟新局");
alert.initOwner(this);
alert.showAndWait();
startGame();
}
}
break;
case "悔 棋":
//第一次接收到悔棋消息時,讓接收方選擇是否悔棋
if (btnMessage.getTimes().equals(MessageTimes.FIRST)) {
//彈窗提醒,通過同意,就開啟新局
Alert alert = new Alert(AlertType.CONFIRMATION,"對方想悔棋");
alert.showAndWait();
//如果用戶選擇同意悔棋,則直接悔棋,并發(fā)送消息
if (alert.getResult() == ButtonType.OK) {
regretChess();
canPlay = false;
//然后用戶不同意悔棋,將按鈕消息的同意情況更改為false
}else {
//不同意,更改isAgree為false
btnMessage.setAgree(false);
}
//向悔棋發(fā)起方發(fā)送消息
btnMessage.setTimes(MessageTimes.SECOND);
NetUtils.sendMessage(btnMessage);
}else if (btnMessage.getTimes().equals(MessageTimes.SECOND)) {
Alert alert = new Alert(AlertType.INFORMATION);
//對方不同意悔棋
if (!btnMessage.isAgree()) {
alert.setContentText("對方不同意悔棋");
alert.show();
}else {
regretChess();
canPlay = false;
Global.regretTimes = 1;
}
}
break;
}
}
}
/**
* 保存棋譜
* @author Dracarys
* @param pane
*/
public void saveChessRecord() {
if (!gameOver) {
return;
}
//創(chuàng)建打開文件彈窗對象
FileChooser fileChooser = new FileChooser();
//設(shè)置彈窗標題
fileChooser.setTitle("保存棋譜");
//設(shè)置默認目錄
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
//設(shè)置文件后綴
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("csv", "*.csv"));
//打開文件保存對話框
File file = fileChooser.showSaveDialog(this);
//點擊取消,則文件為空,停止保存
if (file == null) {
return;
}
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
for (Circle chess : chessList) {
bw.write(chess.getCenterX()+","+chess.getCenterY()+","+chess.getFill());
bw.newLine();
}
Alert alert = new Alert(AlertType.INFORMATION);
alert.setContentText("保存棋譜成功");
alert.show();
} catch (Exception e) {
}
}
/**
* 打開棋譜
* @author Dracarys
* @param pane
*/
public void openChessScore() {
//打開棋譜僅在單機版有效,網(wǎng)絡(luò)版點擊彈框提醒
if (Global.mode.equals(ChessMode.NETWORK)) {
Alert alert = new Alert(AlertType.INFORMATION,"該功能僅在單機版有效");
alert.showAndWait();
return;
}
//創(chuàng)建打開文件彈窗對象
FileChooser fc = new FileChooser();
//設(shè)置對象標題
fc.setTitle("打開棋譜");
//設(shè)置打開的初始路徑
fc.setInitialDirectory(new File(System.getProperty("user.home")));
//設(shè)置打開的文件默認后綴
fc.getExtensionFilters().add(new FileChooser.ExtensionFilter("csv", "*.csv"));
//將選擇的文件賦值給file對象
File file = fc.showOpenDialog(this);
//如果文件為空則不繼續(xù)打開
if (file == null) {
return;
}
//打開棋譜時情況棋子集合和棋盤上的棋子
chessList.clear();
pane.getChildren().removeIf(c-> c instanceof Circle);
i = 0;
//創(chuàng)建緩沖流,讀取棋譜文件
try (BufferedReader br = new BufferedReader(new FileReader(file)) ) {
String content;
while ((content = br.readLine())!=null) {
String[] list = content.split(",");
//根據(jù)文件棋子信息創(chuàng)建棋子
Circle chess = new Circle(Double.parseDouble(list[0]), Double.parseDouble(list[1]), cicreRadius,Color.web(list[2]));
//將棋子添加進集合
chessList.add(chess);
}
} catch (Exception e) {
}
//產(chǎn)生三個按鈕控制棋譜讀取
String[] list = {"<",">","x"};
for (int i = 0; i < list.length; i++) {
Button btn = new Button(list[i]);
btn.setPrefSize(30, 30);
btn.setLayoutX(1060);
btn.setLayoutY(300+60*i);
btn.setOnAction(this);
pane.getChildren().add(btn);
}
}
/**
* 退出游戲
* @author Dracarys
* @param pane
*/
public void exitGame() {
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("退出游戲確認");
alert.setHeaderText("點擊確認按鈕將退出游戲程序");
alert.setContentText("您確認退出嗎?");
alert.showAndWait();
if (alert.getResult() == ButtonType.OK) {
if (Global.mode.equals(ChessMode.SINGLE)) {
System.exit(0);
}else {
UserStatusDAO statusDAO = DAOUtils.getMapper(UserStatusDAO.class);
statusDAO.setStatusByAccount(Global.account, 0);
System.exit(0);
}
}
}
/**
* 悔棋
* @author Dracarys
*/
public void regretChess() {
if (chessList.size()>=1&&gameOver == false) {
chessList.remove(chessList.size()-1);
pane.getChildren().remove(pane.getChildren().size()-1);
isBlack = !isBlack;
}
}
/**
* 開始游戲事件
* @author Dracarys
* @param pane
*/
public void startGame() {
// if (!gameOver) {
// Alert alert = new Alert(AlertType.INFORMATION,"游戲還未結(jié)束不能開始新局");
// alert.showAndWait();
// return;
// }
gameOver = false;
isBlack = true;
chessList.clear();
pane.getChildren().removeIf(c -> c instanceof Circle);
}
/**
* 添加棋盤線
* @author Dracarys
* @param pane
*/
public void drawLine() {
//棋盤700*700,橫豎14條線,每條先間隔50
//第一條橫線起點(350,50),y起點(1050,50);第一條豎線x起點(350,50),y起點(350,650)
//第二條橫線x起點(350,100),y起點(1050,100);第二條豎線x起點(1050,50),y起點(1050,650)
for (int i = 0; i < size; i++) {
//橫線
Line line1 = new Line(leftMargin, topMargin+i*gap, leftMargin+50*14, topMargin+i*gap);
//豎線
Line line2 = new Line(leftMargin+i*gap, topMargin, leftMargin+i*gap, topMargin+50*14);
pane.getChildren().add(line1);
pane.getChildren().add(line2);
}
}
//添加按鈕
public void addButton() {
String[] btnList = {"新 局","保存棋譜","打開棋譜","悔 棋","退 出"};
for (int i = 0; i < btnList.length; i++) {
Button button = new Button(btnList[i]);
button.setPrefSize(buttonWidth, buttonLength);
button.setLayoutX(leftMargin+75*i+buttonWidth*i);
button.setLayoutY(size*topMargin+20);
button.setOnAction(this);
pane.getChildren().add(button);
}
}
/**
* 在鼠標點擊的位置落子
* @author Dracarys
* @param pane
*/
public void playChess() {
pane.setOnMouseClicked(e -> {
//網(wǎng)絡(luò)版:判斷用戶是否已發(fā)送棋子消息,如果已發(fā)送
if (canPlay) {
return;
}
if (gameOver) {
return;
}
double x = e.getX(); //獲取用戶鼠標點擊的坐標x值
double y = e.getY(); //獲取用戶鼠標點擊的坐標y值
//左上頂點坐標(50,50),棋子半徑為18,邊界為坐標-棋子半徑
//右下頂點坐標(750,750),棋子半徑為18,邊界為坐標+棋子半徑
if (x < leftMargin - cicreRadius || x > chessWidth - leftMargin + cicreRadius) {
return;
}
if (y < topMargin - cicreRadius || y > 800 - topMargin + cicreRadius) {
//50-18 = 32
return;
}
//將鼠標的坐標x和y都換算成(1,1)->(14,14)范圍
double xIndex = Math.round(((x - leftMargin) / gap));
double yIndex = Math.round(((y - topMargin) / gap));
//判斷當前鼠標點的位置是否存在棋子
double pieceX = xIndex * gap + leftMargin;
double pieceY = yIndex * gap + topMargin;
if (hasPiece(pieceX, pieceY)) {
return;
}
//落棋子
dropPiece(pieceX,pieceY);
//創(chuàng)建棋子消息對象
if (Global.mode.equals(ChessMode.NETWORK)) {
ChessMessage message = new ChessMessage(xIndex,yIndex);
//發(fā)送棋子消息對象
NetUtils.sendMessage(message);
canPlay = true;
}
});
}
public void dropPiece(double x,double y) {
Circle circle = new Circle();
//設(shè)置棋子落子的坐標
circle.setCenterX(x);
circle.setCenterY(y);
circle.setRadius(cicreRadius);
if (isBlack) {
circle.setFill(Color.BLACK);
} else {
circle.setFill(Color.WHITE);
}
pane.getChildren().add(circle);
isBlack = !isBlack;
Circle chess = new Circle(x, y,cicreRadius, isBlack ? Color.BLACK : Color.WHITE);
chessList.add(chess);
//判斷游戲輸贏
if (isWin(chess)) {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("游戲結(jié)束");
alert.setHeaderText("游戲結(jié)果");
alert.setContentText(isBlack ? "白棋勝" : "黑棋勝");
alert.show();
gameOver = true;
}
}
/** 判斷當前坐標位置是否有棋子
* @author Dracarys
* @param x
* @param y
* @return
*/
private boolean hasPiece(double x,double y) {
//遍歷數(shù)組
for (int i = 0; i < chessList.size(); i++) {
Circle c = chessList.get(i);
if (c.getCenterX() == x && c.getCenterY() == y) {
return true;
}
}
return false;
}
/**
* 判斷是否勝利
* @author Dracarys
* @param chess
* @return
*/
private boolean isWin(Circle chess) {
int count = 1;
//判斷x軸向左邊判斷另外4個位置棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessLeft = new Circle(chess.getCenterX()-i*gap,
chess.getCenterY(),cicreRadius,chess.getFill());
if (contain(chessLeft)) {
count++;
}else {
break;
}
}
//判斷x軸向右邊判斷另外4個位子棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessRight = new Circle(chess.getCenterX()+i*gap,
chess.getCenterY(),cicreRadius,chess.getFill());
if (contain(chessRight)) {
count++;
}else {
break;
}
}
if (count>=5) {
return true;
}
count = 1;
//判讀y軸向上4個位置的棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessTop = new Circle(chess.getCenterX(),
chess.getCenterY()-i*gap,cicreRadius,chess.getFill());
if (contain(chessTop)) {
count++;
}else {
break;
}
}
//判讀y軸向下4個位置的棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessDown = new Circle(chess.getCenterX(),
chess.getCenterY()+i*gap,cicreRadius,chess.getFill());
if (contain(chessDown)) {
count++;
}else {
break;
}
}
if (count>=5) {
return true;
}
count = 1;
//判讀左斜線邊4個棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessTopLeft = new Circle(chess.getCenterX()-i*gap,
chess.getCenterY()-i*gap,cicreRadius,chess.getFill());
if (contain(chessTopLeft)) {
count++;
}else {
break;
}
}
//判讀左斜線邊4個棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessTopRight = new Circle(chess.getCenterX()+i*gap,
chess.getCenterY()+i*gap,cicreRadius,chess.getFill());
if (contain(chessTopRight)) {
count++;
}else {
break;
}
}
if (count>=5) {
return true;
}
count = 1;
//判讀右斜線邊4個棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessDownLeft = new Circle(chess.getCenterX()+i*gap,
chess.getCenterY()-i*gap,cicreRadius,chess.getFill());
if (contain(chessDownLeft)) {
count++;
}else {
break;
}
}
//判讀右斜線邊4個棋子是否同色
for (int i = 1; i < 5; i++) {
Circle chessDownRight = new Circle(chess.getCenterX()-i*gap,
chess.getCenterY()+i*gap,cicreRadius,chess.getFill());
if (contain(chessDownRight)) {
count++;
}else {
break;
}
}
if (count>=5) {
return true;
}
return false;
}
/**
* 判斷棋子集合中是否包含當前棋子
* @author Dracarys
* @param chess
* @return
*/
public boolean contain(Circle chess) {
for (Circle circle : chessList) {
if (circle.getCenterX() == chess.getCenterX() && circle.getCenterY()==chess.getCenterY()&&circle.getFill().equals(chess.getFill())) {
return true;
}
}
return false;
}
/**
* 重寫用戶點擊按鈕的方法
*/
@Override
public void handle(ActionEvent event) {
Button source = (Button) event.getSource();
String text = source.getText();
switch (text) {
case "新 局":
if (Global.mode.equals(ChessMode.SINGLE)) {
startGame();
}else {
ButtonMessage btnMessage = new ButtonMessage(text,true,MessageTimes.FIRST);
NetUtils.sendMessage(btnMessage);
}
break;
case "保存棋譜":
saveChessRecord();
break;
case "打開棋譜":
openChessScore();
break;
case "悔 棋":
if (gameOver) {
return;
}
if (chessList.isEmpty()) {
return;
}
if (Global.regretTimes>=1) {
Alert alert = new Alert(AlertType.INFORMATION,"已經(jīng)悔悔過棋, 不能再次悔棋");
alert.initOwner(this);
alert.show();
return;
}
if (!canPlay) {
Alert alert = new Alert(AlertType.INFORMATION,"不能悔棋");
alert.initOwner(this);
alert.show();
return;
}
if (Global.mode.equals(ChessMode.SINGLE)) {
regretChess();
}else if (Global.mode.equals(ChessMode.NETWORK)) {
System.out.println(Global.regretTimes);
//網(wǎng)絡(luò)模式下,一方選擇悔棋,發(fā)送悔棋消息給另一方,按鈕為名稱為悔棋,發(fā)送方同意悔棋,消息次數(shù)為第一次
NetUtils.sendMessage(new ButtonMessage("悔 棋", true, MessageTimes.FIRST));
}
break;
case "退 出":
exitGame();
break;
case "<":
openChessScoreLast();
break;
case ">":
openChessScoreNext();
break;
case "x":
openChessScoreAll();
break;
}
}
/**
* 打開棋譜,點擊x,顯示所有棋子
* @author Dracarys
*/
private void openChessScoreAll() {
for (int j = i; j < chessList.size(); j++) {
pane.getChildren().add(chessList.get(j));
}
pane.getChildren().remove(36);
pane.getChildren().remove(36);
pane.getChildren().remove(36);
}
/**
* 打開棋譜,點擊上一步按鈕
* @author Dracarys
*/
private void openChessScoreLast() {
if (i == 0) {
return;
}
pane.getChildren().remove(pane.getChildren().size()-1);
i--;
}
/**
* 打開棋譜,點擊下一步按鈕
* @author Dracarys
*/
private void openChessScoreNext() {
if (i == chessList.size()) {
pane.getChildren().remove(36);
pane.getChildren().remove(36);
pane.getChildren().remove(36);
return;
}
Circle chess = chessList.get(i);
pane.getChildren().add(chess);
i++;
}
}
總結(jié)
以上是生活随笔為你收集整理的JavaFx-----五子棋(单机版和对战版)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: adb命令使用bat展示:截屏和双清(清
- 下一篇: 前端脚手架BigFish