Refactored the animation so I can normalize it to one api. Next is to create the concept of a temporary entity that represents a transitive effect.

This commit is contained in:
Zed A. Shaw 2025-09-11 14:18:52 -04:00
parent 0afaa20c1d
commit 8384b11993
10 changed files with 123 additions and 23 deletions

View file

@ -110,8 +110,33 @@ namespace animation {
}
}
Animation load(std::string name) {
bool has(const std::string& name) {
return MGR.animations.contains(name);
}
Animation load(const std::string& name) {
dbc::check(initialized, "You forgot to initialize animation.");
return MGR.animations.at(name);
}
void configure(DinkyECS::World& world, DinkyECS::Entity entity) {
auto sprite = world.get_if<Sprite>(entity);
if(sprite != nullptr && animation::has(sprite->name)) {
world.set<Animation>(entity, animation::load(sprite->name));
}
}
void step_animation(DinkyECS::World& world, DinkyECS::Entity entity, sf::Vector2f& scale_out, sf::Vector2f& pos_out, sf::IntRect& rect_out) {
if(auto animation = world.get_if<components::Animation>(entity)) {
if(animation->playing) animation->step(scale_out, pos_out, rect_out);
}
}
void animate_entity(DinkyECS::World &world, DinkyECS::Entity entity) {
if(world.has<Animation>(entity)) {
auto& animation = world.get<Animation>(entity);
animation.play();
}
}
}

View file

@ -2,6 +2,9 @@
#include "components.hpp"
#include "textures.hpp"
#include "easings.hpp"
#include "dinkyecs.hpp"
#include <SFML/Graphics/Rect.hpp>
namespace animation {
struct AnimationManager {
@ -13,5 +16,9 @@ namespace animation {
void center(sf::Sprite& target, sf::Vector2f pos);
void init();
components::Animation load(std::string name);
components::Animation load(const std::string& name);
bool has(const std::string& name);
void configure(DinkyECS::World& world, DinkyECS::Entity entity);
void step_animation(DinkyECS::World& world, DinkyECS::Entity entity, sf::Vector2f& scale_out, sf::Vector2f& pos_out, sf::IntRect& rect_out);
void animate_entity(DinkyECS::World &world, DinkyECS::Entity entity);
}

View file

@ -1,4 +1,14 @@
{
"burning_effect": {
"_type": "Animation",
"easing": 0,
"ease_rate": 0.5,
"scale": 1.0,
"simple": false,
"frames": 5,
"speed": 0.1,
"stationary": false
},
"ritual_blanket": {
"_type": "Animation",
"easing": 0,
@ -8,5 +18,55 @@
"frames": 3,
"speed": 0.2,
"stationary": true
},
"gold_savior": {
"_type": "Animation",
"easing": 1,
"ease_rate": 0.2,
"scale": 0.1,
"simple": true,
"frames": 10,
"speed": 0.3,
"stationary": false
},
"armored_knight" : {
"_type": "Animation",
"easing": 1,
"ease_rate": 0.2,
"scale": 0.1,
"simple": true,
"frames": 10,
"speed": 0.3,
"stationary": false
},
"axe_ranger": {
"_type": "Animation",
"easing": 3,
"ease_rate": 0.5,
"scale": 0.1,
"simple": true,
"frames": 1,
"speed": 0.6,
"stationary": false
},
"rat_with_sword": {
"_type": "Animation",
"easing": 3,
"ease_rate": 0.5,
"scale": 0.1,
"simple": true,
"frames": 1,
"speed": 1.0,
"stationary": false
},
"hairy_spider": {
"_type": "Animation",
"easing": 2,
"ease_rate": 0.5,
"scale": 0.1,
"simple": true,
"frames": 10,
"speed": 1.0,
"stationary": false
}
}

View file

@ -30,6 +30,11 @@
"hp_status_00": "assets/sounds/hp_status_00.ogg"
},
"sprites": {
"burning_effect":
{"path": "assets/sprites/burning_effect.png",
"frame_width": 256,
"frame_height": 256
},
"gold_savior":
{"path": "assets/sprites/gold_savior.png",
"frame_width": 256,

View file

@ -12,6 +12,21 @@
{"_type": "LightSource", "strength": 35, "radius": 2.0}
]
},
"FLAME_BLOB": {
"components": [
{"_type": "Tile", "display": 10899,
"foreground": "enemies/fg:gold_savior",
"background": "color:transparent"
},
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 1, "dead": false},
{"_type": "Collision", "has": true},
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
{"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
{"_type": "Personality", "hearing_distance": 5, "tough": true},
{"_type": "Sprite", "name": "burning_effect", "width": 256, "height": 256, "width": 256, "height": 256, "scale": 1.0},
{"_type": "Sound", "attack": "fireball_01", "death": "fireball_01"}
]
},
"GOLD_SAVIOR": {
"components": [
{"_type": "Tile", "display": 42586,
@ -23,7 +38,6 @@
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
{"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
{"_type": "Personality", "hearing_distance": 5, "tough": true},
{"_type": "Animation", "easing": 1, "ease_rate": 0.2, "scale": 0.1, "simple": true, "frames": 10, "speed": 0.3, "stationary": false},
{"_type": "Sprite", "name": "gold_savior", "width": 256, "height": 256, "width": 256, "height": 256, "scale": 1.0},
{"_type": "Sound", "attack": "Sword_Hit_2", "death": "Humanoid_Death_1"}
]
@ -39,7 +53,6 @@
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
{"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
{"_type": "Personality", "hearing_distance": 5, "tough": true},
{"_type": "Animation", "easing": 1, "ease_rate": 0.2, "scale": 0.1, "simple": true, "frames": 10, "speed": 0.3, "stationary": false},
{"_type": "Sprite", "name": "armored_knight", "width": 256, "height": 256, "width": 256, "height": 256, "scale": 1.0},
{"_type": "Sound", "attack": "Sword_Hit_2", "death": "Humanoid_Death_1"}
]
@ -56,7 +69,6 @@
{"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
{"_type": "Personality", "hearing_distance": 5, "tough": true},
{"_type": "Sprite", "name": "axe_ranger", "width": 256, "height": 256, "scale": 1.0},
{"_type": "Animation", "easing": 3, "ease_rate": 0.5, "scale": 0.1, "simple": true, "frames": 1, "speed": 0.6, "stationary": false},
{"_type": "Sound", "attack": "Sword_Hit_2", "death": "Ranger_1"}
]
},
@ -71,7 +83,6 @@
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
{"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
{"_type": "Personality", "hearing_distance": 5, "tough": true},
{"_type": "Animation", "easing": 3, "ease_rate": 0.5, "scale": 0.1, "simple": true, "frames": 1, "speed": 1.0, "stationary": false},
{"_type": "Sprite", "name": "rat_with_sword", "width": 256, "height": 256, "scale": 1.0},
{"_type": "Sound", "attack": "Small_Rat", "death": "Creature_Death_1"}
]
@ -87,7 +98,6 @@
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
{"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
{"_type": "Personality", "hearing_distance": 5, "tough": true},
{"_type": "Animation", "easing": 2, "ease_rate": 0.5, "scale": 0.1, "simple": true, "frames": 10, "speed": 1.0, "stationary": false},
{"_type": "Sprite", "name": "hairy_spider", "width": 256, "height": 256, "scale": 1.0},
{"_type": "Sound", "attack": "Spider_1", "death": "Spider_2"}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View file

@ -11,6 +11,7 @@
#include "textures.hpp"
#include "systems.hpp"
#include "shaders.hpp"
#include "animation.hpp"
using namespace fmt;
using std::make_unique, std::shared_ptr;
@ -104,6 +105,7 @@ void Raycaster::apply_sprite_effect(shared_ptr<sf::Shader> effect, float width,
void Raycaster::sprite_casting(sf::RenderTarget &target) {
auto& lights = $level.lights->lighting();
auto world = $level.world;
$level.collision->distance_sorted($sprite_order, {(size_t)$pos_x, (size_t)$pos_y}, RENDER_DISTANCE);
@ -118,7 +120,7 @@ void Raycaster::sprite_casting(sf::RenderTarget &target) {
int half_height = texture_height / 2;
auto& sf_sprite = sprite_texture.sprite;
auto sprite_pos = $level.world->get<components::Position>(rec.entity);
auto sprite_pos = world->get<components::Position>(rec.entity);
double sprite_x = double(sprite_pos.location.x) - rec.wiggle - $pos_x + 0.5;
double sprite_y = double(sprite_pos.location.y) - rec.wiggle - $pos_y + 0.5;
@ -187,10 +189,7 @@ void Raycaster::sprite_casting(sf::RenderTarget &target) {
sf::Vector2f position{x + origin.x * scale.x, y + origin.y * scale.y};
sf::IntRect in_texture{ {tex_x, tex_y}, {tex_render_width, texture_height}};
if($level.world->has<components::Animation>(rec.entity)) {
auto& animation = $level.world->get<components::Animation>(rec.entity);
if(animation.playing) animation.step(scale, position, in_texture);
}
animation::step_animation(*world, rec.entity, scale, position, in_texture);
sf_sprite->setOrigin(origin);
sf_sprite->setScale(scale);

View file

@ -28,5 +28,6 @@ function Build-Images {
Build-Images -Source "Textures" -pixel_count 12
Build-Images -Source "Sprites" -pixel_count 6
Build-Images -Source "Items" -pixel_count 2
Build-Images -Source "Animations" -pixel_count 6
cp -recurse -force C:\Users\lcthw\Pictures\Games\Renders\Raycaster\UI assets\ui

View file

@ -18,6 +18,7 @@
#include "inventory.hpp"
#include "game_level.hpp"
#include "gui/fsm_events.hpp"
#include "animation.hpp"
using std::string;
using namespace fmt;
@ -232,16 +233,6 @@ void System::death() {
}
}
inline void animate_entity(World &world, Entity entity) {
if(world.has<Animation>(entity)) {
auto& animation = world.get<Animation>(entity);
animation.play();
}
if(auto snd = world.get_if<Sound>(entity)) {
sound::play(snd->attack);
}
}
void System::combat(int attack_id) {
auto& level = GameDB::current_level();
@ -292,7 +283,7 @@ void System::combat(int attack_id) {
if(enemy_action == combat::BattleAction::ATTACK) {
result.enemy_did = enemy.combat.attack(player_combat);
animate_entity(world, enemy.entity);
animation::animate_entity(world, enemy.entity);
}
world.send<Events::GUI>(Events::GUI::COMBAT, enemy.entity, result);

View file

@ -8,6 +8,7 @@
#include "textures.hpp"
#include "inventory.hpp"
#include "systems.hpp"
#include "animation.hpp"
using namespace fmt;
using namespace components;
@ -100,6 +101,7 @@ DinkyECS::Entity WorldBuilder::configure_entity_in_map(DinkyECS::World &world, j
}
System::set_position(world, $collision, item, {pos.x, pos.y});
animation::configure(world, item);
return item;
}