效果图:
包含: 登录 保存 读取存档 暂停游戏 继续游戏 增加难度 减少难度的功能
目录结构如下:
部分代码如下:
package save;
import entity.SnakeCell;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class SaveGame implements Serializable {
public SaveCell food;
public int score;
public int speed;
public List<SaveCell> cells;
public SaveGame(SnakeCell food, int score, int speed, List<SnakeCell> cells){
this.food = new SaveCell(food.getX(), food.getY(), food.getCellLen(), food.getShade());
this.score = score;
this.cells = new ArrayList<>();
for(SnakeCell cell : cells){
this.cells.add(new SaveCell(cell.getX(), cell.getY(), cell.getCellLen(), cell.getShade()));
}
this.speed = speed;
}
}
package view;
import constant.Const;
import entity.SnakeCell;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.paint.Color;
import javafx.util.Pair;
import save.SaveCell;
import save.SaveGame;
import task.MoveTask;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
public class SnakePane extends Pane {
public MediaPlayer mp;
private boolean isPaused = false;
private SnakeCell food;
private Canvas canvas;
private GraphicsContext context;
private List<SnakeCell> cells = Const.SNAKE_CELL_LIST;
private int maxScore = 0;
private int score = 0;
private int speed = 200;
private Button startButton;
private Button pauseButton;
private Button saveButton;
private Button readButton;
private Button upBotton;
private Button downBotton;
private volatile static boolean isRequestClose = false;
private MoveTask task;
public void setSpeed(int speed) {
this.speed = speed;
}
public int getSpeed() {
return speed;
}
public SnakePane() {
canvas = new Canvas(800, 800);
context = canvas.getGraphicsContext2D();
context.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
context.setStroke(Color.BLACK);
getInitMaxScore();
context.strokeRect(50, 50, canvas.getWidth() - 60, canvas.getHeight() - 60);
context.strokeText("当前分数为:" + score + " 分",50, 16, 200);
context.strokeText("最高分数为:" + maxScore + " 分",200, 16, 100);
context.strokeText("当前speed:" + speed + "ms/次",350, 16, 100);
getChildren().add(canvas);
startButton = new Button("开始游戏");
startButton.setLayoutX(100);
startButton.setLayoutY(20);
startButton.setOnAction(event -> {
// 显示登录对话框
showLoginDialog();
});
pauseButton = new Button("暂停游戏");
pauseButton.setLayoutX(200);
pauseButton.setLayoutY(20);
pauseButton.setOnAction(event -> {
if(!isPaused){
isPaused = true;
task.setPaused(true);
pauseButton.setText("继续游戏");
mp.pause();
}else{
isPaused = false;
task.setPaused(false);
pauseButton.setText("暂停游戏");
mp.play();
}
});
saveButton = new Button("保存游戏");
saveButton.setLayoutX(300);
saveButton.setLayoutY(20);
saveButton.setOnAction(event -> {
SaveGame saveGame = new SaveGame(food, score, speed, cells);
try {
ObjectOutputStream outputStream = new ObjectOutputStream(Files.newOutputStream(Paths.get("saveGame.dat")));
outputStream.writeObject(saveGame);
outputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
readButton = new Button("读取存档");
readButton.setLayoutX(400);
readButton.setLayoutY(20);
readButton.setOnAction(event -> {
File saveFile = new File("saveGame.dat");
if (saveFile.exists()) {
ObjectInputStream inputStream = null;
try {
context.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
inputStream = new ObjectInputStream(Files.newInputStream(saveFile.toPath()));
SaveGame saveGame = (SaveGame) inputStream.readObject();
food.setX(saveGame.food.getX());
food.setY(saveGame.food.getY());
score = saveGame.score;
speed = saveGame.speed;
cells.clear();
for (SaveCell c : saveGame.cells){
cells.add(new SnakeCell(c.getX(), c.getY(), c.getCellLen(), Color.BLACK, c.getShade()));
}
inputStream.close();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
});
upBotton = new Button("增加难度");
upBotton.setLayoutX(500);
upBotton.setLayoutY(20);
upBotton.setOnAction(event -> {
if(speed > 50){
speed -= 50;
}
});
downBotton = new Button("降低难度");
downBotton.setLayoutX(600);
downBotton.setLayoutY(20);
downBotton.setOnAction(event -> {
speed += 50;
});
getChildren().addAll(startButton, pauseButton, saveButton, readButton, upBotton, downBotton);
mp = new MediaPlayer(new Media("https://prod-1.storage.jamendo.com/?trackid=2016794&format=mp31&from=6Rr2c3RVxoxpElvuWG00DA%3D%3D%7CnhMRidYvNhKs7einivGWJw%3D%3D"));
}
private void showLoginDialog() {
Dialog<Pair<String, String>> dialog = new Dialog<>();
dialog.setTitle("登录");
dialog.setHeaderText("请输入您的帐号和密码");
ButtonType loginButtonType = new ButtonType("登录", ButtonBar.ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL);
GridPane gridPane = new GridPane();
gridPane.setHgap(10);
gridPane.setVgap(10);
gridPane.setPadding(new Insets(20, 150, 10, 10));
TextField username = new TextField();
username.setPromptText("帐号");
PasswordField password = new PasswordField();
password.setPromptText("密码");
gridPane.add(new Label("帐号:"), 0, 0);
gridPane.add(username, 1, 0);
gridPane.add(new Label("密码:"), 0, 1);
gridPane.add(password, 1, 1);
Node loginButton = dialog.getDialogPane().lookupButton(loginButtonType);
loginButton.setDisable(true);
username.textProperty().addListener((observable, oldValue, newValue) -> {
loginButton.setDisable(newValue.trim().isEmpty() || password.getText().trim().isEmpty());
});
password.textProperty().addListener((observable, oldValue, newValue) -> {
loginButton.setDisable(newValue.trim().isEmpty() || username.getText().trim().isEmpty());
});
dialog.getDialogPane().setContent(gridPane);
Platform.runLater(() -> username.requestFocus());
dialog.setResultConverter(dialogButton -> {
if (dialogButton == loginButtonType) {
return new Pair<>(username.getText(), password.getText());
}
return null;
});
Optional<Pair<String, String>> result = dialog.showAndWait();
result.ifPresent(usernamePassword -> {
String usernameStr = usernamePassword.getKey();
String passwordStr = usernamePassword.getValue();
// 检查用户名和密码是否正确
boolean isLoginSuccess = checkLogin(usernameStr, passwordStr);
if (isLoginSuccess) {
// 登录成功,开始游戏
startGame(usernameStr,passwordStr);
} else {
// 登录失败,显示错误提示
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("错误");
alert.setHeaderText("登录失败");
alert.setContentText("帐号或密码错误,请重新输入");
alert.showAndWait();
// 重新显示登录对话框
showLoginDialog();
}
});
}
private boolean checkLogin(String username, String password) {
// 读取用户信息文件
File file = new File("user.txt");
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
String[] userInfo = line.split(",");
if (userInfo[0].equals(username) && userInfo[1].equals(password)) {
// 用户名和密码匹配,登录成功
return true;
}else if(userInfo[0].equals(username) && !userInfo[1].equals(password)){
return false;
}
}
} catch (IOException e) {
e.printStackTrace();
}
// 用户不存在,自动创建一个新用户
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, true))) {
writer.write(username + "," + password + "\n");
} catch (IOException e) {
e.printStackTrace();
}
// 返回登录成功
return true;
}
public void startGame(String username, String password) {
boolean isLoginSuccess = checkLogin(username, password);
if (!isLoginSuccess) {
// 登录失败,直接返回
return;
}
// 播放音乐
mp.play();
preStart();
food = new SnakeCell(190, 150, 20, Color.GREEN, "rectangle");
setFood(food);
task = new MoveTask(this);
task.setOnCancelled(event -> {
// 在关闭主窗口会回调这里
if (MoveTask.isForceCancel) {
String msg = "";
if (MoveTask.isOverBound)
msg = "您已出界!";
if (MoveTask.isEatItSelf)
msg = "您已经不小心把自己给吃了!";
Alert alert = new Alert(Alert.AlertType.NONE, msg + "\n是否重新开局?",
new ButtonType("重新开局", ButtonBar.ButtonData.YES), new ButtonType("退出游戏", ButtonBar.ButtonData.NO));
alert.setTitle("提示");
Optional<ButtonType> buttonType = alert.showAndWait();
if (buttonType.isPresent()) {
if (buttonType.get().getButtonData().equals(ButtonBar.ButtonData.YES)) {
//todo 重新开局
MoveTask.isForceCancel = false;
MoveTask.reDetect();
startGame(username, password);
} else {
//todo 退出
exit(task);
Platform.exit();
}
}
}
});
new Thread(task).start();
Task<Integer> task1 = new Task<Integer>() {
@Override
protected Integer call() throws Exception {
while (!isRequestClose) {
if (MoveTask.isEatItSelf || MoveTask.isOverBound) {
task.cancel(true);
MoveTask.isForceCancel = true;
break;
}
}
return null;
}
};
new Thread(task1).start();
}
public void exit(MoveTask task) {
if (task != null) {
task.cancel();
}
isRequestClose = true;
}
public void preStart() {
Const.SNAKE_CELL_LIST.clear();
SnakeCell cell = new SnakeCell(290, 350, 20, Color.BLACK, "rectangle");
SnakeCell cell1 = new SnakeCell(310, 350, 20, Color.BLACK, "rectangle");
SnakeCell cell2 = new SnakeCell(330, 350, 20, Color.BLACK, "rectangle");
Const.SNAKE_CELL_LIST.add(cell);
Const.SNAKE_CELL_LIST.add(cell1);
Const.SNAKE_CELL_LIST.add(cell2);
setScore(0);
setSpeed(200);
}
public void drawCell(){
cells.forEach(cell -> {
context.setFill(cell.getColor());
context.fillOval(cell.getX(), cell.getY(), cell.getCellLen(), cell.getCellLen());
});
}
public void repaint(){
context.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
context.setStroke(Color.BLACK);
context.strokeRect(50, 50, canvas.getWidth() - 60, canvas.getHeight() - 60);
context.strokeText("当前分数为:" + score + " 分",50, 16, 200);
context.strokeText("最高分数为:" + maxScore + " 分",200, 16, 100);
context.strokeText("当前speed:" + speed + " ms/次",350, 16, 200);
cells.forEach(cell->{
context.setFill(cell.getColor());
context.fillOval(cell.getX(), cell.getY(), cell.getCellLen(), cell.getCellLen());
});
context.setFill(food.getColor());
context.fillRect(food.getX(), food.getY(), food.getCellLen(), food.getCellLen());
}
public void setScore(int score){
this.score = score;
}
public int getScore(){return score;}
public void setFood(SnakeCell food){
this.food = food;
}
public int getMaxScore() {
return maxScore;
}
public void setMaxScore(int maxScore) {
this.maxScore = maxScore;
}
public void getInitMaxScore(){
File file = new File("score.txt");
if (!file.exists()) {
try {
file.createNewFile();
return;
} catch (IOException e) {
e.printStackTrace();
}
}
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
maxScore = Integer.parseInt(line);
}
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public void saveMaxScore() throws IOException {
File file = new File("score.txt");
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try(BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file, false))){
bufferedWriter.write(String.valueOf(maxScore));
}
}
public SnakeCell getFood(){return food;}
}