资料分类: | 游戏底层事件 |
本文所有内容均基于 Java 版,基岩(BE)版不保证所有特性相同(部分已说明 BE 版的内容除外)!
简介
游戏刻(Game Tick,简称 gt )是 Minecraft 底层中用于计量时间的单位,常被用于衡量红石电路延迟、TNT 爆炸时间等重要指标,是 Minecraft 中最小的完整时间循环单位,1s = 20 gt。
深度了解
实质上,游戏刻是可以被再分的,但是再分后不再是一个完整的时间计量(打一个不太恰当的比方,就像是水分子 H₂O 一样,一个水分子内含一个氧原子和二个氢原子,而通过某些手段,其可以被拆分为一个氧原子和二个氢原子,但是拆分后它不再是一个完整的水分子),同时也不能被视为一个完整的循环周期。
在同一个游戏刻内发生的事件其实实际上依然是有先后顺序的,但是他们依然会被定义为“同时”,可以粗浅的理解为这本质上是将一组的同时发生的事件在一个循环内按某些顺序先后处理后打上一个“同时”的标签,这也就使得某些本来玩家期望要在同一个 gt 内按照某种特定顺序发生的事件因为顺序问题不得不更改顺序或将其置于另一个 gt 内进行(实际上这种特性在计算机的运行本身中也有一定的体现)。实际上,对于很多游戏都会有类似的设定,因为用离散的、逻辑性的、有先后顺序的计算机去模拟连续的、同时性的事件本就是一个难题,只是因为 Minecraft 是沙盒游戏的同时加入了红石系统,使得这个现象被特别突出了出来(而不是因为 MOJANG 不会做游戏)。
Minecraft 中的一切事件的时间长短都是基于游戏刻的,现实世界的一秒对应 Minecraft 的 20gt,这意味着电脑必须在 1/20=0.05 秒,即 50 毫秒执行一次游戏刻。如果电脑的运算能力无法在这么短的时间内完成运算,那么一个游戏刻的时间会被延长,使得一秒执行的游戏刻会变少,即对于任意情况,我们都有每秒执行的游戏刻数量 ≤20;同时相应的,当一秒执行的游戏刻变少时,某些游戏内容也会相应的变慢,如熔炉烧制一个物品需要 10 秒即 10*20=200gt,如果此时电脑的处理能力使得一秒仅能执行 10 个 gt 而非 20 个 gt,那么烧制一个物品所需要的时间(现实时间)将会变为 200/10=20 秒。正因为游戏刻的种种特性,使得其变为深入研究 Minecraft 红石系统的玩家游玩中所不可或缺的一部分。
基础概念与部分属性
Minecraft 的循环程序是以每秒 20 周期的固定速度运行的,即 TPS: 20.0。因此每刻发生在每 0.05 秒。在游戏中的一天将正好为 24000 刻,或 20 分钟。但是这个速率也不是完全固定的。如果电脑的性能不足以跟上这个速度,一个游戏刻的运行时间就被延长,每秒的游戏刻就会变少。由于游戏中的绝大多数动作都是以游戏刻而不是真实世界的时钟作为时间基准,这意味着在较慢的电脑上很多事情都要花更长的时间来完成。
每过去一刻,游戏的各方面都会更新:移动的实体位置会发生变化,生物会检查周围环境并更新自身的行为,玩家的生命值和饥饿值会根据玩家的处境发生变化等。这些游戏的方面是服务端的行为,和负责渲染游戏本身的客户端的更新速度没有关系。也就是说,游戏的帧率(FPS)不影响 TPS,电脑的图形性能不会影响到游戏机制。
和每秒刻数(TPS)相关的一个单位是每刻毫秒数(MSPT),即服务器实际上用来计算一刻所需的时间。只有在 MSPT 不超过 50 时,TPS 才可以达到 20。
以下的游戏机制比较消耗资源,容易导致服务端卡顿:
漏斗收集上方物品。可以通过在漏斗上添加容器方块防止这一行为发生,也可以直接换成吞吐量更大的水流运输;
红石电路更新。应该在时钟等线路上增加开关,以避免不必要的状态改变。另外红石造成的亮度更新也会造成卡顿,可以通过尽量减少空气空间避免;
生物 AI。可以使用照明控制怪物的产生,并使用更高效的技术养殖家畜;
有些模组可以优化或简化游戏逻辑。由于模组是第三方产品,不会特别声称其适用性。
本质
游戏刻的本质其实是按照一个特定的执行顺序进行不同类型的更新与运算,并将执行结束后 50 毫秒内的剩余时间进行延迟。以此为基础作一个循环,最后达到做出所谓“时间”这个概念的目的,而对于这个循环的某一个周期本身,我们称之为游戏刻(Game Tick),同时,对于循环周期的次数(即时间量)则称之为游戏刻(Game Time)。
游戏刻的执行顺序
将游戏进行反编译并使用官方的混淆映射表,进行反混淆后从源码进行解析不难看出 Minecraft 的游戏刻执行顺序,值得注意的是,获取 Minecraft 的源码本身就是不合理且不受法律保护的,虽然官方 MOJANG 默许甚至在一定程度上略微支持这种行为,而在没有获取官方允许授权的情况下对 Minecraft 的源码直接进行公布、抄袭还有用于商业行为是违法的。
以下是 Minecraft 在一个游戏刻中执行的顺序(适用于 1.13.2,但是事实上,这个时间顺序在 1.14 以下版本大部分(而不是全部)都适用,对于涉及到底层的执行方面官方很少会对此进行大修改):
成就命令相关;
同步玩家客户端的时间;
极限模式下难度锁困难判断;
群系生成相关;
玩家睡觉逻辑;
生物、怪物刷新;
区块卸载;
天空光衰减计算;
设置 GameTime 与 DayTime(即世界时间相关);
计划刻(Scheduled Tick 或者 Tile Tick 亦或者叫做 Next Time Entry);
随机检查并更新玩家周围的亮度;
天空光的计算与将新增的 Tile Entity 储存至区块内;雷电;下雪与结冰;随机刻;
玩家加载的区块列表更新,并发送客户端方块更新数据包;
村庄运算;
僵尸围城;
地狱门缓存清空;
方块事件(Block Event);
维度卸载判定相关;
维度相关的运算(目前仅有末地的龙战相关逻辑);
天气相关实体运算;
玩家实体运算;
普通实体运算;
方块实体运算;
发送客户端实体更新数据包;
网络玩家信息运算;
自动保存;
由于 Minecraft 的游戏延迟与时间更新不同步,两个游戏刻之间的间隔的选择成了一个问题,参考前人的某些理论分析:作为一个离散的时间量,在游戏运算中一定存在某一个时刻,GameTime(游戏刻)这个时间量发生改变,这就是 GameTime 的分界线,这里前人将其划分为 GameTime 与 GameTime+1,同时给出了游戏刻的确切定义:
“GameTime 为 x 的定义为:所有执行 World.worldInfo.getGameTime() 得到的返回值为x的时刻的集合。”
同时,这样可以得到事件 P 发生于 GameTime x 的定义:
“一个事件 P 发生于 GameTick x 的定义为:发生事件 P 时若执行 World.worldInfo.getGameTime() 得到的返回值为 x。”
这么定义的好处:
与计划刻元件的执行时间相对应,在 GameTick 为 N 时所触发的 X gt 延迟的计划刻元件会在 GameTick 为 N+X 时进行更新;
照应前人研究,鉴于以前红石理论多基于命令方块,而命令方块更新依赖 Game Time,如此定义使一些较为远古的红石理论能与其兼容;
可以直观的在代码中调用 World.worldInfo.getGameTime() 来确定当前的 GameTick;
不会调用代码的可以使用命令方块进行调试。
由此可得新的运算顺序:
设置 GameTime与 DayTime(即世界时间相关);
计划刻(Scheduled Tick 或者 Tile Tick 亦或者叫做 Next Time Entry);
随机检查并更新玩家周围的亮度;
天空光的计算与将新增的Tile Entity 储存至区块内;雷电;下雪与结冰;随机刻;
玩家加载的区块列表更新,并发送客户端方块更新数据包;
村庄运算;
僵尸围城;
地狱门缓存清空;
方块事件(Block Event);
维度卸载判定相关;
维度相关的运算(目前仅有末地的龙战相关逻辑);
天气相关实体运算;
玩家实体运算;
普通实体运算;
方块实体运算;
发送客户端实体更新数据包;
网络玩家信息运算;
自动保存;
成就命令相关;
同步玩家客户端的时间;
极限模式下难度锁困难判断;
群系生成相关;
玩家睡觉逻辑;
生物、怪物刷新;
区块卸载;
天空光衰减计算;
(其实就是把 GameTime 与 DayTime 前面的部分挪到了后面)
简化版顺序
对于较为重要的修改服务端世界相关操作所在的阶段,可得简化版的顺序列表(这个简化表同时适用于两种不同的分界线):
顺序 | 阶段 | 名称 | 缩写 |
---|---|---|---|
1 | 设置世界时间 | World Time Updata | WTU |
2 | 计划刻 | Scheduled Tick/Tile Tick/Next Time Entry | ST/TT/NTE |
3 | 随机刻与气候 | RandomTick&Climate | RT&C(RTC) |
4 | 村庄相关 | Village | V |
5 | 方块事件 | Block Event | BE |
6 | 实体 | Entity Updata | EU |
7 | 方块实体 | Tile Entity | TE |
8 | 玩家操作 | Network Updata | NU |
9 | 刷怪 | Spawning | S |
10 | 区块加载 | Chunk Unload | CU |
一些容易误判顺序的更新顺序
引用前人理论研究:
活塞推出方块:
当活塞普通推出,即用时 3gt 的推出,此时被推出的方块到达目标位置时,更新顺序为 Tile Entity。而当活塞收到短脉冲,即 0gt,1gt 或者 2gt,此时被推出的方块到达目标位置的更新顺序为 Block Event。如果是推出方块使红石线连接,更新顺序也为 Block Event。
活塞破坏绊线:
这是在活塞推出瞬间完成的,绊线被替换为 36 号方块。所以更新顺序为 Block Event。
树苗成熟:
自然成熟的树苗是由随机刻完成,更新顺序为随机刻。发射器骨粉催熟树苗,更新顺序是在发射器更新的 NTE。玩家骨粉催熟的树苗,更新顺序是在玩家更新的 Network Update。
命令输入:
命令方块执行命令的更新顺序为 NTE,而玩家执行命令的更新顺序为 Network Update。
红石灯:
红石灯是个很特殊的元件,它的点亮与熄灭更新顺序不同,点亮时瞬间点亮,与红石导线一样,没有任何延迟,而在熄灭时,有 4gt 的延迟,并且更新顺序为 NTE。
重力方块:
MC 中的沙子、沙砾等,其掉落前 1gt 延迟也是通过 NTE 实现,但它们在接收到 NTE 更新时,并不是立即更新周围方块,而是先创建掉落的沙子实体,等到实体更新时才将方块所在位置设成空气。所以,沙子掉落形成的更新顺序是 Entity Update。
活塞
活塞与黏性活塞的行为与更新顺序十分奇葩,但是此词条仅讨论游戏刻本身,所以此部分将会加入活塞和黏性活塞的词条中。
参考资料
《深度剖析Minecraft #1 游戏流程》——Fallen_Breath
《[理论分析] 红石更新延迟理论》——Gamepiaynmo
《【MC】想学0t?来这里就对了 最详细的0t原理讲解+干货讲解【第一讲之0t基础原理讲解+36号方块篇】》——Donocean
《【MC】XYZ §1.1 红石tick bug【Sancarn&Selulance】》——Sancarn&Selulance(搬运者为:红石科技搬运组)
《【MC】XYZ §1.2-6 红石tick bug【Sancarn&Selulance】》——Sancarn&Selulance(搬运者为:红石科技搬运组)
《游戏刻》——Minecraft wiki
侵删。
资料分类: | 游戏底层事件 |
每个区块被划分为 16 个区段(1.17+ 为 24 个),每个区段包含 16×16×16=4096 个方块。在每个游戏刻,执行区块刻的区块中,每个区段会被随机选出 3 个方块(可以重复)给予一个“随机刻”。可以通过使用命令 /gamerule randomTickSpeed <数量>来改变每个区段给予随机刻的方块数。大部分方块不会有影响,除了如下这些:
农作物可能生长或拔除;
蘑菇可能传播或拔除;
藤蔓可能传播;
火可能熄灭或传播;
树叶可能枯萎;
耕地的湿润程度会更新;
树苗可能长成树;
熔岩可能使附近的方块着火;
发光的红石矿石会熄灭;
下界传送门方块可能生成一个僵尸猪灵;
海龟蛋破裂或孵化;
营火冒出烟雾颗粒;
铜块发生氧化。
因为随机刻是被随机赋予的,因此无法预测某个方块何时会接收到随机刻。随机刻的间隔的中位数为 47.35 秒,即有 50% 概率不超过 47.35 秒,也有 50% 概率超过 47.35 秒,也有可能需要更短或更长的时间:例如,有 1.5% 的概率间隔时间小于 1 秒,也有 1% 的概率超过 5 分钟。随机刻的间隔的平均值为 68.27 秒。
资料分类: | 游戏底层事件 |
本文所有内容均基于 Java 版,基岩(BE)版不保证所有特性相同(部分已说明 BE 版的内容除外)!
计划刻(Scheduled Tick,有时也往往会被称作 Tile Tick,但是他们的翻译都是计划刻)是指 Minecraft 底层 中一种特殊的延时方案。这种方案相当一部分都被用到红石元件之中,如果打算深入研究 Minecraft 的红石系统(特别是生电相关方向),计划刻将会是玩家在游玩之中不可或缺的一部分。
(注:如无深入了解打算可以仅阅读开头的“作用与基础定义”部分)
作用与基础定义
一些方块可能会请求在将来的某一个游戏刻更新方块,这种更新方块的方式被称为计划刻。在一段时间后必定发生并且行为可以预测的变化,往往使用这种“计划刻”——比如在特定情况下,红石中继器会计划 2 游戏刻之后来改变它的状态,水会计划 1 游戏刻之后来流动。同一个方块上,计划刻与随机刻可能会起到不同的影响。
作为游戏刻(Game Tick)的一部分,之前请求的计划刻如果已到达指定时间,如果区块的加载等级小于 33(不包括 33)[仅 Java 版]或区块已加载[仅基岩版],计划刻就会被执行。否则推迟执行,直到区块被加载。
每个游戏刻所能计划的最大方块数是 65,536 个。
本质
计划刻实际上是一种更新(名词,指 Update,而不是那个用于指代游戏版本版本从低版本换到高版本的过程的动词),方块在计划自己未来某一游戏刻要发生的变化时会将这个计划加入 NTE 列表(Next Tick Entry)中,并记录将要发生变化的时间,然后在若干游戏刻之后发生某些变化。
应注意,计划刻其实是一种抽象的存在,它本质上是游戏对 NTE 列表的读取,计划刻的计划数量上限 65,536 实际上是 NTE 列表的最大长度。
计划刻的触发条件
计划刻仅在被特定方块触发计划刻事件时会被创建,而非像随机刻一样在任何时候都在发生。
延时
一个计划被加入 NTE 列表后会被指定一个时间执行某些变化,计划刻事件被创建到执行并被移出列表之间的长度为计划刻的延迟长度。
不同的计划刻事件有着自己的延迟,显然的,在同一游戏刻被创建的计划刻事件延迟短的那个会被先执行。
以下是一些常用方块的计划刻事件的延迟长度(Java 版 1.12.2):
方块 | 延迟长度,单位为游戏刻(gt) |
---|---|
红石中继器 | 红石中继器当前档数* 2(即每一挡延迟 2gt) |
红石比较器 | 2 |
压力板 | 压下:0;弹起:20 |
按钮 | 按下:0;弹起:木质 30gt,石质 20gt |
红石灯 | 亮起:0;熄灭:4 |
发射器 | 4 |
投掷器 | 4 |
红石火把 | 2 |
漏斗 | 0(NTE 阶段) |
水 | 1 |
值得强调的是,漏斗在 NTE 阶段下的延迟是 0,它的物品传输延迟并不依赖于计划刻和 NTE 元件。
优先级
如果有一些计划刻方块的计划事件被指定在同一个游戏刻执行,这些计划的执行顺序将会由计划刻优先级决定,优先级数字越小的优先级越高(也就是优先级为 -1 的比优先级 0 的优先级更高,在同一 gt 中也就要更早执行)(优先级并非是计划刻的延迟,不要理解错误了,优先级指的是两个计划刻方块请求两个不同的更新计划后两个计划的指定时间恰巧在同一个 tick 时的不同计划刻方块更新时的先后顺序优先级)。
以下是所有(至 Java 版 1.12.2 时)计划刻方块的优先级:
方块 | 优先级 |
---|---|
红石中继器 | -1 |
红石比较器 | 0 |
红石火把 | 0 |
侦测器 | 0 |
发射器 | 0 |
投掷器 | 0 |
命令方块 | 0 |
熔岩 | 0 |
水 | 0 |
掉落的沙子 | 0 |
按钮 | 0 |
压力板 | 0 |
探测铁轨 | 0 |
绊线钩 | 0 |
红石中继器与红石比较器会检查自己是否指向另一个红石中继器或红石比较器,如果一个红石中继器指向一个红石比较器则会产生 -3 的优先级,而一个红石比较器指向一个红石中继器则产生 -1 优先级。
不要将一些特殊的看上去是由计划刻决定的方块更新弄混了,如熔炉的烧制、小麦的成长、活塞的伸出收回等并不由计划刻决定。
部分方块的计划刻事件判定
其中红石中继器、红石比较器、红石火把以及侦测器在状态发生变化时(也就是它们开关的时候)创建计划刻事件;
命令方块、投掷器与发射器在被激活的时候创建计划刻事件,在被取消的时候没有;
水、熔岩以及沙子在被更新时创建计划刻事件;
各种按钮在被按下时创建计划刻事件;
压力板和探测铁轨被实体触发时创建计划刻事件;
绊线钩在被取消激活时创建计划刻事件;
红石灯在熄灭时创建计划刻事件(不是取消激活时,红石灯在被取消激活时到熄灭有 4gt 的延迟)。
参考资料
bilibili“红石科技搬运组”的内容【熟肉】剖析MC#15;
Minecraft wiki。
资料分类: | 游戏底层事件 |
资料分类: | 游戏底层事件 |