parent
0a40135f5d
commit
1aa6674e42
@ -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); |
||||
} |
||||
} |
@ -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); |
||||
} |
@ -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 |
||||
} |
||||
} |
After Width: | Height: | Size: 44 KiB |
@ -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); |
||||
} |
||||
} |
Loading…
Reference in new issue