Animator tool can now play, stop, loop, and toggle an animation.

This commit is contained in:
Zed A. Shaw 2026-02-02 00:02:10 -05:00
parent 7d79e75651
commit df730047ac
3 changed files with 200 additions and 142 deletions

View file

@ -5,149 +5,14 @@
#include <iostream>
#include "shaders.hpp"
#include "backend.hpp"
#include "events.hpp"
#include "constants.hpp"
#include "gui/event_router.hpp"
#include "constants.hpp"
#include <guecs/ui.hpp>
#include "gui/event_router.hpp"
#include "gui/guecstra.hpp"
#include "animate2.hpp"
#include "tools/animator.hpp"
using namespace std::chrono_literals;
bool YES_SYNC=true;
namespace animator {
struct UI {
guecs::UI $ui;
void init(const std::string& sprite_name) {
$ui.position(0,0, SCREEN_WIDTH, SCREEN_HEIGHT);
$ui.layout(
"[play|*%=(300,400)viewer|_|_]"
"[stop|_|_|_]"
"[next|_|_|_]"
"[prev|_|_|_]");
for(auto& [name, cell] : $ui.cells()) {
auto comp = $ui.entity(name);
if(name == "viewer") {
$ui.set<guecs::Sprite>(comp, {
sprite_name, 0, false});
} else {
$ui.set<guecs::Rectangle>(comp, {});
$ui.set<guecs::Text>(comp, {guecs::to_wstring(name)});
$ui.set<guecs::Effect>(comp, {});
$ui.set<guecs::Clickable>(comp, {[](auto){
fmt::println("I don't know what to do here.");
}});
}
}
$ui.init();
}
void render(sf::RenderWindow& window) {
$ui.render(window);
}
bool mouse(float x, float y, guecs::Modifiers mods) {
return $ui.mouse(x, y, mods);
}
std::shared_ptr<sf::Sprite> get_sprite() {
auto viewer = $ui.entity("viewer");
return $ui.get<guecs::Sprite>(viewer).sprite;
}
};
enum class State {
START=__LINE__,
ANIMATE=__LINE__,
END=__LINE__,
};
struct FSM : public DeadSimpleFSM<State, game::Event> {
UI $ui;
gui::routing::Router $router;
sf::RenderWindow $window{sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Animation Crafting Tool"};
void init(const std::string &sprite_name) {
$ui.init(sprite_name);
if(YES_SYNC) {
$window.setVerticalSyncEnabled(VSYNC);
if(FRAME_LIMIT) $window.setFramerateLimit(FRAME_LIMIT);
}
$window.setPosition({0,0});
}
void event(game::Event ev, std::any data={}) {
switch($state) {
FSM_STATE(State, START, ev);
FSM_STATE(State, ANIMATE, ev);
FSM_STATE(State, END, ev);
}
}
void START(game::Event ev) {
switch(ev) {
case game::Event::TICK:
state(State::ANIMATE);
break;
default:
state(State::START);
}
}
void ANIMATE(game::Event ev) {
switch(ev) {
case game::Event::TICK:
// stuff
break;
default:
state(State::START);
}
}
void END(game::Event ev) {
}
void handle_keyboard_mouse() {
while(const auto ev = $window.pollEvent()) {
using enum game::Event;
auto gui_ev = $router.process_event(ev);
auto mouse_pos = $window.mapPixelToCoords($router.position);
switch(gui_ev) {
case MOUSE_CLICK:
$ui.mouse(mouse_pos.x, mouse_pos.y, guecs::NO_MODS);
break;
case MOUSE_MOVE:
$ui.mouse(mouse_pos.x, mouse_pos.y, {1 << guecs::ModBit::hover});
break;
case QUIT:
state(State::END);
default:
break; // ignored
}
}
}
void render() {
$window.clear();
$ui.render($window);
$window.display();
}
bool active() {
return !in_state(State::END);
}
};
}
animate2::Sheet sheet{
.width{720*2},
.height{720},
@ -170,7 +35,7 @@ animate2::Transform scale_tr{
.ease_rate{4.0f},
.scaled{true},
.toggled{false},
.looped{false},
.looped{true},
.easing = ease2::in_out_back,
.motion = ease2::move_rush,
};
@ -190,6 +55,148 @@ animate2::Transform move_tr{
.motion = ease2::move_shake,
};
animate2::Animate2 anim{sheet, sequence, scale_tr};
namespace animator {
void FSM::init(const std::string &sprite_name) {
$ui.init(sprite_name, *this);
if(YES_SYNC) {
$window.setVerticalSyncEnabled(VSYNC);
if(FRAME_LIMIT) $window.setFramerateLimit(FRAME_LIMIT);
}
$window.setPosition({0,0});
}
void FSM::event(Event ev, std::any data) {
switch($state) {
FSM_STATE(State, START, ev);
FSM_STATE(State, ANIMATE, ev);
FSM_STATE(State, END, ev);
}
}
void FSM::START(Event ev) {
switch(ev) {
case Event::TICK:
state(State::ANIMATE);
break;
default:
state(State::START);
}
}
void FSM::ANIMATE(Event ev) {
switch(ev) {
case Event::TICK:
// stuff
break;
case Event::PLAY:
if(!anim.playing) anim.play();
break;
case Event::STOP:
if(anim.playing) anim.stop();
break;
case Event::LOOP:
anim.$transform.looped = !anim.$transform.looped;
break;
case Event::TOGGLE:
anim.$transform.toggled = !anim.$transform.toggled;
break;
default:
state(State::START);
}
}
void FSM::END(Event ev) {
}
void FSM::handle_keyboard_mouse() {
while(const auto ev = $window.pollEvent()) {
using enum game::Event;
auto gui_ev = $router.process_event(ev);
auto mouse_pos = $window.mapPixelToCoords($router.position);
switch(gui_ev) {
case MOUSE_CLICK:
$ui.mouse(mouse_pos.x, mouse_pos.y, guecs::NO_MODS);
break;
case MOUSE_MOVE:
$ui.mouse(mouse_pos.x, mouse_pos.y, {1 << guecs::ModBit::hover});
break;
case QUIT:
state(State::END);
default:
break; // ignored
}
}
}
void FSM::render() {
$window.clear();
$ui.render($window);
$window.display();
}
bool FSM::active() {
return !in_state(State::END);
}
void UI::button(const std::string& name, std::function<void(guecs::Modifiers mods)> cb) {
auto comp = $ui.entity(name);
$ui.set<guecs::Rectangle>(comp, {});
$ui.set<guecs::Text>(comp, {guecs::to_wstring(name)});
$ui.set<guecs::Effect>(comp, {});
$ui.set<guecs::Clickable>(comp, {cb});
}
void UI::init(const std::string& sprite_name, FSM& fsm) {
$ui.position(0,0, SCREEN_WIDTH, SCREEN_HEIGHT);
$ui.layout(
"[play|*%=(300,400)viewer|_|_]"
"[stop|_|_|_]"
"[loop|_|_|_]"
"[toggled|_|_|_]");
auto viewer = $ui.entity("viewer");
$ui.set<guecs::Sprite>(viewer, { sprite_name, 0, false});
button("stop", [&](auto){
fsm.event(Event::STOP, {});
});
button("play", [&](auto){
fsm.event(Event::PLAY, {});
});
button("loop", [&](auto){
fsm.event(Event::LOOP, {});
});
button("toggled", [&](auto){
fsm.event(Event::TOGGLE, {});
});
$ui.init();
}
void UI::render(sf::RenderWindow& window) {
$ui.render(window);
}
bool UI::mouse(float x, float y, guecs::Modifiers mods) {
return $ui.mouse(x, y, mods);
}
std::shared_ptr<sf::Sprite> UI::get_sprite() {
auto viewer = $ui.entity("viewer");
return $ui.get<guecs::Sprite>(viewer).sprite;
}
}
int main(int argc, char* argv[]) {
shaders::init();
components::init();
@ -207,7 +214,6 @@ int main(int argc, char* argv[]) {
animator::FSM main;
main.init(argv[1]);
animate2::Animate2 anim{sheet, sequence, scale_tr};
anim.play();
auto sprite = main.$ui.get_sprite();
@ -220,7 +226,7 @@ int main(int argc, char* argv[]) {
auto [ticks, alpha] = anim.commit();
for(int i = 0; i < ticks; i++) {
main.event(game::Event::TICK, {});
main.event(animator::Event::TICK, {});
if(anim.playing) {
anim.update();

50
tools/animator.hpp Normal file
View file

@ -0,0 +1,50 @@
#pragma once
#include <guecs/ui.hpp>
#include "gui/event_router.hpp"
#include "gui/guecstra.hpp"
#include "events.hpp"
namespace animator {
enum class State {
START=__LINE__,
ANIMATE=__LINE__,
END=__LINE__,
};
enum class Event {
TICK=__LINE__,
PLAY=__LINE__,
STOP=__LINE__,
LOOP=__LINE__,
TOGGLE=__LINE__,
};
struct FSM;
struct UI {
guecs::UI $ui;
void button(const std::string& name, std::function<void(guecs::Modifiers mods)> cb);
void init(const std::string& sprite_name, FSM& fsm);
void render(sf::RenderWindow& window);
bool mouse(float x, float y, guecs::Modifiers mods);
std::shared_ptr<sf::Sprite> get_sprite();
};
struct FSM : public DeadSimpleFSM<State, Event> {
UI $ui;
gui::routing::Router $router;
sf::RenderWindow $window{sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Animation Crafting Tool"};
void init(const std::string &sprite_name);
void event(Event ev, std::any data={});
void START(Event ev);
void ANIMATE(Event ev);
void END(Event ev);
void handle_keyboard_mouse();
void render();
bool active();
};
}