回顾昨天的过场动画工作
我们正在制作一个游戏,目标是通过直播的方式完成整个游戏的开发。在昨天的工作中,我享受了制作过场动画的过程,所以今天我决定继续制作多个层次的过场动画。
昨天我们已经开始了多层次过场动画的基本制作,并且成功地做出了一个初步版本。这个版本看起来相当不错,已经具备了一些深度效果,整体效果令人满意。当然,仍然有一些细节可以进一步调整,使它看起来更加完美。
接下来,我的目标是继续制作剩下的所有过场动画,并确保它们的工作方式与目前已经完成的部分一致。这样,我们就能将每个部分组装成完整的过场动画。每个部分都做好之后,接下来可以集中精力让整体更加连贯和易于使用,甚至今天可能也会做一些这样的调整。
总之,今天的工作主要是继续制作和调整过场动画的各个层次,确保它们都能顺利运行,并准备好将它们整合成完整的过场动画。
game_cutscene.cpp:引入 struct layered_scene 来保存所有的过场动画数据
目前的工作内容主要是在设计和实现过场动画的结构。现在的结构非常通用,实际上并没有做任何特殊的处理,仅仅是把必要的过场动画信息进行了整理。例如,主要的内容包括层数以及每一层的操作方式。我们可以像这样简单地定义一个“层数”变量,表示一共有多少层,比如“层数为8”,然后再在每个具体层中定义相应的操作。
这段代码基本上已经包含了所需的所有数据结构,也就是过场动画的全部信息。如果我们想要的话,可以将这部分内容独立出来,实际上并没有什么理由不这么做。例如,可以将这部分称为“过场动画”或者“分层过场动画”。我们可以在这个结构中包含每个层的信息,如层数、镜头索引等数据。还可以通过查看文件格式和资产类型来进一步优化,比如为每个资产指定一个类型ID。
为了更清晰地组织这些数据,我们可以为每一层指定一个指针,指向包含所有信息的“层放置”数组。这样,我们就能有一个非常基础的过场动画结构。虽然有些部分可能需要扩展,但对于目前的需求,这已经基本足够了。
另外,我们还可以考虑增加一些其他字段,例如过场动画的持续时间。可以简单地为每个过场动画设置一个持续时间字段,暂时将其设置为一个默认值(例如0到5秒之间),并在后续的调整中进行更改。这样,我们就能进一步完善过场动画的时长设置。
game_cutscene.cpp:引入 RenderLayeredScene
现在如果我们想把这个系统转换成真正可用的形式,也就是说实现对某个具体场景的渲染,我们可以定义一个函数,比如 RenderLayeredScene
,它接收我们定义好的某个分层场景作为输入。
只需要传入当前的分层场景对象,再传入一个归一化的时间值(T 值),这个时间值表示当前动画进度的位置,有点类似从 0 到 1 的比例。这些信息其实已经足够进行渲染。
当然,还有一些额外的数据也可以一并传入,比如摄像机的起始位置和结束位置,这些都能影响画面显示效果。所以我们会把相关的 CameraStart
和 CameraEnd
变量也一并抽出来,用来提供视角的过渡控制。
我们从现有代码中把这些相关逻辑提取出来,封装成一个通用函数,这样每次需要渲染一个分层场景时,就只需要调用这个函数并传入所需的参数。接着,在调用位置,比如某一帧画面中,我们只需要调用 RenderLayeredScene
,并传入当前所需的数据就可以完成渲染。
这个函数需要的参数包括:资源信息、渲染组、当前帧的归一化时间等。原来用于处理 cutscene 的 T 值逻辑现在就可以直接拿来作为这个归一化时间值的来源了。
这样一来,整个 cutscene 系统就实现了高度通用性与模块化,任何一个场景的渲染,只需要用标准结构传入数据即可完成绘制,大大提高了代码的复用性和维护性。
game_cutscene.cpp:定义 layered_scene
我们现在所需要做的,就是正式定义一个分层场景。之前写的这部分内容,正是用于定义这个分层场景的数据。我们可以直接把这段定义提取出来,单独放到一个结构体中。之后,我们就能直接在程序中使用这个定义好的场景进行渲染。
我们将所有相关的信息封装到这个结构体里,包括层数(layer count)是 8、镜头编号(shot index)是 1、资源类型(asset type)对应的是开场动画。剩下的就是填充图层定位(layer placement)数据,这些数据也已经准备好了,只需要设定一个指针指向这部分信息。
定义好场景之后,就可以在需要渲染的地方传入这个场景结构体。接下来还需要补充两个摄像机参数,分别是 CameraStart
和 CameraEnd
,这样就能在场景切换中进行视角控制了。
将这两个变量也加入定义中,然后设定对应的值,使得这个场景在播放时能完整运行。这样,一个完整的分层动画场景结构就构建完成,可以作为系统中任意 cutscene 的模板,后续其他镜头也能以此为基础进行搭建与替换。
整体思路是把每个镜头都变得模块化、数据驱动,使得系统对 cutscene 的加载和播放更加通用、高效、灵活。
解决问题并从中概括,即面向压缩的编程
我们一贯采用的编程方式非常直接明确。只要已经清楚接下来要完成的核心任务是什么,就会直接动手去写实现代码,这正是之前所做的。首先专注于完成具体功能,等完成后再回过头来,把那些可以重复使用的部分抽离出来,整理成更通用的结构。
这种方式避免了大量前期冗余的设计讨论,也不需要来回斟酌那些在实际问题中根本不重要的细节。只需先解决实际问题,然后再从已完成的解决方案中提炼出通用模式。与其一开始就进行过度设计,不如从具体做起,然后自然演化出更好的架构。
这个方法最适用于我们清楚如何完成某个具体目标的场景。例如,如果已经知道如何编写一个完整的过场动画流程,那就直接把这个过场动画编码出来。编码完成后,所需的通用机制自然就会浮现出来,比如分层场景、镜头参数、动画持续时间等。
如果一开始就不知道如何做某件事情,那确实不能采用这种方式。但当我们已经掌握其中一个案例的实现方法,就可以通过这个案例的构建过程,把其他共性部分提取出来,从而构建整个系统的通用结构。这种方式始终是最轻松、最高效的设计方法。
game_cutscene.cpp:清理一些东西
现在已经完成了分层场景的基本结构设计,接下来就是将其整合到现有的渲染逻辑中。我们所需的内容几乎保持不变,比如传入的归一化时间参数仍然照旧,只是现在像镜头索引、图层数量和资源类型这些信息,全部从分层场景结构中提取出来,而不是单独管理。
对于资源类型 ID,也直接从场景结构中读取,这样就实现了逻辑的集中和简化。通过这种方式,每个场景变得更加独立和自包含,使得渲染函数只需要接收一个完整的分层场景结构,而不再需要依赖外部零散的参数配置。
剩下需要处理的一些小问题,比如单位转换相关的 meters 到 pixels 的操作,还有一些代码细节需要清理。这些目前虽然还不是最理想的状态,但已经能正常工作,只是后续可能需要让这部分逻辑更加清晰和一致。
此外,注意到变量类型有些地方原来用的是 signed 类型,而实际上这里更适合用 uint32
这样的无符号整数,这样可以避免不必要的类型问题或隐式转换引起的错误。
最后,图层位置数组的引用方式也需要调整,现在应该从场景结构中读取,而不是直接访问之前的局部变量。整理完这些内容之后,基本上就完成了一个可复用的、结构清晰的分层场景渲染系统。
game_cutscene.cpp:引入 scene_layer 和 scene_layer_flags
现在开始处理“距地距离”(distance above ground)的问题,这部分稍微有点棘手。因为在某些图层中,例如角色固定不动、或具有特殊显示位置的图层,目前系统里并没有一个明确的方法来表达这些“特别”的层。我们之前的方式是简单地将其远远地移动到背景中去,但这种方式不够明确和灵活。
为了解决这个问题,我们决定为每一层引入更强的表达能力,即每层拥有独立的参数设置。具体来说,我们把之前混合在一起的隐式数据结构,改成显式的数据字段。例如:
- 明确设置图层的位置
p
; - 明确设置图层的高度
height
; - 为图层增加一个“标志位”(flags)机制,让我们能够在每层上添加类似“处于无限远”(at infinity)这样的标记。
于是我们定义了 SceneLayerFlags
,并在里面加上了一个 SCENE_LAYER_FLAG_AT_INFINITY
这个标记。这个标记的意义在于:如果某一层被标记为 at infinity
,那么它的 Z 值不再是一个固定值,而是动态地加上 distance_above_ground
,使其始终处于地面之上的某个相对高度。这样我们就可以给它设置一个例如 -200 的基础值,然后用 distance_above_ground
动态叠加,使其看起来始终处于“背景”或者“空中”的效果。
为了实现这个逻辑,我们将原先叫做 layer_placement
的数据结构重命名为更通用的 scene_layers
,因为它现在不再只是描述“放置”,而是包含了更多的状态信息,如位置、标志等。每一个图层的数据现在会独立处理,并从结构中读取出对应的字段:
layer.p
:图层的位置;layer.flags
:图层的标志位;layer.height
:图层高度(如果有需要可以使用);
我们还使用了宏(macro)来辅助处理标志位的读取,使代码更简洁,虽然这在功能上并不是必须的,只是觉得这样更方便。
这样一来,我们的系统就具备了更强的可扩展性和表达能力,可以根据图层的具体需求设置不同的行为,比如:
- 设置某些图层始终相对地面浮动;
- 将某些图层标记为特殊状态,便于后续逻辑处理;
- 更容易管理不同层之间的显示深度和逻辑区分。
目前结构已经基本清晰,接下来可以在渲染时直接根据这些参数进行调整,确保每个图层根据自己的设定被正确渲染。这样就为后续复杂场景的构建和动画控制打下了坚实的基础。
运行游戏并查看我们的场景
现在我们已经完成了基础构建,接下来要做的就是验证场景能否正常恢复显示。最终场景如预期一样渲染出来了,所有内容看起来都和之前一样,这正是我们想要的效果。
这意味着现有的结构和功能已经成功实现了可复用的“图层化场景”渲染逻辑。接下来就可以继续开展后续工作了。
我们现在可以开始制作其他的场景了。只需要调用刚才完成的渲染函数,并传入新的图层场景数据即可渲染一个新的分镜(cutscene)。这整个过程的重点是:
- 代码的可复用性已经达成:无需再次编写一遍渲染逻辑,只需要提供新的数据;
- 逻辑的通用性已经验证:不论是哪个场景,结构一致的数据都可以通过同一个流程渲染出来;
- 节省了大量重复工作:所有之前写好的逻辑现在都能被重复使用,极大提升了开发效率。
接下来我们要做的,就是继续定义其他新的图层化场景,把它们像之前一样组织好,然后一一调用同样的接口进行渲染。这将让整个剧情系统的分镜管理变得更清晰、更高效,也更易于后续维护和扩展。
game_cutscene.h:将结构体从 game_cutscene.cpp 移到这里
现在我们要开始构建下一个分镜场景,方法会和之前完全一样。我们会按照相同的步骤来搭建。首先我们在代码结构中,准备好一个新的位置来存放场景定义。
接着我们注意到项目中其实已经存在一个名为 game_cutscenes.h
的头文件,虽然之前没有特别注意到它的存在,但既然已经有了这样一个文件,我们就决定把新的场景定义放到这里去。
这个文件可以很好地作为统一管理所有分镜场景的地方,将不同的场景注册和组织起来。这样每次新增一个分镜,都可以清晰地放在统一的结构里,后续查阅和修改也会更加方便。
最后,我们创建了一个新的场景,并将其放入结构中。整个过程令人满意,现在又多了一个明确的、可组织的新场景定义点。
我们正式完成了下一步的准备工作,场景定义文件也变得更加健全,后续只需要继续添加新的分镜内容即可。整个系统正逐步走向更加模块化和规范化的方向。
game_cutscene.cpp:设置以合成更多的镜头
现在我们切换到了全屏模式,能够完整地查看分镜画面,并开始像之前一样进行分镜的编辑。我们进入了 RenderCutscene
的流程中,接下来的目标是定义一个新的分镜场景。
整个过程很基础,没有复杂的内容。我们复制了之前已有的一段分镜代码,作为新的模板,并开始构建一个新的分镜,当前这个新分镜被命名为 “Shot 2”。
我们首先进行了基础的设置,比如用条件判断语句把不同的镜头分开处理,避免变量或设置之间的冲突。虽然目前显示的还是第一个分镜的内容,但结构已经准备好,随时可以替换。
然后我们检查了是否存在标记为“无限远”的图层(此前用于背景固定层的特殊标记),发现当前这个分镜并没有这样的需求,所有图层都会随场景一起移动。因此我们移除了所有与“无限远”相关的设置。
我们暂时还没有确定这个分镜中相机的具体运动方式,所以相机移动部分被注释掉或暂时保留为空。这意味着当前相机在场景中是静止的,仅显示分镜图层的内容,方便我们逐一查看每个图层的效果。
接下来我们会继续调整各个图层的位置、内容,以及确定相机的运动路径,从而完成这个新的分镜场景的搭建。整个流程沿用了前一个分镜的结构和方法,保持了代码的可复用性和一致性。
game_cutscene.cpp:编码 CameraStart 和 CameraEnd 中的 Z 轴运动
我们决定对当前场景的Z轴处理方式进行一次改进。之前我们在渲染过程中把 distance_above_ground
(离地距离)直接硬编码加到了 pz
值上,比如加了一个 10 - 5.5
这样的偏移。但现在回头来看,这样做其实并不理想。
原因在于,我们希望Z轴的变化成为相机运动的一部分,而不是静态地被加到某个变量里。硬编码的Z偏移会限制我们对相机的控制,使得相机的运动不够灵活且不透明。因此我们做了如下调整:
我们将原本硬编码的Z轴偏移移除,改为让相机在 camera_start
到 camera_end
中的Z值承担这一变化。比如我们希望相机从高度10向下移动到高度5,那么就将相机的Z轴从 0.0
移动到 -5.0
。这样就可以:
- 把Z轴运动完整地纳入相机轨迹中,增强了动画可控性;
- 不再需要额外处理
distance_above_ground
的加法逻辑; - 避免层叠数据结构之间的耦合,使得相机行为更直观。
同时我们考虑了一个更简洁的方案:如果不再手动编码 distance_above_ground
,那么我们可以直接将它设为0,因为它的意义已经由相机的Z偏移代替了。也就是说,现在这个变量成了一个“纯形式值”,在渲染逻辑中不会再被直接引用或参与计算。
最终效果是:
- 所有与相机有关的高度变化现在都归纳进
camera_start
和camera_end
的z
分量; - 图层的渲染位置则独立处理,不再混合进相机控制逻辑;
- 渲染系统也变得更干净、分工更明确;
我们恢复到了之前的渲染状态,同时为今后每一个分镜的相机运动留下了更大的自由度和表达能力。每一段Cutscene都可以通过这两个变量来自由控制前后帧之间的镜头移动——这是我们更理想、更合理的做法。
game_cutscene.cpp:合成镜头 2
我们现在回到第二个镜头的制作,开始对其进行具体的图层与相机运动调整。
首先我们注意到第一个图层是静止的,这很正常,因为当前相机还没有任何移动。这个镜头计划是一个缓慢的推进镜头(slow zoom),所以我们预设相机的Z轴从0
推进到-5
,形成一个比较轻微的推进感。如果感觉推进不够明显,也可以进一步加深推进程度,这取决于图层之间的相对距离。
图层的空间摆放也进行了初步的设定。第一个图层,我们计划将其放在相机前方大约-20米
的位置,并设置较小的缩放比例。因为这个镜头是一个近景镜头,图层不多,整体画面也比较紧凑,推进的效果会更加显著。为了控制推进的节奏,我们决定将相机的移动距离设为0.5米,模拟一个非常微妙的拉近。
时间上的控制暂时没有加入,我们计划在后续调整镜头节奏时一并处理。
接下来,我们添加了第二个图层,它需要位于孩子的前方。初始摆放完成后,进一步微调了位置,使之与整体构图更协调。随后检查是否还有其他图层,确认还有一层是冰柱(icicles)。
冰柱图层被放置在画面顶部,并设定为离相机更近的位置,例如-8米
,以便在推进镜头时呈现更强的视差效果(parallax)。这个图层也上移了一些,以确保它在正确的屏幕位置,增强画面层次。
检查后确认该镜头仅由三个图层构成:
- 背景层(远处)
- 中景层(孩子)
- 近景层(冰柱)
我们接下来开始微调视差效果。观察中景层的窗户部分后,决定将其进一步靠近,以增加透视感和空间感。同样也微调了孩子图层的位置,让他更居中地位于窗户中间,同时略微下调其Y轴,使其构图更自然。
调整完所有图层的空间位置后,我们回到相机设置,再次确保相机推进的终点对准孩子的位置,形成一个具有明确视觉焦点的推进镜头。
最终这一组调整达到了我们预期的效果:
- 镜头推进自然;
- 图层间深度关系合理;
- 透视与视差增强了空间感;
- 画面焦点清晰,对准孩子;
- 各图层构图协调。
这一版的镜头感觉良好,基本达到了视觉与运动的双重目标。
(近 → 远) :
- Icicles(前景,最靠近相机,用于增强空间感和运动感)
- Wall and window(中景或前中景,构图元素)
- Hero and tree(中景或背景主角图层,是视觉焦点)
缺少雪花…α
目前我们已经完成了一个剪辑,整体看起来效果不错。这个剪辑场景中只包含了三个图层,分别是:
- 主角和树:这个图层展示的是角色本身以及身边的一棵树,我们将这个角色称为“主角”。
- 墙与窗:第二个图层中包含了一面墙和一个窗户,形成了房屋外部的背景元素。
- 冰柱:第三个图层是一些冰柱(比如从屋檐垂下来的冰锥),被放置在画面的最前景,以增强纵深感和画面的层次。
原本计划中还打算加入一些飘落的雪花,用来进一步丰富场景的动态效果,但我们意识到很可能在资源包中忘记包含雪花图像,这是一个疏漏,后续可能会单独补充雪花相关的素材。
不过目前这些静态图层的组合已经满足该场景的需求。剪辑整体结构和过渡都达到了预期,特别是镜头缓慢推进到主角身上的动态设计也已经调整得比较满意。冰柱等元素的位置也经过微调,以增强前景的视觉运动感,画面层次感更清晰。
总的来说,这个剪辑就是围绕“主角与树”“墙与窗”和“冰柱”这三类图层构建的,整体感觉已经相当不错。接下来我们将继续制作下一个剪辑。
game_cutscene.cpp:合成镜头 3
我们现在开始制作第三个剪辑,在索引中新增一个场景,重新开始配置。这一幕的背景是室外场景。
这个剪辑相对有趣,但也存在一定挑战。在设想这个镜头时,我们并不确定所采用的透视是否能奏效。这个场景的镜头运动方式不是垂直方向,而是横向移动,这是比较少见的一种处理方式。我们有一段是垂直移动的,还有这一段是水平横移的。这种处理方式在这种剪辑技术中相对少见,所以我们也不确定是否能很好实现。但如果最后发现无法达到预期效果,那也没关系,艺术制作中本来就经常要进行反复调整和修改。
我们开始搭建这个剪辑,首先加载天空图层。天空图层在理论上是应该静止的,因为它是实际的天空背景,不应该随着镜头运动。但由于这段镜头模拟的是一个“俯视下来的移动镜头”(Over-the-top camera move),所以也可能需要让天空稍微移动来配合这种透视效果,因此我们暂时保留它是否静止的判断,等后续调整再看。
接下来我们加载第二层,也就是森林背景。这是位于中景的户外环境。这时我们发现这类运动可能需要一种“反向运动”机制——也就是说,为了模拟摄像机“俯视移动”的感觉,部分图层可能要进行反方向的视觉移动来制造真实感。所以我们考虑引入一个新机制,比如设定一个“反向移动标志”,让某些图层在剪辑中反向运动,以达到正确的视觉模拟效果。
我们继续添加更多图层。当前我们已经加载了一个新图层,这是我们要模拟的“顶部视角”的一部分。这个视角的设想是——摄像机从上方向下拍摄,然后缓缓移动,使观众有一种镜头越过人物头顶的感觉。
现在我们把主角(孩子)的图层也加入进来。这个角色图层我们希望它随着摄像机运动一起移动,而背景图层可能不动或反向移动,形成视觉上的“镜头绕着角色移动”的效果。这意味着角色图层应该与摄像机保持一致的运动,而背景图层要模拟出相对运动差。
这个设计思路要求我们后续仔细调试每一层的运动方式,尤其是镜头运动过程中,不同图层产生的视差与节奏必须协调,才能形成理想的透视与动感。整个场景的逻辑是构建一个俯视镜头从背景穿越至主角上方,通过合理的图层组合与运动设计,实现动态的过渡效果。我们将在继续调整位置和动画方式的基础上,进一步测试视觉结果是否符合预期。
game_cutscene.h:将 CounterCameraX 和 CounterCameraY 添加到 scene_layer_flags
我们继续添加功能,在这里我们引入了一个新的标志变量,用来实现“反向相机运动”。我们先切换回常规视图,然后开始添加这个新标志。这个标志可以命名为 counter_y
或更明确的 counter_camera
,具体来说我们最终定义为 counter_camera_x
和 counter_camera_y
等。
这个机制的作用是在某些图层中启用相机运动的反向偏移,即当相机向一个方向移动时,这些特定图层会以相反的方向做相应的位移,从而在视觉上看起来像是保持了相对位置不变。这种效果在模拟“镜头绕物体运动”时非常重要,尤其是在构建俯视镜头(top-down shot)或水平扫动镜头时格外有用。
在具体实现中,我们修改了之前统一使用的相机偏移方式。原来我们是通过一个整体的 camera_offset
直接影响位置计算的,现在我们将其拆分为 px
和 py
两个维度分别处理:
- 对于
px
,我们先减去camera_offset.x
,但根据是否设置了counter_camera_x
,决定是减去还是加上这个偏移值。 - 对于
py
,同样判断是否设置了counter_camera_y
,来选择加减偏移量。
这种方式允许我们对每个图层单独控制它是否要应用反向相机运动,而不是所有图层一视同仁。这样一来,我们就能很灵活地决定哪些图层应该随着镜头运动而移动,哪些图层应该产生相对静止或反向漂移的效果,从而更精确地模拟真实镜头中的透视、空间感与层次深度。
总结:
- 添加了一个新机制
counter_camera_x/y
来支持图层根据需求执行反向相机运动; - 将位置偏移计算拆分为
x
和y
两个方向进行; - 每个方向都支持独立控制是否进行反向偏移;
- 为接下来的复杂镜头设计打下基础,尤其适合处理非线性运动、模拟旋转或环绕等镜头。
game_cutscene.cpp:处理 Z 情况
我们意识到之前的代码逻辑中忽略了对 Z 轴(深度)方向 的处理,这是一处遗漏。在进行相机偏移的处理时,只考虑了 X 和 Y 方向的位置偏移,忘记对 Z 方向进行处理。
Z 轴的处理非常重要,特别是在模拟景深、远近视差(Parallax)或推进镜头等情况下,Z 方向的位移直接影响图层在空间上的表现。
于是我们补上了 Z 轴的偏移处理逻辑,即:
RenderGroup->Transform.OffsetP.z = pz - camera_offset.z
这段代码的意思是将图层在 Z 方向上的位置也根据相机的 Z 偏移进行相应调整,从而保持统一的相对空间位置。因为当相机在 Z 方向上有运动时,如果不对图层的 Z 值做补偿,画面中就会出现错位或不一致的视觉效果。
现在 X、Y、Z 三个方向的偏移都已经正确处理,场景中所有图层在相机移动时应该能够保持准确的空间关系,视差效果和镜头运动也能按照预期表现。这是构建多图层镜头运动系统中非常关键的一步。
game_cutscene.cpp:继续合成镜头 3
我们正在调整一组镜头,实现的是一个具有纵深感和前后视差效果的场景过渡,试图让背景图层与相机运动形成反向关系,从而制造出空间感和动态效果。
首先,我们设置背景图层与相机形成「反向运动」,也就是相机往一个方向移动时,背景图层以相反方向位移。我们不确定这样设置是否会完全起作用,但打算尝试一下看看效果。
接着我们开始微调角色初始位置,让角色一开始处在画面较低的位置,并在镜头移动过程中逐渐脱离画面,以此营造出镜头拉远的感觉。同时我们也延长了镜头时长,原本是5秒,现在改为10秒甚至20秒,以便让整个动作更自然、更缓慢,尤其是与后续配音匹配时能留出更多节奏空间。
为了适配镜头的拉远效果,我们调整了背景尺寸,使其大幅扩大,能够覆盖整个窗口,避免在镜头移动过程中看到边缘。我们还调整了图层的深度顺序和缩放比例,比如让某些远景图层处于“无限远”,保证它们不会因为相机前进而发生明显偏移,从而维持远景稳定。
随后我们对画面进行逐步测试和观察,发现角色的运动速度过快,无法表现出“拉近”效果中应该有的那种头部逐渐变大的视觉印象。于是我们给相机加上了一点Z轴(向前)的运动,营造出景别变化的感觉,这样画面看起来更有动感,也更接近我们想要的效果。
我们也尝试通过移动其他图层位置来配合整体的透视变化,使“男孩头部变大”的效果更加明显。为此还对远景背景位置进行后移,使其更加符合透视关系。由于这个镜头不是简单的网络插值,而是带有透视投影效果,所以我们必须精细控制每个图层的位置和比例。
最终,通过不断试验,我们找到了合适的镜头运动节奏、背景比例、角色位置和景深配置,达到了较为理想的画面效果。虽然这个镜头仍然有一定难度和不确定性,但整体上已呈现出预期的空间感和动势,为后续的过渡镜头和叙事铺垫打下基础。
game_cutscene.cpp:合成镜头 4
接下来,我们开始着手处理下一个镜头。首先清理了一些标记和设置,让场景回到一个更合理的状态。接着,检查了镜头设置,准备进行一个缓慢的缩放。这个镜头没有特殊的相机动作,只是一个简单的镜头缩放,看起来就像是逐渐拉近的效果。
为了调整这个缩放效果,决定让镜头略微靠近一点,并且调整镜头的运动速度,确保场景过渡的流畅性。完成这些调整后,继续添加新的图层,确保每个图层都设置正确。
接下来,发现场景中有一个小小的动画效果,决定在系统中添加一个新功能来支持这个动画。这个动画的效果比较轻微,但仍然需要系统支持才能完成。为了调整这个动画,需要做一些新的功能扩展,但这不会太复杂。
在此过程中,还注意到场景中的人物数量不太对,原本以为会有更多的小孩出现在画面中,但目前只看到一个小女孩在前景中。这可能是资源包中出现了问题,导致人物缺失,需要稍后检查和更新资源包。
此外,还注意到场景中有一些闪亮的装饰物(例如亮片),这些也需要确保正确显示。
最终,确认了这个镜头的设置,并且完成了对动画的处理,保证了整个场景的效果能够如预期般运行。这一切都做好后,准备继续进行后续调整和测试,确保每个镜头都能够顺利呈现。
反正就是挨着调镜头
缺少孩子β
首先,对于场景中的一些问题,决定暂时假设它们是正确的。虽然有感觉应该有更多的小孩出现在画面中,但目前没有找到他们的踪影,可能是资源导出时出现了问题。对不起,小孩们,似乎有些小孩丢失了,但其他部分基本上看起来是对的。
接下来,重点是调整场景中的一些元素,特别是装饰品(如亮片)。亮片的尺寸需要调整,变得更合适一些,应该离摄像机更近一些。调整后,亮片应该不会显得那么抢眼,避免遮挡其他重要元素,应该调整为一个更加合适的比例。
此外,孩子和圣诞老人应该稍微向下调整位置,看起来应该像是稍微低一些的样子。调整这些元素的位置,使得整体效果更加自然。
最后,回顾了一下文件的导出,发现确实有一些小孩缺失,计划稍后重新导出并更新资源包。因为这些小孩应该位于画面的角落,但目前找不到他们的位置,可能是在保存文件时丢失了。
讨论错位的孩子γ
在处理场景中的一些元素时,发现自己可能错位了很多孩子角色。这并非有意为之,完全是个失误。始终尝试尽量保证所有的角色都在场景中,但偶尔一些角色会因为太烦人而决定“失踪”,这时就不小心把他们错放了。其实在游戏中也鼓励这种做法——有时实在是让角色消失更为方便,尤其是当他们真的非常挡路时。
接下来,调整了一些角色的位置,特别是一个小女孩的位置。原先的位置稍微遮挡了另外一个小女孩,所以需要稍微缩小她的大小,让她低一些,这样看起来更加合适。调整后,感觉效果不错,看起来就对了。
接着,又调整了圣诞老人和孩子的位置,原先他们的相对位置有点不对,圣诞老人似乎压住了地面的一些元素,所以稍微向一边移了下,调整后看起来好多了。对于这些细微的调整,感觉每一处的改变都非常细小,但却非常关键。每一个细微的调整,给场景带来的效果可以是巨大的,所以这些小动作和细节处理都非常重要。
game_cutscene.h:将 MinTime 和 MaxTime 添加到 scene_layer,将 Transient 添加到 scene_layer_flags
为了让这个余弦动画按预期执行,我们需要确保两个元素在不同时刻播放。可以通过一个简单的方式来实现这一点,即添加一些“排除”机制。具体来说,我们可以设置一个时间范围,定义某些场景层在特定的时间段内出现或消失。
实现的方式是设置一个类似“瞬态层”的标记,也就是一个仅在特定时间范围内激活的层。这样,我们就能够在时间上控制某些层的显示,确保它们不会同时存在于画面中。
具体做法是,我们可以给这个瞬态层加一个标记,让它只在指定的时间段内存在。通过这种方式,可以轻松地控制动画中各个层的显示时机,确保它们在正确的时刻开始和结束。
game_cutscene.cpp:设置 Transient 层
我们为了解决两个图层不能同时播放的问题,可以采用“瞬态层(transient layer)”的概念,将这两个图层都标记为瞬态层,然后通过设置它们的播放时间范围,使它们分别出现在镜头的前半段和后半段。
实现上,我们可以添加一个参数,例如 thought_change_time
,设定为一个值,比如 10。这个值将作为时间分界点,控制两个瞬态图层的出现时机。例如,第一个图层在前半段(时间小于 10)出现,第二个图层在后半段(时间大于等于 10)出现。
接下来在渲染场景时,遍历每一层时可以加一个 active
标志来判断该层是否应该被渲染。默认情况下,active
是 true
,但如果当前图层被标记为瞬态层,那么就需要判断当前归一化时间 t_normal
是否在其指定的时间范围 [min_time, max_time)
之间。如果在这个范围内,该层就被激活并参与渲染,否则就被跳过。
特别地,在判断时间区间时,使用了“左闭右开”的区间判断方式,即:
t_normal >= min_time
:表示当前时间已经到达或超过该层的起始时间。t_normal < max_time
:表示当前时间还没有超过该层的结束时间。
这样可以实现两个图层在某个关键时刻(比如 0.5)进行无缝切换,保证它们不会在同一个时间点重叠。这个时间判断的精度设计是刻意的,确保不同图层之间不会发生时间上的冲突,从而实现清晰准确的视觉切换效果。
观看动画版本
这是当前动画版本的呈现效果,可以清楚地看到其中的动作变化,比如角色将帽子戴到另一个角色头上的小动作。整个片段中,角色间的交互已经具备了一定的动态表现力。
在此基础上,也可以考虑是否加入一些过渡效果,例如淡入淡出(fade)的方式,让两个图层之间的切换更加平滑自然。这种做法的可行性需要根据整体画面的风格判断是否合适。如果画面风格比较干净利落,淡化切换可能会让视觉语言显得不够利索;但如果想加入一些更具表现力或情绪性的元素,渐变过渡可能是一种值得尝试的方式。
实现这种渐变的方式可以使用渐变函数(ramp)来控制两个图层之间的透明度权重。例如,从前一个图层逐渐降低透明度,同时下一个图层逐步增强透明度,这样在一段时间内两个图层都会存在于画面中,但视觉上只感受到一种柔和的过渡感。
虽然目前还未决定是否要使用这样的渐变方式,但技术实现层面是可行的,关键是是否与整体视觉风格协调一致。现在的版本已经能够展现基本动作逻辑,是否进一步美化转场效果可以作为下一个优化方向来考虑。
game_cutscene.cpp:合成镜头 5
现在继续处理镜头五的部分。在剩下的五分钟内,计划再推进一下整体进度。到目前为止,已经完成了大约一半的镜头,进度良好。
开始查看镜头五的内容。首先看到的是背景图层的导入,属于一个标准的背景画面。观察之后认为这个背景可以稍微往后拉一些,使得整个构图更合理。
继续查看接下来的图层安排,初步判断镜头五仍是一个结构比较常规的画面,没有特别复杂的动态或分层变化。画面中各元素的排列方式和前面处理的镜头类似,继续按部就班地进行就可以。
这一部分的关键在于确认背景位置和后续图层的相对关系是否协调,确保画面空间感与整体节奏的一致性。下一步将继续处理镜头五中的具体图层内容,并根据需要微调位置与层级。
最精彩的镜头δ
这是整个场景中非常重要的镜头——“关键镜头”,一切戏剧性的展开都从这里开始。在这一镜头中,我们首先设置了背景图层,并将其拉得相对较远,以此营造出一个缓慢移动的效果。同时为了保持视觉冲击力,将其整体比例调大,使得它在构图中更具存在感。
接着计划为镜头添加一个更具戏剧性的相机移动,以提升整体的动态张力。然后开始处理画面中的门的图层,将它们添加进场景,并且要确保这些门能够填满整个画面。这一部分的图层也会使用 transient(临时)标志,意味着它们会在画面中短暂出现,然后被后续的图层取代。
设定这些门的显示时间为镜头切换时间点(shot change time),例如 1.0,确保门在特定时间点消失,让下一个重要角色顺利登场。
随着门的爆开,格兰帕斯(Krampus)终于登场——这个众所周知的假日恶魔角色突然出现在孤儿院的大门口。为了让他的出现更具冲击力,将其图层略微缩小,使其比例更合适,并调整其位置以获得更理想的构图。同时,他的图层设置得比前景更接近镜头,使得他在视觉上更突出,并带有明显的动感。
另外还加入了雪的图层,进一步增强了节日氛围和画面层次感。通过合理设置雪的显示方式,使整个画面更加生动丰富。整个镜头完成后,效果非常令人满意,画面节奏紧凑,角色登场充满张力,是目前为止非常喜欢的一个镜头。
“格兰帕斯(Krampus)”是欧洲民间传说中的一个神秘角色,特别流行于奥地利、德国、匈牙利、捷克等地区的圣诞节文化中。他是一个反面节日角色,可以被理解为圣诞老人(Santa Claus)的“邪恶搭档”。
简单来说,Krampus 是谁?
- 外貌:通常被描绘成一个高大、全身长毛、有角、舌头很长的恶魔形象,有时还拖着铁链或手持柳条。
- 职责:他不像圣诞老人那样奖励乖孩子,而是惩罚不听话的孩子。
- 故事背景:
- 圣尼古拉斯(Santa Claus)会奖励乖孩子;
- Krampus 则会惩罚坏孩子,甚至把他们装进麻袋里带走。
Krampus 的“节日”
在一些地区,每年的 12 月 5 日 是“Krampus Night(克兰帕斯之夜)”,年轻人会装扮成 Krampus,在街上游行,制造气氛,也是一种传统庆典。
在创作/影视中
Krampus 经常被用作节日恐怖题材的角色,象征节日文化中的阴暗面。许多动画、电影或游戏会以他为灵感,塑造出一种在喜庆气氛下潜伏的“威胁”角色。
“这就是电子游戏”ε
我们完成了整个镜头的布置,整体效果非常不错,令人满意。这个镜头的构成和动画都体现出一种非常典型的视频游戏式风格,也确实是在做“电子游戏”的感觉。
我们已经把所有需要的元素都加入到了画面中。为了让镜头更有动感,我们打算对摄像机的移动轨迹做一些微调,加入一点向上的平移。这样可以更好地呈现出角色格兰帕斯的气势——因为他像是一个山羊形态的角色,所以镜头缓缓地向上移动,有种仰视的感觉,更具张力。这种镜头运动可以更好地引导观众的视线,把注意力集中到格兰帕斯身上,气氛会更有压迫感。
我们还考虑了发音上的差异——如果按照中欧大陆风格的发音,他的名字可能更像是“克兰帕斯”或“克拉姆普斯”(Krampus / Krampus),无论如何,这种差异在不同地区的表现中都是常见的现象,也增添了一点文化趣味。
这个镜头整体已经让我们感到很满意,最终的构图和节奏也非常到位。接下来可能还会有人对这个部分提出一些问题,比如关于镜头语言、角色氛围或动态表现的细节,但就目前来看,整体已经进入了一个比较稳定和完善的阶段。
game_cutscene.cpp:合成镜头 6
我们继续进行下一个镜头的制作,这是第六镜头。因为场景制作非常有趣,而且能看到它们实际出现在游戏中,非常有成就感,所以决定再做一个。
在这个镜头中,格兰帕斯已经完全登场。按照设定,他自然是要开始追逐孩子们了,这是他的“职责”。他出现的目的就是专门对付那些淘气的孩子,这正是他的“行事风格”,可以说是他存在的意义。
这次的镜头设置依然从两层结构开始,并重新整理了图层数量。我们估算了一下格兰帕斯与前景的距离,大概是八米左右,于是将他放置在适当的位置,使他在画面中的比例和位置更具视觉冲击力。
对格兰帕斯的位置进行了微调,使其更加居中并且具有威胁感,同时保持和前一个镜头的连贯性。从目前的效果来看,这个镜头的构图已经相当不错了,格兰帕斯的存在感强烈,画面张力十足。
整体气氛符合预期的叙事发展节奏,也为接下来的动作铺垫了很好的基础。镜头设定简洁但有力,让格兰帕斯的压迫感直接传达给观众,进一步营造出紧张氛围,令人期待后续的发展。
当克兰普斯出现时ζ
我们继续制作了第六个镜头,这一段是格兰帕斯真正开始追逐孩子的场景——通常来说,格兰帕斯一登场,事情就开始“变得有意思”了。他的出现基本意味着故事节奏的加快,也是游戏中最令人期待的时刻之一。
在这一镜头中,没有使用任何“transient”临时图层,而是采用了标准图层结构,场景构建比较常规。我们依旧从设置图层数量开始,并加载了雪的图层,这一图层为整个画面增加了深度和空间感。雪花是分离图层导出的,这样每个图像都更具专属的空间层次,也便于后续动态表现的调整。
接着我们加载了被格兰帕斯追逐的孩子的图层。这个孩子的位置进行了微调,包括左右和上下的轻微移动,让其在构图中更合理。他应该略低一些,以增强被追逐感。
相机的移动也进行了调整,决定采用略微向上的镜头运动,模拟相机从地面稍微上仰的效果,以增强格兰帕斯的压迫感和画面的戏剧性。
随后添加了代表孩子眼泪的图层。这个图层进行了大小调整,并通过锁定时间轴的位置精确对齐到孩子脸部。我们确保眼泪是从眼角流出而不是鼻子,这种微调虽然细小但非常重要。考虑到眼泪是和角色面部动作绑定的,所以它不会作为独立运动图层,而是紧贴孩子本身。
然后添加了第五图层,也就是另一个小孩。这一图层的位置和比例依旧需要对照前景角色进行合理推算。如果这个小孩位于主要角色稍前方,那么她就需要稍微放大,同时位置稍高一点以匹配透视。
接着加入了第六图层,包含花环等场景道具。这一部分的摆放相对较难确定,因为道具的比例和位置既不能干扰主角的动态,又要保持装饰作用。对这一层进行了多次尝试,包括缩放和垂直位置调整,最终选定了一个比较理想的位置。
最后我们重新审视雪的图层,认为它原本的位置过于前景,因此将其调整为更偏向中景的深度层次,并适当减小雪的颗粒尺寸,使其更自然地融入整体画面。
整体来说,这一镜头构图已经趋于完整,层次分明、动作明确,同时保持良好的故事节奏和视觉冲击力。画面中的雪、人物、装饰和动作结合得自然流畅,镜头运动也增强了画面的动感,整个场景效果令人满意。
为什么不传递每个过场动画的时长,而不是让所有的过场动画时长相同?
目前我们在制作过场动画时,确实还没有区分每个镜头的具体时长,所有镜头默认采用相同的持续时间。但这是暂时的安排,并不是最终的实现方式。
我们之后是会为每个镜头分别传递并设定其各自的时长的,这是已经在计划内的事情。只不过在目前这个阶段,我们的目标是先完成镜头的布局和场景的搭建,还不需要同步到音乐节奏或者做精细的时间调控,所以先使用统一时长作为过渡。
一旦进入到更精细的阶段,比如过场动画需要配合背景音乐、对白或节奏点进行展示,我们就会为每个镜头指定具体的持续时间,确保整个叙事的节奏与氛围更加精准贴合。
总结来说,现在是为了方便快速制作内容,暂时统一了镜头时长,等之后节奏需要更精细化时,我们会对每个镜头的时间单独处理。
对于奇怪的摄像机运动场景,你是否考虑过使用曲线插值,而不是线性插值?
关于镜头的旋转和曲线运动,我觉得使用三拍形式反而能产生一种不同的效果,而不是线性运动。三拍式的运动感觉更有动态感,这样的设计能够在场景中加入一些有趣的变化。
如果是旋转的镜头,可能就需要更柔和的曲线运动来平滑过渡,毕竟这种旋转动作本身是带有曲线感的。但从这个场景来看,我对它最终的效果并不是特别有信心。可能很难做到完全合适的视觉效果,所以我并不确定这会不会真的看起来很好。
至于其他的提问,关于Yankee的工作,她确实是游戏的插画师,负责了大量的艺术设计和视觉工作,具有很强的插画能力。
杨天的艺术真棒!
游戏的艺术风格看起来非常棒,我很喜欢。虽然我自己不太参与艺术设计的细节,我通常会让艺术家去做这部分的工作。我基本上只是给出一些简单的想法,比如“这里应该有一个格兰帕斯从门口出现”,然后我可能会画个简单的草图,标明场景的大概布局,比如格兰帕斯在这儿,其他角色在那儿,接着艺术家就会基于这些简单的描述,展开创作。
艺术家负责了所有角色的设计,包括格兰帕斯的外形、其他人物的形象以及整个游戏的视觉风格。所有这些视觉内容都是艺术家发挥创造力后设计出来的。
请做更多的工作η
大家似乎都希望看到更多的过场动画,那就让我们做更多的吧,感觉这样很有意义。反正如果有问题,明天还可以继续提问,每个工作日都可以随时向我们询问。总的来说,这个过程非常有趣,我可以一直做下去,真的很喜欢做这些事情,感觉每天都能做得很开心。
game_cutscene.cpp:合成镜头 7
在第七镜头中,场景设定相对简单,只有两个层次。一个是格兰帕斯,另一个是孩子,二者在相对运动中。格兰帕斯向左滑动,孩子向右滑动。这一镜头的特点是平移镜头,镜头不会变焦,只是水平移动。镜头的动作是左右平移,展示两者之间的运动,最终使得格兰帕斯显现出来。
此场景使用了“临时标志”(transient flag)和“反向摄像机动作”标志,用来控制镜头和角色的移动。具体来说,格兰帕斯和孩子的相对运动可以通过调整两者的位置来实现,保证镜头拍摄效果和动作的自然过渡。由于这个场景较为简单,调整的元素较少,主要是格兰帕斯和孩子的位置调整,确保两者在镜头中合适的显示。
在场景的后续调整中,重点是移动镜头,以确保格兰帕斯和孩子之间的互动清晰可见。格兰帕斯和孩子的运动要协调,以表现他们之间的对抗和互动。
接下来进入第八镜头,这一镜头不需要做太多的时间变化调整,主要是控制层次的变化和角色的相对移动,确保过场动画的流畅性和视觉效果。
game_cutscene.cpp:合成镜头 8
在第七镜头的设置中,首先检查了镜头和角色的层次,确保每一层都正确地展示了角色和物体的位置。首先是格兰帕斯的设定,镜头需要调整,确保画面清晰,方便查看每一层的元素。镜头的运动采用了放大缩小的方式,具体表现为镜头对格兰帕斯进行一定的放大,保证他在镜头中的位置和动态更清晰。
在具体操作上,镜头和层次的调整需要确保每个元素都居中并覆盖整个画面。这是为了保证视觉效果的平衡,让每个元素都有足够的展示空间,特别是像手套这样的道具。手套作为一个重要的物品,它的大小和位置需要精确调整,保证它看起来与角色的比例匹配。
同时,其他层次也被逐一加入,包括一些孩子的角色和吊饰等,这些元素需要保持协调,避免画面过于拥挤。在这一过程中,注意到了镜头的运动轨迹,特别是对于放大缩小的处理,确保镜头的移动更加自然。
随着镜头的推进,故事情节也有所展开。格兰帕斯从最初的追逐孩子转变为帮助孩子,表现出一种积极向上的形象,与传统的圣诞老人角色形成对比。这个转变让角色更加立体,也为后续的剧情发展奠定了基础。
最后,调整了画面中的装饰元素,如吊饰等,确保它们的位置和大小符合画面整体的视觉效果。为了让镜头更加有层次感,决定让镜头稍微向下倾斜一些,这样可以更好地聚焦于手套等物品,同时突出格兰帕斯和孩子之间的互动。
这个镜头的设置基本完成,主要聚焦在细节调整上,确保每个元素都能在镜头中正确展现,达到预期的视觉效果。
game_cutscene.h:将 v2 参数添加到 scene_layer
在这段过程中,讨论了代码实现中的一些细节,主要是关于参数的设定和代码优化。首先提到,虽然不想过于沉溺于自己设定的细节中,但依然决定尝试做一些修改,优化代码的方式。提出了设定一个“间隔”参数,甚至考虑了设定一个“最大值”或“最小值”的区间参数,像是“32”和“bob”之类的变量名,用来控制某些功能的范围。
接着,介绍了如何使用一个通用的向量作为参数传递,并提到将在这些对象中进行打包处理,目的是为了管理多个数据的组织方式。尽管遇到了一个小问题,即没有收到预期的错误提示,可能是因为代码在实现时没有按照预期执行,产生了不完全的错误提示。尽管如此,对于这些问题并不感到特别烦恼,认为这并不是非常关键的地方,且认为这只是一个技术上的小偏差。
整体来看,这段讨论的重点是对代码中某些参数的调整和优化,以便使其更加符合需求,虽然有些问题出现了,但并没有太大影响,依然在进行相关操作以完成整体工作。
game_cutscene.cpp:合成镜头 9
在这段内容中,讨论了关于第九个镜头的设置。这个镜头主要是一个简单的镜头拉近(zoom),聚焦到一个小孩上。他正回到自己的房间,镜头会从远处拉近到他的身上,确保镜头的移动方向与场景相符,调整了镜头的角度,使得画面更具表现力。
接下来,讨论了需要的几个层级。第一个层级是镜头拉近至孩子身上的效果,第二个层级则是一些细节的处理,例如房间的天花板上的物品。这个层级中的物品相对较小,且会很快被镜头移动过去,因此放置这些物品时也需要考虑它们的大小与位置。
具体来说,镜头中的灯具、装饰物和其他元素都需要调整大小和位置,使得它们与镜头的运动相适应,避免遮挡重要的画面内容。灯具和装饰物的大小被做了适当的调整,有些元素如灯具还需要稍微移动,确保它们出现在画面中的合适位置。
在完成这些调整之后,镜头的整体效果感觉非常顺畅,所有层级都正确排列,镜头的移动和物体的摆放都达到了预期的效果。最终,确认镜头的布局完成,剩下的任务是继续处理后续的镜头,完成所有的镜头序列。
此外,还提到,剩下的工作是将这些镜头按照顺序连接起来,完成开场的动画。虽然目前还没有声音和配音,但整体的画面和动画布局已经准备好了。剩余的工作将在第二天继续完成,只剩下最后的两个镜头。
你喜欢听到链接器的哀嚎和它因无法写入 .exe 而在西西弗式的痛苦中扭动吗?
在这段内容中,讨论了对Microsoft Linker的负面看法,认为它是一个非常糟糕的工具。对于Linker在遇到问题时的痛苦和挣扎,表达了一种轻微的娱乐感,认为它在遇到困难时的表现是可以接受的,甚至某种程度上令人愉快。对于这种情况的态度反映出对这个工具的不满,并且对其在处理问题时的无力感感到某种程度的解脱或满足。
你们有安排配音演员了吗?
在这段内容中,提到由于还没有找到合适的人选来进行开场序列的旁白配音,因此配音并没有包含在当前的包中。等找到合适的配音演员之后,会把旁白部分加入到包中。
第一个镜头中的手套是左手手套,接下来的镜头是右手手套
在这段内容中,提到了第一个场景中的手套是左手手套,而下一个场景中的手套却是右手手套。对于这种情况,可能需要修正,因为不应该出现不同的手套方向。解决方法是可以将左手手套翻转过来,变成右手手套。这样处理是比较简单的,而且手套本身发光,因此没有特别的方向要求,翻转手套应该是个合理的选择。
第一场景中的门是开的,但在克兰普斯进入之前关了
在这段内容中,提到第一个场景中的门是开着的,但在角色进入之前门却是关着的。对于这种情况,应该做一些修正,并且记录下需要修正的地方,特别是一些艺术方面的修订笔记。可能存在一个场景中门是关着的,然而具体情况还不清楚,需要进一步确认和询问,可能会找到相关的素材。
在镜头 2 中,我注意到窗户顶部有个缝隙。那还在吗?
在这段内容中,提到在窗口的顶部似乎有一个空隙,需要确认是否仍然存在。回顾了镜头二和镜头三,提到可能是镜头二中的问题,确实看起来窗户顶部的部分有偏差,似乎过于下移。针对这一点,考虑进行调整,以解决这个问题。尽管这可能是一个缺少视差效果的镜头,但仍需进一步修正。
我可以收养其中一个孩子吗?
提到一个问题,是否可以收养其中一个孩子。对于这个问题,表示不确定,因为不清楚该孤儿院的具体政策。
试试社区中的配音演员?我们中的不少人愿意参与/有很好的麦克风
提到可以尝试在社区中寻找配音演员,表示有不少人愿意并且有一定的基础,但需要找到一个特别合适的人选,特别是能够提供高质量配音的人选。
这个游戏会支持左撇子吗?
关于是否支持左撇子玩家,认为其实玩起来差别不大,不论是左手还是右手,都只是四个方向的操作。因此,不管是左撇子还是右撇子,游戏的玩法基本上是一样的。
你能解释一下动画中的渐变效果吗?
在讨论动画中的渐变效果时,需要明确具体是指哪种渐变效果。渐变可能指的是图像、元素或者场景的淡入淡出效果,或者是一些特定动作、过渡的视觉效果。所以,具体是哪种类型的渐变效果需要进一步确认。
你应该在那个地方加一点雪,这样能增加深度感
需要在画面上加一点雪花效果,以增加深度感。为了记住这些修改,可能需要更新并记录一些事项,包括手套的朝向等细节。考虑到没有笔,决定将这些修改内容写在系统里,以便以后能够查看和提醒自己。
我们仍然需要自己的三角函数,这样就不需要每次都调用 C 标准库中的 sin() 函数了
需要使用一些三角函数来停止当前的签名操作,通过使用C语言的标准库来实现。标准库提供了一些函数,例如sprintf
、sign
、floor
、ceil
、round
等。对于这些函数中的一些,可能不需要花费太多时间来实现,因为它们比较简单,如truncate
。而像floor
和ceil
这样的函数实现起来可能需要更多工作。对于sign
和round
函数,虽然不容易实现,但使用现有的工具可以帮助完成这些工作。
你之前提到过我们可以通过渐变让动画序列更流畅。例如,当圣诞老人给英雄戴上帽子时。我想知道那会是什么样子
提到的这种渐变效果可以通过在时间范围内选择特定部分,改变两个层的透明度来实现。例如,可以让一个层的透明度从1渐变到0,而另一个层的透明度则从0渐变到1。通过这种方式,就能让一个物体(例如角色)渐渐出现或消失,达到逐步显现或隐去的效果。这样的方法非常直观,只需调整时间范围内透明度的变化即可。
我注意到过场动画的位图没有随着窗口大小缩放。这是为什么?
问题出在窗口的大小和渲染的分辨率之间不匹配。当前,游戏在一个固定的分辨率下渲染,因此它不会根据窗口的大小来调整。如果游戏能够请求一个较小的窗口,并根据窗口大小渲染,那么就可以解决这个问题。目前的问题是,尽管我们可以改变分辨率,但渲染并没有根据实际窗口大小进行调整,因此导致了窗口和渲染内容的不一致。
win32_game.cpp:暂时切换到使用更小的后台缓冲区
目前,渲染的问题与窗口大小和分辨率之间的匹配有关。游戏引擎支持分辨率独立性,可以请求引擎生成较小的图像,只需要调整后端缓冲区的大小即可。然而,在当前的测试过程中,窗口的大小是固定的,并没有进行窗口尺寸的动态调整。因此,尽管引擎可以处理更小的窗口和分辨率,但为了测试游戏实际运行时的分辨率(例如 1920x1080),目前并没有让窗口大小与分辨率调整匹配。游戏会有一个窗口,通常是全屏模式,且不会允许用户调整游戏窗口的大小。这种裁剪行为是有意为之的,并且是为了确保游戏在最终发布时在正确的分辨率下运行。
floor 和 ceil 很难吗?
实现 floor
和 ceil
函数并不像 sign
或 tan
函数那样困难,但仍然具有一定的挑战性。要正确处理这些函数,必须使用一些“魔法常量”并执行一些复杂的操作。这些操作的实现涉及到不少细节,需要一些精心设计的代码。例如,如果使用的是 sfc
(某种库),这些函数的实现非常简单,直接可以调用 floor
和 ceil
函数。但如果使用的是其他库(如 sse2
),则需要自己动手实现,并且这并不简单,必须细致地思考并完成一些特定的计算。虽然这项工作并不算特别难,但为了做到正确,确实需要仔细考虑和执行,大约需要四到五条指令。
为什么我们要重做 sin() 函数?
减少使用 sign
函数的原因主要是出于教育目的。虽然在实际应用中,减少使用 sign
函数可能有性能上的考虑,但这里的重点是通过实现这些低级功能来学习底层编程。如果只是调用现有的库函数,我们就无法真正了解这些功能是如何实现的。
sign
函数本身是一个相对复杂的函数,如果只是简单地调用它而不理解它的实现,我们就无法掌握如何使用如幂级数近似、泰勒级数近似等方法。因此,学习如何手动实现这些功能非常重要,尤其是当你需要模拟一个没有现成库函数的功能时。
这种做法的核心理念是,避免依赖库函数,而是通过自己实现,掌握如何做这些低级操作。虽然在实际应用中,可以使用 C 运行时库来简化开发,但出于教育目的,希望尽量减少依赖库函数,确保每个人都能理解如何实现 C 运行时库中的所有功能。最终的目的是通过这种方式,学习和掌握更多的编程技巧,而不是简单地依赖别人已经做好的东西。