π¬ 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