撰写了文章 发布于 2019-03-28 11:06:12
《真菌洞窟(Fungus Cave)》三月开发日志
“你还记得封装过多少个对象吗?”“十三个,我是 C 程序员。”
游戏简介
《真菌洞窟(Fungus Cave)》是一款正在开发的单人、回合制 Unity Roguelike 游戏。最新版本是 0.0.3。上个月 以来主要做了三件事情:
- 重做检查模式(Examine mode)的界面。让检查标记在目标间跳转。
- 重做游戏要素:药剂(Potion),感染(Infection),能力(Power)和行动能量(Energy)。
- 新增四种敌人。
当然,学习设计模式也是很重要的。
图 1:演示动画,三月。
图 2:演示动画,十二月。
学习设计模式
软件工程界有个术语,鲁棒三世,意思是说优秀的代码经过三代人维护,依然像油纸包着的零件一样可靠易懂。要做到这一点,必须依靠设计模式。那么什么时候开始学呢?我是自学编程的,因为遇到了急需解决、但是靠现有知识应付不了的问题,所以用三个月时间初步掌握了设计模式。学习资料包括两本书和一套视频:
- 书籍:C# 3.0 Design Patterns
- 书籍:Head First Design Patterns
- 视频:Design Patterns in Object Oriented Programming
首先,我建议快速通读 C# 设计模式。这本书把设计模式从简单到复杂分为三大类(Structual,Creational 和 Behavioral);在每一类内部,把功能相近的模式组成一章(详见下表)——比如一开始讲解了最容易实现的三种模式:Decorator,Proxy 和 Bridge。针对每种模式,先讲应用场景和涉及到的 C# 语法,再画 UML 图,然后提供两份代码:一份是简短的理论代码,另一份更长,牵涉到更复杂的业务逻辑。通常来说,我只看应用场景、UML 图和理论代码,读不懂的时候参考一下 Head First 这本书。C# 和 Head First 同时详细讲解的设计模式,在图 3 中标记为浅绿色。
图 3:C# 3.0 设计模式。
Head First 是我读的第一本书,但是我觉得太罗嗦了,当词典翻翻还行,完整读一遍意义不大。
读完 C# 设计模式,开始看视频(感谢 UP 主搬运到 B 站)。讲课的瑞典博士小哥肢体语言丰富,非常抓人。每集视频短的半小时,长的九十分钟。小哥用一半时间仔细解释每个设计模式的适用场合,接下来四分之一时间画 UML 图,然后写示例代码,最后两分钟回顾与总结。视频全部剪辑过,没有一分钟冷场。我强力推荐。
图 4:这谁顶得住啊?三花是天!
Update() 方法
先前发现一个问题:NPC 超过二十个,游戏开始卡顿;超过五十个,游戏几乎玩不下去。俺寻思了整整一周,最后注意到两点:
- 每个 NPC 都有一个 NPCActions 组件,其中包含了一个 Update() 方法。
- Sche****ngSystem 的 NextActor() 负责让 NPC 轮流行动,简单说这个方法做了三件事情:禁用当前演员的 NPCActions;让“指针”指向下一个演员;启用当前演员的 NPCActions。
换句话说,如果有 50 个 NPC,每一轮都要调用 50 次 Update()。问题是否出在这里呢?于是略作修改:
图 5:示例代码。
问题顺利解决。背后的技术细节我不太懂,但是从现象看,Update() 太多了会拖慢游戏速度。
设计敌人:1
设计敌人这个话题很复杂,我打算逐步分享一些自己琢磨出来的方法。首先,为什么要在游戏里添加敌人?《游戏机制》花了大量篇幅描述资源如何在游戏内流动,我们可以把许多游戏要素抽象理解成“资源”——玩家人物的装备当然是资源,生命值和魔法值是资源,有冷却时间的技能也可以看做短时间内使用次数有限、过一段时间自动补充的资源。从这个角度看,敌人有两个作用:
- 消耗 PC 的资源。
- 补偿 PC 损失的资源。
敌人是资源转换器,但这种转换往往是不平衡的。可能是数量上不平衡:消耗 5 瓶药水杀死敌人,但是对方只掉落了 1 瓶药水;也可能是时间上不平衡:消耗 5 瓶药水,对方掉落了最终战斗才有用的誓约胜利之剑。
由此出发,我设计敌人的第一步是列出 PC 拥有的资源:
- 生命值。
- 伤害。
- 药剂。
- 行动能量。
- 感染(感染率,持续时间)。
怎样理解“伤害”是“资源”?不妨把“攻击”看作是支付三个“伤害”筹码,购买对方的三个“生命值”筹码。“伤害”筹码在每次行动前自动补充到最大值(三个),“生命值”筹码很难得到补充。
接下来,我们假定每种资源都有一个对应的原型敌人,他的攻击能力是直接改变(减少或增加)这种资源,比方说生命值原型能够减少 PC 的生命值。感染有两个原型:提高 PC 遭到攻击时的感染率,或者延长感染的持续时间。它是敌人强制赠送给 PC 的筹码(或者说负面资源),遵循两条规则:
- PC 每次行动前,自动失去一个“感染”筹码。
- 只要“感染”筹码还有剩余,PC 将持续受到负面效果影响。
在 PC 资源列表和原型敌人列表的基础上,根据 SSD 原则设计出实际的敌人。SSD 原则是指:
- NPC 起初和 PC 完全相同(Same)。
- NPC 是简化的(Simplified)PC。
- 每个 NPC 都是截然不同的(Different)个体。单个 NPC 的多种能力也是截然不同的。
所谓完全相同,从编程的角度讲,是让 PC 和 NPC 继承同一个接口 IAction,但是具体的实现方法不同——PCAction 监听键盘事件,NPCAction 根据 AI 算法决定下一步行动;从纸面设计的角度讲,是让原型敌人拥有 PC 的一切资源,包括(上文提到的)基础资源以及高级资源(改变资源的资源)。《真菌洞窟》的高级资源是能力(Power)。PC 有九种能力,三组防御,三个攻击,分别影响生命值、感染和能量:
- 急救(First Aid),死神(Reaper):恢复生命值。
- 免疫(Immunity),快速治疗(Fast Heal):降低感染率,缩短感染时间。
- 活力(Vigor),肾上腺素(Adrenaline):恢复行动能量。
- 流血(Bleed):攻击时增加伤害。
- 瘟疫(Plague):攻击时提高感染率。
- 虹吸(Siphon):攻击时消耗对方的能量。
简化有两层含义。第一,把浮动数值变成固定数值。比方说,PC 的最大生命值随着等级提升而提升,但是某个 NPC 的最大生命值始终是固定的。第二,删除或合并部分资源。详见下表。
图 6:NPC 数据表。
与 PC 相比,NPC 只有四种能力,而且都表现为固定数值。
- 急救,死神:全部删除。直接修改最大生命值。
- 免疫,快速治疗:删除免疫,保留快速治疗(Defense-Duration)。
- 活力,肾上腺素:删除肾上腺素,保留活力(Defense-Energy)——每轮额外恢复 X 点能量。
- 流血:删除。直接修改伤害。
- 瘟疫:保留(Attack-Rate)。
- 虹吸:保留(Attack-Energy)。
每种敌人应当努力带来截然不同的游戏体验。不能指望玩家注意到细微的数值差异,比如大地精的伤害比地精高出 5%,可以这样修改:地精攻击 3 次才能击杀 PC,大地精攻击 1 次就能重伤重甲 PC。这种不同除了来自数值,也可以来自游戏元素——地精造成 3 点伤害,大地精造成 4 点伤害,并且能够晕眩 PC。
以上是我目前的设计心得。这张纸还剩点空白,我来写个证明。
- 大乔无头,学姐也无头。
- 大乔是绅士,学姐是淑女。
- 大乔的后代承太郎拯救了世界,学姐的后辈鹿目圆也拯救了世界。
- 大乔身边的 Dio 是恶人救世主,学姐身边的 QB 也是恶人救世主。
- 乔纳森 25 画;巴麻美 24 画,少了一点勇气。
“虚渊玄荒木替身说”成立。Q.E.D.