🎬 Animation System
Fase: 4 — O Cérebro
Namespace: Caffeine::Animation
Arquivo: src/animation/AnimationSystem.hpp
Status: 📅 Planejado
RF: RF4.9
Visão Geral
Sistema de animação 2D com clipes de sprite e state machine. Cada entidade tem um componente Animator que gerencia a state machine de animação.
Fase 5 adiciona skeletal animation (bones, skinning, blend trees) — ver docs/fase5/skeletal-animation.md.
API Planejada
namespace Caffeine::Animation {
// ============================================================================
// @brief Clipe de animação — sequência de frames de sprite sheet.
// ============================================================================
struct AnimationClip {
FixedString<32> name;
u32 fps = 12;
std::vector<Rect2D> frames; // regiões na textura/atlas
bool loop = true;
f32 duration() const { return (f32)frames.size() / (f32)fps; }
};
// ============================================================================
// @brief Transição entre estados de animação.
// ============================================================================
struct AnimationTransition {
FixedString<32> toState;
std::function<bool()> condition; // quando esta condição é verdade, transiciona
f32 blendTime = 0.1f; // segundos de crossfade
bool hasExitTime = false; // true = espera clip terminar
};
// ============================================================================
// @brief Estado de animação (nó na state machine).
// ============================================================================
struct AnimationState {
FixedString<32> name;
const AnimationClip* clip = nullptr;
f32 speed = 1.0f;
std::vector<AnimationTransition> transitions;
};
// ============================================================================
// @brief Componente de animação da entidade.
//
// Contém a state machine e estado atual.
// O AnimationSystem itera sobre todas as entidades com este componente.
// ============================================================================
struct Animator {
HashMap<FixedString<32>, AnimationState> states;
FixedString<32> currentState;
FixedString<32> previousState;
f32 timeInState = 0.0f;
f32 blendWeight = 1.0f; // para crossfade (0 → 1)
f32 playbackScale = 1.0f; // multiplicador de velocidade
bool paused = false;
// Eventos de animação (frame específico dispara callback)
std::vector<std::pair<u32, FixedString<32>>> frameEvents;
std::function<void(const FixedString<32>&)> onFrameEvent;
};
// ============================================================================
// @brief Sistema de animação ECS.
//
// Por frame:
// 1. Verifica condições de transição
// 2. Avança timeInState
// 3. Calcula frame atual do clipe
// 4. Atualiza Sprite.srcRect com o frame correto
// 5. Dispara frameEvents se frame atingido
// ============================================================================
class AnimationSystem : public ECS::ISystem {
public:
void update(ECS::World& world, f64 dt) override;
i32 priority() const override { return 200; }
const char* name() const override { return "Animation"; }
// API imperativa (útil para gameplay code)
void play(ECS::Entity e, const char* stateName,
f32 blendTime = 0.1f);
void pause(ECS::Entity e);
void resume(ECS::Entity e);
void setSpeed(ECS::Entity e, f32 speed);
bool isPlaying(ECS::Entity e, const char* stateName) const;
private:
void evaluateTransitions(ECS::Entity e, Animator& anim, f64 dt);
void advanceFrame(ECS::Entity e, Animator& anim, Sprite& sprite, f64 dt);
void checkFrameEvents(ECS::Entity e, Animator& anim);
};
} // namespace Caffeine::Animation
State Machine Visual
jump == true
idle ──────────────────► jump_rise
│ │
│◄──────────────────────────┤ apex (vel.y < 0.1)
│ land ▼
│◄──────────────── jump_fall
│
│ moveX != 0
├────────────────► walk
│
│ attack btn
└────────────────► attack_1 ──► attack_2 (combo)
Exemplos de Uso
// ── Setup do Animator ─────────────────────────────────────────
Animator anim;
AnimationClip idleClip;
idleClip.name = "idle";
idleClip.fps = 8;
idleClip.frames = { {0,0,64,64}, {64,0,64,64} };
idleClip.loop = true;
AnimationClip walkClip;
walkClip.name = "walk";
walkClip.fps = 12;
walkClip.frames = { {128,0,64,64}, {192,0,64,64}, {256,0,64,64}, {320,0,64,64} };
walkClip.loop = true;
AnimationState idleState { "idle", &idleClip };
idleState.transitions.push_back({
.toState = "walk",
.condition = [&]() { return input.axisValue(Axis::MoveX) != 0.0f; }
});
AnimationState walkState { "walk", &walkClip };
walkState.transitions.push_back({
.toState = "idle",
.condition = [&]() { return input.axisValue(Axis::MoveX) == 0.0f; }
});
anim.states["idle"] = idleState;
anim.states["walk"] = walkState;
anim.currentState = "idle";
world.add<Animator>(player, std::move(anim));
// ── Registrar sistema ─────────────────────────────────────────
world.registerSystem<AnimationSystem>();
// ── Controle programático ─────────────────────────────────────
auto* animSys = world.getSystem<AnimationSystem>();
animSys->play(player, "attack_1", 0.0f); // sem blend (combos)
// ── Frame events ──────────────────────────────────────────────
world.get<Animator>(player)->frameEvents.push_back({3, "attack_hit"});
world.get<Animator>(player)->onFrameEvent = [](const FixedString<32>& evt) {
if (evt == "attack_hit") damageEnemiesInRange();
};
Decisões de Design
| Decisão |
Justificativa |
| State machine |
Lógica de animação separada da lógica de gameplay |
condition como lambda |
Flexível sem overhead de parsing |
blendTime por transição |
Crossfade suave por padrão |
| Frame events |
Sincronizar gameplay com quadros específicos da animação |
priority = 200 |
Roda após PhysicsSystem (100) e MovementSystem (150) |
Critério de Aceitação
Dependências
Referências
🎬 Animation System
Visão Geral
Sistema de animação 2D com clipes de sprite e state machine. Cada entidade tem um componente
Animatorque gerencia a state machine de animação.Fase 5 adiciona skeletal animation (bones, skinning, blend trees) — ver
docs/fase5/skeletal-animation.md.API Planejada
State Machine Visual
Exemplos de Uso
Decisões de Design
conditioncomo lambdablendTimepor transiçãopriority = 200Critério de Aceitação
advanceFrame()< 0.1ms para 100 entidades animadasDependências
Referências
docs/architecture_specs.md— §12 Animation Systemdocs/fase5/skeletal-animation.md— extensão para 3D