Skip to content

11 · 时间事件系统 ​

时间事件系统是灵泛引擎的特色功能,让游戏世界随时间自行运转。本章讲解回调驱动的时间事件。

两种游戏模式 ​

灵泛引擎支持两种互斥的模式:

模式EnableTimeSystem特点
叙事模式false(默认)静态世界,玩家推动才变化,支持逐句回溯
小说世界模式true动态世界,时间驱动自行运转,禁用逐句回溯

两种模式互斥

EnableTimeSystem 是硬开关。启用时间系统后,逐句回溯被禁用(改为场景级时间锚点)。

深入了解小说世界模式

小说世界模式有完整的设计哲学——时间事件生命周期、场景名称即坐标、时间锚点、NPC 时间尊严等。详见 进阶专题 · 小说世界模式。

时间配置 ​

在 LingFanEngineOptions 中配置:

配置默认值说明
EnableTimeSystemfalse是否启用时间系统
SecondsPerGameMinute1.0现实多少秒 = 游戏多少分钟
TimeStartDay1起始天数
TimeStartHour0起始小时
TimeStartMinute0起始分钟

时间控制 ​

启动时间 ​

启用时间系统后,时间默认不会自动运行,需要显式启动:

dsl
time_resume

暂停 / 恢复 ​

dsl
time_pause    // 暂停游戏时间
time_resume   // 恢复游戏时间

跳过时间 ​

dsl
skip_time 60   // 跳过 60 分钟
skip_time 1440 // 跳过 1 天

skip_time 逐分钟推进,确保中间时间点的事件被检查。

时间事件 ​

set_time_event 注册事件 ​

dsl
set_time_event "morning_event" 8
  say "早上 8 点,阳光照进窗户。" speaker="旁白"
  set "player.energy" {player.energy + 10}
参数说明
第一个参数事件 ID(唯一标识)
小时触发小时(0-23)
minute=N分钟精度(可选,默认整点)
day=N天数(可选)
once=true是否只触发一次(默认 false)
weekdays="Mon,Tue"星期过滤(可选)
condition="{expr}"条件表达式(可选)
desc="描述"描述(可选)

缩进块内是事件触发时执行的代码。

4 层 AND 过滤 ​

事件触发需要同时满足 4 个条件:

  1. 小时——e.Hour == currentHour
  2. 分钟——null=整点(minute==0 触发),指定值=精确匹配
  3. 天数——null=不限制;once=true→绝对天;once=false→间隔天
  4. 星期——null/空=每天,非空=匹配星期

天数语义 ​

dsl
// 第 3 天触发一次
set_time_event "day3_event" 10 day=3 once=true

// 每 2 天触发一次
set_time_event "every_2days" 10 day=2 once=false
  • once=true + day=N——第 N 天触发一次
  • once=false + day=N——每 N 天触发一次

星期过滤 ​

dsl
set_time_event "weekday_event" 9 weekdays="Mon,Tue,Wed,Thu,Fri"
  say "工作日早上 9 点。" speaker="旁白"

星期缩写:Mon/Tue/Wed/Thu/Fri/Sat/Sun

条件过滤 ​

dsl
set_time_event "conditional" 12 condition="{player.hp > 50}"
  say "你身体健康,中午散步。" speaker="旁白"

unregister_time_event 注销 ​

dsl
unregister_time_event "morning_event"

全局事件 vs 场景事件 ​

事件来源分两类:

类型说明注册方式
全局事件直接全局注册set_time_event
场景挂载事件由场景声明注册C# InTimeEvents 特性

事件生命周期

时间事件一旦注册就独立存在,不绑定到注册它的场景。场景只是事件的"出生地",事件可以在任意时间影响任意场景。

全局事件 vs 场景事件 ​

事件来源分两类(两者都走 EventScheduler,区别只是挂载时机):

  • 全局事件——开局即注册的事件(如春夏秋冬、日夜更替),本质上也是通过 set_time_event / SetTimeEventAsync / InTimeEvents() 注册到 EventScheduler 的时间事件,并非引擎内部有另一套"自适应计算"系统
  • 场景挂载事件——由场景声明注册的事件,一旦挂载就独立运行,场景只是它的"出生地"

存档与时间事件 ​

存档 ​

  • 导航驱动事件(IsLegacyNavigation=true)——序列化为列表
  • 回调驱动事件——只持久化 ID 集合和已触发记录

读档 ​

  • 清空 + 恢复已触发记录
  • 导航事件直接恢复
  • 回调事件在场景初始化时重新注册

C# API ​

csharp
// 注册回调驱动时间事件(返回 void,无需 await)
gameController.SetTimeEventAsync(
    id: "morning_event",
    hour: 8,
    callback: async () =>
    {
        await gameController.SayAsync("早上 8 点。", speaker: "旁白");
    },
    once: false,
    weekdays: null,
    minute: 0,
    day: null);

// 注销
gameController.UnregisterEvent("morning_event");

动手练习 ​

注册一个每天早上 8 点的事件:

dsl
define "player.energy" 100 once

set_time_event "morning_wake" 8 once=false desc="每天早上醒来"
  say "早上 8 点,你醒了过来。" speaker="旁白"
  set "player.energy" 100
  say "精力完全恢复!" speaker="系统"

set_time_event "night_sleep" 22 once=false desc="每天晚上睡觉"
  say "晚上 10 点,你感到困倦。" speaker="旁白"
  set "player.energy" {player.energy - 30}
  if {player.energy < 0}
    set "player.energy" 0
    say "你太累了。" speaker="系统"

下一步 ​

时间系统掌握了!下一章学习用 C# 扩展——突破 DSL 的边界。

基于 .NET 10 + Avalonia 12 · Apache-2.0 License