# 时间规则

温馨提示:开始阅读这篇指南之前,我们希望你对《我的世界》基岩版附加包有一定了解,有能力撰写 JSON 数据格式,对 Python 进行模组开发有了解,并能够独立阅读《我的世界》开发者官网-开发指南或其他技术引用文档。

本文将带你了解 MC 中的时间的问题,并从零开始带你搭建起一个基础的 UI 来帮助我们了解当前 MC 中的时间情况。

在本教程中,您将学习以下内容。

  • ✅MC 中的时间规则;
  • ✅简单 UI 搭建指南;

# MC 中的时间规则

这一部分可以查看 MC 的官方介绍 (opens new window)

# 时间换算

在我的世界中的时间正好是现实时间中流逝速度的 72 倍。这是因为现实世界中的 1 天有 24 * 60 = 1440 分钟,而在我的世界中,1 个完整的 Minecraft 天只有 20 分钟。1440 / 20 = 72,正好是 72 倍。

如果要进行时间单位的换算的话,那么可以得到下面两个表。

一个表是 Minecraft → 现实时间的换算表:

Minecraft时间 Minecraft 刻 现实时间
1秒 0.27 0.0138秒
1分钟 16.6 0.83秒
1小时 1,000 50秒
1天 24,000 20分钟
1周(7天) 168,000 2.3小时
1个月(30天) 720,000 10小时
1年(365.25 天) 8,766,000 121.75小时(5.072916天

另一个表是现实时间 → Minecraft 时间的换算表:

现实时间 Minecraft时间
1⁄20秒(1游戏刻) 3.6秒
1秒 1分钟12秒(72秒)
10秒 12分钟(720秒)
50秒 1小时(60分,3600秒)
1分钟 1小时12分钟
1小时 3天
1天 2.4个月 = 72天
1周 约1.385年 ≈ 17个月 = 72周 = 504天
1个月 6年 = 72个月 ≈ 308.5周 = 2,160天
1年 72年 ≈ 876.5个月 ≈ 3,757周 ≈ 26,297.5天

# 游戏刻

你可以把游戏想象成一个巨大的机器,它需要不断地运转才能工作。MC 就是这样一个机器。就像时钟里的每个部件都要跟着钟摆的节奏一起动一样,游戏里的每个事情都要跟着游戏的节奏一起发生。我们把游戏的节奏叫做游戏循环,它就像是游戏的心跳。每次心跳,游戏就会更新一下自己的状态,比如玩家的位置,方块的变化,怪物的行动等等。我们把每次心跳的时间叫做一刻(tick),它是游戏的最小时间单位。

游戏的一刻是指 Minecraft 的游戏循环运行一次所占用的时间。正常情况下,游戏固定以每秒钟 20 刻的速率运行,因此一刻的时间为 0.05 秒(50 毫秒,或一秒钟的二十分之一),使得游戏内的一天 (opens new window)刚好持续 24000 刻,也就是 20 分钟。

Minecraft 时间与现实时间的大约换算图

# 游戏中的时间

有了上面我们对 MC 时间的了解,加上时间刻与现实时间的换算关系,我们就知道了游戏中的一天是如何度过的了。

# 白天

一天的开始

白天是一天周期中最长的一节,历时 10 分钟。

开始:0 刻(早上06:00:00.0)

中午:6000 刻(下午12:00:00.0)

结束:12000 刻(下午06:00:00.0)

# 日落

在地图的高处看到的日落

日落是介于白天和夜晚之间的时间段,持续 1 分半钟。

开始:12000 刻(下午06:00:00.0)

中点:12400 刻(下午06:54:00.0)

结束:13800 刻(下午07:48:00.0)

# 夜晚

栋原的夜晚;可以看到多种生物

夜晚持续 7 分钟。

开始:13800 刻(下午07:48:00.0)

午夜:18000 刻(早上12:00:00.0)

结束:22200 刻(早上04:12:00.0)

晴朗的夜晚时,玩家可以在 12542 刻(下午06:32:31.2)到 23460 刻(早上05:27:36.0)时睡觉。在雨天,玩家可以在 12010 刻(下午06:00:36.0)到 23992 刻(早上05:59:31.2)时睡觉。

# 日出/黎明

太阳从地平线上升起

日出是介于夜晚和白天之间的时间段,持续 1 分半钟。

开始:22200 刻(早上04:12:00.0)

中点:23100 刻(早上05:06:00.0)

结束:24000(0)刻(早上06:00:00.0)

# 月相

游戏中每过一天,时间计数便会增加 24000 刻。虽然每天的交替是一样的,但月亮 (opens new window)会经历 8 种月相。虽然没有命令直接更改月相,但/time add 24000命令可以快进至下一个月相。进一步而言,使用以下命令可以直接指定不同的月相:

命令 月相
/time set night 满月
/time set 38000 亏凸月
/time set 62000 下弦月
/time set 86000 残月
/time set 110000 新月
/time set 134000 娥眉月
/time set 158000 上弦月
/time set 182000 盈凸月

游戏中月亮的原版贴图,自行对应:

原版贴图的截图

# 昼夜更替

如果开启了命令。我们可以使用 /gamerule doDaylightCycle [*true/false*] 来控制是否开启昼夜更替。

当我们关闭昼夜更替之后,游戏中的时间刻虽然会继续运行,但是数值上不会有所变动了,而是固定在某一刻

# 时间相关的 API

我们可以在官方的文档 (opens new window)中查看到最新的、与时间相关的 API:

官方文档截图

看着这么多,如果不算设置昼夜更替的 API 的话,其实总体就分为了两类:

  • 获取时间类;
  • 设置时间类;

不管是维度的局部时间,还是其他任何时间,都符合上面介绍的时间规则。

# 实操:左上角时钟 UI 显示

接下来我们将带大家实操制作一个 UI,能够实时显示当前维度的时间,效果如下:

时间UI

# Step1. 新增界面文件

首先,打开我们的 MC Studio,在界面一览选择新建一个「界面文件」:

点击下一步之后命名为「timeDisplayUI」就可以了:

我们的需求很简单,只需要在界面的左上角,使用「文本」控件显示出当前维度的时间就可以了,所以整个界面也十分简单,一个「文本」控件,设置在左上方即可:

把文本大小选择为大,并且把层级设置在 20 层以上(保证在原版的 UI 上方,不会被遮挡),这样方便我们查看。

OK,界面文件就此告成。

# Step2. 注册 UI

注册和创建 UI 需要监听 UiInitFinished 之后执行:

# -*- coding: utf-8 -*-
import mod.client.extraClientApi as clientApi

CompFactory = clientApi.GetEngineCompFactory()


class TimeRuleClientSystem(clientApi.GetClientSystemCls()):

    def __init__(self, namespace, name):
        super(TimeRuleClientSystem, self).__init__(namespace, name)
        self.ListenEvent()
        self.mUINode = None

    def ListenEvent(self):
        self.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), "UiInitFinished",
                            self, self.OnUiInitFinished)

    def UnListenEVent(self):
        self.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), "UiInitFinished",
                              self, self.OnUiInitFinished)

    def Destroy(self):
        self.UnListenEVent()

    def OnUiInitFinished(self, args=None):
        # 注册 UI
        uiClsPath = 'timeRuleScripts.uIScripts.UIScript'
        uiScreenDef = 'timeDisplayUI.main'
        clientApi.RegisterUI('timeRuleMod', 'timeDisplayUI', uiClsPath, uiScreenDef)
        # 创建 UI
        self.mUINode = clientApi.CreateUI('timeRuleMod', 'timeDisplayUI', {"isHud": 1})
        if self.mUINode:
            self.mUINode.Init()  # 调用初始化函数

# Step3. UI 代码

UI 代码也非常简单,也就两个功能:1)每秒更新 label 的文字;2)把游戏时间转换成与现实世界对应的时间。

完整代码如下:

# -*- coding: utf-8 -*-
import mod.client.extraClientApi as clientApi

ScreenNode = clientApi.GetScreenNodeCls()

CompFactory = clientApi.GetEngineCompFactory()
gameComp = CompFactory.CreateGame(clientApi.GetLevelId())


class UIScript(ScreenNode):
    def __init__(self, namespace, name, param):
        ScreenNode.__init__(self, namespace, name, param)
        self.mPlayerId = clientApi.GetLocalPlayerId()

        # 组件注册地址
        self.mLabelPath = '/label'

        # 界面需要使用的自定义属性
        self.mTimeCounter = 0

    def Create(self):
        print("===== UI Create =====")

    # 1 秒 30 帧
    def Update(self):
        self.mTimeCounter += 1
        perSec = self.mTimeCounter % 30 == 0
        if perSec:
            self.UpdateLabelContent()

    # region 类函数
    # --------------------------------------------------------------------------------------------
    def Init(self):
        print '=== UI 初始化 ==='
        self.UpdateLabelContent()

    def UpdateLabelContent(self):
        timeComp = CompFactory.CreateTime(clientApi.GetLevelId())
        pressedTime = timeComp.GetTime()
        timeStr = self.GameTime2RealTime(pressedTime)
        self.GetLabel(self.mLabelPath).SetText(timeStr)

    def GameTime2RealTime(self, gameTick):
        # 定义游戏中一天的刻数
        gameDayTicks = 24000
        # 定义游戏中一小时的刻数
        gameHourTicks = gameDayTicks / 24
        # 定义游戏中一分钟的刻数
        gameMinuteTicks = gameHourTicks / 60
        # 计算游戏中的天数
        gameDay = gameTick // gameDayTicks + 1
        # 计算游戏中的小时数
        gameHour = (gameTick % gameDayTicks) // gameHourTicks
        # 计算游戏中的分钟数
        gameMinute = (gameTick % gameHourTicks) // gameMinuteTicks
        # 把游戏中的小时数转换成现实中的小时数,加上6小时的偏移量
        realHour = (gameHour + 6) % 24
        # 把现实中的小时数、分钟数转换成字符串,补齐两位
        realHourStr = str(realHour).zfill(2)
        realMinuteStr = str(gameMinute).zfill(2)
        # 返回转换后的格式
        return "第{}天第{}时第{}分".format(gameDay, realHourStr, realMinuteStr)

    def GetLabel(self, path):
        control = self.GetBaseUIControl(path)
        if control:
            return control.asLabel()
        return None

    # endregion

# Step4. 测试并验证

我们可以尝试使用 /time set xxx 命令来设置当前的时间,来验证 UI 代码的正确性。比如 /time set 0 界面会正确显示上面规则介绍的 06:00:00 这个时间:

至此,UI 就完成了。

# 课后作业

本次课后作业,内容如下:

  • 给模组左上角新增一个当前时间显示的 UI;
  • 熟悉并测试时间相关的 API;

MC 中的时间规则

时间换算

游戏刻

游戏中的时间

白天

日落

夜晚

日出/黎明

月相

昼夜更替

时间相关的 API

实操:左上角时钟 UI 显示

Step1. 新增界面文件

Step2. 注册 UI

Step3. UI 代码

Step4. 测试并验证

课后作业