跳到主要内容

状态机

StateMachineInstance 是 C++ 中的播放单元:

class StateMachineInstance {
public:
bool advanceAndApply(float elapsedSeconds);
void draw(Renderer*);

HitResult pointerDown(Vec2D, int pointerId = 0);
HitResult pointerMove(Vec2D, float timeStamp = 0, int pointerId = 0);
HitResult pointerUp(Vec2D, int pointerId = 0);
HitResult pointerExit(Vec2D, int pointerId = 0);
};

要驱动状态机(设置值、触发触发器、响应变化),请使用 Data Binding。请参阅 Data Binding

推进

advanceAndApply(dt) 会以 dt 秒运行求解器(状态机、动画、布局、数据绑定),并将结果应用到 artboard 的组件图。请在每次绘制前调用它:

sm->advanceAndApply(deltaSeconds);
sm->draw(&renderer);

返回值为 true 表示状态机仍在动画中,下一帧应该重新绘制。false 表示所有内容都已稳定。

💡

使用固定时间步长累加器。状态机和动画在固定步长下具有数值确定性,这能让播放在不同帧率下保持一致。请参阅 Rendering Loop

强制执行布局 Pass

传入 dt = 0 可以在不推进时间的情况下重新求解布局。这在调整窗口大小,或更改 artboard 的 width() / height() 之后非常有用:

artboard->width(newWidth);
artboard->height(newHeight);
sm->advanceAndApply(0.f);

指针事件

状态机会监听编辑器中放置在 Listener 组件上的指针事件。在转发之前,请将窗口空间坐标映射到 artboard 本地空间:

#include "rive/renderer.hpp"

Mat2D align = computeAlignment(
Fit::contain,
Alignment::center,
AABB(0, 0, windowWidth, windowHeight),
artboard->bounds());

Vec2D toArtboard(int x, int y) {
return align.invertOrIdentity() * Vec2D{(float)x, (float)y};
}

sm->pointerMove(toArtboard(mx, my));
sm->pointerDown(toArtboard(mx, my));
sm->pointerUp(toArtboard(mx, my));
sm->pointerExit(toArtboard(mx, my));

HitResult 会返回以下三个值之一,用于告诉你如何将同一事件路由到 Rive 后方的其他 UI:

  • none — 事件穿过 Rive,未触发任何监听器。将它转发给后方的任意 UI。
  • hit — 触发了监听器,但监听器所在的形状是透明的。Rive 不会阻挡该事件;也应继续转发。
  • hitOpaque — 触发了监听器,并且形状是不透明的。Rive 已消费该事件;不要继续转发。

读取状态变化

每次调用 advanceAndApply 之后,你都可以在 StateMachineInstance 上自省发生了什么。直接在你的 StateMachineInstance 上调用:

for (size_t i = 0, n = sm->stateChangedCount(); i < n; ++i) {
const LayerState* s = sm->stateChangedByIndex(i);
// s->name(), s->is<AnimationState>(), etc.
}

这是你在 C++ 中响应 .riv 文件里定义的状态时使用的钩子——例如记录分析事件、播放声音,或触发回调到你的引擎中。