Java作业记录一下
游戏分为三部分
Constant里面放一些公共变量
import javafx.scene.image.Image;
import java.awt.*;
public class Game_Constant {
Image image_flag = new Image("file:D:\\IDEA\\MineClearance\\flag.jpg");
Image image_window = new Image("file:D:\\IDEA\\MineClearance\\icon.jpg");
Image image_bomb0 = new Image("file:D:\\IDEA\\MineClearance\\bomb0.jpg");
final int row = 520;
final int col = 585;
protected int Easy_MineNum = 20;
}
MyButton是每个区块(雷or数字or旗子)
import javafx.scene.control.Button;
import java.awt.*;
public class My_Button extends Button {
private boolean flag = false;//是否是雷区
private int pos_x;
private int pos_y;//记录其在坐标系中的位置
public boolean judge_flag = false;//是否遍历过
public void SetPosX(int x){
pos_x = x;
}
public void SetPosY(int y){
pos_y = y;
}
public int GetPosX(){
return pos_x;
}
public int GetPosY(){
return pos_y;
}
public My_Button(String text)
{
super(text);
setPrefWidth(25);
setPrefHeight(5);
}
// private void setPrefHeight(int i) {
// }
//
// private void setPrefWidth(int i) {
// }
public void SetFlag()
{
flag = true;
}
public void ReSetFlag()
{
flag = false;
}
public boolean GetFlag()
{
return flag;
}
}
my_MineClearance是主类
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Orientation;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import java.io.*;
import java.util.Calendar;
public class my_MineClearance extends Application
{
//设置游戏参数
Game_Constant constant = new Game_Constant();
String background_url = "file:D:/IDEA/MineClearance/background.png";
String bomb_url ="file:D:/IDEA/MineClearance/bomb.jpg";
String flag_url ="file:D:/IDEA/MineClearance/flag.jpg";
int i,j;//用于访问buttons[][]
int mine_num = constant.Easy_MineNum;//总雷数
int MineClearNum;// 已清除雷数
int MineSuspected;// 疑似雷数( 个数)
int mine_around = 0;//应用于计算周围雷数时的初始化
//计时器的初始时间
long startTime;
//退出该窗口的flag
//原理是点击在其他窗口点击退出时,将exit_flag设置为ture,再由鼠标移入窗口事件判断,关闭窗口,比较无脑,但好用
boolean exit_flag = false;
//剩余量文本
Text left;
String str1 = "剩余 数:" + (mine_num - MineSuspected);
//设置困难选择
ComboBox<String> comboBox = new ComboBox<>();
//存放周围雷数 数组
int[][] button_aroundMineNum = new int[500 / 25][500 / 25];
AnchorPane root = new AnchorPane();
Scene scene = new Scene(root , constant.row,constant.col);
ScrollBar scrollBar = new ScrollBar();
public static void main(String[] args)
{
Application.launch(args);
}
@Override
public void start(Stage primaryStage)
{
//开始游戏窗口
Stage start_game = new Stage();
start_game.setTitle("GameStart");
start_game.getIcons().addAll(new Image("file:D:\\IDEA\\MineClearance\\bomb.jpg"));
Pane start = new Pane();
Scene start_game_scene = new Scene(start,600,200);
String game_str = "Welcome to my_MineClearance!";
Text Welcome = new Text(200,40,game_str);
Welcome.setStyle("-fx-font-family: 'FangSong';-fx-font-size: 20;-fx-text-fill: pink");
Button game_exit = new Button("Exit");
game_exit.setStyle("-fx-background-color:red");
Button game_start = new Button("Start");
game_start.setStyle("-fx-background-color:lightblue");
game_exit.setOnMouseClicked(event -> {
start_game.close();
});
game_start.setOnMouseClicked(event -> {
//设置开始时间
startTime = System.currentTimeMillis();
//开启计时器
//ps:myTask是计时器,会实时显示在窗口内
//而startTime endTime只是时刻,游戏胜利结束时才会用到
myTask.start();
start_game.close();
primaryStage.show();
});
//装两个按钮
FlowPane flowPane = new FlowPane();
flowPane.setLayoutX(220);
flowPane.setLayoutY(170);
flowPane.getChildren().addAll(game_exit,game_start);
flowPane.setHgap(150);//内横距
start.setStyle("-fx-background-image: url(" + background_url + "); " +
"-fx-background-position: center center; " +
"-fx-background-size: 600 200; " +
"-fx-background-repeat: stretch;" +
"-fx-background-color: transparent;");
start.getChildren().addAll(Welcome,flowPane);
start_game.setScene(start_game_scene);
start_game.show();
//历史记录窗口
Stage History_stage = new Stage();
History_stage.setTitle("History_list");
Pane History_pane = new Pane();
Scene History_scene = new Scene(History_pane,350,200);
Button History_close = new Button("Close");
History_close.setLayoutX(130);
History_close.setLayoutY(175);
History_close.setOnMouseClicked(event -> {
ReStart();
History_stage.close();
});
scrollBar.setLayoutX(335);
scrollBar.setLayoutY(10);
scrollBar.setPrefHeight(180);
scrollBar.setOrientation(Orientation.VERTICAL);
scrollBar.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
vBox.setLayoutY(- newValue.doubleValue());
}
});
History_pane.getChildren().addAll(History_close,vBox,scrollBar);
History_stage.setScene(History_scene);
addHistory_List();
primaryStage.setTitle("Mine_Clearance");
//设置窗口图标
primaryStage.getIcons().addAll(constant.image_window);
//历史记录按钮
Button ShowHistory = new Button("History_List");
ShowHistory.setOnMouseClicked(event -> {
myTask.cancel();
History_stage.show();
});
ShowHistory.setStyle("-fx-background-color:lightgreen");
ShowHistory.setLayoutX(400);
ShowHistory.setLayoutY(60);
//设置退出按钮,在这里设置的原因是便于操控primaryStage
Button exit = new Button("Exit");
exit.setOnMouseClicked(event -> {
primaryStage.close();
});
exit.setStyle("-fx-background-color:lightblue");
exit.setMinSize(20,20);
exit.setLayoutX(50);
exit.setLayoutY(70);
gridPane.setVgap(1);
gridPane.setHgap(1);
gridPane.setLayoutY(100);
root.getChildren().addAll(gridPane,exit,ShowHistory);
//添加顶层组件
addTopCom();
AddButton();//添加布局按钮
AddMine();//随机添加雷
aroundMineNum();//周围雷数数组
addCom();//添加组件
//退出窗口,原理在exit_flag处说明
scene.setOnMouseEntered(event -> {
if(exit_flag) primaryStage.close();
});
primaryStage.setScene(scene);
}
//显示🚩剩余量
public void ShowMineLeft()
{
str1 = "剩余 数:" + (mine_num - MineSuspected);
left.setText(str1);
}
//重新开始游戏
public void ReStart()
{
//初始化剩余雷数文本
str1 = "剩余 数:" + (mine_num - MineSuspected);
//初始化开始时间
startTime = System.currentTimeMillis();
//对于buttons[][]进行初始化
for(i = 0;i < 20 ; i++){
for(j = 0;j < 20 ;j++){
buttons[i][j].setText("");
buttons[i][j].ReSetFlag();
buttons[i][j].setStyle("-fx-background-color:lightgray");
button_aroundMineNum[i][j] = 0;
}
}
//初始化 已清除雷数 和 疑似雷数(🚩)
MineClearNum = 0;
MineSuspected = 0;
//清除按钮
for(i = 0;i < 500 / 25 ; i ++)
{
for(j = 0;j < 500 / 25 ; j++) {
gridPane.getChildren().removeAll(buttons[i][j]);
}
}
//清除爆炸雷图片
gridPane.getChildren().removeAll(bomb_image);
root.getChildren().removeAll(left);//避免Restart后导致mine_left的覆盖
//计时器重新开始
myTask.restart();
//右上角🚩可见
top_flag.setVisible(true);
//加入按钮
AddButton();
//随机添加雷
AddMine();
//构造周围雷数数组
aroundMineNum();
//显示🚩数
ShowMineLeft();
//重新加入mine_left(剩余🚩数)
addCom();
}
//判断 除了雷区的按钮是否都被点击了
public boolean CheckWin_EveryButtonClicked_butMine()
{
boolean result = true;
for(i = 0;i < 20 ; i ++){
for( j = 0; j < 20 ;j ++){
if(!buttons[i][j].GetFlag())
result = result & buttons[i][j].isMouseTransparent();
}
}
return result;
}
String level;
String YMD_Time;//year month day
//游戏胜利
public void CheckWin()
{
if(MineClearNum == mine_num && CheckWin_EveryButtonClicked_butMine()){
//计时器暂停
myTask.cancel();
//获取所用时间
long endTime = System.currentTimeMillis();
long usedTime = (endTime - startTime)/1000;
//时间( 时:分:秒 )格式化
long Hour,Minute,Second;
Second = usedTime%60;
Minute = ((usedTime - Second)/60)%60;
Hour = (usedTime - Second - Minute*60)/3600;
String usedTime_str = "UsedTime: " + Hour +" h: " +Minute +" m: " + Second +" s";
Text time_txt = new Text(130,66,usedTime_str);
Stage wingame = new Stage();
wingame.setTitle("GAME_WINNING");
Pane win = new Pane();
Scene win_scene = new Scene(win,400,100);
String win_str = "YOU WIN!!";
Text win_txt = new Text(160,40,win_str);
//游戏结束,所有按钮不可再更改
for(i = 0;i < 20 ; i++){
for(j = 0;j < 20 ;j++){
buttons[i][j].setMouseTransparent(true);
}
}
//退出游戏按钮
Button win_exit = new Button();
win_exit.setText("EXIT");
win_exit.setOnMouseClicked(event -> {
wingame.close();
exit_flag = true;
});
//重新开始按钮
Button win_restart = new Button();
win_restart.setText("Restart");
win_restart.setOnMouseClicked(event -> {
wingame.close();
ReStart();
});
//装两个按钮
FlowPane flowPane = new FlowPane();
flowPane.getChildren().addAll(win_exit,win_restart);
flowPane.setPadding(new Insets(70));//外边距
flowPane.setHgap(150);//内横距
//加入节点
win.getChildren().addAll(win_txt,time_txt,flowPane);
wingame.setScene(win_scene);
wingame.show();
//对历史记录txt写入
try {
File History_List_file = new File("D:\\IDEA\\MineClearance\\History_List.txt");
FileWriter fileWriter = new FileWriter(History_List_file,true);//对历史记录以追加形式进行写入
if(mine_num == 20) level = "Easy";
else if(mine_num == 40) level = "Simply";
else if(mine_num == 60) level = "Hard";
Calendar now = Calendar.getInstance();
YMD_Time = "" +now.get(Calendar.YEAR) +"."+now.get(Calendar.MONTH)+"."+now.get(Calendar.DAY_OF_MONTH)+
" "+now.get(Calendar.HOUR_OF_DAY)+":"+now.get(Calendar.MINUTE)+":"+now.get(Calendar.SECOND);
fileWriter.write( "时间:" + YMD_Time + " 难度:" + level + " 用时:" + usedTime + "s\r\n");
fileWriter.flush();
fileWriter.close();
}catch (IOException e) {
e.printStackTrace();
}
addHistory_List();//写入history_list
}
}
VBox vBox = new VBox();
//历史记录显示,按行读取文件
public void addHistory_List()
{
try {
vBox.getChildren().clear();//清除之前的记录
File History_List = new File("D:\\IDEA\\MineClearance\\History_List.txt");
FileReader fr = new FileReader(History_List);
BufferedReader bf = new BufferedReader(fr);
String str;
// 按行读取字符串
while ((str = bf.readLine()) != null) {
Label label = new Label(str);
vBox.getChildren().add(label);
}
bf.close();
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//游戏结束
public void GameOver()
{
//计时器暂停
myTask.cancel();
//右上角旗帜🚩消失
top_flag.setVisible(false);
//弹窗
Stage gameover = new Stage();
gameover.setTitle("GAME_OVER");
Pane over = new Pane();
Scene over_scene = new Scene(over,400,100);
String over_str = "YOU LOSE!";
Text over_txt = new Text(160,40,over_str);
//退出游戏按钮
Button over_exit = new Button();
over_exit.setText("EXIT");
over_exit.setOnMouseClicked(event -> {
gameover.close();
exit_flag = true;
});
//重新开始按钮
Button over_restart = new Button();
over_restart.setText("Restart");
over_restart.setOnMouseClicked(event -> {
gameover.close();
ReStart();
});
//用于装上面两个按钮
FlowPane flowPane = new FlowPane();
flowPane.getChildren().addAll(over_exit,over_restart);
flowPane.setPadding(new Insets(70));//外边距
flowPane.setHgap(150);//内横距
//游戏结束,所有按钮不可再更改
for(i = 0;i < 20 ; i++){
for(j = 0;j < 20 ;j++){
buttons[i][j].setMouseTransparent(true);
//游戏输了,将剩余雷的位置显现出来
if(buttons[i][j].GetFlag()) buttons[i][j].setStyle("-fx-background-image: url(" + bomb_url + "); " +
"-fx-background-position: center center; " +
"-fx-background-size: 25 25; " +
"-fx-background-repeat: stretch;" +
"-fx-background-color: transparent;");
}
}
//加入节点
over.getChildren().addAll(over_txt,flowPane,left);
gameover.setScene(over_scene);
gameover.show();
}
//计时器的对象
MyTask myTask = new MyTask();
//用于显示右上角的旗帜🚩(装图片并显示)
ImageView top_flag = new ImageView();
//添加顶层组件,不会随游戏更新而被覆盖,包括但不仅限于针对需要更新文本的节点
private void addTopCom(){
//重新开始的按钮
Button restart = new Button();
restart.setText("ReStart");
restart.setOnMouseClicked(event -> {
ReStart();
});
restart.setMinSize(10,30);
restart.setStyle("-fx-background-color:lightpink");
restart.setLayoutX(50);
restart.setLayoutY(10);
//选择困难程度的按钮
comboBox.getItems().addAll("Easy","Simple","Hard");
comboBox.setLayoutX(200);
comboBox.setLayoutY(10);
comboBox.setPromptText("Click to Choose Level");
comboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
root.getChildren().removeAll(left);//避免Restart后导致mine_left的覆盖
String str = comboBox.getValue();
switch (str) {
case "Easy":
mine_num = constant.Easy_MineNum;
ReStart();
break;
case "Simple":
mine_num = constant.Easy_MineNum * 2;
ReStart();
break;
case "Hard":
mine_num = constant.Easy_MineNum * 3;
ReStart();
break;
}
});
//利用计时器,显示游戏所用时间
Label usedTime = new Label();
Label usedTimeTitle = new Label();
usedTime.textProperty().bind(myTask.messageProperty());
usedTimeTitle.setText("UsedTime: ");
usedTime.setLayoutX(250);
usedTime.setLayoutY(50);
usedTimeTitle.setLayoutY(50);
usedTimeTitle.setLayoutX(190);
//右上角显示一个旗帜🚩(疑似雷剩余量)
top_flag.setImage(constant.image_flag);
top_flag.setLayoutX(425);
top_flag.setLayoutY(35);
//加入到root中
root.getChildren().addAll(restart,comboBox,usedTime,usedTimeTitle,top_flag);
}
//添加组件
private void addCom()
{
//剩余疑似雷(🚩)数显示
left = new Text(400,50,str1);
root.getChildren().addAll(left);
}
//添加游戏区,使用gridPane的原因是有 i, j 便于和按钮数组buttons[][]结合存按钮
GridPane gridPane = new GridPane();
//按钮数组
My_Button[][] buttons = new My_Button[500 / 25][500 / 25];
//用于存储爆炸的雷的图片并显示
ImageView bomb_image = new ImageView();
//鼠标点击按钮事件的处理
class MouseClickedEvent implements EventHandler<MouseEvent>{
public void handle(MouseEvent event){
My_Button my_button = (My_Button) event.getSource();
MouseButton mouseButton = event.getButton();
if(mouseButton == MouseButton.SECONDARY)//右键
{
if(my_button.getText().equals(" ")){//第二次右键该点(取消判断)
my_button.setStyle("-fx-background-color:white");
my_button.setText("");
MineSuspected --;
ShowMineLeft();
if(buttons[my_button.GetPosX()][my_button.GetPosY()].GetFlag()){//该点是雷
MineClearNum --;//排雷数减一
}
}
else {//第一次右键该点
if(MineSuspected < mine_num) {//疑似点数不能多余总雷数
//设置🚩背景
my_button.setStyle("-fx-background-color:lightgray");
my_button.setStyle("-fx-background-image: url(" + flag_url + "); " +
"-fx-background-position: center center; " +
"-fx-background-size: 25 25; " +
"-fx-background-repeat: stretch;" +
"-fx-background-color: transparent;");
// 右键时,按钮的文本设置为“ ”的原因是避免遮挡背景图片🚩
my_button.setText(" ");
MineSuspected++;//疑似雷数加一
ShowMineLeft();
if (buttons[my_button.GetPosX()][my_button.GetPosY()].GetFlag()) {//该点是雷
MineClearNum++;//排雷数加一
CheckWin();
}
}
}
}
else if(mouseButton == MouseButton.PRIMARY) {//左键
if (!my_button.getText().equals(" ")) {
if (!buttons[my_button.GetPosX()][my_button.GetPosY()].GetFlag()) {//非雷区
my_button.setStyle("-fx-background-color:lightgray");
//不是雷,且周围没有雷
if(button_aroundMineNum[my_button.GetPosX()][my_button.GetPosY()] == 0) my_button.setText("");
//不是雷但周围有雷
else my_button.setText(Integer.toString(button_aroundMineNum[my_button.GetPosX()][my_button.GetPosY()]));
OpenAroundButton(my_button);//递归打开这个按钮的周围格子,实际上只有周围没有雷的时候才会有用
} else {//是雷
//将爆炸的雷添加到该处
bomb_image.setImage(constant.image_bomb0);
bomb_image.setFitWidth(25);
bomb_image.setFitHeight(25);
gridPane.add(bomb_image,my_button.GetPosX(),my_button.GetPosY());
//游戏结束
GameOver();
}
}
else {//右键时左键,清除🚩
if (!buttons[my_button.GetPosX()][my_button.GetPosY()].GetFlag()) {//非雷区
MineSuspected--;
//更新剩余🚩数
ShowMineLeft();
my_button.setStyle("-fx-background-color:lightgray");
//下面操作同左键
if(button_aroundMineNum[my_button.GetPosX()][my_button.GetPosY()] == 0) my_button.setText("");
else my_button.setText(Integer.toString(button_aroundMineNum[my_button.GetPosX()][my_button.GetPosY()]));
OpenAroundButton(my_button);
} else {
//同点击到雷,游戏结束
bomb_image.setImage(constant.image_bomb0);
bomb_image.setFitWidth(25);
bomb_image.setFitHeight(25);
gridPane.add(bomb_image,my_button.GetPosX(),my_button.GetPosY());
GameOver();
}
}
}
}
}
//建立每个按钮的鼠标点击事件
MouseClickedEvent mouseClickedEvent = new MouseClickedEvent();
//向GridPane中添加按钮
void AddButton(){
for(i = 0;i < 500 / 25 ; i ++)
{
for(j = 0;j < 500 / 25 ; j++)
{
My_Button my_button = new My_Button("");
//绑定鼠标点击事件
my_button.setOnMouseClicked(mouseClickedEvent);
my_button.setStyle("-fx-background-color:white");
//设置按钮对应位置,以便点击的按钮my_button可以与按钮数组buttons[][]进行交互
my_button.SetPosX(i);
my_button.SetPosY(j);
buttons[i][j] = my_button;
gridPane.add(my_button,i,j);
}
}
}
//添加雷
void AddMine(){
int m,n;
for(int count = 0;count < mine_num ;)
{
m = (int)(Math.random()*500/25);
n = (int)(Math.random()*500/25);
if(!buttons[m][n].GetFlag())//没有雷就设雷
{
buttons[m][n].SetFlag();
count++;
}
}
}
//周围雷数 数组赋值
void aroundMineNum() {
for (i = 0; i < 500 / 25; i++) {
for (j = 0; j < 500 / 25; j++) {
mine_around = 0;
if (buttons[i][j].GetFlag()) continue;
if (i > 0 && j > 0 && buttons[i - 1][j - 1].GetFlag()) mine_around++;
if (i > 0 && buttons[i - 1][j].GetFlag()) mine_around++;
if (i > 0 && j < 19 && buttons[i - 1][j + 1].GetFlag()) mine_around++;
if (j > 0 && buttons[i][j - 1].GetFlag()) mine_around++;
if (j < 19 && buttons[i][j + 1].GetFlag()) mine_around++;
if (i < 19 && j > 0 && buttons[i + 1][j - 1].GetFlag()) mine_around++;
if (i < 19 && buttons[i + 1][j].GetFlag()) mine_around++;
if (i < 19 && j < 19 && buttons[i + 1][j + 1].GetFlag()) mine_around++;
button_aroundMineNum[i][j] = mine_around;
}
}
}
void OpenAroundButton(My_Button btn)//递归展开
{
btn.setStyle("-fx-background-color:lightgray");
btn.setOpacity(100);//设置透明度
// btn.setDisable(true);
btn.setMouseTransparent(true);//不可再被点击
if(button_aroundMineNum[btn.GetPosX()][btn.GetPosY()] == 0)
{
for(int p = -1; p < 2 && p + btn.GetPosX() < 20; p++){
if(btn.GetPosX() + p < 0) continue;
for(int q = -1; q < 2 && q + btn.GetPosY() < 20; q++){
if(btn.GetPosY() + q < 0) continue;
if(p == 0 && q == 0) continue;//自己
btn.judge_flag = true;//是否判断过,避免进入死循环
My_Button new_btn = buttons[p + btn.GetPosX()][q + btn.GetPosY()];
if(!new_btn.judge_flag)
OpenAroundButton(new_btn);
}
}
}
else btn.setText(button_aroundMineNum[btn.GetPosX()][btn.GetPosY()]+"");//边界显示
}
//计时器
static class MyTask extends Service<Void> {
@Override
protected Task<Void> createTask() {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
for (int i = 0; i <=100000 ; i++) {
updateMessage(Integer.toString(i));
Thread.sleep(1000);
}
return null;
}
};
}
}
}
还有个history_list.txt文件是记录游戏历史的
所有的图片路径引用时需修改成自己的
level只是改变了雷的数量
可根据需要改界面大小