基于JAVA的葫芦娃游戏

Krismile

发布日期: 2019-04-13 22:06:10 浏览量: 410
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

一、游戏介绍

1. 展示

葫芦娃失败

葫芦娃胜利

2. 游戏机制

  • 按空格键直接开始或者重开

  • 只给葫芦娃一方做了技能,为平衡游戏,妖怪一方的基础攻击力和血量都很高

  • 由于是随机移动,为了使游戏更快结束,每过五秒,生物的攻击力会增加5

  • 攻击效果

    • 实现了Shoot接口的生物可以发射炮弹,双方炮弹不一样
    • 炮弹打到生物身上会有“炸”的效果
    • 生物死亡了会在原地生成小坟墓,持续一段时间
  • 技能特效

    左下角有个技能栏,总共七个技能(对应七个葫芦娃),有冷却时间,生物死亡的话,上面会显示一个“死”字

    大娃技能:增加自身攻击力10。

    二娃技能:定位到蛇精,使其减少一定量生命值(蛇精死亡的话,技能无效)

    三娃技能:增加自身防御力,并回复已损失生命的20%

    四娃技能:群伤,对对所有人造成伤害,伤害量为自己攻击力的80%,由于是群伤,固有初始冷却值。

    五娃技能:群伤,对面所有人造成固定的30点伤害

    六娃技能:隐身伪装,会假死一段时间,这段时间他会变成小坟墓,无法移动攻击,但其他人也无法攻击到他。

    七娃技能:瞬间将一个怪物吸到自己的葫芦里秒杀(除蛇精),初始冷却值30秒。

    人工释放技能:按数字键1到7,释放时左边会有淡入淡出的特效。

  • 游戏结束

    当葫芦娃一方全部死亡时,游戏失败,当妖怪一方全部死亡时,游戏胜利。此时会弹出失败或胜利的特效。

二、游戏实现

1.游戏元素的类图

  • Being类是这个游戏元素的基类(空的)

  • 子弹(Bullet)生物(Creature)继承Being,但他们都不够具体、丰富,所以他们的实现是抽象类,他们里面实现了各自派生类所共有的行为和属性

  • 更为详细的怪物(Monster)葫芦娃(CalabashBrother)以及其具体的派生类,都赋予了各自生物不同的基础属性

  • 怪物葫芦娃都实现了Shoot这个接口,代表其可以发出子弹,目前子弹只派生出两个具体子弹类,区分了葫芦娃和怪物的子弹(方向不同,原画不同等),如果需要的话,可以派生出更多的子弹类,为每种生物都赋予不同的子弹,让游戏世界更丰富多彩。这种结构也极易扩展。符合开闭原则

  1. public Bullet shoot() {
  2. return new MonsterBullet(getPosition().getY(), getPosition().getX(), -1, this);
  3. }
  4. //通过派生子弹类,丰富子弹类型。
  • 生物类实现了Skill接口,分别都在之后的子类中具体实现,赋予不同生物不同的技能

  • 子弹生物都实现了Runnable接口,多线程编程使得游戏世界能动起来,用synchronized关键字锁定一定的资源,使得不会出现资源冲突等现象

2.阵营

  • 游戏战斗分为两方,所以出现了阵营(Sides)这个实体,分别派生出正义方(JustSide)邪恶方(EvilSide)两个阵营,阵营里面是鼓励师(爷爷和蛇精)和生物群(葫芦娃和蝎子精喽啰),用到了组合(composition)这种复用代码的方法

  • 阵营也实现了Runnable接口,此线程主要是初始化里面的生物线程

  • 此外,阵营在一场游戏里,不同阵营只有一个,因此在这里运用了单例模式,这样访问里面的东西就更加方便了

  1. public class EvilSide extends Sides{
  2. private static EvilSide evilSide = new EvilSide();
  3. public static EvilSide getInstance(){
  4. return evilSide;
  5. }
  6. private EvilSide() {}
  7. }

3.游戏模型

  • 因为前面那些都只是在数据上进行修改没有到UI界面上。因此这个游戏模型就是去联系UI和数据逻辑方面所做的工作

  • 此游戏模型也是用的单例模式,方便获取它里面的一些元素

  • 游戏模型主要是去做两件事情,更新UI和更新数据,并协调两者。所以主要去分俩线程分别去做这样的事情

  1. public class GameModel implements Runnable{
  2. enum GameStatus{
  3. going, justWin, evilWin
  4. }
  5. private static GameModel gameModel;
  6. private GameStatus gameStatus;//游戏状态
  7. private Board board = Board.getInstance();
  8. private ExecutorService exec = Executors.newCachedThreadPool();
  9. private List<Bullet> bullets = new ArrayList<>();
  10. private Canvas canvas;
  11. public static GameModel getInstance() {
  12. if (gameModel != null)
  13. return gameModel;
  14. else {
  15. return null;
  16. // throw new Exception("GameModel还没有初始化好");
  17. }
  18. }
  19. GameModel(Canvas canvas) {
  20. Creature.setBoard(this.board);
  21. gameStatus = GameStatus.going;
  22. this.canvas = canvas;
  23. gameModel = this;
  24. }
  25. public void restart(){
  26. clearThreads();
  27. gameModel = new GameModel(canvas);
  28. JustSide.getInstance().restart();
  29. EvilSide.getInstance().restart();
  30. }
  31. @Description(todo = "得到战场")
  32. public Board getBoard() {
  33. return board;
  34. }
  35. @Description(todo = "线程分配")
  36. private void initThreads(){
  37. exec.execute(JustSide.getInstance());
  38. exec.execute(EvilSide.getInstance());
  39. }
  40. @Description(todo = "战场的的显示内容")
  41. private void displayBoard(){
  42. double width = this.canvas.getWidth();
  43. double height = this.canvas.getHeight();
  44. int n = this.board.getSIZE();
  45. double boardWidth = height * 6 / 7;
  46. double startLayoutX = (width - height) / 2 + 70;
  47. double startLayoutY = (height - boardWidth) / 2;
  48. double creatureSize = boardWidth / n;
  49. Image image = new Image("map.jpg");
  50. synchronized (board) {
  51. this.canvas.getGraphicsContext2D().drawImage(image, 0, 0, image.getWidth(), image.getHeight());
  52. for (int i = 0; i < n; i++) {
  53. for (int j = 0; j < n; j++) {
  54. double x = startLayoutX + i * creatureSize;
  55. double y = startLayoutY + j * creatureSize;
  56. if (!(this.board.get(j, i).isEmpty())) {
  57. this.board.get(j, i).getCreature().draw(this.canvas.getGraphicsContext2D(), x, y, creatureSize);
  58. }
  59. }
  60. }
  61. }
  62. synchronized (bullets) {
  63. for (Bullet bullet : bullets) {
  64. bullet.draw(this.canvas.getGraphicsContext2D(), startLayoutX, startLayoutY, creatureSize);
  65. }
  66. }
  67. }
  68. @Override
  69. public void run() {
  70. initThreads();
  71. new Thread(() -> {
  72. int i = 0;
  73. Image image = new Image("win.png");
  74. while (true){
  75. if(JustSide.getInstance().isDeadAll()){
  76. gameStatus = GameStatus.evilWin;
  77. System.out.println("妖怪赢了");
  78. image = new Image("fail.png");
  79. break;
  80. }else if(EvilSide.getInstance().isDeadAll()){
  81. gameStatus = GameStatus.justWin;
  82. System.out.println("葫芦娃赢了");
  83. break;
  84. }
  85. try {
  86. i++;
  87. System.out.println("第" + i + "次");
  88. synchronized (GameModel.class){
  89. GameModel.class.wait();
  90. }
  91. } catch (InterruptedException e) {
  92. e.printStackTrace();
  93. }
  94. }
  95. displayBoard();
  96. Image finalImage = image;
  97. Platform.runLater(()->{
  98. GameController.playBox(finalImage);
  99. });
  100. System.out.println("Game Over");
  101. clearThreads();
  102. try {
  103. TimeUnit.SECONDS.sleep(1);
  104. } catch (InterruptedException e) {
  105. e.printStackTrace();
  106. }
  107. ThreadInfo.threadPrint();
  108. }).start();
  109. new Thread(()->{
  110. while (gameStatus == GameStatus.going){
  111. Platform.runLater(this::displayBoard);
  112. try {
  113. TimeUnit.MILLISECONDS.sleep(50);
  114. } catch (InterruptedException e) {
  115. e.printStackTrace();
  116. }
  117. }
  118. }).start();
  119. }
  120. @Description(todo = "线程清理")
  121. private void clearThreads(){
  122. JustSide.getInstance().allToDie();
  123. EvilSide.getInstance().allToDie();
  124. for(Bullet bullet: bullets){
  125. bullet.fade();
  126. }
  127. }

4.UI界面

  • UI界面使用javafx做的,主要就是去显示以及响应键盘鼠标事件等,并将其传给游戏模型(GameModel)响应信息

  • 这里我们主要做动画的是利用canvas去一帧一帧的播放我们数据层面的东西。以及利用了FadeTransition和ScaleTransition两种变换去做效果

三、课程内容相关

1. 面向对象思想

整个设计过程中都是从面向对象的角度去做的,像丰富的生物体,子弹体等。

2. 注解

主要实现了两个注解

一个是对方法的注解,主要用于描述方法的作用

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.CLASS)
  3. public @interface Description {
  4. String todo();
  5. }

一个是对类的注解,用于描述作者信息

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.CLASS)
  3. public @interface Author {
  4. String name() default "吴刚";
  5. }

3. Maven构建项目

整个项目用的是Maven的结构,并编写的Test用例去做测试。

4.整个项目最重要的一点就是:多线程编程

  • 在我的世界里,GameModel是一个中间人的角色,他也是一个线程,从他出发,会生成两个主要功能的线程:一个是UI的更新线程,一个是数据的更新线程

  • 数据的更新线程会做一系列的初始化工作,生成生物的每个线程,并进行管理

  • 游戏结束后,会清理多余的数据线程

上传的附件 cloud_download 基于JAVA的葫芦娃游戏.zip ( 25.63mb, 2次下载 )
error_outline 下载需要11点积分

发送私信

真正的强者不是要压倒一切,而是不被一切压倒

16
文章数
12
评论数
最近文章
eject