Created a nice utility library for doing animations, and used it in the ritual crafting UI.
This commit is contained in:
parent
0a40135f5d
commit
1aa6674e42
14 changed files with 213 additions and 150 deletions
2
Makefile
2
Makefile
|
@ -22,7 +22,7 @@ tracy_build:
|
||||||
meson compile -j 10 -C builddir
|
meson compile -j 10 -C builddir
|
||||||
|
|
||||||
test: build
|
test: build
|
||||||
./builddir/runtests
|
./builddir/runtests "[animation]"
|
||||||
|
|
||||||
run: build test
|
run: build test
|
||||||
powershell "cp ./builddir/zedcaster.exe ."
|
powershell "cp ./builddir/zedcaster.exe ."
|
||||||
|
|
103
animation.cpp
Normal file
103
animation.cpp
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
#include "animation.hpp"
|
||||||
|
|
||||||
|
namespace components {
|
||||||
|
void Animation::play() {
|
||||||
|
if(!playing) {
|
||||||
|
current = 0;
|
||||||
|
subframe = 0.0f;
|
||||||
|
playing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float Animation::twitching() {
|
||||||
|
switch(easing) {
|
||||||
|
case ease::NONE:
|
||||||
|
return 0.0;
|
||||||
|
case ease::SINE:
|
||||||
|
return ease::sine(float(frames) / subframe * ease_rate);
|
||||||
|
case ease::OUT_CIRC:
|
||||||
|
return ease::out_circ(ease::sine(float(frames) / subframe * ease_rate));
|
||||||
|
case ease::OUT_BOUNCE:
|
||||||
|
return ease::out_bounce(ease::sine(float(frames) / subframe * ease_rate));
|
||||||
|
case ease::IN_OUT_BACK:
|
||||||
|
return ease::in_out_back(ease::sine(float(frames) / subframe * ease_rate));
|
||||||
|
default:
|
||||||
|
dbc::sentinel(
|
||||||
|
fmt::format("Invalid easing {} given to animation",
|
||||||
|
int(easing)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Animation::step(sf::Vector2f& scale_out, sf::Vector2f& pos_out, sf::IntRect& rect_out) {
|
||||||
|
if(playing && current < frames) {
|
||||||
|
float tick = twitching();
|
||||||
|
scale_out.x = std::lerp(scale_out.x, scale_out.x + scale, tick);
|
||||||
|
scale_out.y = std::lerp(scale_out.y, scale_out.y + scale, tick);
|
||||||
|
|
||||||
|
if(stationary) {
|
||||||
|
pos_out.y = pos_out.y - (pos_out.y * scale_out.y - pos_out.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!simple) {
|
||||||
|
rect_out.position.x += current * frame_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
subframe += speed;
|
||||||
|
current = int(subframe);
|
||||||
|
} else if(!looped) {
|
||||||
|
playing = false;
|
||||||
|
current = frames - 1;
|
||||||
|
subframe = float(frames - 1);
|
||||||
|
|
||||||
|
if(!simple) {
|
||||||
|
rect_out.position.x += current * frame_width;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
playing = false;
|
||||||
|
current = 0;
|
||||||
|
subframe = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace animation {
|
||||||
|
using namespace components;
|
||||||
|
using namespace textures;
|
||||||
|
|
||||||
|
static AnimationManager MGR;
|
||||||
|
static bool initialized = false;
|
||||||
|
|
||||||
|
bool apply(Animation& anim, SpriteTexture& target) {
|
||||||
|
auto size = target.texture->getSize();
|
||||||
|
anim.frame_width = int(size.x) / (unsigned int)anim.frames;
|
||||||
|
sf::IntRect rect{{0,0}, {anim.frame_width, int(size.y)}};
|
||||||
|
sf::Vector2f scale{1.0, 1.0};
|
||||||
|
sf::Vector2f pos{0, 0};
|
||||||
|
|
||||||
|
anim.step(scale, pos, rect);
|
||||||
|
|
||||||
|
target.sprite->setTextureRect(rect);
|
||||||
|
target.sprite->setPosition(pos);
|
||||||
|
target.sprite->setScale(scale);
|
||||||
|
|
||||||
|
return anim.playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
if(!initialized) {
|
||||||
|
Config config("assets/animations.json");
|
||||||
|
|
||||||
|
for(auto& [name, data] : config.json().items()) {
|
||||||
|
auto anim = components::convert<Animation>(data);
|
||||||
|
MGR.animations.insert_or_assign(name, anim);
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation load(std::string name) {
|
||||||
|
return MGR.animations.at(name);
|
||||||
|
}
|
||||||
|
}
|
14
animation.hpp
Normal file
14
animation.hpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
#include "components.hpp"
|
||||||
|
#include "textures.hpp"
|
||||||
|
#include "easings.hpp"
|
||||||
|
|
||||||
|
namespace animation {
|
||||||
|
struct AnimationManager {
|
||||||
|
std::unordered_map<std::string, components::Animation> animations;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool apply(components::Animation& anim, textures::SpriteTexture& target);
|
||||||
|
void init();
|
||||||
|
components::Animation load(std::string name);
|
||||||
|
}
|
12
assets/animations.json
Normal file
12
assets/animations.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"ritual_blanket": {
|
||||||
|
"_type": "Animation",
|
||||||
|
"easing": 0,
|
||||||
|
"ease_rate": 0.5,
|
||||||
|
"scale": 1.0,
|
||||||
|
"simple": false,
|
||||||
|
"frames": 3,
|
||||||
|
"speed": 0.2,
|
||||||
|
"stationary": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,7 +52,8 @@
|
||||||
"devils_fingers_stage": "assets/devils_fingers_stage.png",
|
"devils_fingers_stage": "assets/devils_fingers_stage.png",
|
||||||
"tunnel_with_rocks": "assets/tunnel_with_rocks.png",
|
"tunnel_with_rocks": "assets/tunnel_with_rocks.png",
|
||||||
"tunnel_with_rocks_stage": "assets/tunnel_with_rocks_stage.png",
|
"tunnel_with_rocks_stage": "assets/tunnel_with_rocks_stage.png",
|
||||||
"ritual_crafting_area": "assets/ritual_crafting_area.png"
|
"ritual_crafting_area": "assets/ritual_crafting_area.png",
|
||||||
|
"the_ritual_circle": "assets/the_ritual_circle.png"
|
||||||
},
|
},
|
||||||
"worldgen": {
|
"worldgen": {
|
||||||
"enemy_probability": 50,
|
"enemy_probability": 50,
|
||||||
|
|
BIN
assets/the_ritual_circle.png
Normal file
BIN
assets/the_ritual_circle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
|
@ -32,7 +32,7 @@ namespace gui {
|
||||||
void BossFightUI::configure_sprite() {
|
void BossFightUI::configure_sprite() {
|
||||||
$sprite_config = $world->get<components::Sprite>($boss_id);
|
$sprite_config = $world->get<components::Sprite>($boss_id);
|
||||||
$animation = $world->get<components::Animation>($boss_id);
|
$animation = $world->get<components::Animation>($boss_id);
|
||||||
$animation.texture_width = $sprite_config.width;
|
$animation.frame_width = $sprite_config.width;
|
||||||
|
|
||||||
$boss_image = textures::get($sprite_config.name);
|
$boss_image = textures::get($sprite_config.name);
|
||||||
sf::IntRect frame_rect{{0,0},{$sprite_config.width,$sprite_config.height}};
|
sf::IntRect frame_rect{{0,0},{$sprite_config.width,$sprite_config.height}};
|
||||||
|
|
|
@ -28,62 +28,4 @@ namespace components {
|
||||||
components::enroll<Animation>(component_map);
|
components::enroll<Animation>(component_map);
|
||||||
components::enroll<Sound>(component_map);
|
components::enroll<Sound>(component_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::play() {
|
|
||||||
if(!playing) {
|
|
||||||
current = 0;
|
|
||||||
subframe = 0.0f;
|
|
||||||
playing = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float Animation::twitching() {
|
|
||||||
switch(easing) {
|
|
||||||
case ease::NONE:
|
|
||||||
return 0.0;
|
|
||||||
case ease::SINE:
|
|
||||||
return ease::sine(float(frames) / subframe * ease_rate);
|
|
||||||
case ease::OUT_CIRC:
|
|
||||||
return ease::out_circ(ease::sine(float(frames) / subframe * ease_rate));
|
|
||||||
case ease::OUT_BOUNCE:
|
|
||||||
return ease::out_bounce(ease::sine(float(frames) / subframe * ease_rate));
|
|
||||||
case ease::IN_OUT_BACK:
|
|
||||||
return ease::in_out_back(ease::sine(float(frames) / subframe * ease_rate));
|
|
||||||
default:
|
|
||||||
dbc::sentinel(
|
|
||||||
fmt::format("Invalid easing {} given to animation",
|
|
||||||
int(easing)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Animation::step(sf::Vector2f& scale_out, sf::Vector2f& pos_out, sf::IntRect& rect_out) {
|
|
||||||
if(playing && current < frames) {
|
|
||||||
float tick = twitching();
|
|
||||||
scale_out.x = std::lerp(scale_out.x, scale_out.x + scale, tick);
|
|
||||||
scale_out.y = std::lerp(scale_out.y, scale_out.y + scale, tick);
|
|
||||||
|
|
||||||
if(stationary) {
|
|
||||||
pos_out.y = pos_out.y - (pos_out.y * scale_out.y - pos_out.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!simple) {
|
|
||||||
rect_out.position.x += current * texture_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
subframe += speed;
|
|
||||||
current = int(subframe);
|
|
||||||
} else if(!looped) {
|
|
||||||
playing = false;
|
|
||||||
current = frames - 1;
|
|
||||||
subframe = float(frames - 1);
|
|
||||||
|
|
||||||
if(!simple) {
|
|
||||||
rect_out.position.x += current * texture_width;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
playing = false;
|
|
||||||
current = 0;
|
|
||||||
subframe = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,7 @@ namespace components {
|
||||||
float subframe = 0.0f;
|
float subframe = 0.0f;
|
||||||
bool looped = false;
|
bool looped = false;
|
||||||
// BUG: this is weirdly not used in most animations but also named wrong should be frame_width
|
// BUG: this is weirdly not used in most animations but also named wrong should be frame_width
|
||||||
int texture_width = TEXTURE_WIDTH;
|
int frame_width = TEXTURE_WIDTH;
|
||||||
|
|
||||||
void play();
|
void play();
|
||||||
float twitching();
|
float twitching();
|
||||||
|
|
4
main.cpp
4
main.cpp
|
@ -3,6 +3,7 @@
|
||||||
#include "sound.hpp"
|
#include "sound.hpp"
|
||||||
#include "autowalker.hpp"
|
#include "autowalker.hpp"
|
||||||
#include "ai.hpp"
|
#include "ai.hpp"
|
||||||
|
#include "animation.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
@ -10,6 +11,9 @@ int main(int argc, char* argv[]) {
|
||||||
textures::init();
|
textures::init();
|
||||||
sound::init();
|
sound::init();
|
||||||
ai::init("assets/ai.json");
|
ai::init("assets/ai.json");
|
||||||
|
animation::init();
|
||||||
|
|
||||||
|
|
||||||
sound::mute(true);
|
sound::mute(true);
|
||||||
gui::FSM main;
|
gui::FSM main;
|
||||||
main.event(gui::Event::STARTED);
|
main.event(gui::Event::STARTED);
|
||||||
|
|
|
@ -79,6 +79,8 @@ dependencies += [
|
||||||
sources = [
|
sources = [
|
||||||
'ai.cpp',
|
'ai.cpp',
|
||||||
'ai_debug.cpp',
|
'ai_debug.cpp',
|
||||||
|
'animation.cpp',
|
||||||
|
'animation.cpp',
|
||||||
'autowalker.cpp',
|
'autowalker.cpp',
|
||||||
'boss_fight_ui.cpp',
|
'boss_fight_ui.cpp',
|
||||||
'camera.cpp',
|
'camera.cpp',
|
||||||
|
@ -138,6 +140,7 @@ executable('runtests', sources + [
|
||||||
'tests/rituals.cpp',
|
'tests/rituals.cpp',
|
||||||
'tests/sound.cpp',
|
'tests/sound.cpp',
|
||||||
'tests/spatialmap.cpp',
|
'tests/spatialmap.cpp',
|
||||||
|
'tests/animation.cpp',
|
||||||
'tests/stats.cpp',
|
'tests/stats.cpp',
|
||||||
'tests/textures.cpp',
|
'tests/textures.cpp',
|
||||||
'tests/tilemap.cpp',
|
'tests/tilemap.cpp',
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "components.hpp"
|
#include "components.hpp"
|
||||||
#include "guecs.hpp"
|
#include "guecs.hpp"
|
||||||
#include "rand.hpp"
|
#include "rand.hpp"
|
||||||
|
#include "animation.hpp"
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
using namespace guecs;
|
using namespace guecs;
|
||||||
|
@ -13,12 +14,12 @@ namespace gui {
|
||||||
$gui.position(STATUS_UI_X, STATUS_UI_Y, STATUS_UI_WIDTH, STATUS_UI_HEIGHT);
|
$gui.position(STATUS_UI_X, STATUS_UI_Y, STATUS_UI_WIDTH, STATUS_UI_HEIGHT);
|
||||||
$gui.layout(
|
$gui.layout(
|
||||||
"[_]"
|
"[_]"
|
||||||
"[inv_slot5 | inv_slot6 | inv_slot7| inv_slot8]"
|
|
||||||
"[inv_slot9 | inv_slot10 | inv_slot11| inv_slot12]"
|
"[inv_slot9 | inv_slot10 | inv_slot11| inv_slot12]"
|
||||||
"[inv_slot13 | inv_slot14 | inv_slot15| inv_slot16]"
|
"[inv_slot13 | inv_slot14 | inv_slot15| inv_slot16]"
|
||||||
"[inv_slot17 | inv_slot18 | inv_slot19| inv_slot20]"
|
"[inv_slot17 | inv_slot18 | inv_slot19| inv_slot20]"
|
||||||
"[inv_slot21 | inv_slot22 | inv_slot23| inv_slot24]"
|
"[inv_slot21 | inv_slot22 | inv_slot23| inv_slot24]"
|
||||||
"[*%(100,500)circle_area]"
|
"[*%(100,600)circle_area]"
|
||||||
|
"[_]"
|
||||||
"[_]"
|
"[_]"
|
||||||
"[_]"
|
"[_]"
|
||||||
"[_]"
|
"[_]"
|
||||||
|
@ -27,17 +28,17 @@ namespace gui {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RitualUI::init() {
|
void RitualUI::init() {
|
||||||
// $gui.world().set_the<Background>({$gui.$parser});
|
|
||||||
|
|
||||||
for(auto& [name, cell] : $gui.cells()) {
|
for(auto& [name, cell] : $gui.cells()) {
|
||||||
auto button = $gui.entity(name);
|
auto button = $gui.entity(name);
|
||||||
|
|
||||||
if(name == "circle_area") {
|
if(name == "circle_area") {
|
||||||
// $gui.set<Rectangle>(button, {});
|
// $gui.set<Rectangle>(button, {});
|
||||||
|
$gui.set<Sprite>(button, {"the_ritual_circle"});
|
||||||
$gui.set<Clickable>(button, {
|
$gui.set<Clickable>(button, {
|
||||||
[this](auto, auto){ dbc::log("circle clicked"); }
|
[this](auto, auto){ dbc::log("circle clicked"); }
|
||||||
});
|
});
|
||||||
} else if(name.starts_with("inv_slot")) {
|
} else if(name.starts_with("inv_slot")) {
|
||||||
|
$gui.set<Sprite>(button, {"the_ritual_circle"});
|
||||||
// $gui.set<Rectangle>(button, {});
|
// $gui.set<Rectangle>(button, {});
|
||||||
$gui.set<Clickable>(button, {
|
$gui.set<Clickable>(button, {
|
||||||
[this, name](auto, auto){ dbc::log(fmt::format("inv_slot {}", name)); }
|
[this, name](auto, auto){ dbc::log(fmt::format("inv_slot {}", name)); }
|
||||||
|
@ -53,13 +54,7 @@ namespace gui {
|
||||||
$ritual_ui.sprite->setPosition({0,0});
|
$ritual_ui.sprite->setPosition({0,0});
|
||||||
$ritual_ui.sprite->setTextureRect($ritual_closed_rect);
|
$ritual_ui.sprite->setTextureRect($ritual_closed_rect);
|
||||||
$ritual_state = RitualUIState::CLOSED;
|
$ritual_state = RitualUIState::CLOSED;
|
||||||
$ritual_anim.simple = false;
|
$ritual_anim = animation::load("ritual_blanket");
|
||||||
$ritual_anim.looped = false;
|
|
||||||
$ritual_anim.easing = ease::NONE;
|
|
||||||
$ritual_anim.stationary = true;
|
|
||||||
$ritual_anim.texture_width = 380;
|
|
||||||
$ritual_anim.frames = 3;
|
|
||||||
$ritual_anim.speed = 0.2f;
|
|
||||||
|
|
||||||
$gui.init();
|
$gui.init();
|
||||||
}
|
}
|
||||||
|
@ -75,20 +70,11 @@ namespace gui {
|
||||||
void RitualUI::toggle() {
|
void RitualUI::toggle() {
|
||||||
using enum RitualUIState;
|
using enum RitualUIState;
|
||||||
|
|
||||||
switch($ritual_state) {
|
if($ritual_state == OPEN) {
|
||||||
case OPEN:
|
$ritual_state = CLOSING;
|
||||||
$ritual_state = CLOSING;
|
} else if($ritual_state == CLOSED) {
|
||||||
break;
|
$ritual_state = OPENING;
|
||||||
case CLOSED:
|
$ritual_anim.play();
|
||||||
$ritual_state = OPENING;
|
|
||||||
$ritual_anim.play();
|
|
||||||
break;
|
|
||||||
case OPENING: // ignored
|
|
||||||
break;
|
|
||||||
case CLOSING: // ignored
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dbc::sentinel("INVALID RitualUIState");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,40 +84,16 @@ namespace gui {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RitualUI::render(sf::RenderWindow &window) {
|
void RitualUI::render(sf::RenderWindow &window) {
|
||||||
sf::IntRect rect;
|
|
||||||
sf::Vector2f scale{1.0, 1.0};
|
|
||||||
sf::Vector2f pos{0, 0};
|
|
||||||
|
|
||||||
using enum RitualUIState;
|
using enum RitualUIState;
|
||||||
switch($ritual_state) {
|
|
||||||
case OPEN: {
|
|
||||||
rect = $ritual_open_rect;
|
|
||||||
} break;
|
|
||||||
case CLOSED: {
|
|
||||||
rect = $ritual_closed_rect;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case OPENING: {
|
|
||||||
if($ritual_anim.playing) {
|
|
||||||
rect = $ritual_closed_rect;
|
|
||||||
$ritual_anim.step(scale, pos, rect);
|
|
||||||
} else {
|
|
||||||
$ritual_state = OPEN;
|
|
||||||
rect = $ritual_open_rect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CLOSING: {
|
|
||||||
rect = $ritual_closed_rect;
|
|
||||||
$ritual_state = CLOSED;
|
|
||||||
} break;
|
|
||||||
default:
|
|
||||||
dbc::sentinel("INVALID RitualUIState");
|
|
||||||
}
|
|
||||||
|
|
||||||
$ritual_ui.sprite->setTextureRect(rect);
|
if($ritual_state == OPENING) {
|
||||||
$ritual_ui.sprite->setPosition(pos);
|
if(!animation::apply($ritual_anim, $ritual_ui)) {
|
||||||
$ritual_ui.sprite->setScale(scale);
|
$ritual_state = OPEN;
|
||||||
|
}
|
||||||
|
} else if($ritual_state == CLOSING) {
|
||||||
|
$ritual_ui.sprite->setTextureRect($ritual_closed_rect);
|
||||||
|
$ritual_state = CLOSED;
|
||||||
|
}
|
||||||
|
|
||||||
window.draw(*$ritual_ui.sprite);
|
window.draw(*$ritual_ui.sprite);
|
||||||
if($ritual_state == OPEN) $gui.render(window);
|
if($ritual_state == OPEN) $gui.render(window);
|
||||||
|
|
53
tests/animation.cpp
Normal file
53
tests/animation.cpp
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include "animation.hpp"
|
||||||
|
#include "dinkyecs.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace components;
|
||||||
|
using namespace textures;
|
||||||
|
|
||||||
|
TEST_CASE("animation easing tests", "[animation]") {
|
||||||
|
Animation anim;
|
||||||
|
|
||||||
|
anim.easing = ease::NONE;
|
||||||
|
float res = anim.twitching();
|
||||||
|
REQUIRE(res == 0.0);
|
||||||
|
|
||||||
|
anim.easing = ease::SINE;
|
||||||
|
anim.subframe = 1.0f;
|
||||||
|
res = anim.twitching();
|
||||||
|
REQUIRE(!std::isnan(res));
|
||||||
|
|
||||||
|
anim.easing = ease::OUT_CIRC;
|
||||||
|
res = anim.twitching();
|
||||||
|
REQUIRE(!std::isnan(res));
|
||||||
|
|
||||||
|
anim.easing = ease::OUT_BOUNCE;
|
||||||
|
res = anim.twitching();
|
||||||
|
REQUIRE(!std::isnan(res));
|
||||||
|
|
||||||
|
anim.easing = ease::IN_OUT_BACK;
|
||||||
|
res = anim.twitching();
|
||||||
|
REQUIRE(!std::isnan(res));
|
||||||
|
|
||||||
|
anim.easing = ease::FUCKFACE;
|
||||||
|
bool throws = false;
|
||||||
|
try { anim.twitching(); } catch(...) { throws = true; }
|
||||||
|
REQUIRE(throws);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("animation utility API", "[animation]") {
|
||||||
|
textures::init();
|
||||||
|
animation::init();
|
||||||
|
|
||||||
|
auto blanket = textures::get("ritual_crafting_area");
|
||||||
|
auto anim = animation::load("ritual_blanket");
|
||||||
|
|
||||||
|
anim.play();
|
||||||
|
|
||||||
|
while(animation::apply(anim, blanket)) {
|
||||||
|
fmt::println("animation: {}", anim.subframe);
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,34 +60,3 @@ TEST_CASE("make sure json_mods works", "[components]") {
|
||||||
auto boss2 = world.get<BossFight>(devils_fingers);
|
auto boss2 = world.get<BossFight>(devils_fingers);
|
||||||
REQUIRE(boss2.stage != std::nullopt);
|
REQUIRE(boss2.stage != std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("animation component special cases", "[components]") {
|
|
||||||
Animation anim;
|
|
||||||
|
|
||||||
anim.easing = ease::NONE;
|
|
||||||
float res = anim.twitching();
|
|
||||||
REQUIRE(res == 0.0);
|
|
||||||
|
|
||||||
anim.easing = ease::SINE;
|
|
||||||
anim.subframe = 1.0f;
|
|
||||||
res = anim.twitching();
|
|
||||||
REQUIRE(!std::isnan(res));
|
|
||||||
|
|
||||||
anim.easing = ease::OUT_CIRC;
|
|
||||||
res = anim.twitching();
|
|
||||||
REQUIRE(!std::isnan(res));
|
|
||||||
|
|
||||||
anim.easing = ease::OUT_BOUNCE;
|
|
||||||
res = anim.twitching();
|
|
||||||
REQUIRE(!std::isnan(res));
|
|
||||||
|
|
||||||
anim.easing = ease::IN_OUT_BACK;
|
|
||||||
res = anim.twitching();
|
|
||||||
REQUIRE(!std::isnan(res));
|
|
||||||
|
|
||||||
anim.easing = ease::FUCKFACE;
|
|
||||||
bool throws = false;
|
|
||||||
try { anim.twitching(); } catch(...) { throws = true; }
|
|
||||||
REQUIRE(throws);
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue