« June 2024 | Main

July 16, 2024

飞船建设部分的设计草案

像异星工厂、缺氧、边缘世界都有大量的 mod 。可以通过大改游戏机制,把本体游戏改造成完全体验不同的游戏。这些好玩的 mod 几乎都是一个人完成的。所以我觉得,固然游戏的外层玩法决定了整体体验,但其实开发的总工作量并不大。而且,一旦玩法不满意,也容易修改。

我的游戏开发计划是先完成一些底层基础系统,再考虑完整游戏的全貌。没有上层玩法支撑,光有底层系统玩起来一定寡淡无味,但我认为它们是设计和开发中最重要的一环。

在进一步实现 demo 之前,我设计了一下飞船的建造系统。

游戏世界中的物质分为三种:建筑,零件,原料。

摆放在场景中(飞船)的是建筑,墙体、门、机器、家具这些属于建筑,一旦修建出来就不可任意移动位置。建筑是由零件构成,拆毁建筑会 100% 返还零件。建筑修建是可逆的。

零件是由原料在机器中合成出来,这部分和异星工厂相同。并不一定存在逆向的合成配方。例如,铁板可以合成铁丝,但铁丝并不一定存在变回铁板的方法。所以和建筑不同,加工零件可能是一个不可逆的过程。

飞船(场景)是由四边形网格构成。每个格子永远只能容纳一种物质,要么是建筑的一部分,要么是一种零件。而原料无法直接存在于场景格中,它们必须存在于容器里。建筑和工人都可以是容器。

工人可以在场景格的过道中移动,工人之间不设阻挡。没有建筑的格天然称为过道,部分建筑会占据场景格,成为障碍物。有些建筑将所占据的部分场景格保留为过道。例如门。

每种零件在同一场景格内有一堆叠上限,超过上限就无法容纳在同一格内。有些建筑拥有容器格,容器格和场景格相同,但不存在于场景中(只附属于建筑或工人)。容器还可以存放原料。

每次建设建筑的时候:

  1. 把所占场景格清空。
  2. 把所需零件堆在所占用的场景格上。
  3. 工人在建组所占格的建设邻接位(每个格子有 8 个建设邻接位)工作。
  4. 工人必须通过过道接近建筑蓝图,工人在过道中只能以四个邻接向移动。

注:由多种零件构成的建筑,所占格的数量必须大于等于零件种类。换句话说,如果一格建筑是 1x1 格大小,它就只可能由一种零件构成。

当拆建筑时,也需要工人站在建筑所占格的建设邻接位工作。建筑拆卸后,完全返还零件,置于所占空地。

有些建筑带有不只一格的容器,例如货架,这类建筑会扩展场景的收纳能力。但,拆卸这种建筑必须先排空容器,才能进入拆卸环节。一旦建筑损坏,容器内的物品就不可取出。

加工机器通常带有几格容器,用于保存原料和产品。

工人可以在场景中移动,工人身上带有一或多个容器格。用于建设建筑和安装配件。配件是一种特殊的建筑,存在于工人行动层之外,例如电线和管道,它们可以和建筑同个场景格,拆卸不会回到场景格内,而是进入工人身上的容器中。


游戏的建设部分会类似异星工厂和边缘世界的混合体。和异星工厂不同,建设需要把零件运送到场景格上,并由工人消耗工时修建。一旦修好,不可以在场景上移动位置,但可以无损拆卸。零件加工又比较像异星工厂的加工机制,由合成配方决定了生产过程。但又不允许原料直接放在地板上。

我的设计思路是:游戏里的东西尽量具象化,每个东西就是一个视觉上可见的东西,而不是表单上的一个数字。自动化像矮人要塞或缺氧那样,由工人的工作系统驱动完成。不排除后面加上传送带和自动装卸机,但可能把传送带机制和流体管道合并成同一机制(类似缺氧)。

July 10, 2024

室内空气流动模拟

在设计飞船建造的游戏时,我想做一个空气流动的模拟系统。这里有一个初步的方案,不知道好不好,先记录一下:

  1. 空间是由二维的正方形格子构成的,有些格子如墙体会阻止空气流动。每个格子有温度属性及气体质量的记录。不同气体可以同时存在于同一个内,质量单独记录。温度传导系统另外设计。
  2. 每个游戏 tick 对每个格子做一次独立计算,根据温度值给出一个概率,这个概率决定了该格气体向周围扩展的比例。
  3. 温度越高,越可能将更大比例的气体平均扩散到最多邻接的 8 格空间。
  4. 当格子因为建墙而导致空间变化时,把该格内气体全部扩散到临接格,当极端情况下无法做到时,空气锁定在墙体内,不会消失,等待以后满足条件后再扩散出去。

为什么要做基于格子而不是基于舱室的空气模拟?例如, FTL 的氧气气压模拟就是基于房间的。

因为,这个模拟需要结合游戏本身其它玩法的需要。预计之后会做气闸舱,真空室等设定。需要有一个更微观的空气流动模拟,简单的给船舱加上气压的数值是不够的。和 FTL 不同,我希望做一个基地建设风格的游戏,舱室的结构是动态的,难以以房间为单位计算。

目前有气体流动模拟的游戏中,最流行是缺氧。为什么不直接使用缺氧的模拟系统?

缺氧的基本规则是:每个格子只能有一种物质,这种物质可能有三态:固态、液态、气态。物质会因为温度发生三态转换。不同形态的物质属于不同的东西,每一格不能混装。特殊情况下,气体会直接消失。气体倾向于从高压格流向临近的低压格,并受重力影响向下运动。

缺氧的流体系统是和其它游戏系统相结合的:动植物需要在不同的气体环境下生长,小人会对环境气体尤其是有毒气体做出反应。这些玩法我应该不会做。尤其是,我的游戏地图是平面的,和缺氧不同,没有上下之分,所以不考虑气体重力分层。

对于缺氧来说,它的核心玩法之一是治理无序的自然空间。在玩家拓展空间时,必须考虑原有空间的环境。配合这个设定,才有衍生玩法。而我暂时不想在我的游戏中设计这些。我需要和气体模拟相配合的玩法主要是,根据环境气压影响人物或动植物的状态。

我希望模拟系统简单、准确、符合直觉。

简单:每个格子可以单独运算,不涉及复杂的公式。

直观:尽可能符合玩家的预期。例如,空气从高温区流向低温区,从高气压处流向低气压处,直到平衡。如果空气中混入一点毒气,也会缓慢的随时间扩展,稀释。

准确:规则上可以保证,不会因为模拟计算的误差而出现气体总量的变换。


下面还有一些对玩法的初步想法,尚未推敲细节:

  1. 飞船内需要部署制氧机。制氧机只能通过管道和其它设备连接,不直接向环境排放气体。
  2. 氧气需要通过特定设备输入通风管道,通风管道需要通过排气口向环境排放。
  3. 船舱中可部署一些氧气面罩供应点,它们由管道供气。
  4. 角色随身携带一格氧气面罩,当环境气压在阈值附近变化时自动戴上或取下。氧气消耗完毕后,会自动寻找临近的面罩更换。

July 02, 2024

角色动画系统

Ant 引擎的角色动画系统还需要完善。

之前我们用 Ant 引擎开发的游戏以机械装置为主,所以并不需要人型角色动画。对于人物角色动作的动画控制,最好有更多的引擎支持。

通常,角色逻辑上的属性和动画表现存在一个映射关系。一个角色,它逻辑上的基本属性可能只有在空间中的坐标。我们编写代码控制它时,只关心它在哪里。但是,在做画面表现时,则需要根据空间坐标这组简单属性,转换为动画播放:如果角色静止不动,就播放 idle 动画,如果正在运动,就播放 walk 动画。

在引擎底层,每帧只通过坐标这个属性来计算角色该播放哪个动画以及怎样播放,信息是不够的。通常还需要结合过去时间线上的状态变化来推算出当前状态;或是通过几个独立的逻辑属性的组合,得到动画需要的信息。

例如,可以通过空间坐标的变化过程计算出速度;根据是否处于战斗状态决定是警戒行动还是自由行动……

这种从逻辑属性数据到表现用数据的映射关系,一般使用状态机来实现。Unity 文档对此有一个很好的描述。通过这个状态机,可以生成动画系统底层每帧需要的数据,而开发者只需要简单修改逻辑上的基本属性即可。

动画系统底层看到的是 Entity 当前状态下用于动画渲染的基础数据:一个动画片段的当前帧,或是几个动画片段的加权混合。而状态机只用运行当前帧的当前状态关联的转换逻辑去加工那些基本属性输入。

因为状态机永远处于单一状态,但对于动画来说,从一种状态到另一种状态通常有一个表现过程,所以,状态机中的状态和表现上的状态是有区别的。Unity 把两者区分开,idle ,walk 这种叫 state ,从 idle 到 walk 的过渡期叫 state transition 。从状态机的实现角度看,其实它们都是状态机的节点 node 。

对于非帧动画来说,处于 state 时,通常只有一个动画片段;在 state transition 阶段,则为它连接的两个 state 的动画片段的混合。state 可以触发特定的行为 behaviour ,开发者可以围绕每个特定的 state 来编写逻辑;而 state transition 通常由几个参数控制,对开发者是透明的。

动画状态机的 transition 和动画表现上的多个动画片段混合,是不同的两个东西。

从走路到跑步的过渡阶段可以直接切换两个做好的动画片段,如果是帧动画,动画片段有若干帧构成,通常几个可以衔接在一起的片段,每个片段的最后一帧可以和第一帧衔接在一起,保证循环播放,不同动画片段的第一帧是相同的,允许片段间切换。那么,状态机的 transition 要做的工作一般是,保持上一个状态的动画片段序列播放到最后一帧,可以顺利切换到下一个状态。

以走路过度到跑步为例,状态机在更新时,如果处于走路状态,一旦发现移动速度超过跑步的阈值,就可以切换到 walk to run 这个 transition ,在这个新的 node 中,状态机会继续保持走路的帧动画片段提供给动画底层,直到一个片段周期结束,然后切换到 run 这个新 state ,新的 state 会使用跑步的动画片段从头播放。

对于骨骼动画,往往不受单一动画片段的限制。它可以将多个动画片段混合在一起。在 transition 中,可以逐帧调整多个动画片段的混合权重。

关于人物运动怎样用多个动画片段混合,这里有一篇论文 做了非常详细的解释。

如果是简单的两组动画混合,例如运动方向不变的走路到跑步的过度,使用一维的混合即可。即逐步降低前一个动画片段的权重,同时增加目标动画的权重。

而如果要考虑人物在移动过程中转向,则需要二维的插值。通常使用 Gradient Band Interpolation 。当需要考虑速度变化时,把插值放在极坐标系下效果更好。虽然这需要 O(n2) 的算法复杂度,但通过一张预运算的表,就可以减少到常数时间。

btw, 即使是在 state 中,也可以用到动画的混合。比如,负伤走路的角色和正常走路的角色表现不一样。负伤走路的动画可以是由负伤动画和行走动画叠加混合而来。