设计就是实现

有兴趣了解更多有关Reset Hard的信息吗? 是否想跟进本文中的某些内容? 访问该 网站 或直接在 Discord 上与我聊天


Reset Hard是一个颇具竞争力的时间旅行竞技场射击游戏,它可以设置陷阱,使您的朋友聪明,并学习如何做不可能的事情。

游戏的玩法是基于当您产生新的想法或概念后,您会开始思考所有可以使用它进行的疯狂操作时所得到的惊人感觉。 《重置重装》中的时间旅行就像一个玩具,我一直在鼓励玩家打破游戏规则,并以创造性的方式重新应用它们。

在竞争性多人游戏中,能够以新颖的方式创造性地结合机制或使用工具进行试验,使一切都充满了紧张感,因为您的对手可以使用与您相同的工具,并且他们可以组合动态策略和陷阱,而您可能不会能够预期。

但是为了鼓励玩家发挥创造力,我需要一个支持其创造力的系统。 为了使玩家感到一切皆有可能,我需要一个时间旅行框架,其中一切皆有可能。 我无法在游戏的各个部分中解构玩家并深入思考机制,而在这些区域中,我告诉他们不要过分关注事物运作的细节。

游戏需要完全一致。

这种一致性表明,在《 Reset Hard》中绝对不能有片刻的时间,您会看到意想不到或不直观的内容,我告诉您不要考虑它。 实际上,您在游戏中看到的所有内容都是技工。

通过最小化游戏代码和实现代码之间的抽象数量,我已经能够构建出一套我所知极其强大的游戏机制。 对我来说,游戏时间游玩和代码时间游玩没有区别。 这意味着我不设计花样或实施快捷方式来使它看起来像正在发生的事情。 相反,我只是做到了。

这对我谈论力学的方式产生了深远的影响。 当我解释时间旅行的方式时,我有一种额外的权威感。 我从不担心自己忘记了游戏机制中的某些极端情况或悖论。 当我谈论力学时,我不会使用“宇宙”之类的字眼,因为就我而言,《重装重制》没有假装力学或对任何事物进行宇宙解释。

与《口袋妖怪》,《动物穿越》或《失忆症》等游戏不同,我的目标是使用隐藏的机制来创造深度并允许玩家与我分享幻觉,而《重置硬汉》是一种内部一致的模拟游戏。 游戏机制是事实。

这意味着Reset Hard中的难题和教程确实可以归结为我,告诉我我的代码如何工作,以及我们一起探索该代码的后果。 只要我停留在这个领域,在大多数情况下,我实际上就不必担心机械的“绘图孔”。

到目前为止,我已经将其作为一个非常理论化,深奥的想法进行了介绍,但这确实很实用。

《 Reset Hard》中时间的解释是,时间轴上的各个“帧”每个都包含完美的世界副本。 当玩家在时间中移动时,真正发生的是他们逐渐意识到下一帧是什么。 重装重制世界完全是确定性的。 如果玩家向后移动时间,那么他们真正要做的就是将焦点转移到前一帧。

因此,用代码表示这一点相对简单。 时间是一个有界数组。 在每个“帧”时间内,我存储了序列化游戏状态的完整副本。 我对计算增量没有任何幻想。 我没有做任何复杂的事情,例如从关键帧重新运行模拟。

  var timeline = { 
长度:30 * 60 * 10,// 30 fps 10分钟
框架:[{/ * json序列化的初始框架* /}],
波浪 : [{
帧:0
}],
};

函数update(){
_.each(waves,function(wave){
var world =框架[wave.frame];
var next_world = world_update(world);
frame [wave.frame ++] = next_world;
});
}

重置困难(Reset Hard)中的级别最长可能只有10分钟,而30分钟游戏逻辑中10分钟的内存需求却微不足道。 如果将来内存确实成为问题,或者如果我只是想使游戏在低端计算机上更高效,那么我将针对世界各国切换到更高效的数据压缩格式。 我不会更改实现来做一些聪明的事情,例如添加部分世界状态或记录键盘输入。

您会在上面注意到,我使用“波形”来确定要更新和覆盖的帧。 这些也表现为游戏机制-有一组游戏规则可确定何时创建波浪,波浪的速度,波浪如何相互碰撞等。 再次,这只是我公开代码的工作方式。

“但是等等”,我听到你在问。 “如果世界是确定性的,玩家将如何覆盖过去? 当然,那里确实存在某种实施上的诡计。”

不是!

  var timeline = { 
长度:30 * 60 * 10,
框架:[{}],
波:[{帧:0}]
};

var meta = {
玩家:{
焦点:0
}
};

播放器控制器不存在于时间中。 他们使用与我内部使用的相同的系统与世界互动。 播放器焦点确定将向播放器显示哪一帧; 并将玩家的输入投射到聚焦的框架上,以便他们可以指挥自己的化身的动作。

这甚至扩展到坐在电脑前椅子上的实际人类。

玩家的动作如何传递到游戏中? 播放器处于我称为“元时间”的状态。 通过键盘和操作系统将他们的动作投影到元时间(游戏的全局状态)中。 这就是为什么在游戏中没有人会注视某个新人物正好存在的日子的原因。 玩家存在于元时间,玩家的化身被投射到元时间,然后他们的身体状态从元时间被投射到常规时间线上。

当然,在这一点上,我开始偏离上面我所说的实用内容。 但是,我举一个例子说明我愿意走多远以避免挥舞手法或掩盖叙事与现实的矛盾。 当《重装重制》的故事与玩家的现实生活有所出入时,我会改变故事。 我不假装现实改变了。

从纯粹的工程角度来看,所有这些的第二个好处是,对于我来说,实现实际上变得非常简单。 我将以一个人的开发团队的身份组建Reset Hard,因此我的工作范围很多:市场营销,艺术品,设计,编程,社区管理,音乐创作和声音作品,等等。

在编写游戏代码时,能够一次对自己的游戏保持规范的看法对我来说是非常有利的。 如果Reset Hard的机制基于聪明的幻觉,那么我将需要记住大约两倍的游戏运作方式:与玩家相关的机制以及我在代码中用来支持这些机制的技巧和幻觉。

当我与人们谈论《重装重制》时,他们经常评论说,一个内部一致,强大的多人时间旅行系统必须极其复杂。 不是。 在游戏的当前状态下,可能有300行代码专门用于使时间旅行工作。 我希望在开发结束时可能会增加600-800行代码。

Reset Hard没有复杂的代码库。

我对代码库的发展方式充满信心,因为我已经知道它将如何结构化。 它的结构将反映游戏机制。 我粗略地说,大多数核心机制是如何工作的。 当然,事情会发生变化。 我将根据感觉良好或增加策略的其他层次来做出决策。 但是我知道,只要不改变游戏的根本内容,我就不需要进行大量的大规模重写。

可能使这种哲学走得太远。 当我第一次为世界范围内的实体构建更新系统时,我尝试使用事件和特殊方法来传递信息。

  //遍历实体,附加事件 
entityA.on('x',function(properties){
entityB.storeProperties(properties); //也许以后我需要这些
});
entityA.on('x-end',function(properties){
entityB.removeProperties(properties); //但是我现在不应该拥有它们
}); //对于更新顺序,我需要非常小心
// A必须在B之前更新,以便事件触发。
//如果A错过任何事件,世界也会进入崩溃状态...
entityA.update();

我这样做是因为我坚信每个实体都无法访问整个世界状态。 毕竟,我打算对播放器隐藏信息。 为什么内部机制应该不同?

造成这个错误主意的原因有两个:

  • 我实际上并不知道隐藏信息周围的所有中央游戏机制。 在构建设计之前,我正在构建一个实现。
  • 我可以从一个简单的实现开始(让每个实体访问整个世界), 然后在确实需要的时候做类似过滤该世界状态的操作。
  • 此时匹配游戏玩法的实现将比没有实现的实现复杂得多。
  • 最后,当我设计关卡并与玩家讨论关卡时,我没有想到这些。 因此,我没有得到上面提到的任何好处。

相反,我现在允许任何实体在其更新循环期间访问整个世界中的任何属性或对象。 我仍然使用事件,但是只有在合理的情况下才使用事件。 只有在使用它们时,我才能以与在白板上表达关卡逻辑相同的方式来表达游戏逻辑。

如果将来我确实提出了一套可靠的游戏机制来解决某些实体何时可以访问的问题,那么我很可能会回来并重做实现。 但是我不会更改代码,除非我知道设计是什么样的。

我上面概述的方法适用于系统驱动的游戏; 您希望玩家深刻了解机制结构的游戏。 这可能不是每个游戏的好方法,并且如果您习惯于在编码时强制使用特定的范例的第三方框架或引擎,则可能很难做到。

即使在这些情况下,您也应该花时间考虑开发模式是否与您要创建的设计相匹配。 您应该避免将游戏的体系结构与设计完全无关。

这篇文章可能要花更长的时间,但在这里,我将引用《最后的守护者》的首席动画制作人田中真男(Manasoba Tanaka)的话。 在讨论《卫报》风的动画制作过程时,面试官对田中会自己提出许多算法感到惊讶。 那不是程序员的工作吗?

田中回应:

“当您要求程序员自行设计算法时,会反复进行确认和调整,直到获得令人满意的最终结果为止。 这会花费时间,并使您从最初的工作中分散精力,因此有时我会在可能的时候自行设计算法,并请他们使用计算来实现。 我们尝试传达不明确的想法,并确保最终实现与算法的设计尽可能接近。”

同样,在设计游戏时,您应该意识到使各个不同的部分(音乐,美术,编程)对齐以使每个部分都增强整体的方式带来的巨大困难。 在可能的情况下,我正在寻找减少这些系统之间的冲突或战斗量的方法,并允许它们相互协作以相互促进。