基于java的葫芦娃大战妖精

lonelyperson

发布日期: 2019-03-25 21:11:15 浏览量: 539
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

葫芦娃与蝎子精带领的喽啰们在草地上自动战斗直到一方全部阵亡,爷爷与蛇精在阵地后排观战。

1. 项目概述

1.1 项目目录与构建

  • /Demo: 预存的精彩站斗日志

  • /mdSupport: 实现README的辅助图片

  • /src: 源代码以及资源文件

  • /test: 测试文件

  • 通过命令mvn clean test package即可得到/target/calabash-1.0-SNAPSHOT.jar可执行文件。

1.2 使用说明

  • 双击可执行文件后即出现游戏默认初始界面,葫芦娃以長蛇阵型出站,妖精们以鶴翼阵型迎战。

  • 点击start按钮后战斗开始,开始计时。敌对双方寻找敌人厮杀,相互接触后随即判定其中一方的死亡(该概率由双方初始人数比确定),死亡后化作墓碑实体。游戏开始6s后每隔3s随机选择消失一个墓碑,以防战场堵塞。

  • 战斗结束后战斗过程已被记录在/target/Combat.log文件中,可以点击Load按钮或按下L键加载回放该场战斗,若觉得精彩可以重命名后加以保存,新一轮战斗会覆盖Combat.log文件。

  • 战斗结束或回放结束后,可变换妖精们的阵型重新战斗。共有八种阵型可供选择,判定胜负的概率也随阵型变化而变化。

  • 项目根目录中Demo文件夹已事先存储部分阵型下的精彩站斗,可直接Load进行回放。(其中yanyue.log即偃月阵型的回放时间越两分钟)

2. 实现细节

2.1 代码结构说明

2.1.1 Main — 项目程序的出入口

2.1.2 package Being

  • 生物体类,包含Being基类与Calabash, Grandpa, Scorpion, Snake, Soldiers五个派生类,以及为Calabash类服务的枚举类COLOR

  • 生物体有自己的属性,包括姓名、生死、位置、好坏等,拥有自己生存与死亡时的图片、出场或者胜利动画,以及各自的线程。
  1. public Timer timer = new Timer();

2.1.3 package Field

  • 战场类,包括Field战场属性类、FieldControl战场安置类、Square战场基本单元类。

  • Square战场基本单元,可且仅可放置一个生物体。

  • Field, Height = 11, Width = 16, 由Square组成,具有一系列判断战场状态的功能。

  1. boolean posQualified(Position p) //判断位置是否出界
  2. boolean emptySquare(Position p) //判断位置是否为空
  3. Being getLiveBeing(Position p) //返回位置上活着的生物体
  4. Position isBeside(Position p) //判断上下左右是否有敌人,如果有,返回位置
  5. boolean isOpposite(Position a, Position b) //判断位置是否敌对
  6. Position getRandomPos(Position p) //随机返回上下左右的一个可前进方位
  7. Position findNext(Position a, Position b) //返回a到b路径上的下一个位置
  • FieldControl, 是FieldBeing类的聚合,主要功能在于初始化战场。
  1. void initField(String formation) {
  2. //以formation阵型初始化战场
  3. field.clearField();
  4. scorpion.setFormation(formation);
  5. this.setAlive(); //全部复活
  6. this.setField(); //将生物体放到战场上
  7. field.qualified = true; //初始化完成的flag
  8. }

2.1.4 package Formation

  • 阵型类,共”鶴翼”, “雁行”, “長蛇”, “鋒矢”, “偃月”, “方门”, “鱼鳞”, “衡轭”八种阵型,只有蝎子精可以变阵。
  1. public static Integer max = 18; //最大人数
  2. public String name; //阵型名
  3. public Position leader; //参照点位置
  4. public ArrayList<Position> pos = new ArrayList<>(); //相对于参照点的位置集合

2.1.5 package Position

  • 位置类,具有横纵坐标的属性,并且可返回上下左右四个方位的位置。

2.1.6 package Sort

  • 排序类,可打乱葫芦娃的顺序、按葫芦娃姓名冒泡排序,按葫芦娃颜色二分排序。

2.1.7 package FileControl

  • 文件回放类,拥有自己的线程,可以加载、解析、写入、回放文件
  1. private Timer replayTimer = new Timer();
  2. private File loadFile() //通过FileChooser选择文件
  3. private ArrayList<String> parseFile(File file) //装饰器模式解析文件,存储在字符串列表
  4. reader = new BufferedReader(new FileReader(file));
  5. private void writeFile(String filename, String Log) //将日志写入文件
  6. writer = new BufferedWriter(new FileWriter(filename, false));
  7. public void replayGame() //开启线程开始回放

2.1.8 package GameControl

  • GuiControlGUI实现类

    • 图形界面的实现类,实现了Text/Label/Timeline/Button等一系列控件,以及刷新界面的功能。
  • GameLogic游戏逻辑类,游戏在此初始化,所有生物体线程在此并发。

    • GuiControl图形界面类、FieldControl战场控制类、FileControl文件回放类的聚合。
    • 三者相互交互实现本游戏的主逻辑
    • 用户通过GuiControl上的Button操作。
      • Start游戏开始多线程并发。
      • Load经由文件回放FileControl单线程进行。
      • 八个ChangeFormation的按钮经由FieldControl的初始化。

2.2 主要功能思想

2.2.1 多线程并发,每个生物体得到自己的线程。

  1. //考虑到每隔一段时间定时执行操作的需求,我发现实现TimerTask的run()接口是很不错的选择。
  2. //通过synchronized对代码加锁以实现多线程并发的安全
  3. private synchronized TimerTask getTask(Being en) {
  4. return new TimerTask() {
  5. @Override
  6. public void run() {
  7. Platform.runLater(() -> {
  8. //生物体逻辑
  9. });
  10. }
  11. };
  12. }
  13. for (Calabash c : brothers)
  14. c.timer.scheduleAtFixedRate(getTask(c), 1000, 1000);
  15. scorpion.timer.scheduleAtFixedRate(getTask(scorpion), 1000, 1000);
  16. for (Soldiers s : soldiers)
  17. s.timer.scheduleAtFixedRate(getTask(s), 1000, 1000);
  18. //刷新屏幕线程
  19. refreshTimer.scheduleAtFixedRate(new TimerTask() {
  20. @Override
  21. public void run() {
  22. //刷新屏幕逻辑
  23. }
  24. }, 100, 500);
  25. //清理墓碑线程
  26. clearTimer.scheduleAtFixedRate(new TimerTask() {
  27. @Override
  28. public void run() {
  29. //清理墓碑逻辑
  30. }
  31. },6000, 3000);//6s后每3s清空一个墓碑

2.3 文件读写、输入输出、异常处理

  1. //考虑到读写文件是消耗较大的操作
  2. //使用字符串缓存使得每次游戏只需一次读取或写入操作
  3. private ArrayList<String> parseFile(File file) {
  4. if(file == null) return buffer;
  5. BufferedReader reader;
  6. try {
  7. //装饰器模式
  8. reader = new BufferedReader(new FileReader(file));
  9. String text;
  10. while((text = reader.readLine()) != null) {
  11. buffer.add(text);
  12. }
  13. } catch (IOException e) {
  14. throw new RuntimeException("加载存档错误");
  15. }
  16. try {
  17. reader.close();
  18. } catch (IOException e) {
  19. throw new RuntimeException("关闭流失败");
  20. }
  21. return buffer;
  22. }
  23. public void writeFile(String filename, String Log) {
  24. try {
  25. //装饰器模式
  26. BufferedWriter writer = new BufferedWriter(new FileWriter(filename, false));
  27. writer.write(Log);
  28. writer.close();
  29. } catch (IOException e) {
  30. throw new RuntimeException("存档错误!");
  31. }
  32. }
  33. //响应键盘
  34. class KeyParse implements EventHandler<KeyEvent> {
  35. public void handle(KeyEvent event) {
  36. if (!GameLogic.isGaming || !GameLogic.isReplaying) {
  37. if (event.getCode() == KeyCode.L)
  38. fileReplay.replayGame();
  39. }
  40. }
  41. }

2.4 单元测试

对战场阵型逻辑以及葫芦娃排序功能,使用第三方测试工具junit进行针对测试。

2.5 设计原则

  • 单一职责、聚合复用

    • GuiControl, FieldControl, FileReplay等类各司其职、互不干扰、聚合实现游戏逻辑。
  • 开放封闭

    • 生物体类通过实现抽象基类BeingsetView方法完成对自己图像的初始化。
  • 里氏替换

    • 生物体类均可替换Being基类。
  • 接口隔离

    • 不存在“胖子”类。

3. 项目特点

3.1 动画丰富、阵型齐全

阵型变换

战斗胜利

回放清晰

计时功能

3.2 清理墓碑、不会堵死

  1. //设置一个墓碑列表
  2. //每次生物死亡记录墓碑位置
  3. //定时随机选取墓碑消失
  4. private ArrayList<Position> tomb = new ArrayList<>(); //墓碑列表

3.3 一次游戏、一次读写

  1. //一次游戏就是一次字符串,文件读写只一次
  2. private String combatLog; //记录战斗的字符串
  3. combatLog = "New Log\n" + "Game Start\n" + scorpion.getFormation().name + '\n';
  4. /*...*/
  5. if // Calabash win
  6. combatLog += "Calabash win\n";
  7. else
  8. combatLog += "Soldiers win\n";

3.4 Lambda表达式

  1. //在setonAction时十分好用
  2. Button buttonStart = new Button("Start");
  3. buttonStart.setOnAction((ActionEvent t) -> playGame());
  4. Button buttonLoad = new Button("Load");
  5. buttonLoad.setOnAction((ActionEvent t) -> fileReplay.replayGame());
  6. Button button1 = new Button("鶴翼");
  7. button1.setOnAction((ActionEvent t) -> forceRestart("鶴翼"));
  8. Button button2 = new Button("雁行");
  9. button2.setOnAction((ActionEvent t) -> forceRestart("雁行"));
  10. /*...*/

4. 总结与致谢

首先不后悔选择了这门课程,但最后的考试确实让我认识到了JAVA语言的博大精深以及自己所下功夫的严重欠缺,平时没有能够在每一次课后及时应用所学的知识到代码中、没有能够结合书籍详细地实操,是一个很深的教训,更能体会到了老师们的良苦用心。

课程的结束、大作业的完成,是我精通JAVA是起点,感谢老师与助教的辛勤付出。

上传的附件 cloud_download 基于java的葫芦娃大战妖精.zip ( 5.56mb, 8次下载 )
error_outline 下载需要9点积分

发送私信

曾经输掉的东西,只要你想,就一定可以再一点一点赢回来

15
文章数
25
评论数
最近文章
eject