当前位置: 首页 > article >正文

基于Java的愤怒的小鸟游戏的设计与实现【源码+文档+部署讲解】

目录

摘要

Abstract

1 绪论

1.1 游戏开发的背景

1.2 典型的Java游戏介绍

1.2.1 Minecraft介绍

1.2.2 Super Mario Bros介绍

1.2.3 The Sims介绍

1.3 游戏开发的意义

2 开发环境

2.1 开发语言

2.2 开发工具

2.3 JDK介绍

2.4 Java Awt介绍

2.5 Java Swing 介绍

2.6 Java语言开发平台搭建

3系统需求分析

3.1 可行性分析

3.1.1 技术可行性

3.1.2 经济可行性

3.1.3 操作可行性

3.1.4 发展可行性

3.2 性能需求分析

3.3 功能需求分析

3.4 系统UML分析

3.5界面需求分析

4 系统设计

4.1系统流程设计

4.2 系统架构设计

5 详细设计

5.1 主界面实现

5.2 游戏玩法实现

5.3 面板管理功能实现

5.4 等级、级别管理功能实现

5.5 碰撞管理功能实现

5.6 鸟类管理功能实现

5.7 胜利管理功能实现

5.8 玩家管理和备份管理功能实现

6 系统测试

6.1系统测试简介

6.2 系统测试方法

6.3 本系统测试

6.3.1 测试用例设计

6.3.2 测试方法和结论

结论

参考文献

致  谢

附录

外文原文

中文原文

 

3系统需求分析

3.1 可行性分析

可行性分析是要求以经济方法和效益为核心进行全面的系统分析,围绕影响项目的各种因素、收集大量数据分析论证项目的可行性。对整个项目进行可行性研究是为了对项目进行分析和评估,突出项目的优缺点和讨论如何对项目进行改进。为了结论的可行性,经常还需要添加一些配件,如测试数据、演示材料、电子表格、图纸等,以提高可行性研究的严谨性,使其更具有说服力。

3.1.1 技术可行性

技术可行性是从重要技术项目执行实施的角度,合理设计技术解决方案,进行比较评估不同行业和不同深度的大型技术可行性研究项目。这一款软件开发,在硬件方面不存在特殊要求,只需要在普通的硬件配置中便可以轻松实现,但必须保证系统的正常运行,并提高效率。如果硬件特别弱、性能差从而使得系统效率低下,导致整个系统不顺畅。但对于如今的个人PC的一般配置,进行软件开发是很容易满足条件的。 因此,该系统的开发在硬件方面完全可行。操作系统选择WINDOWS操作系统,开发软件为Eclipse,该系统的设计实现在软件方面是可行的。因此可以看出,该系统的开发是没有问题的。

3.1.2 经济可行性

经济可行性是在资源配置、区域经济发展目标、经济资源有效配置、增加供给、创造就业机会、改善环境、改善生活等方面,对项目的价值进行评估。本基于Java开发的愤怒的小鸟游戏,需要的软硬件环境,在市场上都是很方便可以购买得到,开发人员主要是进行软件开发和简单的维护。所以人力和财力资源开发方面没有问题,开发周期长,经济可行性很大。

3.1.3 操作可行性

操作可行性是设想项目验证是可行的,它提出了各种方案的实施,并解释了不同方案的优缺点。该系统使用基于Java语言编写,使用个人PC安装Eclipse来进行访问和操做,并且界面简单易用,只要用户稍加以学习和多试几次,完全可以熟悉访问和操作。这一款愤怒的小鸟游戏,有着界面上易于管理和良好的互动操作等各种优点。

3.1.4 发展可行性

软件生命周期的观点:随着中国游戏方面还处于成长期,这类游戏软件将会被广大用户提出需求。因此,这是游戏软件长寿的主要原因,开发一款游戏软件,合理的运营将会使它和谐的发展,而且这并不是一个太难的开发项目,它不仅可以在PC端进行操作使用,甚至可以跨平台传播,而且用户操作简单,使用起来非常方便,绝大多数用户都可以不用花费大量的时间便可以完全上手,这不仅是适应用户对游戏的追求,同时适应当前的游戏发展趋势。

3.2 性能需求分析

为了保证愤怒的小鸟程序的长期、稳定和有效的运作,必须保证系统的发展。 在开发基于Java语言的愤怒的小鸟程序的过程中,系统必须确保使用适当方法来保证程序的安全性和有效性。 我们必须充分考虑以下几点:

安全性:在信息时代,信息是确保信息安全的重要资产,特别是个人信息也需要强大的安全性,所以在游戏个人信息保存方面,应该确保安全性是第一位。

超前性:结合当今时尚潮流,开发满足用户需求,让用户总是可以第一时间获得超前的游戏体验,获得新鲜感,获得新的乐趣。

可扩展性:基于Java的愤怒的小鸟游戏不仅可以在PC端进行使用,由于Java语言的可扩展性,注定这个游戏也可以进行多平台使用,更加方便的获取以及更加方便的操作。

3.3 功能需求分析

需求分析是对用户需求和要求的分析。需要准确评估用户需求的结果从而反映实际用户需要,这将直接影响整个设计的流程,也会对系统的使用产生影响。关注需求评估来完成调查收集和研究,可能会受到数据管理和信息安全过程的影响。一般用户相对于开发者来说,绝大部分用户都是缺少计算机相关知识,并且无法确定计算机是否可以为自己做到或者做不到一些事情,准确表达他们的需求是需要的,必须通过跟用户的深入了解来获取用户的需求从而准确的确定计算机的功能。

搜索用户分析和细化特征等用户的描述的信息是必要的。它是软件开发过程的第一阶段主要部分,主要任务是了解您需要什么,需要做个什么样的系统,使完整的目标系统拥有清晰准确的功能,并且可以进行书面表达。

在愤怒的小鸟程序中,主要实现以下功能

游戏玩法功能:

  1. 能实现发射器创建、发射角度、发射力度等
  2. 可以实现小鸟飞行功能,包括小鸟飞行抛物线、小鸟飞行悬停
  3. 实现游戏中猪的移动功能,包括猪的根据级别不同改变移动速度、猪在哪里移动
  4. 碰撞功能:实现小鸟与障碍的碰撞、小鸟与猪的碰撞、鸡蛋与障碍物的碰撞、鸡蛋与猪的碰撞、猪与障碍物的碰撞

页面管理功能:实现各个页面功能,包括主界面、载入游戏界面、控制帮助界面、跳转界面功能等

难度、级别功能:实现游戏难度功能,难度不同猪的移动速度不同,实现级别功能,级别不同地图难易程度也不同

实体类管理功能:创建游戏中各种实体类,包括鸟的种类、猪、障碍物、草地等

玩家管理功能和备份功能:实现玩家信息管理以及游戏信息保存的功能

如下图3-1所示:

图3-1 总体功能需求图

3.4 系统UML分析

UML是在面向对象的方法Booch,TMO,OOSE等基础上开发的方法,以及根据许多其他方法和材料的基础从而衍变出来的。 UML符号是为各种图形符号表示,消除混淆、冗余符号或很少使用以及容易导致的图形符号,同时添加一些新的图形符号。

UML是统一建模语言的缩写,也称为统一建模语言。它是用于可视化建模的一种语言。 UML是开发人员用于建模事物的客观标记,同时也为开发人员了解什么样的系统工作以及整个过程做出了准备工作。 现在我们在这个基础上对愤怒的小鸟进行建模分析,UML用例图如下图3-2所示:

3.5界面需求分析

目前,界面设计已成为软件质量评估的重要指标,良好的用户界面能提高用户操作系统的信心和增加兴趣、提高效率。 客户界面是指与用户与机器进行交互的软件系统界面,通常覆盖界面格式输出,输入和其他人机对话。

1.输出设计

输出是游戏程序解决基本信息的输入,产生高质量的正确信息,并使其具有一定的格式,供管理人员使用,这是发布设计和目标的主要责任和目标。

与系统开发过程的实现不同,不是从输入设计到输出,而是输处设计到输入到设计。 这是因为输出表与用户直接链接,应设计为确保用户可以方便地使用输出表格,及时将有用的信息可以反映在各个部门。

2.输入设计

收集和输入输出数据更加不方便,需要大量的人力物力,而且经常出错。 一旦不正确的数据输入系统,处理后的输出会扩大这些错误,所以输入数据的准确性对系统的整体性能起决定性的作用。输入设计有以下几点原则:

1)输入量必须最小程度的满足处理要求。 输入数据越少,错误率会越小,数据准备时间也较少。

2)如果可能的话,尝试尽可能方便的准备和输入过程,以减少更多的发生错误发生率。

3)尽早检查输入数据(尽可能靠近原始数据生成点),及时进行纠错更改。

4)可能会尽可能早地将输入数据记录所需的处理形式进行记录,以防止从一个介质传输到另一个介质而可能发生的转录数据错误。

4 系统设计

4.1系统流程设计

在设计开发基于Java语言的愤怒的小鸟的时候,必须先进行需求分析,这样才可以对系统总体进行概括性规划,从而进行系统功能模块的设计、测试、基于Java语言的愤怒的小鸟的总体设计流程图,

如下图4-1所示:

图4-1 总体流程设计图

该程序在游戏概念和属性中使用面向对象的模型。 运行程序后,用户可以选择运行选项菜单,屏幕刷新屏幕重新设计一定频率,实时反映游戏状态。

如下图3-2显示了游戏控制的流程图。 首先,当游戏运行时会成为测试的关键角色,同时响应方向。 这是对鸟类在屏幕的主要方向的响应,同时检测释放的鸟类的强度。 如果你摧毁对象超过一个固定的分数,导致游戏结束。 当物体被鸟类摧毁时,根据物体的破坏得到不同的分数。

同时,还有其他检测键,主要用于检测菜单键。 菜单按钮包括检测输出和返回参数。 当游戏检测到退出按钮时,游戏结束。 当检测到游戏的返回键时,游戏将返回到主屏幕。

图4-2 游戏控制流程图  

4.2 系统架构设计

系统架构设计模型采用MVC经典建模模型,使用面向对象设计思想来帮助实现。 MVC全名模型视图控制器(Model-View-Controller),M、V、C分别是指逻辑模型、指视图模型、控制器。MVC已经开发成了用于图形用户界面中常规映射输入,处理和输出逻辑功能的独特结构。

我们参考的模型是一个对应于多个逻辑模型的视图模型,我们使用这个模型将视图模型的代码和逻辑模型的代码分开,以便我们可以以不同的形式实现相同的程序,然后控制台的控制着两者同时工作,其中一个变化, 另一个应该同时更新。

用户对此模型的最大优势是根据自己的方式选择更加适合自己的方式来浏览数据。 对于那些开发人员来说,这种模式的最大优点就是分离界面和应用程序的逻辑层,这个界面和程序员的设计者可以在格子的领域工作,而不会互相干扰。

Model(模型):

GameModel():实现游戏主要功能,包括碰撞、发射、得分。

Level():实现游戏难度、地图功能。

LevelNumber():存储地图功能

ListChangedEvent()、ListListener():碰撞功能的监听

Player():玩家备份功能

Entity()、Bird()、Block()、Egg()、Enemy()、EntityThread()、Grass()、HummingBird()、Pig()、Pigeon()、Sparrow() :定义游戏中各种模型的实体类。

图4-3 游戏实体类图

View(视图):

GameView():实现游戏中发射皮筋功能

GameViewMenu():实现背景界面及标题功

MenuDifficultyView():实现难度选择界面

MenuHomeView():实现主界面

MenuLevelView():实现级别选择界面

MenuLoadView():实现确认及删除界面

MenuNewView():实现增加玩家信息界面

MenuOptionsView():实现控制帮助

Controller(控制器):

GameController():实现监听事件(键盘,鼠标,更改实体列表)

MenuController():实现菜单控制界面跳转等功能

图4-4 游戏总体类图

5 详细设计

5.1 主界面实现

 (见图5-1)当玩家开始游戏时,共有4个选择:开始一个新游戏,加载一个保存的游戏,了解游戏控制或离开游戏。 如果启动新游戏,则需要输入其名称,以创建备份。 如果决定加载保存的游戏,玩家从已经备份的名称中进行选择。如果点击控制帮助选项,玩家将会看到游戏控制的帮助内容,以便更好的了解游戏、更容易上手。如果选择退出游戏,则游戏会立刻关闭。

图5-1 主界面

部分代码以及相应的页面布局:

public class MenuHomeView extends GameViewMenu

{

private JButton newButton,loadButton,optionsButton,exitButton;

public MenuHomeView() {

        newButton = new JButton("新游戏");

        newButton.setSize(250,40);

        newButton.setLocation(frameWidth/2-115, 150);

        

        loadButton = new JButton("载入游戏");

        loadButton.setSize(250,40);

        loadButton.setLocation(frameWidth/2-115, 225);

        

        optionsButton = new JButton("控制帮助");

        optionsButton.setSize(250,40);

        optionsButton.setLocation(frameWidth/2-115, 300);

        

        exitButton = new JButton("退出游戏");

        exitButton.setSize(250,40);

        exitButton.setLocation(frameWidth/2-115, 375);

        

        backButton.setVisible(false);

      this.add(newButton,new Integer(1));

      this.add(loadButton,new Integer(1));

      this.add(optionsButton,new Integer(1));

      this.add(exitButton,new Integer(1));

}

public JButton getNewButton()

{

return newButton;

}

public JButton getLoadButton()

{

return loadButton;

}

public JButton getOptionsButton()

{

return optionsButton;

}

public JButton getExitButton()

{

return exitButton;

}}

5.2 游戏玩法实现

(见图5-2及5-3)选择一个级别后游戏开始。 首先玩家必须选择弹出一只鸟(游戏中总共有三只鸟供玩家使用),根据鸟的特点,玩家可以决定在飞行期间是否停止鸟,同时鸟也可以扔一个鸟蛋(鸟蛋的数量取决于不同的鸟的种类)。 玩家可以使用鸟蛋碰撞或直接鸟本身碰撞消灭阻碍物。 最后,玩家还可以借助鸡蛋或鸟类本身杀死绿猪,有时可能需要摧毁阻碍物才能到达消灭某些猪的目的。

图5-2游戏玩法图

图5-3为刚进入游戏时的界面,右上角显示还剩下几种鸟可以使用以及剩余鸟的数量,金色的蛋表示鸟剩余蛋的数量

图5-3 游戏内容图1

图5-4 游戏内容图2

图5-4表示鸟在飞行过程中的状态,飞行中可以按S键将小鸟悬停在空中,然后可以按space键释放鸟蛋

部分代码(创建面板以及地图的代码)以及相应的页面布局:

public GameView(ArrayList<Entity> entities) {

        setFocusable(true);

        setDoubleBuffered(true);

        ImageIcon img1 = new ImageIcon(slingShotName1);

    slingShotImg1 = img1.getImage();

    ImageIcon img2 = new ImageIcon(slingShotName2);

    slingShotImg2 = img2.getImage();

        

        this.entities = entities;

}

public void paint(Graphics g) {

        super.paint(g);

     Graphics2D g2d = (Graphics2D)g;

        g2d.drawImage(map.getImage(), 0, 0,frameWidth,frameHeight, this);

        g2d.drawImage(slingShotImg2, 100,412, this);

    int k = 0;

    for(int i = entities.size()-1; i >=0;i--) {

     Entity e = entities.get(i);

     if(e instanceof Bird && e != currentBird) {

     g2d.drawImage(e.getImage(), frameWidth-100-k*15, 100,e.getImageWidth(),e.getImageHeight(), this);

     k++;

     }

     if(e == currentBird)

     {

     Egg egg = new Egg(0,0,28,30);

     for (int j = 0; j < currentBird.getEggLeft(); ++j) {

         g2d.drawImage(egg.getImage(), frameWidth-100+j*15, 20, this);

     }

     }

    }

        for (Entity e : entities) {

         if (!(e instanceof Bird))

         {

         g2d.drawImage(e.getImage(), (int) e.getPosition().getX(), (int) e.getPosition().getY(), this);

         }

         if(e== currentBird )

         {

         g2d.setStroke(new BasicStroke(7.0f));

         g2d.setColor(new Color(54, 28, 13));

         //drawLine()划线的方法

         if(!currentBird.isFlying())

g2d.drawLine(135,442, (int)e.getPosition().getX()+e.getImageWidth()/2, (int)e.getPosition().getY()+e.getImageHeight()/2);

  

         g2d.drawImage(e.getImage(), (int) e.getPosition().getX(), (int) e.getPosition().getY(),e.getImageWidth(),e.getImageHeight(), this);       

 

         if(!currentBird.isFlying())

         g2d.drawLine(120,442, (int)e.getPosition().getX()+e.getImageWidth()/2, (int)e.getPosition().getY()+e.getImageHeight()/2);

        

         }

         g2d.setStroke(new BasicStroke(7.0f));

         g2d.setColor(new Color(54, 28, 13));

         if(currentBird.isFlying())

         g2d.drawLine(135,442,120,442);

         g2d.setStroke(new BasicStroke(1.0f));

         g2d.setColor(new Color(0, 0, 0));

        }

        

        g2d.drawImage(slingShotImg1, 100, 412, this);

        g.drawString("Highest Score : " + currentHighestScore, 10, 15);

        g.drawString("Flying time left : " + currentBird.getFlyingTimeLeft(), 10, 30);

        Toolkit.getDefaultToolkit().sync();

        g.dispose();

    }

public void setEntityList(ArrayList<Entity> entities) {

this.entities = entities;

}

public Level getMap() {

return map;

}

public void setMap(Level map) {

this.map = map;

}

public void setCurrentHighestScore(int highestScore) {

this.currentHighestScore = highestScore;

}

@Override

public void listChanged(ListChangedEvent event) {

entities = event.getEntityList();

currentBird = event.getCurrentBird();

repaint();

}

}

5.3 面板管理功能实现

菜单管理通过6个不同的面板进行如图5-5所示: 

MenuHomeView:游戏启动时的主页

MenuNewView:创建新部分的页面  

MenuLoadView:加载游戏页面

MenuControlsView:游戏控制信息页面

MenuDifficultyView:游戏选择页面

MenuLevelView:级别选择页面

图5-5 管理菜单图

每个面板的实现一个特定的功能,但是它们都继承自GameViewMenu(abstract)类,该类具有所有面板共同的功能,例如Menu背景。GameViewMenu继承自JLayeredPane类,它允许向放置在面板上的元素分配不同的深度,这使得我们将按钮按照自己的意愿放置在在已经创建好背景上。菜单中的导航按钮通过创建鼠标的各种监听来完成,对于返回按钮的设置,使用 “转义”键通过键盘设置了额外的控制(相当于使用按钮“返回”)。 然后,检测窗口和窗口中的先前显示的面板。 在选择面板的时候,可以从新的游戏或者加载游戏等进行面板的访问。接下来的所示一系列图为各个面板:

创建备份面板:在此处可以输入玩家的用户名,点击确认后可以保存玩家信息

图5-6创建备份面板

载入游戏面板:在此处玩家可以选择之前的存档,然后接着上次游戏内容继续进行游戏

图5-7 载入游戏面板

控制游戏面板:此处是整个游戏需要学会的操作方式

难度选择面板:此处可以对游戏进行难度选择,选择不同的难度,小猪的移动各不相同,随着难度的提高,小猪的移动速度越来越快

图5-9 难度选择面板

级别选择面板:此处选择不同的级别,导致进入的关卡不同,随着关卡级别的提高,关卡的地形难度会越来越高

图5-10 级别选择面板

5.4 等级、级别管理功能实现

创建一个文本文件来管理等级,所有的游戏等级都依赖于这个文本,以下内容为等级管理功能的详细解释:

此功能可用于实现某个级别的鸟类、阻碍物以及猪在这个环境中的位置、鸟类列表列在文件的顶部。

鸟类的等级与其出现顺序相同。在行中的字符串和不同类型的鸟的名字对等,以创建相应的鸟,并将其添加到实体的ArrayList。当程序阅读文本时,遇到“地图(Map)”一词时,程序便可以从该文本中知道可用于该级别的鸟类列表是完整的,并且文件同时也包含表示级别的地图。这些是相同大小的文本行,包含将在二维数组中实现的不同字符(文本文件的每一行对应于2D数组中的一行)。每个元素都包含在具有26 * 26像素的相应图像中,并且在此图像对应的尺寸为1200 * 600像素的窗口中,该表的一行包含47个元素,一列包含22.每个字符对应于不同的元素装饰:

“0”表示地图上没有任何内容。

“1”表示地图上现在有一个草块。

“2”表示地图上现在有一块阻碍物。

“3”表示敌方猪的起始位置。

图5-11所示的为游戏的基本地图:

图5-11 游戏地图

相应的实体(猪,草块或阻碍物)是通过特定元素(“猪”,“草块”或“阻碍物”)创建的。 该实体通过构造函数创建,取其位置x(使用表的列索引和与实体对应的图像的大小计算)及其位置y(以l为单位计算)使用数组的行索引和 与实体对应的图像的大小)。

部分代码以及相应的页面布局:

public class Level {  

private String backgroundImagePath = "res/images/background.png"; private Image image;

private int tabMap[][];

private int tabMapSizeX = 47;

private int tabMapSizeY = 22;

private int blockSize = 26;

private String grassImagePath = "res/images/grass.png";

private Image grass;

private String blockImagePath = "res/images/block.png";

private Image block;

private boolean isLoaded;

private ArrayList<Entity> entities;

private int pigSpeed;

public Level(String fileMapPath, String difficulty) {

ImageIcon ii = new ImageIcon(backgroundImagePath);

image = ii.getImage();

ImageIcon gr = new ImageIcon(grassImagePath);

grass = gr.getImage();

ImageIcon bl = new ImageIcon(blockImagePath);

block = bl.getImage();

if (difficulty == "easy")

pigSpeed = 0;

if (difficulty == "medium")

pigSpeed = 1;

if (difficulty == "hard")

pigSpeed = 2;

if (difficulty == "extreme")

pigSpeed = 3;

entities = new ArrayList<Entity>();

try {

FileInputStream ips = new FileInputStream(fileMapPath);

isLoaded = true;

InputStreamReader ipsr = new InputStreamReader(ips);

BufferedReader br = new BufferedReader(ipsr);

String line;

line = br.readLine();

while (!line.equals("Map")) {

if (line.equals("Pigeon")){

entities.add(new Pigeon());

}

if (line.equals("Humming Bird")){

entities.add(new HummingBird());

}

if (line.equals("Sparrow")){

entities.add(new Sparrow());

}

line = br.readLine();

}

tabMap = new int[tabMapSizeY][tabMapSizeX];

for (int i = 0; i < tabMapSizeY; i++) {

line = br.readLine();

for (int j = 0; j < tabMapSizeX; j++) {

char car = line.charAt(j);

String st = String.valueOf(car);

tabMap[i][j] = Integer.parseInt(st);

if (tabMap[i][j] == 3)

entities.add(new Pig(j * blockSize, i * blockSize, pigSpeed));

if (tabMap[i][j] == 2)

entities.add(new Block(j * blockSize, i * blockSize, blockSize, blockSize));

if (tabMap[i][j] == 1)

entities.add(new Grass(j * blockSize, i * blockSize, blockSize, blockSize));

}

}

}

catch (Exception e) {

isLoaded = false;

}

}

5.5 碰撞管理功能实现

碰撞管理和与其相关的事件在updateEntity()函数中执行。 创建的每个实体都具有hitBox,也就是说,每个图形对象都被一个碰撞矩形包围。 然后,按照实体的处理,通过测试其hitBox是否与其他人的hitBox相交的方式,来进行碰撞的各种处理。

如果一个元素与另一个元素的冲突导致删除两个对象之一,则程序会将一个或多个元素添加到实体列表(toRemove()调用)。 反过来,这在函数结束时遍历,以执行GameModel中实体的删除。 这使得可以在算法中删除相同级别的所有相关实体,并确保在删除它们之前已经执行了实体上的所有进程。 各种管理冲突如下:

鸡蛋碰撞:

•与绿猪:当检测到碰撞时,绿猪和当前的鸡蛋被添加到要删除的实体列表中。

•蛋与阻碍物:在要删除的实体列表中涉及到阻碍物和相关蛋的处理。

•与草块:碰撞导致当前的蛋被地添加到toRemove()列表。 草地是坚不可摧的。

猪碰撞:

•与阻碍物:

管理碰撞:碰撞的管理是不同的,在这里,因为在阻碍物与猪的碰撞矩形上没有完成。 实际上,猪可以在阻碍物上自由移动,但是当它们从侧面撞击一块阻碍物时,碰撞必须导致猪的方向移动。 必须能够测试块的hitBox的哪个边缘,猪的hitBox相交。

事件:当猪碰到一块阻碍物的左边缘或右边缘时,猪会改变运动方向(左侧或右侧); 当猪不与其中一块阻碍物(即猪的任何阻碍物上移动)的一个上边缘碰撞时,猪坠落,直到它碰到其中一个块阻碍物或草,则猪会进行反向移动,而块的下边缘猪不会进行改变方向。

猪:当两头猪相遇时,这两头猪就会改变方向。

鸟类碰撞:

阻碍物:当一只鸟与一块阻碍物碰撞时,这两个实体被添加到删除列表中。可以再次证明在功能结束时选择对缺失的处理。只有当鸟类被第一个块的碰撞检测直接攻击时,其他将会崩溃的块将不会被测试。

与猪一起:鸟和猪的碰撞导致在删除列表中添加这两个实体。

在theupdateEntity()函数中,可以看到位置所在的实体(左,右或底部)。 相关要素被添加到删除列表中。

部分代码以及相应的页面布局:

if(entity == currentBird)

{

Bird bird = (Bird) entity;

if(!bird.isVisible())

toRemove.add(bird);

Rectangle hitBoxBird = bird.getHitBox();

for(Entity entity2 : entities) {

if(entity2 instanceof Block)

{

Block block = (Block) entity2;

Rectangle hitBoxBlock = entity2.getHitBox();

if(hitBoxBird.intersects(hitBoxBlock))

{

toRemove.add(block);

toRemove.add(bird);

}

}

}

for(Entity entity2 : entities) {

if (entity2 instanceof Pig) {

Pig pig = (Pig) entity2;

Rectangle hitBoxPig =  pig.getHitBox();

if(hitBoxBird.intersects(hitBoxPig)) {

toRemove.add(pig);

toRemove.add(bird);

             boolean win = true;

             for (Entity entity3 : entities) {

             if (entity3 instanceof Pig && !toRemove.contains(entity3)) {

             win = false;

             break;

             }

             }

             if (win) {

            

             for (Entity entity3 : entities) {

             if (entity3 instanceof Bird)

             ++score;

             }

             win();

             }

}

}

}

for (Entity entity2 : entities) {

         if (entity2 instanceof Bird && !toRemove.contains(entity2)) {

         currentBird = (Bird) entity2;

         break;

         }

}

}

}

for(Entity entity : toRemove)

entities.remove(entity);

for(Entity entity4 : entities){

if(entity4 instanceof Bird)

birdTest++;

if(entity4 instanceof Pig)

pigTest++;

if(entity4 instanceof Egg)

{

eggTest++;

}

}

if(birdTest==0 && eggTest==0 && pigTest!=0){

angryView.repaint();

lose();

}

}

5.6 鸟类管理功能实现

所有鸟类都继承了抽象类Bird。

创建当前的鸟:当前的鸟,位于弹弓上的鸟是通过浏览实体列表顺序找到的第一只鸟。 要将鸟类定义为当前目录,扫描实体列表,当检测到Bird的第一个实例时,将在该实例上放置currentBird指针。

鸟的状态:当鸟儿飞行时,他们有两个阶段:第一个准备飞行和一秒钟的飞行。 准备不良:鸟类位于抛石机上,玩家有可能试着对弹弓进行拉伸,以确定初始速度和进行鸟投掷的初始角度。 要做到这一点,玩家必须点击弹弓上的鸟,然后拖动它,同时按住鼠标按钮。 当释放鼠标按钮时,鸟类切换到飞行模式(isFlying布尔值设置为true)。

飞行阶段:飞行中的鸟类受到加速度公式,Y中的加速度为9.81(模拟重力)。鸟的位置被修改为时间,角度初始速度和施加的初始速度的函数。 在飞行中,玩家有可能释放鸡蛋(在分配的鸡蛋数量的限制内:eggLeft)。玩家也可以将鸟类允许(鸽子或蜂鸟)悬停。目前的鸟的死亡:当目前的鸟死亡(与块碰撞,与猪碰撞,在窗口中消失或飞行时间用完),鸟被添加到updateEntity函数的删除列表中删除。然后,再次返回列表,以确定新的当前鸟。

部分代码以及相应的页面布局:

public abstract class Bird extends Entity {

protected short flyingTime;    

protected int eggLeft;

protected ArrayList<Egg> eggs;

protected boolean isFlying;     

protected boolean isMoving;

private double time;            

protected Dimension frameSize;  

private double accelX;

private double accelY;

protected int startLocationX;   

protected int startLocationY;

private long lastTime;

private long flyingTimeLeft;

public Bird(int width, int height) {

super(100,440,width,height);  

isMoving = false;  

time = 0.1;   

accelX = 0;   

startLocationX = 100;         

startLocationY = 440;  

accelY = 9.81;  

flyingTimeLeft = 10000;      

}

public int getEggLeft() {

return eggLeft;

}

public void setEggLeft(int i) {

this.eggLeft = i;

}

public int getStartLocationX()

{

return startLocationX;

}

public int getStartLocationY()

{

return startLocationY;

}

public abstract void hovering();

public boolean isFlying() {

return isFlying;

}

public void move() {

     if(isFlying){

     if(isMoving){

     hitBox.x = (int) Math.round(speed*Math.cos(angle)*time+0.5*accelX*time*time+startLocationX);

     hitBox.y = (int) Math.round(0.5*accelY*time*time-Math.sin(angle)*speed*time+startLocationY);

     time+=0.1;

     }

     long currentTime = System.currentTimeMillis();

     flyingTimeLeft -= (currentTime - lastTime);

     lastTime = currentTime;

     }

     if (hitBox.y > (int) frameSize.getHeight() || hitBox.x > (int) frameSize.getWidth() ||flyingTimeLeft <= 0 )

     visible = false;

     //

    }

    

public void launch(){

     isMoving = true;  

     isFlying = true;  

     lastTime = System.currentTimeMillis();

    }

    

public void moveRight() {

accelX+=0.1;

    }

    

public void moveLeft() {

     accelX-=0.1;

    }

public long getFlyingTimeLeft() {

return flyingTimeLeft;

}

}

5.7 胜利管理功能实现

每个级别都有一些敌人(猪)。 当所有级别的猪都被杀死(从实体列表中删除)时,游戏将获胜。 胜利测试在updateEntity()函数中执行。 当浏览实体列表时,我们更新猪的行为,或者如果实体列表不再包含任何猪实例,则游戏级别完成。 一旦达到这个级别,该程序将进入WIN()函数,这将允许执行胜利的所有条件。 与胜利管理相同的方式,失败事件在upledateEntity()函数中触发,但在这种情况下,事件不是在实体中的鸟的实例中完成的。 当列表中没有更多的鸟类时,将调用lost()函数。 在这种情况下,首先处理猪是重要的,所以当没有更多的鸟或猪时,不会调用lost()函数(对于win()函数的调用将优先进行)。

图5-12  胜利后的提示信息

图5-13 失败后的提示信息

部分代码以及相应的页面布局:

public void win() {

javax.swing.JOptionPane.showMessageDialog(null, "祝贺你胜利 ,你的分数是: " + score+".");

currentPlayer.finished(currentLevel, difficulty, score);

Level lvl = new Level("res/maps/lvl0" + (currentLevel+1) + ".txt",difficulty);

if (lvl.isLoaded()) {

angryView.setMap(lvl);

this.setMap(lvl);

this.setCurrentLevel(currentLevel+1);

}

else {

javax.swing.JOptionPane.showMessageDialog(null, "没有此难度的地图!");

}

}

public void lose() {

javax.swing.JOptionPane.showMessageDialog(null, "游戏结束,再试一次?");

Level lvl = new Level("res/maps/lvl0" + (currentLevel) + ".txt",difficulty);

if (lvl.isLoaded()) {

angryView.setMap(lvl);

this.setMap(lvl);

}

}

5.8 玩家管理和备份管理功能实现

这个功能目的是为了保存玩家的进度,让他恢复之前所成功进行的游戏。 因此,设置了一个“配置文件”系统:保存文件夹包含每个视图页面的备份文件,其中包含他的名字。 该文件包含对应于游戏的对象的序列化。

Player类专门为此而设计。 游戏可以选择从先前保存的配置文件中选择,或者创建一个新的配置文件。 

玩家相关数据:对于每个玩家,希望存储他的名字,他成功的水平以及获得的分数。 因此,Player类包含一个名称的String字段和每个难度和级别的两个ArrayLists,一个包含相应文件的解锁级别,另一个包含后续级别的分数。

序列化:为了在双方之间保存数据,有必要序列化Player类。为此,最简单的方法是使用Java提供的ObjectOutputStream和ObjectInputStream。我们不需要担心对象的序列化实现的细节,或者从文件中读取的细节。这种技术防止备份文件被我们的其他程序读取,但这并不是这个项目的问题。另一个问题可能是游戏的名称:在游戏中,备份文件被命名为“nom.save”,但是在创建新配置文件时输入的名称没有进行验证。

得分:只有最高的分数保留给玩家和给定的水平。当玩家完成一个级别时,Player类的finished()方法被调用,其中级别和score作为参数。完成后检查,看看给定的分数是否是一个新的记录

图5-14 已经存在的玩家信息

部分代码以及相应的页面布局:

public Player(String name) {

this.name = name;

easy = new ArrayList<Integer>();

medium = new ArrayList<Integer>();

hard = new ArrayList<Integer>();

extreme = new ArrayList<Integer>();

easyScores = new ArrayList<Integer>();

mediumScores = new ArrayList<Integer>();

hardScores = new ArrayList<Integer>();

extremeScores = new ArrayList<Integer>();

highestEasyScores = new ArrayList<Integer>();

highestMediumScores = new ArrayList<Integer>();

highestHardScores = new ArrayList<Integer>();

highestExtremeScores = new ArrayList<Integer>();

for (int i = 0; i < LevelNumber.getLevelNumber(); ++i) {

easyScores.add(0);

mediumScores.add(0);

hardScores.add(0);

extremeScores.add(0);

highestEasyScores.add(0);

highestMediumScores.add(0);

highestHardScores.add(0);

highestExtremeScores.add(0);

}

this.save();

}

public static Player loadFromFile(String name) {

try {

FileInputStream fichier = new FileInputStream("save/" + name

+ ".save");

ObjectInputStream ois = new ObjectInputStream(fichier);

Player player = (Player) ois.readObject();

return player;

} catch (java.io.IOException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

return null;

}

public void save() {

try {

File file = new File("save/" + name + ".save");

file.delete();

file.createNewFile();

FileOutputStream fos = new FileOutputStream(file);

ObjectOutputStream oos = new ObjectOutputStream(fos);

oos.writeObject(this);

oos.flush();

oos.close();

} catch (java.io.IOException e) {

e.printStackTrace();

}

}

public String toString() {

return name;

}

public boolean isFinished(int level, String difficulty) {

if (difficulty.equals("easy")) {

return easy.contains(level);

} else if (difficulty.equals("normal")) {

return medium.contains(level);

} else if (difficulty.equals("hard")){

return hard.contains(level);

} else if (difficulty.equals("extreme")){

return extreme.contains(level);

}

return false;

}

public void finished(int level, String difficulty, int score) {

if (difficulty.equals("easy")) {

if (!(easy.contains(level)))

easy.add(level);

easyScores.set(level-1, score);

if (score > highestEasyScores.get(level-1))

highestEasyScores.set(level-1, score);

} else if (difficulty.equals("medium")) {

if (!(medium.contains(level)))

medium.add(level);

mediumScores.set(level-1, score);

if (score > highestMediumScores.get(level-1))

highestMediumScores.set(level-1, score);

} else if (difficulty.equals("hard")) {

if ( !(hard.contains(level)))

hard.add(level);

hardScores.set(level-1, score);

if (score > highestHardScores.get(level-1))

highestHardScores.set(level-1, score);

} else if (difficulty.equals("extreme")) {

if (!(extreme.contains(level)))

extreme.add(level);

extremeScores.set(level-1, score);

if (score > highestExtremeScores.get(level-1))

highestExtremeScores.set(level-1, score);

}

save();

}

public String getName() {

return name;

}

6 系统测试

6.1系统测试简介

系统测试(System Testing)。它是软件、计算机硬件和已被定义为信息系统进行各种装配测试和验证测试的外围设备的组合。系统测试是对软件需求分析、设计和编码实现的回顾。通常系统测试描述的定义有两个,一个是找到程序执行错误的过程;另一个是基于软件开发的各个阶段的规范和程序的内部结构,并仔细设计了一些测试用例,并使用这些测试用例来运行程序并找到错误的过程。系统测试是对整个产品系统的测试,目的是验证任何系统满足规格要求的规范,找出符合要求的规格和不匹配或矛盾的地方,以便于公开全面的检查系统的弊端与不足。系统测试后发现问题,尝试找出错误的原因和位置,然后纠正它。根据黑匣子类测试的整体系统要求,应覆盖系统的所有组件。包括测试软件的需要,还有软件依赖于硬件、外设支持的软件及其界面。

必须尽快进行测试,防止在开发过程中形成了软件的漏洞,并且引入了缺陷。测试的目的是设计测试用例,通过这些测试用例找到软件缺陷,以最小的成本和时间来发现不同类型的错误并且纠正。

图6-1所示为基本系统测试方案:

图6-1 系统测试方案

6.2 系统测试方法

测试方法系统可以分为静态测试和动态测试两种,测试分为静态检测的计算机以及黑盒测试和白盒测试的动态测试。黑盒测试,也称为功能测试,则测试程序模块为黑盒,即不管内部配置和程序的处理,只对界面操作来测试程序。白盒测试,程序为透明白盒,即了解内部结构和详细的处理步骤,对程序的内部结构进行测试。即设计每个测试所需的逻辑路径,并检查每个循环和每个分支。另外在设计测试用例时,应该根据软件测试的原理,选择那些发现测试数据错误的可能性很大的输入数据。

6.3 本系统测试

6.3.1 测试用例设计

如下表1位测试用例设计表

表1 测试用例设计表

测试用例

系统测试

测试项目名称

基于Java的愤怒的小鸟游戏的设计与实现

测试用例编号

01

测试人员

李晨阳

测试时间

2017-5-20

测试项目标题

所有基本页面能正确显示连接并且游戏功能正常体现

测试内容

验证系统客户端首页是否能够正常显示

验证各功能是否能够正常实现

验证系统客户端内各模块内容信息是否正确

验证游戏功能是否正常实现

测试环境与系统配置

软件环境

Eclipse nexo

硬件环境

CPU:I5-3230M  内存:8G 显卡:GT-730M

网络环境

2人共享50MB/s 带宽

测试输入数据

测试次数

15

预期结果

可以正确显示系统客户端首页

可以实现各个功能

可以正确显示系统客户端内各模块内容

可以正确的进行各种游戏功能

测试过程

多次打开软件进入首页看首页是否正常显示

查看各功能的实现结果是否正确

查看每个页面的连接是否有误

进行不同角度、力度释放弹弓,查看小鸟飞行轨迹以及碰撞效果是否有误

测试结果

可以正确显示系统客户端首页

可以正确的实现各个功能

可以正确显示系统客户端内各模块内容信息

可以正确的系统内各种游戏功能

实现限制

6.3.2 测试方法和结论

主要测试系统是当前系统的测试方法,系统要求的总体规格是基于黑盒测试类别。 测试系统是基于计算机系统的要素、硬件项目、外围设备、一些支持软件,数据和人员的组合以及应用程序健康管理正确的功能,调试和其他测试的整体的软件系统。

测试系统的不同功能模块后,结果表明该软件工作良好,达到系统设计目标和功能要求达到预期目标

结论

经过近两个多月的忙碌之后,游戏开发之愤怒的小鸟可以根据用户的需求完成全部功能。我亲身经历了这一段程序从小到大,从无到有,伴随着整个设计过程,也正是我的学习过程。在整个设计过程中是不断学习、识别问题、分析问题和解决问题的过程,从这次毕业设计中我获得了很多很多感受、想法和经验。在完成的学习设计中吸取的经验教训,对我的未来工作都有很大的影响。另外,使用参考文献的过程中,有很多文献中精湛的细节值得认真学习和理解。我毕业设计的项目是我刚刚完成的最重要的程序。在此期间,Java学习在慢慢进步,使我感觉回到了初学者的时候,各种需要解决的问题我都是从互联网得知,由于Java是一个成熟的技术,网络上参考资料非常多,特别是陈锐的《Java游戏课程原理与实践》对我的帮助非常之大,而且我在网络上获得的资源,都是由作者、翻译者辛苦的工作成果,他们发布在在互联网上免费下载,是开源精神的完美体现。我相信在未来的学习中,我将把我的游戏设计的更加完美和成熟!这次毕业时机经历也是我大学学习中最重要的一段经历,更是我人生中不可或缺的一部分。


http://www.kler.cn/a/503830.html

相关文章:

  • 【make】makefile 函数全解
  • EasyCVR视频汇聚平台如何配置webrtc播放地址?
  • Anaconda安装(2024最新版)
  • 逻辑测试题
  • [Flutter] 使用ScrollController配合EasyRefresh实现列表预加载:在还未滑动到底部时加载下一页数据
  • R语言的语法糖
  • CSS | CSS实现两栏布局(左边定宽 右边自适应,左右成比自适应)
  • 支持Google Analytics快捷添加的CMS:费用与部署形式详解
  • 数据结构—《二叉树的定义与特性》
  • 软件设计模式的原则
  • pg_hba.conf是PostgreSQL中控制客户端认证和访问权限的配置文件
  • C# 将 List 转换为只读的 List
  • vue3 实现 “ fly-cut 在线视频剪辑 ”
  • 【MySQL】count(*)、count(1)和count(列名)区别
  • JAVA:利用 RabbitMQ 死信队列实现支付超时场景的技术指南
  • 第424场周赛:使数组元素等于零、零数组变换 Ⅰ、零数组变换 Ⅱ、最小化相邻元素的最大差值
  • OJ题目下篇
  • AI赋能下的美颜API与滤镜SDK:从传统到深度学习的进化之路
  • 深入理解 Python 的装饰器
  • Elasticsearch ES|QL 地理空间索引加入纽约犯罪地图
  • 计算机的错误计算(二百一十一)
  • 交互数字人:革新沟通的未来
  • Java Agent(三)、ASM 操作字节码入门
  • 【机器学习】神经网络训练技巧
  • 使用VSCode搭建Ruby on Rails集成开发环境
  • mac intel芯片下载安卓模拟器