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