MIT OpenCourseWare
OCW Home Course List About OCW Help with OCW Feedback


Search
» 高级搜索
 课程主页
 教学大纲
 教学日程
 参考读物
 讲义
 复习
 实验
 作业
 考试
 项目
 工具
 相关资源

项目


阅读本文的修正请点击这里

讲义

目录: 

介绍

你们的最终课题是设、归档、建立和测试一个撞球游戏。 撞球是弹球的一种, 这个游戏的玩法是保持一个球在游戏区中不断移动,不能让球接触游戏区的底部。游戏者控制一系列挡板对球的运动方向进行控制。

撞球游戏优于传统弹球游戏的地方在于它允许玩家自己通过放置一些小物件(比如障碍物,挡板,吸收器)设置他们的游戏机布局。这些游戏机布局同样允许组成复杂的"Rube Goldberg" 装置,但是这样一来它就更适合于看而不是玩。 (如果你不懂什么叫Rube Goldberg ,请查看http://www.anl.gov/OPA/rube/index.htmlhttp://www.rube-goldberg.com/). 作为一个可选项 (当你已经设计,归档,实现,测试了所有的需求功能),你可以创建一些不同的撞球游戏。
返回顶部

撞球游戏预览

因为这个项目部分来说是一个设计练习,这个任务具体说明了用户应该做什么并且由你决定如何设计模块和界面。这一部分总体预览一下撞球游戏。 更详细的说明参看附录1。为了自动化的进行测试,除了对图形用户界面进行粗略的说明外,你的实现文件必须支持一种文件格式 (定义在附录 2)。

撞球游戏有两种模式的图形用户界面,建造和运行。在建造模型中,用户可以:

  • 在游戏界面创建和编辑方块,圆形,三角形障碍物

     

  • 创建和编辑挡板

  • 将挡板和障碍物的动作与触发相连,比如一个键被按下或者 一个障碍物被凸起,和

     

  • 在文件中保存和装载用户的游戏设置

     

在运行模式,用户可以运行这个游戏。


撞球游戏执行时的一个截图
你的实现可能看起来有些不一样球的运动不一定和图示的一致


上面的图片展示了撞球游戏最终要的特点。

  • 游戏界面边上的调色板向用户提供了许多不同的操作(方块, 圆形, 三角形, 挡板) 以便在游戏区放置一些小物件。
  • 底部的“修正”工具栏向用户提供一些不同的操作 (移动, 删除, 旋转) 用以在游戏区编辑小物件。
  • 修正工具栏同样提供一个连接 按钮用来将一个小物件的触发连接到另一个的动作上面. 当这个按钮被按下以后,用户能所有的小物件连接起来。例如,用户可以按下连接键,然后点击一个圆形障碍物,再点击一个挡板。结果,每次激活这个障碍物(当球撞击障碍物),挡板将沿着它的轴旋转,换句话说,用户可以按下连接按钮,然后按下一个键,然后回答一个键按下或者弹起那个更喜欢一些这个问题,然后点击一个挡板,结果每次用户按下一个键(或者释放),挡板将会移动。几个不同的触发可能激活同一个小物件。
  • 横跨游戏区底下的紫色条是吸收器,当球进入吸收器,球停止运动同时在吸收器的右角被从新弹起,吸收器的作用是将它吸收到的球重新竖直的弹起来,将吸收器与其自身连接允许游戏可以不停止的运动:每次当球进入吸收器,它马上被重新弹起来。
  • 在窗口顶部的按钮菜单允许用户存储或者装载游戏设、启动或者终止游戏。在上面的动画中:弹球是一个蓝色的小圆圈,它从游戏区的右下角弹起,弹球在红色,绿色,蓝色的障碍物上反弹,同时被黄色的挡板击打。

返回顶部

打分及安排 打分

你们将会三四个人分成一个小组,除了特殊情况,同一组内的成员将得到同样的分数。

项目阶段 成绩百分比 打分原则
初步设计
10%
你了解问题么?
与助教的每周会晤
5%
组内所有的成员都积极的参与么?
初步发行
25%
这是不是一个好的设计?所要求的功能是否已经实现?
设计审评
25%
有没有认真地分析合同及交易?
实现与测试
35%
软件最后能不能工作?你是否已经进行演示了?

任务的每一阶段都应该按时提交给你的助教,不管是用电子形式或者是真正的拷贝。所有的源代码和已经编译的代码都必须在2001年十二月10前公布在你的小组的项目词典里面。因为组队有时间限制,所以不可能有太晚任务分配。

返回顶部 初步设计

初始设计必须在规定日期以书面文件提交。

你的初始设计应该在15页以下。 它是 软件系统归档 所描述的册子中的一部分。它包括一个经过修改的说明,一个设计(包括一个问题对象模型),还有一个实现概览(包括一个或者多个代码对象模型和一个模块依赖图)。你不需要提交你的详细设计信息。

修正的说明、设计和实现概览必须进行讨论:

  • 用户用来构造游戏面板的编辑器。例如,说明里面应该包括图片以及编辑器的屏幕截图还有一些建模用例。
  • 你的物理环(physics loop)。它描述了怎样以及按何种顺序运动,冲突检测,分析决定,触发,摩擦/重力,绘图以及其他因素被操作的。
  • 将触发器与动作连接起来的触发系统。

初始设计应该包括一个项目计划,里面列出了小组的里程碑以及每个成员的任务分配。里程碑是指期望的某个时间前需要完成的任务,例如一个测试模块或者一个规定的功能。在初始设计以及最终期限之间必须包括两个里程碑。劳动力的分配应该是公平的。
(将里程碑和和任务分配合并到一个单一的包括任务,分配人员,完成时间列的表将会比把里程碑和任务分配写在两个单独的文档中更为有用。)

初始设计将根据表达的清晰度,是否已经认真地思考了问题,了解了主要问题以及遇到的挑战,是否已经提出了一个有意义的设计来进行打分。

返回顶部

与助教的每周例会

小组与他的助教每周会晤半个小时。会晤时间安排在课堂时间。为了达到更好的参与效果:

  • 每次会议所有的成员必须参与。
  • 每次会议中每个成员必须回答问题和参与讨论。
  • 每周例会中必须提交一份对讨论有用的清晰的进度文档,在第一次会晤中,初步设计的草案可以作为进度文档。

虽然要求进度文档必须是清晰的,但是它仍然是简短的和不正式的。这些文档将是会晤讨论的基础。助教将会把它作为进度记录保存在文件里面。会晤中小组应该有多份文档拷贝,助教和成员各持一份。进度文档应该包括以下的信息:

  • 前几周内发现的新问题的描述,包括新发现的bug和还没有解决的设计问题。
  • 在过去几周内已经解决的问题的描述,包括已经修复的bug以及如何修复的说明,还包括一些已经解决的设计问题及如何解决的。
  • 一直没有的到解决的问题。
  • 下周的计划,规划每个成员的具体工作和目标。
  • 上周计划实现的评估。
  • 文档还应该包括一些其他的你觉得能说明你的进度的材料,例如对象模型或者一些表明设计变动的MDD片段。

返回顶部

初步发行

初步发行版包括两个部分:一个最终的设计的书面记录以及基本功能的演示。

返回顶部

最终设计

最终设计文档是在初步设计文档的基础上经过修改和完善完成的。(例如,它应该包括你的测试策略的一部分。)你的最终设计文档应该在20页以下。在项目计划部分,说明所有你错过的里程碑以及为何错过?

既然最终设计是作为初步发行的一个里程碑看待的,那么你应该在你的设计中找出所有的主要的错误。因此,最终设计应该按照它是否是一个好的设计以及表达是否清晰来进行打分。

返回顶部

需求功能

在初步发行前,我们会要求你们演示一些规定的功能。

为了演示在初步发行之前你做都做了什么工作,即使演示推迟一些或者在这期间你也做了一些修改,在演示之前你必须保存一份你的代码。你可以把你的源代码拷贝下来或者用jar file将编译后的代码存下来,然后,你可以将jarfile放到你的类路径里面来运行你的演示,或者直接把jar做成可执行文件。

可以尝试运行四种单独的Java™ 程序来演示以下的功能:

  1. 展示挡板的设计情况。当一个键被按下,挡板应该旋转90度;当这个键被释放,挡板应该回到原来的位置。当挡板在原来初始的位置时,这个动作可以不断地重复。(你不需要演示在建模过程中如何将键盘键与挡板连接起来。)
  2. 演示一个起作用的或者工作的吸收器,球的运动,重力,摩擦。在运行模式,屏幕上没有障碍物和挡板,球停留在吸收器上,当按下一个键时,可以观察到球从吸收器上弹起,随着它的升高速度越来越慢,最后返回到吸收器,回到它初始的位置。同样,这个过程也是可以重复的。 (注意你不需要支持可配置的重力和摩擦常数.)
  3. 控制球与障碍物和墙的碰撞。在这一步不需要关注球与挡板的碰撞。在运行模式,当球从吸收器发射出来后,它与障碍物和墙的碰撞行为必须正常。
  4. 展示装载文件的标准格式。给定一个测试文件,实现必须展示文件中所说明的东西。你可以装载和显示所有的标准物件

运行状态下的动画必须是平滑的而且足够展示上面所要求的一些特点。

返回顶部

修正

提交初步发行版本以后,很快你会收到一份程序说明的修正本,在大型的项目中,随着开发过程需求经常变动的。你的设计应该足够灵活以适

应说明中一些可能的变动。

返回顶部

实现与评价

你应当在不多于20页的文档中描述你的实现和评价你的设计(除了在项目2中的说明外,它占用了5到10页文档并且应该被放在附录中)。文档必须简要。

赢得1999年撞球设计大赛的队伍最终的报告只有7页。

将你的代码放在你们组的项目词典中,并且可以在线访问,你们不需要把代码打印出来。

最终报告中包括一部分专门说明设计变动。详细说明到底设计中那些部分做了变动。说明必须精确。有一点非常重要,你们的助教必须有修改过的说明,实现概览,有效性策略的描述部分。

这些描述部分应该正确地描述了你提交的代码以及你为测试和有效性所做的工作。如果描述不是正确的,提交可能不会被精确的打分。

为了演示(下面即将提到的),你必须做一个实现的jar file ,这个文件必须是可运行的并且包括你所有的代码,不管是源代码还是编译过的代码。

给你们的助教展示你们的程序。组内所有的成员必须参加并且给出一个简短的演示以及回答与他的工作相关的问题,陈述与演示不应该超过15分钟。除了另外的半个小时的问题与讨论。

返回顶部

设计竞赛

当你们提交你们的最终实现和评估时,可以在第一页文档中指明你们是否想要下列奖项:

  1. 最佳设计:具有最佳抽象,最佳模块化,最佳扩展性,简洁性等的将获得此奖,同时最终报告也会考虑到。
  2. 最佳撞球游戏:这项奖励将给于具有最佳游戏性的项目。此奖的申请书应该包括一个建立游戏区域的输入文件;这个奖是设给游戏本身的可玩性,而不是设给建造游戏的工具箱。
  3. 最佳艺术奖:设给具有最佳视觉效果的项目。申请书应该包括一个建立游戏区域的输入文件,当用此文件运行游戏的时候,在没有用户操作任何键的情况下,弹球也应该永远反弹。

在考虑最佳设计奖项时将不考虑你的程序是否只实现了基本的功能或者有额外的功能,但是另外两项奖项将会考虑这点。

获奖情况开始由助教提名,最终获奖将有讲师宣布。一个项目可以获得多个奖项。获奖者将在最后一堂课中宣布。

返回顶部

我们将给你的

用 Java™ 实现动画非常有挑战性。 你要用java.awt和javax.swing包来构建你的图形用户界面(GUI), 我们在Example.Java™ t中提供了一个例程来展示如何动画演示小球在窗体中的碰撞反弹过程。同时它还演示了如何让你的程序监听用户事件,比如点击一个工具栏按钮,按下一个键或者拖动一个鼠标。组内的所有成员都应该编译执行这个例程。

我们还提供了一个物理例程 (参看附录 3) 用以计算弹性碰撞的动力学状态。你可以随意的使用这些代码。

返回顶部

提示 概要
  • 设计
    一个细心的的设计将会给你节省很多时间,大家都知道初始阶段一个小小的错误在将来可能变得很大。初始设计是这个项目一个重要的部分(实际上比它在平分过程中占的比重还要大很多)。认真地进行初始设计,设法预测可能遇到的问题,那样你们项目的后续阶段将会很容易并且富有乐趣。

  • 原型
    这种设计问题的一个最大挑战是计算出“gotchas”在那里?如果你觉得开始构建设计有些困难,可以先建造一个原型。最后抛弃这个原型。一旦你学会如何正确的设计一些东西,你会发现修改一个破旧的版本是没有意思的。

  • 早期并且经常进行有效性验证
    有效性验证不应该在事后才想起。你可能因为一个设计的实现容易测试而选择它。保证每次实现后都要进行有效性验证。

  • 早期并且经常进行文档记录
    不完整的文档总比没有文档好,如果一个潜在的问题发生,但是你没有时间或者不能解决它,就在你的文档中加一句话记录下来,等到以后有时间了在回去修改它。

  • 不要记录多余的东西
    不要包括任何多余的材料,例如,你没有必要解释黑盒测试和白盒测试间的区别。只需要标明你用的是那种测试。同样的,描述必须是严格的而且是明晰的。你可以假定你的助教知道什么是集合和栈。你不需要从头解释一个东西,直接用标准术语就行。

  • 快乐的参与到小组中
    快乐的参与到你的小组中,有好的创意与你的队友分享,与他们讨论遇到的问题。阅读讨论每个人的代码。发现bug的一个好的方法就是让别人阅读你的代码。开始行动吧!

  • 有效的通信
    每次会议应该包括:
    • 会议议程。如果不懂原因就不要开会,这样就不会浪费时间。
    • 一个会议领导。会议领导必须保证会议按预定的方向进行,鼓励所有的成员积极的参与帮助解决问题。
    • 一个秘书,专门负责会议记录以及将它们分发到组内成员。这些记录重点记录了一些重要的决定以及一些已经解决或者还没有解决的问题。它们保证所有的决定必须得到组内全体成员的同意,并且每个人都清楚会议提到的问题。
    会议中每个人扮演的角色应该轮换。在6.170中,每个人都平均的扮演角色。

  • 列入优先地位


    我们在整理这个项目中将会有很多乐趣,我们的目标是给你提供的这个项目既有挑战性,而且也有很多机会展示你的创造性。我们鼓励你们进行尝试。尽量让你的实现好看而且好玩。也就是说在你添加一些新的功能或者铃声或者哨音前,你应该先确保项目基本功能的实现。要使这个项目变得可扩展的最好方法就是保证你的初始设计灵活而且易于扩展。

返回顶部

编码

撞球文件的语法设计得容易阅读。 BufferedReader.readLine() 方法可以读到一个命令,StringTokenizer 类可以将命令分解到它的组成部分中。

在尝试编写你的图形用户界面前你应该拥有关于SWING的基本知识。参考SUN公司的Swing 指南 (特别是快速漫游 和总体概览 部分)。

不要用实时时钟去计算时间。相反,应该每祯时间内接受一个时间事件,并且处理仿真还有屏幕更新。如果你落后了并且时间变慢了,那就由它吧。解决这个问题的简单的方法是用类Javax.swing.Timer , 就像在例子 GUI。用这种方法可以简化代码实现,并且可以避免处理在多线程程序中的同步问题。

如果你使用SWING并且希望用它来绘制你的部件就像你为了绘制底板物件还有弹球,你应该扩展Javx.swing.JComponent 并且实现你自己的绘图例程。为了这么做,你必须重载你的paint 方法,绘图工作时通过调用所提供的Java.awt.Graphics 对象。除非你很明确地把它关掉了,否则SWING的部件会自动双缓冲地减少闪烁。如果你不理解,别担心。除了图形 Java™ 现在还有一个可选的图形上下文Java.awt.Graphics2D ,它比传统的图形对象提供更复杂的功能。要注意到你的部件受到的用于绘图的调用总会有一个GRAPHICS2D,这个调用是作为参数传递的。所以如果你想使用 你应该简单的列出这个图形对象。你可以用任何一种图形风格实现撞球游戏,但是你可能会考虑这两种绘图风格的一些区别:

  • 图形对象以整数值表示的像素工作,允许你更直接的控制哪个像素应该被更新。
  • 另一方面,Graphics2d接受浮点值定义几何外形用以润色和实现光栅。这个在某种程度上是自动的,但是也使直接设置单个像素更加困难。
  • Graphics2D类同样允许将 应用于它。(一个仿射变换是一种保持平行的变换。)

为了响应用户的鼠标和键盘动作,你需要创建和安装MouseListener, MouseMotionListener, 和 KeyListener 所有这些你都可以在Java.awt.event 包中找到。 关于 Java™ 键控代码的信息可以在Java.awt.event.KeyEvent的文档中找到。

按键

撞球游戏中对于键盘操纵的说明必须写明当那个键被按下或者释放,与此键相连的对象必须被触发。这个提供的行为与真正的撞球游戏一样:按下按钮使挡板向上运动,释放按钮则挡板回到原来的位置。

键盘事件

Java™ 关于Java.awt.event.KeyEvent 的说明描述了三种类型的键盘的事件:KEY_PRESSED, KEY_TYPED, and KEY_RELEASED这些文件中指明当一个键被用户按下后,KEY_PRESSED 事件发生,当这个键被释放后,KEY_RELEASED 事件发生。因此当收到邦定到小物件的KEY_PRESSED 或者KEY_RELEASED 事件时它就是一个合理的触发。

不幸的,当用户只按下一个键时,大多数 Java™ 运行时用了多重的KEY_PRESSED 以及有些情况下多重的KEY_RELEASED 事件。 此外,在有些环境下你可能释放一个键而不会受到KEY_RELEASED 事件。这是因为这两个事件是系统依赖的。这个行为通过与操作系统交互发生,如果你按下一个键持续一段时间那么这个行为将重复发生。

在Windows下

在windows下,当按下一个键时Java™ 将产生多个KEY_PRESSED 事件,当这个键释放时却只产生一个KEY_RELEASED 事件,例如,按下“A”键将会产生下列事件:

PRESSED 'A'
PRESSED 'A'
...
RELEASED 'A'
在Unix下

在unix下,当按下一个键时多个成对的key事件将发生:

PRESSED 'A'
RELEASED 'A'
PRESSED 'A'
RELEASED 'A'
...
PRESSED 'A'
RELEASED 'A'

返回顶部

测试程序

如果你想在你的环境下看看你的程序的行为,你可以用提供的KeypressTest 类。这个应用程序将把所有的键盘事件传递到控制台用以监视。

源程序可以点击这里看到:KeypressTest.java

返回顶部

解决方案

当你察看程序包时你应该很容易的把握Java™ API 的细微差别,一个简单的解决办法是关闭操作系统的键盘自动按键重复机制,因此可以让事件KEY_PRESSEDKEY_RELEASED 与它的实际动作响应的更贴合。

  • Unix/Linux: 键入“xset -r”关闭自动重复,开启自动重复用“xset r”

  • Windows: 进入控制面板的辅助功能选项程序,在键盘选项卡上选择筛选键设置按钮,选择忽略快速击键并减缓重复速度,选择下一个设置按钮。确保选择“没有键盘重复”。滑动slowkeys滑动快到短边(0.00)。按下ok键两次。选定“使用筛选键”并且按下ok。同样,取消这个功能只需反向操作即可。

要求最终用户进行这样的设置是可以接受的,但是这一点应该包括在你的撞球文档中。

另一种方法是利用提供的一种特别的键监听装置。这个类以编译过的形式存在gb-lib.jar 文件中,名称为staffui.MagicKeyListener。参考关于 MagicKeyListener 的文档或者直接用提供的源代码 作为你的出发点。

返回顶部

系统管理以及计算机工具 返回顶部 修正控制

我们强烈的建议你们使用修正控制包比如CVS 来帮助你们协调工作,防止代码丢失,允许给以前的版本进行备份。

返回顶部

构造:系统模型

另一个你会觉得有用的工具是make,这个工具允许你编写一个一个makefile文件,它是你系统的一个模型:哪一个文件包含代码,编译系统要做些什么工作,测试时需要做什么工作,清理词典需要做什么工作,等等。一旦你编写了这个文件,你可以通过键入一个简单的命令来做前面说到的任务中的任何一个。

在Athena中,键入info make。

返回顶部

Java™ Archive (JAR) 文件

你应该把你的应用程序的所有部分收集起来创建一个jar文件提交给你的助教。jar文件是非常有用的,用它可以存储所有的源代码,编译过得代码,把一些数据文件(比如图像或者声音)关联到一个集中的文档中,便于以后发布。

要了解更多的jar文件,参看Sun公司的jar 工具 手册。

作为一个快速的参考,这里有一些使用jar命令的例子:

从gizmo和ball包的类中创建gizmo.jar:

jar cvf gizmo.jar gizmo ball

列出jar文件的内容,使用:

jar tf gizmo.jar

要创建一个可运行的gizmo.jar,使用:

jar cvfm gizmo.jar JarMainManifest gizmo ball

在上面的例子中,文件JarMainManifest应该包括命名进入点的单独的一行:

Main-Class: gizmo.StartGizmoball

运行这个应用程序,使用:

Java -jar gizmo.jar

要点: 注意,当使用-jar选项运行一个应用程序时,CLASSPATH变量以及-cp命令行开关是被忽略的 。因此,为了把存储在可运行jar中的应用程序中的物理库包括进来,你必须把.class文件从我们的jar中提取出来,并且把它们包含在你们自己的里面。你可以用这种方法把一个jar文件提取到当前目中:

jar xvf /mit/6.170/lib/gb-lib.jar

使用makefile,就像上面部分说过的,可以很简单并且自动的创建jar文件,对于将physics类文件合并到你的应用程序中所包含的复杂性以及维护问题,我们建义你使用一个Makefile来自动完成这个过程 。

返回顶部

附录 1: 详细需求 概要

你的实现必须支持两种执行模式:建造和运行。在建造模式,用户可以在游戏区增加小物件并且能修改已经存在的小物件。在运行模式,弹球在游戏区运动并且与小物件发生碰撞。

游戏区

游戏区必须至少20L宽,20L高,也就是说,在游戏区可放置400块障碍物,并且没有重叠。左上角是(0,0),右下角是(20,20)。当我们说一个小物件在某个位置时,指的是它的原点在那个位置。每个小物件的原点指的是它的外围框的左上角。所以在一个20LX20L的面板上一个小物件被放置的位置的最深点是(19,19)。弹球的原点在它的中心。

在建造模式,小物件必须一格一格的放置,也就是说,用户只能这样放置小物件:(0,0),(0,1),(0,2)...

在运行模式,动画珊格的粗糙度不能超过0.05L.假设弹球在位置(1,1)并且正向(1,0)方向运动,也就是由左到右,图像每绘制一桢小球移动0.05L。弹球应该依次显示在位置(1,1)(1.05,1)(1.10,1),如果你希望动画流畅一些,应该在更多的位置显示弹球。挡板的旋转动画可以显示的粗糙一些;可以参看下面关于--的详细描述。如果弹球运动比每桢图像中绘制珊格的速度还快,那就不需要在每个动画珊格位置重新绘制图像。

返回顶部

建造模式

在建造模式,用户可以:

  • 在游戏区增加任何一种可用的小物件类型。
    • 尝试把一个小物件放置在一个已有的小物件上面或者游戏区的边界上面是不允许的(也就是说,它应该是无效的)。
  • 在游戏区把一个小物件从一个位置移动到另一个位置。
    • 尝试把一个小物件放置在一个已有的小物件上面或者游戏区的边界上面是不允许的(也就是说,它应该是无效的)。
  • 对任何一个小物件进行顺时针90度旋转。
    • 旋转对于轴向对称的小物件是不起作用的。例如,圆形的障碍物不管对它进行多少次旋转,它看起来都是一样的。
  • 把某个特定小物件的触发连接到某个特定的小物件的动作上。
    • 当弹球碰撞一个标准小物件时,它产生一个触发,并且最多展示一个动作(例如,移动一个挡板,把弹球从吸收器弹射出去,变幻一个障碍物的颜色)。一个小物件产生的触发能被连接到多个小物件的动作上。同样的,一个小物件的动作也可以被多个触发激活。最基本的小物件的要求的触发与动作 在下面描述。
    • 注意触发并不形成链,也就是说当A被连接到B,B被连接到C,弹球碰撞A只能引起B的动作的触发。
  • 把一个按键触发连接到一个小物件的动作上。
    • 每个键盘键当按下时都产生一个唯一的触发。同小物件产生的触发一样,键盘键触发同样可以被连接到许多小物件的动作上面。
  • 从游戏区删除一个小物件。
  • 向游戏区添加一个弹球。
    • 用户可以确定弹球的起始位置以及运动速度。
    • 尝试将弹球放置在一个小物件或者游戏区边框上面是不被允许的(也就是说,它应该是无效的),在标准小物件设置中有一个例外:静止的弹球应该被放置在吸收器里面。
  • 将设置保存在用户自己命名的文件中。
    • 你必须能按照附录 2给出的 标准格式 将你的设置存入文件中,如果你愿意,你可以定义一个标准格式的扩展,用来处理你的实现中的一些特别之处。果真如此的话用户必须有选择两种格式中一种的可能。
    • 存储的文件必须包括所有在游戏区的小物件,所有的触发与动作之间的连接,弹球的现在位置以及运动速度的信息。
  • 装载用户命名的文件。你必须能够把保存的游戏装载进来。
  • 切换到运行模式。
  • 退出程序。

返回顶部

运行模式

在运行模式,用户可以:

  • 在任何时候可以切换到建造模式。
    • 当一个挡板正在运动时,用户要求切换到建造模式,此时可以延迟切换,等到挡板运动到它的轨道的末端。
    • 同样当小物件的状态正在改变时也是允许延迟的。
  • 按键,因此产生连接到小物件动作的触发。
  • 退出程序。

在运行模式,撞球游戏应该:

  • 提供比较流畅的弹球的运动动画。
    • 默认的弹球的直径大约必须为0.5L。
    • 弹球的速度可在0.01L/秒到200L/秒的范围内变化,并且能覆盖很大的范围。如果你愿意游戏应该能支持.0/秒(静止)。
    • 应该用一个可以接受的刷新频率来产生比较流畅的动画。我们发现每秒20桢的刷新速度在一个相当宽范围的平台上也可以工作的不错。
  • 在游戏区显示比较合理的弹球与小物件的行为动作。也就是说,弹球应该弹回它应该弹回的方向,并且应该拥有它该有的速度,就像在实际撞球游戏中一样。
  • 不断的修改弹球的速度模仿重力的影响。
    • 你应该支持标准的重力加速度:25L/秒2, 就像在撞球游戏中倾斜的游戏表面一样。
  • 不断的修改弹球的速度模仿摩擦的影响。
    • 你应该用摩擦常数mumu2。来给摩擦建模,对于充分小的delta_t's 你可以用如下公式计算:Vnew = Vold * (1 - mu * delta_t - mu2 * |Vold| * delta_t).
    • 默认的mu值是每秒0.025。
    • 默认的mu2值是每L0.025。

返回顶部

标准小物件

你必需提供7种标准的小物件:障碍物(方块,圆圈和三角),挡板(左边的和右边的),吸收器,以及外围墙。

1.0的反射系数是指弹球离开障碍物时的能量与它撞击障碍物时的能量是相等的,只是撞击以后运动方向改变了。作为一个扩展,你可以让你的程序支持大于或者小于1.0的反射系数。

 

方块障碍物

边长为 1L的方型障碍物
触发: 当弹球撞击它时产生
动作: 没有要求
反射系数:1.0

返回顶部

圆形障碍物

直径为 1L的圆形障碍物
触发: 当弹球撞击它时产生
动作:没有要求
反射系数:1.0

返回顶部

三角形障碍物

一边为 1L 斜边为 Sqrt(2)L的右三角形
触发: 当弹球撞击它时产生
动作: 没有要求
反射系数: 1.0

返回顶部

挡板

边框为 2Lx2L的矩形旋转框
触发:当弹球撞击它时产生
动作:旋转90度 (参看下面)
反射系数: 0.95 (参看下面)

挡板有两种类型,左挡板和右挡板。左挡板按逆时针旋转,右挡板顺时针旋转。

在运行模式,挡板不能超出它的外框。在编辑模式,挡板不能放置的在运行模式挡板会超出它的边框,或者它的边框会与其它小物件(的边框)重叠。

下面的图片显示挡板的初始位置。在运行模式,当一个挡板被触发,它按照箭头指示的方向旋转90度。如果在此被触发,挡板又回到原来的位置。

在这个图片中,挡板的外形和设计仅供参考,你的最终设计可以不同。


挡板的初始位置以及初始旋转方向。

像那三个标准障碍物一样,当弹球撞击挡板时它产生一个触发。

当一个挡板的动作被触发,挡板以每秒1080度的角速度旋转90度。当地二次触发后,挡板以同样的角速度旋转回原来的位置。

当挡板正在旋转时有一个触发,产生的动作由你定义。这里有一些建议,但是你不必局限于此:

  1. 当挡板在运动时忽略触发。这种安排可能由于按下或者释放一个键并不能促使挡板返回原来的位置而对于用户来说是不合适的。
  2. 等到挡板完成旋转(由以前的触发促使的)以后再响应动作。这种安排可能发生多次快速按键造成挡板重复动作一段时间,因而对用户来说可能不合适。
  3. 初始正向运动时最多将一个触发入队列,在返回时不允许入队列。在这种模型下,一个产生两个触发的键盘按下动作可能引起挡板动作并且返回,但是快速按键多次并不能将挡板控制比较长的时间。
  4. 马上响应所有的触发。如果挡板正向运动并且被触发,它马上切换到反向运动。在这种方式下,以键盘键按下和弹出为触发的的挡板的动作将与真实世界中撞球游戏的动作一样。

挡板的标准反射系数是0.95,然而,当计算弹球从挡板上反弹回来时的行为时,你必需考虑挡板上接触球的部分的线速度;因此,弹球在弹开挡板后可能比它接触挡板前有更高的能量。

返回顶部

吸收器

一个与游戏界面一样长度的矩形
触发: 当弹球撞击它时产生
动作: 发射一个贮存的弹球(参看下面)
反射系数:不可用; 球被捕获

当弹球撞击吸收器,弹球将被吸收器吸收并且停留在吸收器的右下角,球的中心距离吸收器底部0.25L,距离吸收器的右边0.25L。

如果吸收器正贮存着一个弹球,当吸收器的动作被触发,它将把这个弹球直射上去。默认的,弹球的初始速度是每秒50L,(在默认的重力以及摩擦值下,如果吸收器的底部的坐标y=20L,则每秒50L的速度可以使弹球有足够的能量碰到顶部的墙。)如果吸收器中没有贮存弹球或者刚刚弹射的球还没有离开吸收器,则它对刚刚收到的触发信号没有响应。

吸收器不能被旋转。

返回顶部

外围墙

围绕游戏区的密封障碍物
触发:当弹球撞击它时产生
动作: 没有要求
反射系数: 1.0

撞球游戏有一套完整的外围墙,用户不能移动,删除,旋转外围墙。外围墙刚好将游戏区包围着。

  • 在坐标y=0L处上面有一个水平的围墙
  • 在坐标y=20L处下面有一个水平的围墙
  • 在坐标x=0L处左边有一个竖直的围墙
  • 在坐标x=20L处右边有一个竖直的围墙

我们并不要求用户使用GUI把外围墙产生的触发与任何别的小物件连接起来。然而,标准文件格式 支持这种连接

返回顶部

附录 2: 撞球文件格式非形式描述

撞球文件时命令行脚本,它的语法也许在某种程度上说来比单独为一个游戏设计的更加丰富,但是同样也可以用来调试以及测试程序,而且独立于GUI。也就是说,文件格式规定了一个标准的接口,那样,助教就可以测试程序所实现的功能了。文件不仅包括一系列小物件以及它们的位置,而且包括执行撞球游戏功能的命令,具体涉及放置,删除,修改小物件。

撞球文件的每一行包括一个命令。每个命令由一个操作码和0或者一些参数组成;各个部分通过空格键、/或者跳个键分开,例如:

Triangle T1 0 7

这是一条单独的命令,它的操作码是“Triangle”,参数是“T1”,“0”和“7”。这条命令指定程序应该创建一个新的三角形障碍物,放置在位置(0,7)。这个三角形最后能通过“T1”这个名字在文件中查阅。同样,也有一些相似的命令用来创建方形的,圆形的障碍物以及挡板。
上面的那条命令会把三角形障碍物以默认的旋转角度放置好,如果你想要它以不同的角度放置,用“Rotate”命令:

Triangle T2 3 5
Rotate T2
Rotate T2

上面的命令在位置(3,5)创建一个三角形障碍物,然后两次顺时针旋转90度,总共旋转180度。旋转一个挡板不会改变它的外围框,但是会改变它的轴。与旋转命令一样,同样有移动以及删除小物件的命令,例如:

Square S1 10 2
Square S2 15 15
Move S1 19 17
Delete S2

上面的命令在位置(10,2)创建一个方形的障碍物,然后又在位置(15,15)创建一个,然后把第一个方形的障碍物移动到(19,17),然后删除第二个。

同样有一些用于把触发连接到动作的命令,例如:

Square S3 13 13
LeftFlipper LF1 5 18
Connect S3 LF1

这些命令在位置(13,13)创建一个方形的障碍物,在位置(5,18)创建一个左撇的挡板。“Connect”命令指定只要弹球撞计这个方形障碍物,挡板的动作就会被触发。

KeyConnect命令指定把一个小物件的动作连接到一个特定的键:

RightFlipper RF1 9 18
KeyConnect key 32 down RF1
KeyConnect key 32 up RF1

这些命令指定当空格键被按下或者释放的时候,在位置(9,18)的右撇的挡板会被触发(在 Java™ AWT 中空格键的号码是“32”)。

由于你可能想允许外围墙触发一些动作,为此保留了一个特殊的标识符

Connect OuterWalls GIZ

这条命令将保证撞击外围墙的弹球将触发一个称为“GIZ”的小物件的动作。

Ball命令允许你确定弹球的在游戏中的位置以及速度。因为弹球可能在某个方体内的中间点,因此坐标数是用浮点数表达的:

Ball B1 14.2 4.5 -3.4 -2.3

这条命令把一个弹球放置在位置(14.2,4.5),初始速度为向左每秒3.4L,向上每秒2.3L。

Gravity和Friction命令用来设置游戏的全局属性。都是用浮点数作为参数的,例如:

Gravity 16.0 Friction 0.0 0.0

这条命令将把游戏中的重力减小到16L/秒2,去掉摩擦。文件中只有最后出现的Gravity和Friction命令有效。

这里是上面展示的例子 中的一些撞球文件。它在右上角创建了一个三角形障碍物,然后又创建了一些圆形和方形的障碍物以及一些挡板。上挡板的动作通过“空格”键触发,下档板的动作通过键“q”“w”触发,同样也可以通过撞击某个圆形状碍物触发。吸收器的动作通过“delete”键以及它自身触发!这样将会使游戏不停的运行。每次当弹球撞击吸收器,吸收器将迅速的把小球反弹回去。

Triangle T 19 0 Rotate T

Triangle T2 1 1

Square S02 0 2
Square S12 1 2
Square S22 2 2
Square S32 3 2
Square S42 4 2
Square S52 5 2
Square S62 6 2
Square S72 7 2
Square S82 8 2
Square S132 13 2
Square S142 14 2
Square S152 15 2
Square S162 16 2
Square S172 17 2
Square S182 18 2

Circle C43 4 3
Circle C54 5 4
Circle C65 6 5
Circle C76 7 6
Circle C99 9 9
Circle C109 10 9
Circle C1110 11 10
Circle C129 12 9
Circle C139 13 9
Circle C156 15 6
Circle C165 16 5
Circle C174 17 4
Circle C183 18 3


LeftFlipper LF92 9 2
KeyConnect key 32 down LF92
KeyConnect key 32 up LF92 RightFlipper RF112 11 2
KeyConnect key 32 down RF112
KeyConnect key 32 up RF112
LeftFlipper LF87 8 7
KeyConnect key 81 down LF87
KeyConnect key 81 up LF87
Connect C43 LF87
Connect C54 LF87
Connect C65 LF87
Connect C76 LF87
Connect C109 LF87
Connect C1110 LF87
Connect C139 LF87

RightFlipper RF137 13 7
KeyConnect key 87 down RF137
KeyConnect key 87 up RF137
Connect C99 RF137
Connect C1110 RF137
Connect C129 RF137
Connect C156 RF137
Connect C165 RF137
Connect C174 RF137
Connect C183 RF137
Absorber A 0 19 20 20
KeyConnect key 127 down A
Connect A A
Ball B 1.0 11.0 0.0 0.0

返回顶部

形式语法
<file> ::= <commandline>*

<commandline> ::= <command>"\n" | "\n"

<command> ::= <gizmoOp> <name> <int-pair> |
Absorber <name> <int-pair> <int-pair> |
Ball <name> <float-pair> <float-pair> |
Rotate <name> |
Delete <name> |
Move <name> <number-pair> |
Connect <name> <name> |
KeyConnect <keyid> <name> |
Gravity FLOAT |
Friction FLOAT FLOAT

<name> ::= IDENTIFIER

<gizmoOp> ::= Square | Circle | Triangle | RightFlipper | LeftFlipper

<number-pair> ::= <int-pair> | <float-pair>

<int-pair> ::= INTEGER INTEGER

<float-pair> ::= FLOAT FLOAT

<keyid> ::= "key" KEYNUM "down" |
"key" KEYNUM "up"
IDENTIFIER   表示由{'0'..'9','A'..'Z','a..z','_'}组合形成的字符串。标识符"OuterWalls" 是一个为外围墙保留的字 ;不能作为其他用途
INTEGER   表示整形数
FLOAT   表示浮点数
KEYNUM   表示键盘的数字号 (整形)

撞球游戏关键字是大小写不敏感的

返回顶部

语义
"Square" (IDENTIFIER name) (INTEGER x) (INTEGER y)
"Circle" (IDENTIFIER name) (INTEGER x) (INTEGER y)
"Triangle" (IDENTIFIER name) (INTEGER x) (INTEGER y)
"RightFlipper" (IDENTIFIER name) (INTEGER x) (INTEGER y)
"LeftFlipper " (IDENTIFIER name) (INTEGER x) (INTEGER y)
创建给定的小物件,它的左上角的坐标为(x,y),方位是默认的。在文件中,命名必须是唯一的,有可能被用于引用到这个小物件。每个小物件
的默认方位是:
方形
没有(所有的方位是相同的)
圆形
没有(所有的方位是相同的)
三角形
一个角在东北,一个角在西北,最后一个角在西南。斜边是从西南角到东北角。
左挡板
轴在西北角,另一个在西南角
右挡板
轴在东北角,另一个在东南角
"Absorber" (IDENTIFIER name) (INTEGER x1) (INTEGER y1) (INTEGER x2) (INTEGER y2)
创建一个吸收器,它的左上角在位置(x1,y1),它的右下角在位置(x2,y2)。第二个位置必须在第一个位置的右边至少1L,下边至少1L。在文件中,命名必须是唯一的,以后可以用来作为这个吸收器的引用。
"Ball" (IDENTIFIER name) (FLOAT x) (FLOAT y) (FLOAT vx) (FLOAT vy)
创建一个弹球,它的中心位置时(x,y),速度为(vx,vy)。在文件中,命名必须是唯一的,以后可以用来作为这个弹球的引用。
"Rotate" (IDENTIFIER name)
这个命令在被称为name的项上进行90度的顺时针旋转。注意有些项(例如吸收器,外围墙,或者弹球)不能被旋转。旋转一个挡板不会改变它的外围框,但是会改变它的轴。
"Delete" (IDENTIFIER name)
删除成为name的项,进行这个操作以后,这个项将不复存在。
"Move" (IDENTIFIER name) (INTEGER x) (INTEGER y)
"Move" (IDENTIFIER name) (FLOAT x) (FLOAT y)
在第一种形式中,按名移动小物件,它的左上角是(x,y)。在第二种形式中,按名移动弹球,它的中心为(x,y)。
"Connect" (IDENTIFIER producer) (IDENTIFIER consumer)
这个命令使称为consumer的小物件成为一个产生器描述的小物件产生的触发的消费者。也就是说,每当弹球撞击产生器,消费者的动作将发生。
"KeyConnect" "key" (KEYNUM num) "down" (IDENTIFIER consumer)
"KeyConnect" "key" (KEYNUM num) "up" (IDENTIFIER consumer)
这个命令使称为consumer的项成为一个键产生的触发的消费者。
"Gravity" (FLOAT g)
这个命令改变面板的重力,修改成gL/秒2 ,这个命令覆盖以前的重力设置。如果文件中没有这条命令,那么将使用默认值
"Friction" (FLOAT mu) (FLOAT mu2)
这个命令改变全局摩擦常数为mu和mu2,在摩擦公式。This command overrides any previous setting of friction. If no Friction command appears in the file the default values should be used.

返回顶部

附录 3:物理包

提供的物理库包括一些不可改变的抽象数据类型,例如 AngleVectLineSegment 和  Circle, 还有一个类 Geometry ,它包括一些静态的方法,可以用来处理一些弹球和其他圆形以及线段的弹性碰撞。你们可以随意的使用这些代码,可以把它们修改以后来使用。

关于物理库的文档可以在documentation-code/generated-documentation/physics中找到。.class文件存储在/mit/6.170/lib/gb-lib.jar, 这个文件会自动地在你的类路径里面。

物理库的源代码可以在Java-source-code/physics中找到。如果你想分析或者修改这些源代码,那么你将会很失望。在过去,没有按照原样使用物理库的学生将得到比较低的成绩。许多队伍都不需要这些源代码拷贝到他们的目录中,不需要添加到资料库,或者编译它。可以直接用gb-lib.jar文件并且分析它的说明书。

返回顶部

变动日志

这部分将详细记录版本修正中做过的改动。你可以把版本号标在你的发行版的底部来指出这个版本发行时一共做了哪些改变。

  • v. 11/02/2001:
    • Angle.equals() 加入到物理包中。 介绍 EPSILON容错到 equals() 比较中。



 
MIT Home
Massachusetts Institute of Technology Terms of Use Privacy