跳到正文
Yayin Story Studio Yayin Story Studio 项目文档

Visindigo::General::TickLoop Class

class Visindigo::General::TickLoop

TickLoop是一个基于Qt事件循环的时间驱动器. 详情...

头文件: #include <TickLoop>
自以下版本: Visindigo 0.16.0

公开类型

(自 Visindigo 0.16.0 引入) enum class FixTickTimeoutPolicy { RealTime, FixInterval }

公开成员函数

(自 Visindigo 0.16.0 引入) TickLoop(QThread *targetThread)
(自 Visindigo 0.16.0 引入) virtual ~TickLoop()
(自 Visindigo 0.16.0 引入) void disableTickObject(Visindigo::General::TickObject *obj)
(自 Visindigo 0.16.0 引入) void enableTickObject(Visindigo::General::TickObject *obj)
(自 Visindigo 0.16.0 引入) qint64 getCurrentMillisecondsSinceEpoch() const
(自 Visindigo 0.16.0 引入) Visindigo::General::TickLoop::FixTickTimeoutPolicy getFixTickTimeoutPolicy() const
(自 Visindigo 0.16.0 引入) bool isAutoStepping() const
(自 Visindigo 0.16.0 引入) void setAutoStepping(bool autoStepping)
(自 Visindigo 0.16.0 引入) void setFixTickTimeoutPolicy(Visindigo::General::TickLoop::FixTickTimeoutPolicy policy)
(自 Visindigo 0.16.0 引入) void stepTick(double elapsedTime_ns = -1.0)

静态公开成员

(自 Visindigo 0.16.0 引入) Visindigo::General::TickLoop *getThreadInstance(QThread *targetThread = nullptr)

受保护成员函数

(自 Visindigo 0.16.0 引入) bool event(QEvent *event)

详细说明

TickLoop是一个基于Qt事件循环的时间驱动器,它会在每个Tick周期调用所有启用的TickObject的onUpdate或onFixUpdate方法,取决于UpdateType设置。

TickLoop寄生在Qt事件循环中,因此刻循环执行频率就是Qt事件循环的频率,如果不频繁处理大量耗时任务,速率甚至可以高达上千次每秒。 值得指出的是,TickLoop不保证在每个Tick周期内调用所有启用的TickObject的更新方法的顺序,因此用户不应依赖于特定的调用顺序来实现逻辑。

在FixUpdate模式下,TickLoop会根据设定的固定时间间隔(毫秒)来调用onFixUpdate方法。 在大部分情况下(当应用负载较低时),TickLoop调用onFixUpdate的频率理应基本接近设置预期。 但在某些情况下(如应用负载过高时),TickLoop可能无法按预期频率调用onFixUpdate方法。为此,TickLoop提供了 两种超时策略。

RealTime 超时策略

FixTickTimeoutPolicy设置为RealTime时,TickLoop在每次调用onFixUpdate时都会传入实际的时间增量,而不是设定的固定时间间隔。 这意味着如果应用负载过高导致TickLoop无法按预期频率调用onFixUpdate方法,elapsedTime_ms参数的值会相应增加,以反映实际的时间增量。 它不追求在超时之后继续严格按期望的次数去调用onFixUpdate,而追求每次调用时忠实反应时间的流逝。

这种策略适用于需要根据实际时间增量进行物理计算的场景,可以在一定程度上保持物理计算的准确性。大部分情况下使用RealTime 超时策略即可,这也是默认设置。

FixInterval 超时策略

FixTickTimeoutPolicy设置为FixInterval时,TickLoop在每次调用onFixUpdate时永远传入固定的时间间隔,且按时间间隔的倍数去调用onFixUpdate方法。 这在忠实进行每一次计算远比保持物理计算的准确性更重要的场景中非常有用。但这可能会导致灾难性的性能问题,因为如果应用负载过高, TickLoop可能会试图在短时间内调用大量的onFixUpdate方法来弥补未按预期频率调用的情况,从而进一步增加应用负载,形成恶性循环。

除非你非常清楚你的应用场景需要FixInterval超时策略,否则强烈建议使用默认的RealTime超时策略。

线程默认TickLoop与自己的TickLoop

当通过getThreadInstance方法获取TickLoop实例时,如果目标线程尚未创建默认TickLoop实例, 则会为其创建一个新的实例。也就是说,每个线程都有一个默认的TickLoop实例。大部分情况下,这就已经满足使用要求。

但如果你需要在一个线程中使用多个TickLoop实例,则在该线程中直接创建新的TickLoop实例即可。 在一些游戏引擎中,为TickObject分组比较有用,譬如可以只针对某些对象进行更新,而冻结其他更新行为。

每个TickLoop实例都可以独立管理自己的TickObject列表,并且它们之间不会互相干扰。

Note: 此类中部分常用函数都是线程安全的,不必额外加锁。

注意事项

  • TickLoop寄生在Qt事件循环中,因此它的更新频率取决于Qt事件循环的频率。 如果你在TickObject的更新方法中执行了大量耗时任务,可能会导致更新频率下降,从而影响应用的响应性。 请确保在更新方法中避免执行过于耗时的操作,或者将耗时操作分散到多个Tick周期中。
  • 你可以在onUpdate和onFixUpdate中安全的调用对应线程的exec()函数进入新的循环层级,这不会发生递归调用, 因为TickLoop的事件处理函数会在每次循环结束时才调用更新方法。在exec()进入新的循环层级后,TickLoop会被 冻结在当前层级,直到新的循环层级退出后才会继续调用更新方法。

    话虽如此,这里的安全指的也仅仅是TickLoop不会发生递归调用, 但新增事件循环层级仍然可能破坏一些逻辑设计,因此还是应当慎用exec()

成员类型文档

[since Visindigo 0.16.0] enum class TickLoop::FixTickTimeoutPolicy

ConstantValueDescription
Visindigo::General::TickLoop::FixTickTimeoutPolicy::RealTime0实际时间超时策略
Visindigo::General::TickLoop::FixTickTimeoutPolicy::FixInterval1固定间隔调用策略

这个enum 从 Visindigo 0.16.0 开始支持。

成员函数文档

[since Visindigo 0.16.0] TickLoop::TickLoop(QThread *targetThread)

targetThread 目标线程。

构造函数。 构造后默认开始循环。

这个function 从 Visindigo 0.16.0 开始支持。

[virtual noexcept, since Visindigo 0.16.0] TickLoop::~TickLoop()

析构函数。

这个function 从 Visindigo 0.16.0 开始支持。

[since Visindigo 0.16.0] void TickLoop::disableTickObject(Visindigo::General::TickObject *obj)

obj 要禁用的TickObject对象。

禁用TickObject对象,使其停止接收TickLoop的更新调用。 如果对象已经禁用,则此函数不会有任何效果。

这个函数是线程安全的,你可以在任何线程中调用它。

这个function 从 Visindigo 0.16.0 开始支持。

[since Visindigo 0.16.0] void TickLoop::enableTickObject(Visindigo::General::TickObject *obj)

obj 要启用的TickObject对象。

启用TickObject对象,使其开始接收TickLoop的更新调用。 如果对象已经启用,则此函数不会有任何效果。

请注意,启用一个TickObject会将其添加到TickLoop的更新列表中,因此在启用之前请确保你已经正确设置了TickObject的属性 (如UpdateType和FixUpdateInterval),以避免在启用后出现不符合预期的行为。

这个函数是线程安全的,你可以在任何线程中调用它。

这个function 从 Visindigo 0.16.0 开始支持。

[protected, since Visindigo 0.16.0] bool TickLoop::event(QEvent *event)

event 事件对象。

重写事件处理函数,用于TickLoop寄生到Qt事件循环中。如果你要在派生类 中重写事件处理函数,请务必调用基类的实现以确保TickLoop的正常工作。

显然,此函数不应被用户调用。

return 事件是否已被处理;返回 true 表示该事件不再继续传递。

这个function 从 Visindigo 0.16.0 开始支持。

[since Visindigo 0.16.0] qint64 TickLoop::getCurrentMillisecondsSinceEpoch() const

return 当前的毫秒级时间戳,单位为毫秒。

注意,这个时间戳不是QDateTime::currentMSecsSinceEpoch的实时值,而是 当前(或最后一次)Tick循环开始时的值,它在每次Tick循环开始时更新一次,周期内保持不变。

这个函数被TickObject::getCurrentMillisecondsSinceEpoch调用,以提供给TickObject的更新方法使用。

它存在的价值在于,获取系统时间戳实际实际上相当耗时,而大部分场景下又对时间戳的实时性要求并不高, 整个Tick循环内所有TickObject要获取当前时间,实际上基本可以使用同一个值。 因此提供了这个相对较为轻量的时间戳获取方式,以供TickObject的更新方法使用。

这个function 从 Visindigo 0.16.0 开始支持。

[since Visindigo 0.16.0] Visindigo::General::TickLoop::FixTickTimeoutPolicy TickLoop::getFixTickTimeoutPolicy() const

return FixUpdate的超时策略。

这个function 从 Visindigo 0.16.0 开始支持。

[static, since Visindigo 0.16.0] Visindigo::General::TickLoop *TickLoop::getThreadInstance(QThread *targetThread = nullptr)

targetThread 目标线程,如果为nullptr,则默认为当前线程。

获取目标线程默认的TickLoop实例,如果目标线程尚未创建默认TickLoop实例,则会为其创建一个新的实例。

这个function 从 Visindigo 0.16.0 开始支持。

[since Visindigo 0.16.0] bool TickLoop::isAutoStepping() const

return 是否自动步进。

这个函数是线程安全的,你可以在任何线程中调用它。

这个function 从 Visindigo 0.16.0 开始支持。

[since Visindigo 0.16.0] void TickLoop::setAutoStepping(bool autoStepping)

autoStepping 是否自动步进。 设置是否自动步进Tick事件,如果为true,则TickLoop会自动与Qt事件循环一起步进。

如果为false,则需要手动调用stepTick方法来继续循环。 默认值为true,通常不需要修改它。

这个函数是线程安全的,你可以在任何线程中调用它。

这个function 从 Visindigo 0.16.0 开始支持。

另请参阅 isAutoStepping().

[since Visindigo 0.16.0] void TickLoop::setFixTickTimeoutPolicy(Visindigo::General::TickLoop::FixTickTimeoutPolicy policy)

policy 超时策略。

设置FixUpdate的超时策略。

这个function 从 Visindigo 0.16.0 开始支持。

[since Visindigo 0.16.0] void TickLoop::stepTick(double elapsedTime_ns = -1.0)

elapsedTime_ns 手动步进的时间增量,单位为纳秒。

手动步进Tick事件,只有当自动步进被禁用时才有意义。调用此函数后,TickLoop会计划在 下一个事件循环内,用此函数设置的手动步进时间增量进行相关调度。

如果elapsedTime_ns参数为负值(默认即为负值),则TickLoop会自动计算自上次Tick事件以来的实际时间增量,并使用该增量进行调度。

如果手动指定时间增量,则TickLoop会使用该增量进行调度,而不考虑实际时间的流逝。这在某些特殊场景下可能会有用。

这个函数是线程安全的,你可以在任何线程中调用它。

这个function 从 Visindigo 0.16.0 开始支持。