首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >一小时的比赛,录像为什么只有几百 KB

一小时的比赛,录像为什么只有几百 KB

原创
作者头像
PC电脑医生
修改于 2026-09-22 15:48:26
修改于 2026-09-22 15:48:26
1780
举报

打完一局帝国时代2:征服者,习惯性地点了"保存录像"。

文件大小是几百 KB。

而这局打了将近一个小时。

同样的时长,如果用录屏软件录下来,几个 GB 是起步。

差了四个数量级。

为什么?

因为那个文件里装的根本不是画面。

它记录的是操作,不是像素

先对比一下两种做法的差别。

录屏软件做的事,是每秒钟把整个屏幕的像素抓下来几十次。

1920×1080 的画面,单帧就是 200 多万个像素——每个像素 3 个字节表示颜色。

一秒钟 30 帧,数据量就是 180MB 量级。

即使上压缩算法,也压不掉多少——因为每一帧的画面都不一样。

而游戏录像记录的东西完全不同。

它记的是:你在第几秒,选中了哪些单位,下达了什么命令。

一条指令大概长这样:

  • 时间戳:第几逻辑帧
  • 玩家编号:谁操作的
  • 指令类型:移动 / 建造 / 攻击
  • 目标参数:坐标或目标单位

几十个字节。

一局比赛下来,玩家总共下达成百上千次操作。

乘起来,也就是几百 KB 的量级。

这就是体积差的来源——一个记的是结果,一个记的是原因。

那光有指令,怎么还原出画面

这是这套方案里最关键的一环。

录像文件里没有画面,但播放时你确实看到了画面。

那些画面是播放的时候现算出来的。

游戏引擎重演一遍比赛:

第一步,按录像里的地图和参数,把初始局面搭出来。

第二步,按时间顺序把指令一条条喂给游戏逻辑。

第三步,逻辑算出每个单位该在哪、在干什么,然后渲染出来。

所以你看到的不是"播放录像",是用引擎重新打了一遍。

只不过这次的操作输入,来自文件而不是键盘。

这解释了几个现象

理解了"现算",有些平时觉得理所当然的功能,就有了技术依据。

比如可以从任意玩家视角观看。

因为每个单位的真实位置在逻辑层都是已知的——包括当时被战争迷雾遮住的部分。

迷雾只是渲染时的一层遮挡,数据本身一直都在。

再比如可以随时暂停、快进、跳转。

因为只需要算出那一时刻的状态——不需要"解码"到某帧才能显示。

而录屏文件想跳到中间,就得先解码前面所有帧。

前提是"算出来的结果必须一样"

到这里,一个隐含的要求浮现出来了。

既然画面是现算的,那不同机器算出来的结果必须完全一致。

否则就会出现:录像是按 A 的机器算的,你在 B 的机器上播放,单位跑到了别的位置。

这个要求有个名字,叫确定性。

它的含义是:给定相同的初始状态和相同的输入序列,任何一台机器都必须算出完全相同的结果。

注意"完全相同"这四个字。

不是"差不多",不是"误差在可接受范围内"。

必须一个比特都不差。

因为游戏是连续演进的——第 1 帧的微小差异会传导到第 2 帧,然后被放大,几十秒后就面目全非了。

这也是联网对战时必须满足的条件——两个玩家的屏幕上,战场必须一模一样。

而这个要求,直接决定了游戏逻辑该怎么写。

第一个被禁掉的:浮点数

这是最违反直觉的一条。

日常写程序,小数都用浮点数——方便、通用、有硬件加速。

但在需要确定性的游戏逻辑里,浮点数是不能用的。

原因在于:同一个算式,在不同环境下的结果可能不一致。

差异来自好几处:

  • CPU 架构:x86 和 ARM 的浮点实现细节不同
  • 指令集:用了不同 SIMD 指令,中间精度可能不一样
  • 编译器优化:不同的重排、融合策略会改变舍入结果
  • 数学库实现:三角函数等函数的实现各平台有差异

这些差异通常在最后几位有效数字上。

单看一次计算,完全可以忽略。

但问题是游戏里位置是累加出来的——每帧加上一点速度。

误差会累积。

一场比赛跑几千帧,前面那点可以忽略的偏差,最后可能变成一个单位跑偏了几十个像素。

表现就是:一台机器上单位已经到位了,另一台还差一点。

如果这个单位是关键单位——比如正在采资源的农民,或者正在逃跑的英雄——后续的整条剧情线都会分岔。

怎么绕开

既然浮点数不可靠,就换成可靠的。

主流做法是定点数。

思路是:把小数放大若干倍,用整数来存。

比如约定所有数值都放大 1000 倍——那么"1.5"在程序里就是整数 1500。

加减法直接算整数。

乘除的时候多一步:乘完要除回那个倍数,除的时候按整数取整。

关键就在于取整规则是明确的、和平台无关的——比如统一用向下取整。

这样同一组输入,在任何机器上都会得到同一个整数结果。

除了数值类型,还有几处也得改:

  • 随机数:用固定种子的伪随机算法,初始种子随录像一起存
  • 三角函数:查表,或自己实现整数版本
  • 时间:用逻辑帧计数,不用系统时钟
  • 容器遍历顺序:需要保证顺序稳定,否则运算次序不同结果就不同

最后一条容易被忽略。

如果代码里遍历一个哈希表——不同平台、不同版本的元素顺序可能不一样。

那遍历过程中做的计算,顺序就不同了。

而浮点加法不满足结合律——先加 A 再加 B,和先加 B 再加 A,结果可能差最后一位。

虽然换成定点数之后这个问题会缓解,但顺序问题本身仍需注意。

所以游戏逻辑通常要求使用有序容器,或者在遍历前排序。

这一整套约束,解释了一个老现象

玩过的人可能记得:游戏更新之后,旧录像可能打不开了。

或者能打开,但播放到一半就乱套——单位乱跑、建筑造错位置。

这不是文件损坏,而是这套机制本身的副作用。

原因是:录像里只有指令,没有画面。

要还原那场比赛,得用和当时完全相同的游戏逻辑去重演。

如果补丁调整了某个单位的移动速度——哪怕只改了一点点,重演出来的结果就完全不同了。

农民走到资源的路径变了,采集节奏变了,后续的建造顺序也跟着变。

从某个时间点开始,重演的战场就和当年的真实战场分道扬镳了。

所以每次改动了影响游戏逻辑的内容,旧录像就不再可用。

这也是为什么这类游戏通常会把录像和版本号绑定——打开时先校验版本是否匹配。

联网对战也依赖同一套机制

顺带说一句,这套机制和联网对战是共用的。

对战时的做法叫帧同步:

每个客户端只把自己的操作发给服务器。

服务器收齐所有玩家的操作,打包广播出去。

每个客户端拿到所有人的操作,各自在本机跑一遍游戏逻辑。

注意——服务器不计算游戏状态,它只负责转发和收集指令。

这和很多人的直觉相反——RTS 的服务器并不"模拟这场战斗",它只是传递操作。

这么设计的原因是:如果服务器要实时计算几百个单位的战斗,负载会非常大。

而只转发指令,成本极低——一局帝国时代2:征服者的对战,数据量和你本地的录像文件差不多。

代价是:所有客户端必须保证算得一样。

也就是前面说的确定性。

一旦某个客户端算出了不同的结果,它和别人的战场就对不上了——这就是所谓的"不同步"。

表现是:你的单位在打敌人,队友屏幕上你的单位在发呆——两边看到的已经不是同一场战斗了。

一句总结

帝国时代2:征服者 的录像文件小,是因为它记录的不是画面,是操作。

播放时由游戏引擎重新演算一遍,画面是现场渲染出来的。

而支撑这套方案的前提是确定性——相同的初始状态加相同的指令序列,必须算出完全相同的结果。

为了满足这一点,游戏逻辑不能随便用浮点数,要换成定点数;随机数要用固定种子;时间要用帧计数。

这些约束不是性能优化,是正确性要求。

它们同时也解释了另外两件事:为什么版本更新会让帝国时代2:征服者的旧录像失效,为什么联网对战可以让服务器只转发指令、不承担计算。

三者是同一套设计的不同侧面。

安装包地址:

https://www.ijinshan.com/software/diguo2.html?channel=4018

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 它记录的是操作,不是像素
  • 那光有指令,怎么还原出画面
    • 这解释了几个现象
  • 前提是"算出来的结果必须一样"
  • 第一个被禁掉的:浮点数
  • 怎么绕开
  • 这一整套约束,解释了一个老现象
  • 联网对战也依赖同一套机制
  • 一句总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档