Skip to main content

Project 3 BYOW

FAQ

每个作业的顶部都会有FAQ链接。你也可以在URL末尾加上'/faq'来访问。项目 3 的 FAQ 位于此处

介绍

在项目 3 中,你将创建一个引擎来生成可探索的世界。这是一个大型设计项目,需要你和一位合作伙伴一起完成从构思到演示的每个开发阶段。 目标是教你如何处理更大的代码,而无需过多的初始代码,从而模拟类似产品开发周期的过程。 因此,本项目的评分将与其他项目不同。 因为世界设计和实现没有标准答案,所以除了通用的自动评分器,你们还会像在实习或工作中一样,接受绩效评估。 虽然评分可能略带主观性,但我们保证会像尊重辛勤工作的员工一样尊重你们,做一个好老板。 如果你觉得评分方案不公平,请与我们联系。

这个项目需要你进行大量的探索和实验。 在整个过程中,经常上网搜索答案(但不要直接抄袭往届的答案)应该是一项常规活动。 请注意,没有正确或错误的答案,因为这是一个非常开放的项目。 但是,有些实现和想法比其他实现和想法更好。 在确定你认为好的东西之前,你会经历几次迭代是可以接受的,也是预期的。 也就是说,这个项目是关于软件工程的。

你不需要使用课堂上任何高级的数据结构或概念(A*、MST、不相交集等)。 这个项目是关于软件工程的,而不是关于数据结构或算法。 我们在课堂上学到的数据结构和算法将使你的代码更简单、更高效,但请不要仅仅因为我们在课堂上学到它们就使用它们。 仅当你在你的实现中觉得使用它们很舒服时,才使用这些工具。

观看关于项目技巧的视频播放列表(来自 2018 年春季)可以在此链接中找到。 观看新骨架代码的演练也可在此处获得here

请注意,由于项目结构已变更,第一阶段指的是项目3A部分。

重要日期

项目 3 价值 125 分。 此作业有几个关键截止日期:

  • 团队组建 (2 分): 截止日期为 4 月 3 日晚上 11:59 分
    • 请务必提交 Project 3 Partnerships Form。提交后将无法更换队友。开始作业前,请务必阅读并理解合作指南
    • 小组仓库发布后,请务必及时接受 GitHub 邀请,否则邀请链接将在一周后失效。
  • Project 3A (19 分): 截止日期为 4 月 15 日晚上 11:59 分
  • Project 3B (9 分): 截止日期为 4 月 22 日晚上 11:59 分
    • 交互性自动评分 (9 分): 请在 Gradescope 上提交
  • Project 3C (95 分): 截止日期为 4 月 22 日晚上 11:59 分
danger

进阶功能演示将在 Project 3BC 截止日期当周的实验课上进行线下演示。 所有组员必须准时参加,否则小组整体成绩将被扣除 20% 。

请注意:除非有特殊情况,Project 3B 和 3C 不允许延期提交。本次作业不适用以往的延期政策,请尽早开始!

请注意,Project 3B 和 3C 的截止日期为同一天。但由于“进阶功能”部分需要进行人工检查,因此我们将“交互性”和“进阶功能演示”分为了两个部分。在您提交至 Gradescope 的 Project 3B 代码中,请务必包含“进阶功能”,因为在 Project 3C 的提交表单中,我们需要您提供 Project 3B 在 Gradescope 上的提交 ID。

Project 3B 和 3C 不允许迟交,因为需要参加实验课,由助教进行检查评分。 强烈建议不要延期提交 3A 以及相关的实验 (Lab 9 和 Lab 10)。Project 3B 和 3C 都是建立在这些基础之上的,如果 3A 延期,您可能无法按时完成 Project 3B。

下面开始介绍本次作业的具体要求!

概述

在接下来的几周里,您的任务是设计和实现一个基于 2D 图块的世界探索引擎。 "基于图块" 是指 您生成的世界将由 2D 图块网格组成。 "世界探索引擎" 是指您的 软件将构建一个世界,用户可以通过在世界中行走并与对象交互来探索该世界。您的世界将具有俯视视角。作为一个比您 将构建的系统复杂得多的例子,NES 游戏 "Zelda II" (有时) 是一个基于图块的世界探索引擎,恰好是一个视频 游戏:

Zelda2

您构建的系统可以使用图形图块 (如上图所示),也可以使用基于文本的图块,例如 下图所示的游戏

brogue

我们将提供瓦片渲染器、少量初始瓦片,以及一些必要方法的接口(或声明),这些方法需要在你的世界引擎中实现,以供自动评分器使用。本项目有两个主要截止时间。第一个截止时间前,你需要能够生成符合以下标准的随机世界;第二个截止时间前,用户应能探索并与世界互动。

本项目的核心目标是让你体验并学习如何管理大型系统的复杂性。需要提醒的是,你构建的系统可能不那么“好玩”!对于新手程序员来说,三周时间确实很紧张。不过,我们希望你能从中获得成就感,甚至创造出精美的世界。

项目设置

danger

此项目的设置与其他实验/项目不同。请不要跳过此步骤!

小组仓库设置

在本项目的这一阶段,你将主要使用小组仓库进行开发。要在本地计算机上配置小组仓库,请参考以下步骤(规范文档中也有相同说明):

  • 请查收邮件,并接受收到的 GitHub 仓库邀请。请尽快接受邀请,因为邀请会在7天后失效。如果邀请已过期,请在Ed论坛发帖求助。
  • 登录Beacon平台,点击“小组”标签页。你应该能看到你的小组信息。
  • 点击“在GitHub上查看仓库”链接。
  • 你将跳转到GitHub上的新仓库,这是一个空仓库。复制地址栏中显示的克隆链接(截图已将链接涂黑)。
info

推荐使用ssh方式克隆仓库,而非https。图中示例使用的是https,这可能与Lab 1中的GitHub认证方式不兼容。如需切换,请点击ssh按钮并使用显示的链接。

group-repo

  • 打开一个新的终端窗口,进入你存放CS 61B相关文件的目录(通常学生会创建一个名为cs61b的目录)。
danger

重要提示: 请勿使用cd命令进入你的个人仓库sp24-s****!不要将小组仓库克隆到你的个人61b仓库里。

  • 在终端中依次输入以下命令,每输完一行按回车键执行:
git clone <在此处粘贴来自 GitHub 的链接>
cd sp24-proj3-g*** // 将 *** 替换为你的团队仓库编号
git remote add skeleton https://github.com/Berkeley-CS61B/proj3-skeleton-sp24.git
git pull skeleton main --allow-unrelated-histories

完成以上步骤后,你应该能在本地文件系统中看到名为sp24-proj3-g***的小组仓库。打开该仓库,你会看到proj3骨架文件夹。之后,你和你的组员就可以像往常一样,通过添加、提交、推送和拉取等操作来管理这个仓库了。

框架代码

框架代码的使用演示视频请见此处

在你的小组仓库中使用git pull skeleton main命令来拉取框架代码。框架代码包含你将要使用的几个关键包:TileEngineCoreUtils。其中,TileEngine包提供了一些基本的渲染方法和瓦片的基本代码结构,具体包括:

  • TERenderer.java - 包含与渲染相关的方法。
  • TETile.java - 用于表示游戏中瓦片的类型。
  • Tileset.java - 一个预置的瓦片库。
danger

请勿修改TETile.java中的character字段或character()方法,否则可能导致自动评分器评分异常。 此外,如果添加了新的地板或墙壁瓦片,请务必相应地修改isBoundaryTileisGroundTile方法,确保自动评分器能够正确识别。

Core包包含了所有与图块无关的内容。我们建议您将该项目的所有代码都放在Core包中,但这并不是强制性的。Core包包含以下类:

  • AutograderBuddy.java - 提供了两种与系统交互的方法。其中,TETile[][] getWorldFromInput(String input) 模拟游戏运行,但不会进行渲染,而是直接返回当用户输入特定字符串时所生成的地图。你需要实现这个方法,以便自动评分器进行测试。
  • Main.java - 程序的入口点。它负责读取命令行参数,并调用World.java中相应的函数来启动整个系统。
  • World.java - 你的世界!

这是一个开放式项目。正如你所看到的,我们只为你提供了一个名为World.java的文件,你可以在其中执行创建世界所需的必要操作!该项目的目的是让你自由地创建具有不同设计选择的自己的世界。你可以根据需要创建任何其他类。基本上,你可以使用World.java来实现世界创建的逻辑。

Utils包则包含了一些可能对你实现World.java类有帮助的工具函数。

  • RandomUtils.java - 提供了一些可能有用的工具函数。
  • FileUtils.java - 提供了便捷的文件操作功能。你可以在此处此处找到相关的 API。建议参考lab09来回顾相关知识。

该项目大量使用了StdDraw库,它提供了基本的图形渲染功能,并且支持用户通过键盘和鼠标进行交互。在开发过程中,你可能需要查阅StdDraw的API文档,链接如下:此处

你的项目应该只使用标准的Java库(通过java.*导入)以及我们提供的library-sp24中的库。你最终提交的3B和3C版本,不应该使用除了框架代码提供的库之外的任何第三方库。

danger

除非你声明静态变量为final,否则请不要使用它们!2018年的项目经历表明,使用非final的静态变量会导致严重的调试问题。非final的静态变量会极大地增加系统的复杂性。另外,不要在getWorldFromInput方法中调用System.exit(),这会导致自动评分器异常退出并判为失败。

3A:世界生成

如前文所述,该项目的首要目标是创建一个世界生成器。你所生成的世界必须是有效的,并且具有足够的随机性。具体要求如下:

有效的

  • 世界必须是一个二维网格,使用我们的图块引擎绘制。图块引擎在 lab 9 中描述。
  • 生成的世界必须包括不同的房间和走廊,尽管它也可能包括室外空间。
  • 至少一些房间应该是矩形的,尽管您也可以支持其他形状。
  • 你的世界生成器必须能够生成包含转弯的走廊(或者等效地,相交的直走廊)。随机世界应以适度的频率(20% 或更多的世界)生成转弯走廊。
  • 不允许死胡同走廊。
  • 房间和走廊必须有在视觉上与地板不同的墙壁。墙壁和地板在视觉上应与未使用的空间不同。
  • 角落墙是可选的。
  • 房间和走廊应该连接,即相邻的房间或走廊之间的地板上不应有间隙。
  • 所有房间都应该是可到达的,即不应该有无法进入的房间。
  • 房间不能剪切掉世界的边缘。换句话说,世界的边缘不应该有地板砖。
  • 世界中不应存在过多的未利用空间。虽然这个标准比较主观,但请尽量保证房间和走廊能够占据世界面积的50%以上。

足够的随机性

  • 世界必须是以伪随机方式生成的。伪随机性的概念在实验 9 中讨论过。
  • 世界应该包含随机数量的房间和走廊。
  • 房间和走廊的位置应该是随机的数量。
  • 房间的宽度和高度应该是随机的尺寸。
  • 走廊的宽度应为 1 个图块单位,长度随机。
  • 每次生成的世界都应该显著不同,也就是说,不应出现具有容易预测特征的相同基本布局。

作为满足所有这些要求的世界的一个例子(点击以获得更高的分辨率),请参见下图。在此图中,# 表示墙壁 tile,点表示地板 tile。所有未使用的空间都留为空白。

compliant_world_example

完成实验 9 后,您可以开始研究您的世界生成算法。

你很可能会最终会丢弃你的第一个世界生成算法。这是很正常的!在真实世界的软件开发中,通常需要构建多个全新版本,才能得到满意的结果。

欢迎在网上搜索优秀的世界生成算法。虽然不应复制粘贴现有游戏或在线图形演示的代码,但欢迎从网络上的代码中汲取灵感。请务必使用 @source 标签注明来源。 为了获得灵感,可以尝试玩现有的 2D tile-based 游戏。 Brogue 是一个特别优雅、美丽的游戏的例子。Dwarf Fortress 是一个令人难以置信的拜占庭式、极其复杂的世界生成引擎的例子。

Tile 集和 Tile 渲染

我们提供的 tile 渲染引擎接受 TETile 对象的二维数组,并将其绘制到屏幕上。我们现在将这个数组称为 TETile[][] worldworld[0][0] 对应于世界的左下角 tile。 第一个坐标是 x 坐标,例如 world[9][0] 指的是从左下角 tile 向右 9 个单位的 tile。第二个坐标是 y 坐标,其数值随着我们向上移动而增加,例如 world[0][5] 是从左下角 tile 向上 5 个单位的 tile。所有值都不能为 null,请确保在调用 renderFrame 之前填充所有值。请务必理解世界网格的坐标系统! 如果不确定,请编写简单的示例程序来在网格上绘制 tile,加深理解。如果混淆了 x 和 y 坐标,或者向上和向下的方向,调试过程将会非常令人困惑。

我们在 Tileset.java 中提供了一小部分默认 tile,这些 tile 可以作为创建 TETile 对象的示例。我们强烈建议您也添加自定义 tile。

Tile 引擎还支持图形 tile!要使用图形 tile,只需将 tile 的文件名作为 TETile 构造函数的第五个参数传入即可。图像必须是 16x16 像素,并且最好是 PNG 格式。网上有很多开源 tile 集可用于 tile-based 游戏。欢迎使用它们。

您创建的任何 TETile 对象都应被赋予一个其他 tile 未使用的唯一字符。即使您使用自己的图像来渲染 tile,每个 TETile 仍然应该有自己的字符表示。

如果未提供文件名,或者无法打开文件,tile 引擎将使用提供的 Unicode 字符代替。这意味着,如果其他人在您指定的相同位置没有该图像文件,您的世界仍然可以显示,但会使用 Unicode 字符而不是您选择的纹理。

Tile 渲染引擎依赖于 StdDraw。我们不建议使用 StdDraw 命令,例如 setXScalesetYScale,除非您非常清楚自己在做什么,否则可能会严重改变或损害系统的 a e s t h e t i c

启动程序

您的程序将通过运行 Main 类的 main 方法来启动。您会发现,该方法会渲染您的程序,并在后续过程中提供交互功能。为了在3A阶段测试您的世界,您的项目必须支持getWorldFromInput功能。具体来说,您应该能够处理"N#######S"格式的输入,其中#代表任意数量的数字。N表示请求新世界,#s提供种子,按下S表示种子输入完成。Main类和getWorldFromInput方法中的世界生成逻辑应保持一致。虽然您需要在Main中渲染程序,但在getWorldFromInput中则不应进行渲染,因为该方法主要用于自动评测。

最后,我们建议您对 core.Main 类进行最小的修改。将程序的所有工作委托给您将创建的其他类是一个更好的主意。

当您的 core.Main.main() 方法运行时,您的程序必须显示一个主菜单,该菜单至少提供启动新世界、加载先前保存的世界和退出的选项。主菜单应该完全可以通过键盘导航,使用 N 表示“新世界”,L 表示“加载世界”,Q 表示退出。您可以根据需要包含其他选项或导航方法。

main-menu_example

在键盘上按 N 表示“新世界”后,应提示用户输入一个“随机种子”,这是一个由他们选择的长整型数值。此长整型数据类型将用于随机生成世界(如稍后和实验 12 中所述)。用户界面(UI)应实时显示已输入的种子值。用户输入完所有种子数字后,按下S键通知系统。您的世界生成器应该能够处理任何高达 9,223,372,036,854,775,807 的正种子。如果种子大于该值,程序的行为将是未定义的。

“加载”命令的行为将在本规范的后面部分描述。

当系统通过core.AutograderBuddy.getWorldFromInput被调用时,不应显示任何菜单或在屏幕上进行任何绘制。系统应像用户通过main()方法手动输入一样处理给定的字符串。例如,调用getWorldFromInput("N3412S")时,程序应生成一个种子为3412的世界,并返回相应的2D图块数组。请注意,输入字符串中的字母可以是大小写,并且您的引擎应该能够接受任何按键(即“N”和“n”都应该启动世界生成过程)。 使用 getWorldFromInput 时,您不应渲染任何图块或播放任何声音。

如果您希望允许用户自定义角色属性或指定世界生成参数等,可以添加其他选项。例如,如果您希望用户能够选择扮演哪种生物,则可以在主菜单中添加第四个选项“S”,用于“选择生物并创建新世界”。这些附加选项可以有任何自定义行为,但NLQ的功能必须与规范中的描述完全一致!

要求

对于 3A 阶段,您需要能够通过提供输入字符串来运行 Main.main,并且您的程序所创建的世界必须符合上述所有要求,以及我们在提交与评分章节中定义的随机性标准。请注意,为了检查代码,您应当编写自己的 main 方法来渲染游戏世界。但对于自动评分器,getWorldFromInput 方法不应进行渲染,而仅返回一个 TETile 类型的数组。对于不同的种子,程序生成的世界在视觉上应该有明显的差异。

info

对于 3A,您不需要有主菜单界面。

设计文档

由于我们没有为 Project 3 提供任何重要的骨架代码,并且由于该项目非常开放,我们预计 BYOW 的实现方式在学生之间会有很大差异。我们建议您编写一个设计文档,以反映您项目的当前状态。

在您开始编写任何代码之前,请使用此处列出的指南为您的 BYOW 程序的每个功能创建一个计划,并说服自己您的设计是正确的。编写设计文档是一个迭代过程。在提出您的初始设计后,您可能会发现其中的一些缺陷,需要您重新审视您的设计并根据您的新发现更新其描述。

您可能会发现软件工程讲座对于学习如何管理这个项目的复杂性和协作性很有帮助。

danger

我们不会对本文档进行评分,但是您需要完成它才能获得在线和办公时间的帮助。

设计文档指南

设计文档的主要作用是为您的项目奠定基础。在开始编码之前,进行充分的思考和构思至关重要。我们希望在设计文档中看到的内容:

  • 确定您将在实现中使用的我们在课堂上学到的数据结构。
  • 您实现的算法的伪代码/总体概述。

您可以为您的 BYOW 设计文档使用以下格式。 您可以使用您自己的格式创建设计文档,或者使用此模板

设计文档章节

1. 类和数据结构

在此处包含任何类定义。对于每个类,列出实例变量(如果有)。包括每个变量的简要描述及其在类中的用途。

2. 算法

您可以在此描述您的代码是如何运作的。对于每个类,包括该类中方法的高级描述。也就是说,不要包含代码的逐行分解,而是您在方法上方的 javadoc 注释中编写的内容,包括您正在考虑的任何边界情况。

3. 持久性

您应该只在完成 3A 后才处理本节。本节应描述您将如何保存世界的状态,并按照规范中的要求再次加载它。同样,请尽量保持您的解释清晰且简洁。包括您的程序与之交互的所有组件 - 类、特定方法以及您可能创建的文件。您可以查看 lab 9

3B:交互性

在项目的第二阶段,您将为游戏添加用户交互功能,并加入用户界面 (UI) 元素,以增强游戏的沉浸感和信息呈现。

交互性的具体要求如下:

  • 用户需要能够控制一个角色(avatar),通过W、A、S、D键来移动它。 这里的“化身”指的是用户在屏幕上控制的角色。例如,在我的项目中,我使用了一个可以四处移动的“@”。
  • 角色必须能够以某种方式与游戏世界互动。
  • 系统必须具有伪随机性。也就是说,在给定相同种子的情况下,相同的按键操作序列必须产生完全一致的结果! 请注意,Random 对象保证每次都输出相同的随机数。
  • 为了支持保存和加载,您的程序需要在您的 proj3 目录中创建一些文件(更多详细信息请参见规范和骨架代码的后面部分)。 您可以创建的唯一文件必须具有“.txt”后缀(例如“save-file.txt”)。 如果不这样做,您将遇到自动评测系统问题。

你也可以选择加入让玩家获胜或失败的游戏机制。 除了这些功能要求之外,您的系统还有一些技术要求,下面将更详细地介绍。

UI(用户界面)外观

用户输入种子值并按下S键后,应该显示带有用户界面的游戏世界。 您项目的用户界面必须包括:

  • 显示游戏世界当前状态的 2D 图块网格。
  • 一个抬头显示界面 (HUD),它提供可能对用户有用的附加信息。 最起码,这应该包括描述当前鼠标指针下方的图块的文本。 抬头显示界面不应闪烁,否则可能会影响评分。

作为最低限度的示例,下面的简单界面显示了一个图块网格和一个 HUD,该 HUD 显示鼠标指针下方的图块的描述(单击图像以获得更高的分辨率):

mouseover_example1

您可以根据需要包含其他功能。 在下面的例子中(点击图片可查看高清图),与之前的例子一样,鼠标光标当前位于墙上,因此 HUD 在右上角显示文本“wall”。 但是,此 HUD 还为用户提供了 5 颗心,代表角色的“生命值”。 请注意,这个世界不符合上述规范的要求,因为它是一个巨大的、不规则的洞穴空间,而不是由走廊连接的房间。

mouseover_example1

例如,下面的游戏(单击图像以获得更高的分辨率)使用 GUI 列出其他有效的按键,并在用户将鼠标悬停在图块上时提供更详细的信息(“You see grass-like fungus.”)。 下图是一个专业游戏,因此我们不希望您的项目具有如此高的细节水平(但我们鼓励您尝试一些有趣的视觉效果)。

mouseover_example2

有关如何指定 HUD 位置的信息,请参阅 TERendererinitialize(int width, int height, int xOffset, int yOffset) 方法或参见 lab 9。

UI 行为

生成游戏世界后,用户必须能够控制显示在游戏世界中的角色。 用户可以使用WASD键分别控制角色向上、向左、向下和向右移动。 这些键也可以执行其他操作,例如推动物体。 您可以在您的引擎中包含其他键。 当尝试移动到墙内时,角色不应移动,并且程序不应出错。

系统必须具有伪随机性。也就是说,在给定某个种子的情况下,相同的按键操作序列必须产生完全一致的结果! 除了移动键以外,如果用户输入:Q(注意冒号),程序应立即退出并保存。保存和加载功能的说明请参考下一节。此命令必须立即退出并保存,无需任何额外确认步骤,例如询问是否确定。我们将这个同时执行退出和保存的操作简称为“保存退出”。此命令不区分大小写,:q同样有效。 此外,: 之后输入任何其他字符都应无任何反应。

本项目使用StdDraw来处理用户输入。这导致了一些重要的限制:

  • StdDraw不支持组合键。当我们说:Q时,我们指的是:后跟Q
  • 它只能识别并响应那些能够产生字符的按键。这意味着任何 Unicode 字符都可以,但像箭头键和 Escape 键这样的键将不起作用。
  • 在某些计算机上,可能无法直接支持长按按键的功能,需要进行额外的设置或修改。 这意味着,例如,你无法通过持续按住E键来实现持续向东移动。如果你能找到一种支持按住键的方式,并且与getWorldFromInput兼容,欢迎你这样做。

由于系统需要通过 getWorldFromInput 处理字符串输入,因此引擎不能依赖任何需要实时性的机制。 也就是说,任何依赖于现实世界时间流逝的功能都无法通过字符串输入来重现,从而导致程序行为的不确定性。记录已进行的回合数是一个完全合理的设计,并且可以为游戏增加趣味性。 例如,可以设置游戏世界随着回合数的增加而逐渐变暗。欢迎你包含其他按键,例如允许用户按空格键以等待一个回合。实时行为是为自动评分器准备的。你可以随意忽略 3C 的实时要求并修改你的代码。

保存和加载

在这种情况下,能够保存进度并在之后重新加载就显得非常重要。你的系统必须能够保存在探索世界时的状态,以及随后将世界加载到上次保存时的确切状态。

在运行中的 Java 程序中,我们使用变量来存储和加载值。请记住,当你的程序结束时,所有变量都将超出范围。因此,你需要将程序的状态保存到程序创建的文件中。

当用户重新启动 core.Main 并按下 L 键时,游戏世界应该恢复到程序结束前的完全一致的状态。此状态包括随机数生成器的状态!更多细节将在下一节中说明。如果用户尝试加载游戏,但之前没有保存任何数据,系统应直接退出,并关闭UI界面,且不应产生任何错误提示。

在基本要求中,命令:Q应该保存并完全终止程序。这意味着,如果输入字符串中包含 :Q,则其后不应再有任何其他字符。 重新加载游戏需要再次运行程序,并以 L 作为输入字符串的开头。

与输入字符串交互

你的 getWorldFromInput(String input) 方法必须能够正确处理包含移动指令的输入字符串。 例如,字符串 N543SWWWWAA 表示用户创建了一个种子值为 543 的世界,然后向上移动四次,再向左移动两次。如果我们调用 getWorldFromInput("N543SWWWWAA"),您的系统将返回一个 TETile[][],这个 TETile[][] 精确地代表了我们使用 main 方法并手动输入这些按键后所得到的世界。由于系统必须根据种子和输入字符串来确定行为,这使得用户可以重现给定输入序列所产生的精确结果。 这对于测试您的代码以及我们的自动评测机也很有帮助。

getWorldFromInput(String s) 还必须能够处理在回放字符串中保存和加载的操作。例如,N25SDDWD:Q 表示启动一个种子值为 25 的新世界,然后向右移动两次,向上移动一次,再向右移动一次,最后退出并保存。该方法随后会返回保存时对应的 2D TETile[][] 数组。 如果我们随后使用回放字符串 LDDDD 启动引擎,我们将重新加载之前保存的世界,向右移动四次,然后返回第四次移动后对应的 2D TETile[][] 数组。

在不同保存状态之间,您的世界不应该发生任何改变。也就是说,在以下所有情况下,对 getWorldFromInput 的最后一次调用都应该返回完全相同的 TETile[][]

  • getWorldFromInput(N999SDDDWWWDDD)
  • getWorldFromInput(N999SDDD:Q),然后 getWorldFromInput(LWWWDDD)
  • getWorldFromInput(N999SDDD:Q),然后 getWorldFromInput(LWWW:Q),然后 getWorldFromInput(LDDD:Q)
  • getWorldFromInput(N999SDDD:Q),然后 getWorldFromInput(L:Q),然后 getWorldFromInput(L:Q),然后 getWorldFromInput(LWWWDDD)

如果我们接着使用输入 L:Q 调用 getWorldFromInput,我们期望保存并返回与之前我们输入 LDDDD 时相同的世界状态,并将其作为 TETile[][] 返回。

您无需担心包含多次保存的回放字符串。例如,N5SDD:QD:QDD:Q 不会被视为有效的回放字符串,因为程序应该在遇到第二个 :Q 时就终止运行。 您无需担心无效的回放字符串。也就是说,您可以假设自动评测机提供的每个回放字符串都以 N#SL 开头,其中 # 代表用户输入的种子值。

getWorldFromInput 方法的返回值不应该取决于输入字符串是否以 :Q 结尾。 唯一的区别在于世界状态是否会作为该方法的副作用而被保存。

3C:进阶功能与演示

您项目总分的 28 分将取决于您所选择的功能,我们将其称为“进阶功能得分”。 主要目的是,在满足项目基本要求的基础上,我们鼓励您进一步完善您的作品,并添加一些有趣的功能。 下面列出了一些功能,其中主要功能价值 21 分,次要功能价值 7 分。 在这两类功能中,您至少需要实现一个主要功能才能获得满分(即使不实现次要功能也可以)。 “进阶功能”这一项总共 28 分。 即使您实现了总价值超过 28 分的进阶功能,也不会获得额外加分。 但是,如果您有时间和意愿,请随意添加任意数量的功能。

您的项目必须始终满足上述所有基本要求! 例如,即使您允许用户使用鼠标点击操作,项目仍然需要支持键盘移动。

在一些主要功能的描述下方,我们提供了一些 GIF 示例,这些示例展示了如何在对应的进阶功能项上获得满分,希望能帮助您消除疑惑。 您实现的功能不必与示例完全一致。 你不一定非要实现我们下面列出的功能!我们强烈鼓励你提出自己的想法。我们会在 Ed 上开设一个专门的帖子,你可以把你的想法发给我们,看看是否符合要求。

21分 主要功能

  • 创建一个系统,使瓦片渲染器仅显示化身视野范围内的屏幕上的瓦片。视野范围的显示必须能通过按键开启和关闭,否则会影响评分。

line-of-sight

  • 添加光源效果,让光源能够影响世界的渲染方式。至少要有一个可以通过按键开启和关闭的光源。光照强度应该随着与光源距离的增加而逐渐减弱。光线也应该无法穿透墙壁。

toggle-lights

  • 添加能够追逐玩家或其他角色的敌人,这些敌人使用课堂上学过的搜索算法,并且可以通过开关来显示它们的预测路径。

enemy

  • 创建一个“遭遇战”系统。当玩家与游戏世界中的实体互动时,会进入一个新的界面。遭遇战结束后,玩家会返回到之前的游戏界面(例如,宝可梦)。

encounter

  • 添加允许用户切换游戏视角的功能(例如第一人称、等轴测 2.5D、3D 等)。(我们之前还没见过有人做过等轴测 2.5D 或完整 3D!任天堂 64 游戏《星之卡比 64》就是一个等轴测 2.5D 世界的例子。)Dorottya Urmossy 和 David Yang 在 2022 年秋季提交的作品是一个非常有趣的例子,它采用了 2.5D 第一人称视角,即游戏世界是 3D 的,但角色是 2D 的。

  • 实现一套战斗系统,允许玩家与移动的敌人和障碍物进行互动。为玩家设置生命值,在地图上放置可以恢复生命值的收集品,并创建随机移动的敌人或物体来对玩家造成伤害,玩家也可以对它们造成伤害。

7分 次要功能

  • 添加“回放”功能,允许用户重现最近一次保存的游戏状态,并以可视化的方式展示自上次创建新世界后进行的所有操作。回放必须能够准确还原到用户加载最近存档时的游戏状态。也就是说,回放结束后,游戏应恢复可玩状态。
  • 添加多个存档位,可通过新的菜单选项访问。同时,添加新的键盘快捷键,允许保存到除存档位 1 以外的其他存档位。请务必保留默认的保存和加载功能,以确保与回放字符串的要求相符。
  • 添加无需关闭并重新启动程序即可创建新世界的功能。此功能可以设置为在探索过程中可随时触发的特殊选项,或者在游戏达到“游戏结束”状态时启用。
  • 添加一个菜单选项,允许用户将游戏角色的外观设置为自定义图像。
  • 添加一个菜单选项,或者通过随机方式来决定世界的环境或主题。
  • 添加一个菜单选项,允许用户更改界面中所有文本的显示语言。默认语言应为英语,并提供切换回英语的选项。
  • 为主菜单上所有可通过键盘按键完成的操作添加鼠标点击支持。
  • 使游戏引擎渲染图像,而非使用 Unicode 字符。
  • 为菜单和/或探索界面添加动听的音乐。同时,为游戏角色与世界的互动添加音效。
  • 在界面中添加一个小地图,显示整个地图和当前游戏角色的位置。如果游戏地图大于屏幕显示范围,此功能将更实用。
  • 添加旋转游戏世界的功能,例如将游戏界面旋转 90 度,并相应调整移动控制键。
  • 添加对通过鼠标点击任何可见方格来移动游戏角色的支持。为此,需要实现某种寻路算法。
  • 添加对两名玩家同时进行游戏的支持。这将需要在屏幕上显示两个可移动的游戏角色,并为每个角色提供独立的控制方案。
  • 添加撤销移动操作的功能(即使该操作发生在加载当前存档之前的存档中)。撤销操作应将游戏世界恢复到最近一次按键操作之前的状态,并将该撤销操作记录到回放字符串中,而不是删除任何已有的字符(即,撤销命令应被记录在回放字符串中)。

要求摘要

以下是您项目的要求和限制列表。请注意,本节不能代替阅读整个规范,因为此处未捕获许多细节。

  • 当使用 main 函数时,你的程序必须提供一个菜单界面,包含“新建世界 (N)”、“加载 (L)”和“退出 (Q)”选项,这些选项应支持键盘导航,且不区分大小写。
  • 选择“新建世界”后,用户应输入一个整数种子,然后按下 S 键。按下 S 键后,程序会生成并显示相应的世界。
  • 用户在输入种子时,用户界面 (UI) 应实时显示已输入的数字。
  • 程序必须能够伪随机地生成不同的世界;也就是说,对于不同的种子,生成的世界也应有所不同。
  • 所有生成的世界必须包含前文 3A 部分描述的所有视觉特征。
  • 用户应能使用 W、A、S 和 D 键在世界中移动。
  • 用户应能按下“:Q”组合键来退出程序。再次启动程序后,在主菜单选择“加载 (L)”选项时,程序应恢复到退出前的状态
  • 所有随机事件都应具有伪随机性。也就是说,在给定相同种子的情况下,程序应表现出确定性行为。
  • 用户应能通过字符串输入,使用 getWorldFromInput 函数与程序进行交互。除接受输入和绘制屏幕外,该函数的行为应与 main 函数一致。
  • getWorldFromInput 函数必须返回一个 TETile[][] 类型的数组,该数组代表处理完输入字符串的最后一个字符后游戏世界的当前状态。
  • getWorldFromInput 函数必须能够像 main 函数一样处理保存和加载操作。
  • 你的程序必须使用我们提供的 TileEngineStdDraw 库来渲染图形界面。
  • 你的程序必须包含一个 HUD(抬头显示器),用于在游戏世界/图块显示区域之外显示相关信息。
  • 当鼠标悬停在某个图块上时,HUD 必须显示该图块的描述信息。
  • 你的程序不应依赖于实时操作。在没有接收到任何用户输入的情况下,游戏世界中的任何元素都不应发生变化。此项要求适用于自动评分器;在 3C 部分,你可以自由地添加依赖于实时操作的功能。
  • 你的程序必须包含至少一个主要功能,以及其他来自 Ambition 类别的功能,这些功能的总分应达到 28 分。

提交和评分

像往常一样,我们将在 Gradescope 上为这个项目设置一个评分器。请记住将你的合作伙伴作为组成员添加到你的 Gradescope 提交中。 此外,你还需要在完成项目后提交此表格。有关检查的更多详细信息,请参见下一节。如果你不提交此表格,你将在项目的检查部分收到 0 分。 你的合作伙伴中只需要一个人提交此表格,但你们应该一起写答案。

合作偏好表格:2 分

填写项目 3 合作偏好表格 对于本项目价值 2 分。你必须在 4 月 3 日星期三晚上 11:59 之前填写表格才能获得这些分数。

自动评分器:12 分

有关自动评分器的详细信息,请参见本节

  • 3A:3 分
  • 3B:9 分

3A 异步手动审查:6 分

助教将会下载你的代码,并使用 5 个不同的种子分别运行你的游戏 5 次。随后,他们会检查这 5 个不同的游戏世界是否符合我们下文定义的随机性标准

  • 房间的位置每次都应该在不同的地方。
  • 房间的大小每次都应该不同。
  • 房间和走廊的数量每次都应该不同。你必须至少有两个走廊,其中一个是转弯的。
  • 世界每次都应该有很大的不同,也就是说,你不应该有具有容易预测特征的相同基本布局。

如果你对你的世界是否符合这些标准有疑问或疑虑,你可以向办公时间的助教询问以确认。

为了获得 3A 异步人工审核的学分,你必须在 4 月 15 日星期一晚上 11:59 之前填写此表格 您可以在3C演示检查之前修复在3A异步人工审核中未获得的分数,以此来弥补这些损失的分数。我们会在您提交的内容上留下详细反馈,方便您了解需要改进的地方。

danger

异步审核将在3A截止日期后的3到5天内进行,之后我们将不再审核3A的提交内容。 这意味着该项目此部分的延期申请将受到限制。如果您在审核开始时未能提交,则只能通过3C演示检查来弥补这些分数。

合作反思 (20分)

3C演示检查 (85分)

要获得演示检查的学分,请务必提交此表格

  • 57 分:遵守 3A 和 3B 的基本规范。
  • 28 分:雄心壮志分。

您还需要指定一个提交版本,以便我们进行评分。 您应该:

  1. 确定您希望我们评分的演示提交的 SHA 值。 这个SHA值必须与您提交给Gradescope的版本一致。 即使不完全相同也没关系,但我们建议使用相同的提交版本,以确保其能编译并按预期运行。 当然,您也可以为了通过自动评分器而注释掉某些行,然后在检查时再取消注释。 您可以使用 git log 在这里为您提供帮助。 复制此 SHA 值并将其保存在某处。
  2. 请确保此提交版本在截止日期之前,并运行代码进行复核,确认这的确是您希望我们评分的版本。 如果您的提交是在截止日期之后提交的,您将受到 50% 的处罚。
  3. 小心地将其粘贴到表格提交中。
  4. 请勿忽略第3步。 如果粘贴了错误的SHA值,我们将对错误的代码版本进行评分;如果SHA值无效,我们将默认对origin/HEAD版本进行评分(这可能会导致延迟处罚)。

检查脚本和表格

为了提高过程的透明度,请点击此处查看讲师在检查项目时使用的脚本。 虽然检查是同步的,但我们希望将每个人的项目记录在文件中,以防在检查期间出现技术困难,或者我们需要出于任何原因查看您的项目。 请务必描述清晰简洁,以便评分人员理解如何使用您的功能。 如果您发现解释如何使用您的功能过于复杂,请考虑简化用户必须执行的工作才能使用它。 如果在使用雄心壮志功能时有任何已知的怪癖,请在此处告知我们:例如“打开灯后,您需要按 T 才能更改亮度”。

再次阅读规范并确保您的项目完全符合规范也极其重要。 由于规范较多,容易遗漏,请在提交前再次阅读,确认您已尝试获得雄心壮志分数,并清楚说明如何使用相关功能。 请务必描述清晰简洁,以便评分人员理解如何使用您的功能。 如果您发现解释如何使用您的功能过于复杂,请考虑简化用户必须执行的工作才能使用它。

一旦您确保拥有:

  1. 完成项目
  2. 再次通读所有规范,确保没有遗漏任何内容
  3. 确定您希望被评分的提交版本(commit),请参考上一节。
  4. 确保您只使用 library-sp24java.* 中的库

那么,您就可以提交 Project 3C Checkoff Form了。

自动评测机详情

我们为 BYOW 提供了两个自动评测机:3A 评测机和 3B 评测机。对于这些评测机,我们没有令牌 (token) 限制。

3A 评测机

3A 的截止日期为4 月 15 日晚上 11:59,共 3 分。本评测机将测试:

  • getWorldFromInput 能否返回一个世界
  • getWorldFromInput 能否使用相同的种子 (seed) 多次生成相同的世界
  • getWorldFromInput 能否使用不同的种子生成不同的世界

这些测试中不包含移动操作。

3B 评测机

3B 的截止日期为4 月 22 日晚上 11:59,共 9 分。本评测机将测试:

  • getWorldFromInput 能否使用相同的种子和相同移动多次生成相同的世界
  • getWorldFromInput 能否使用不同的种子和不同移动生成不同的世界
  • getWorldFromInput 即使在整个输入字符串中进行保存和加载,也能创建相同的世界

请记住在提交给自动评测机时,将您的合作者添加为团队成员。

大型语言模型 (LLM)

请注意,在协作政策中,我们有如下说明:

“如果只是为了生成少量样板代码,我们允许您非常谨慎地使用 GitHub Copilot / GPT3 等工具。但是,请勿使用此类工具生成重要的、非简单的方法。我们希望培养您的编程基本功。过度依赖 AI 工具可能会在您无法使用 AI 的场合(例如考试)给您带来困难。任何 AI 生成的代码都必须注明出处并明确注释。”

对于项目 3B 和 3C,我们放宽了此项规定,允许使用大型语言模型 (LLM) (例如 ChatGPT、Bard、Bing、CoPilot 等)。您可以根据需要使用它们,但请务必注意,所有由 AI 生成的代码都必须明确注明。

如果您希望 LLM 在项目 3B 和 3C 中发挥作用,请尝试将任务分解为更小的部分。您可以将 LLM 视为您聘请的助理程序员——他们编码速度很快,但可能比较粗心,缺乏常识,因此需要您提供非常明确的指令。

如果您提出过于宽泛的需求,例如“使用下面的代码为我的游戏编写一个光照引擎”,LLM 的表现可能不佳。相反,如果提出更具体的需求,例如“编写一个函数,该函数接收颜色和亮度值,并返回具有指定亮度的新颜色”,则 LLM 更容易胜任。

LLM 的另一种用途是辅助调试!我有时会将代码和问题描述提供给 LLM,它能够帮助我找到问题所在。