Animations are refactored to let me spawn in an 'attack animation' but I think the data model is wrong. Rather than spawning in an animation every time I can probably just make one, reposition it, then tell it to play. I'll have to try it.
This commit is contained in:
parent
8384b11993
commit
ad3e580495
15 changed files with 109 additions and 56 deletions
|
|
@ -70,18 +70,17 @@ namespace animation {
|
||||||
static AnimationManager MGR;
|
static AnimationManager MGR;
|
||||||
static bool initialized = false;
|
static bool initialized = false;
|
||||||
|
|
||||||
bool apply(Animation& anim, SpriteTexture& target) {
|
bool apply(Animation& anim, sf::Sprite& sprite) {
|
||||||
auto size = target.texture->getSize();
|
sf::IntRect rect{{0,0}, {anim.frame_width, anim.frame_height}};
|
||||||
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 scale{1.0, 1.0};
|
||||||
sf::Vector2f pos{0, 0};
|
sf::Vector2f pos{0, 0};
|
||||||
|
|
||||||
anim.step(scale, pos, rect);
|
anim.step(scale, pos, rect);
|
||||||
|
|
||||||
target.sprite->setTextureRect(rect);
|
sprite.setTextureRect(rect);
|
||||||
target.sprite->setPosition(pos);
|
sprite.setPosition(pos);
|
||||||
target.sprite->setScale(scale);
|
sprite.setScale(scale);
|
||||||
|
|
||||||
|
|
||||||
return anim.playing;
|
return anim.playing;
|
||||||
}
|
}
|
||||||
|
|
@ -99,11 +98,27 @@ namespace animation {
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
if(!initialized) {
|
if(!initialized) {
|
||||||
Config config("assets/animations.json");
|
Config animations("assets/animations.json");
|
||||||
|
Config config("assets/config.json");
|
||||||
|
auto& sprites = config["sprites"];
|
||||||
|
|
||||||
for(auto& [name, data] : config.json().items()) {
|
for(auto& [name, data] : animations.json().items()) {
|
||||||
auto anim = components::convert<Animation>(data);
|
try {
|
||||||
MGR.animations.insert_or_assign(name, anim);
|
auto anim = components::convert<Animation>(data);
|
||||||
|
auto& sprite_config = sprites[name];
|
||||||
|
|
||||||
|
anim.frame_width = sprite_config["frame_width"];
|
||||||
|
anim.frame_height = sprite_config["frame_height"];
|
||||||
|
|
||||||
|
dbc::check(anim.frame_width > 0 && anim.frame_height > 0,
|
||||||
|
fmt::format("invalid frame width/height for animation: {}",
|
||||||
|
name));
|
||||||
|
|
||||||
|
MGR.animations.insert_or_assign(name, anim);
|
||||||
|
} catch(...) {
|
||||||
|
dbc::log(fmt::format("error in sprite config: {}", name));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@
|
||||||
#include "easings.hpp"
|
#include "easings.hpp"
|
||||||
#include "dinkyecs.hpp"
|
#include "dinkyecs.hpp"
|
||||||
#include <SFML/Graphics/Rect.hpp>
|
#include <SFML/Graphics/Rect.hpp>
|
||||||
|
#include <SFML/Graphics/Sprite.hpp>
|
||||||
|
|
||||||
namespace animation {
|
namespace animation {
|
||||||
struct AnimationManager {
|
struct AnimationManager {
|
||||||
std::unordered_map<std::string, components::Animation> animations;
|
std::unordered_map<std::string, components::Animation> animations;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool apply(components::Animation& anim, textures::SpriteTexture& target);
|
bool apply(components::Animation& anim, sf::Sprite& target);
|
||||||
void rotate(sf::Sprite& target, float degrees);
|
void rotate(sf::Sprite& target, float degrees);
|
||||||
void center(sf::Sprite& target, sf::Vector2f pos);
|
void center(sf::Sprite& target, sf::Vector2f pos);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"burning_effect": {
|
"burning_animation": {
|
||||||
"_type": "Animation",
|
"_type": "Animation",
|
||||||
"easing": 0,
|
"easing": 0,
|
||||||
"ease_rate": 0.5,
|
"ease_rate": 0.5,
|
||||||
|
|
@ -9,7 +9,17 @@
|
||||||
"speed": 0.1,
|
"speed": 0.1,
|
||||||
"stationary": false
|
"stationary": false
|
||||||
},
|
},
|
||||||
"ritual_blanket": {
|
"lightning_animation": {
|
||||||
|
"_type": "Animation",
|
||||||
|
"easing": 0,
|
||||||
|
"ease_rate": 0.5,
|
||||||
|
"scale": 1.0,
|
||||||
|
"simple": false,
|
||||||
|
"frames": 5,
|
||||||
|
"speed": 0.5,
|
||||||
|
"stationary": false
|
||||||
|
},
|
||||||
|
"ritual_crafting_area": {
|
||||||
"_type": "Animation",
|
"_type": "Animation",
|
||||||
"easing": 0,
|
"easing": 0,
|
||||||
"ease_rate": 0.5,
|
"ease_rate": 0.5,
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,13 @@
|
||||||
"hp_status_00": "assets/sounds/hp_status_00.ogg"
|
"hp_status_00": "assets/sounds/hp_status_00.ogg"
|
||||||
},
|
},
|
||||||
"sprites": {
|
"sprites": {
|
||||||
"burning_effect":
|
"burning_animation":
|
||||||
{"path": "assets/sprites/burning_effect.png",
|
{"path": "assets/sprites/burning_animation.png",
|
||||||
|
"frame_width": 256,
|
||||||
|
"frame_height": 256
|
||||||
|
},
|
||||||
|
"lightning_animation":
|
||||||
|
{"path": "assets/sprites/lightning_animation.png",
|
||||||
"frame_width": 256,
|
"frame_width": 256,
|
||||||
"frame_height": 256
|
"frame_height": 256
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -12,21 +12,6 @@
|
||||||
{"_type": "LightSource", "strength": 35, "radius": 2.0}
|
{"_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": {
|
"GOLD_SAVIOR": {
|
||||||
"components": [
|
"components": [
|
||||||
{"_type": "Tile", "display": 42586,
|
{"_type": "Tile", "display": 42586,
|
||||||
|
|
|
||||||
BIN
assets/sprites/burning_animation.png
Normal file
BIN
assets/sprites/burning_animation.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
BIN
assets/sprites/lightning_animation.png
Normal file
BIN
assets/sprites/lightning_animation.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
|
|
@ -26,6 +26,10 @@ namespace components {
|
||||||
std::shared_ptr<sf::Shader> effect;
|
std::shared_ptr<sf::Shader> effect;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Temporary {
|
||||||
|
bool is = true;
|
||||||
|
};
|
||||||
|
|
||||||
struct Collision {
|
struct Collision {
|
||||||
bool has = true;
|
bool has = true;
|
||||||
};
|
};
|
||||||
|
|
@ -124,7 +128,8 @@ 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 frame_width = TEXTURE_WIDTH;
|
int frame_width = -1;
|
||||||
|
int frame_height = -1;
|
||||||
|
|
||||||
void play();
|
void play();
|
||||||
float twitching();
|
float twitching();
|
||||||
|
|
|
||||||
|
|
@ -412,6 +412,7 @@ namespace gui {
|
||||||
draw_gui();
|
draw_gui();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System::clear_attack();
|
||||||
$window.display();
|
$window.display();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ namespace gui {
|
||||||
$ritual_ui = textures::get_sprite("ritual_crafting_area");
|
$ritual_ui = textures::get_sprite("ritual_crafting_area");
|
||||||
$ritual_ui.sprite->setPosition($gui.get_position());
|
$ritual_ui.sprite->setPosition($gui.get_position());
|
||||||
$ritual_ui.sprite->setTextureRect($ritual_closed_rect);
|
$ritual_ui.sprite->setTextureRect($ritual_closed_rect);
|
||||||
$ritual_anim = animation::load("ritual_blanket");
|
$ritual_anim = animation::load("ritual_crafting_area");
|
||||||
|
|
||||||
auto open_close_toggle = $gui.entity("ritual_ui");
|
auto open_close_toggle = $gui.entity("ritual_ui");
|
||||||
$gui.set<Clickable>(open_close_toggle, {
|
$gui.set<Clickable>(open_close_toggle, {
|
||||||
|
|
@ -84,7 +84,6 @@ namespace gui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UI::CLOSED(Event ev) {
|
void UI::CLOSED(Event ev) {
|
||||||
if(ev == Event::TOGGLE) {
|
if(ev == Event::TOGGLE) {
|
||||||
$ritual_anim.play();
|
$ritual_anim.play();
|
||||||
|
|
@ -95,7 +94,7 @@ namespace gui {
|
||||||
|
|
||||||
void UI::OPENING(Event ev) {
|
void UI::OPENING(Event ev) {
|
||||||
if(ev == Event::TICK) {
|
if(ev == Event::TICK) {
|
||||||
if(!animation::apply($ritual_anim, $ritual_ui)) {
|
if(!animation::apply($ritual_anim, *$ritual_ui.sprite)) {
|
||||||
state(State::OPENED);
|
state(State::OPENED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,6 @@ void Raycaster::sprite_casting(sf::RenderTarget &target) {
|
||||||
auto world = $level.world;
|
auto world = $level.world;
|
||||||
$level.collision->distance_sorted($sprite_order, {(size_t)$pos_x, (size_t)$pos_y}, RENDER_DISTANCE);
|
$level.collision->distance_sorted($sprite_order, {(size_t)$pos_x, (size_t)$pos_y}, RENDER_DISTANCE);
|
||||||
|
|
||||||
|
|
||||||
// after sorting the sprites, do the projection
|
// after sorting the sprites, do the projection
|
||||||
for(auto& rec : $sprite_order) {
|
for(auto& rec : $sprite_order) {
|
||||||
if(!$sprites.contains(rec.entity)) continue;
|
if(!$sprites.contains(rec.entity)) continue;
|
||||||
|
|
@ -184,6 +183,10 @@ void Raycaster::sprite_casting(sf::RenderTarget &target) {
|
||||||
int d = y * texture_height - $height * half_height + sprite_height * half_height;
|
int d = y * texture_height - $height * half_height + sprite_height * half_height;
|
||||||
int tex_y = ((d * texture_height) / sprite_height) / texture_height;
|
int tex_y = ((d * texture_height) / sprite_height) / texture_height;
|
||||||
|
|
||||||
|
// BUG: this data could be put into the world
|
||||||
|
// as frame data, then just have a system that
|
||||||
|
// constantly applies this to any sprite that
|
||||||
|
// has an animation and is visible
|
||||||
sf::Vector2f origin{texture_width / 2.0f, texture_height / 2.0f};
|
sf::Vector2f origin{texture_width / 2.0f, texture_height / 2.0f};
|
||||||
sf::Vector2f scale{sprite_scale_w, sprite_scale_h};
|
sf::Vector2f scale{sprite_scale_w, sprite_scale_h};
|
||||||
sf::Vector2f position{x + origin.x * scale.x, y + origin.y * scale.y};
|
sf::Vector2f position{x + origin.x * scale.x, y + origin.y * scale.y};
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,9 @@ function Build-Images {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Build-Images -Source "Textures" -pixel_count 12
|
# Build-Images -Source "Textures" -pixel_count 12
|
||||||
Build-Images -Source "Sprites" -pixel_count 6
|
# Build-Images -Source "Sprites" -pixel_count 6
|
||||||
Build-Images -Source "Items" -pixel_count 2
|
# Build-Images -Source "Items" -pixel_count 2
|
||||||
Build-Images -Source "Animations" -pixel_count 6
|
Build-Images -Source "Animations" -pixel_count 6
|
||||||
|
|
||||||
cp -recurse -force C:\Users\lcthw\Pictures\Games\Renders\Raycaster\UI assets\ui
|
cp -recurse -force C:\Users\lcthw\Pictures\Games\Renders\Raycaster\UI assets\ui
|
||||||
|
|
|
||||||
55
systems.cpp
55
systems.cpp
|
|
@ -27,8 +27,6 @@ using namespace DinkyECS;
|
||||||
using lighting::LightSource;
|
using lighting::LightSource;
|
||||||
|
|
||||||
void System::set_position(World& world, SpatialMap& collision, Entity entity, Position pos) {
|
void System::set_position(World& world, SpatialMap& collision, Entity entity, Position pos) {
|
||||||
dbc::check(world.has<Tile>(entity), "entity doesn't have tile");
|
|
||||||
|
|
||||||
world.set<Position>(entity, pos);
|
world.set<Position>(entity, pos);
|
||||||
bool has_collision = world.has<Collision>(entity);
|
bool has_collision = world.has<Collision>(entity);
|
||||||
collision.insert(pos.location, entity, has_collision);
|
collision.insert(pos.location, entity, has_collision);
|
||||||
|
|
@ -238,11 +236,6 @@ void System::combat(int attack_id) {
|
||||||
auto& level = GameDB::current_level();
|
auto& level = GameDB::current_level();
|
||||||
auto& collider = *level.collision;
|
auto& collider = *level.collision;
|
||||||
auto& world = *level.world;
|
auto& world = *level.world;
|
||||||
auto& the_belt = world.get_the<ritual::Belt>();
|
|
||||||
|
|
||||||
if(!the_belt.has(attack_id)) return;
|
|
||||||
|
|
||||||
auto& ritual = the_belt.get(attack_id);
|
|
||||||
const auto& player_pos = GameDB::player_position();
|
const auto& player_pos = GameDB::player_position();
|
||||||
auto& player_combat = world.get<Combat>(level.player);
|
auto& player_combat = world.get<Combat>(level.player);
|
||||||
|
|
||||||
|
|
@ -272,13 +265,7 @@ void System::combat(int attack_id) {
|
||||||
};
|
};
|
||||||
|
|
||||||
if(result.player_did > 0) {
|
if(result.player_did > 0) {
|
||||||
using enum ritual::Element;
|
spawn_attack(world, attack_id);
|
||||||
|
|
||||||
if(ritual.element == FIRE || ritual.element == LIGHTNING) {
|
|
||||||
auto effect = shaders::get(
|
|
||||||
ritual.element == FIRE ? "flame" : "lightning");
|
|
||||||
world.set<SpriteEffect>(enemy.entity, {100, effect});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(enemy_action == combat::BattleAction::ATTACK) {
|
if(enemy_action == combat::BattleAction::ATTACK) {
|
||||||
|
|
@ -656,3 +643,43 @@ gui::Event System::shortest_rotate(Point player_at, Point aiming_at, Point targe
|
||||||
|
|
||||||
return normalized < 180.0 ? gui::Event::ROTATE_LEFT : gui::Event::ROTATE_RIGHT;
|
return normalized < 180.0 ? gui::Event::ROTATE_LEFT : gui::Event::ROTATE_RIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void System::clear_attack() {
|
||||||
|
auto world = GameDB::current_world();
|
||||||
|
std::vector<Entity> dead_anim;
|
||||||
|
|
||||||
|
world->query<Animation, Temporary>([&](auto ent, auto& anim, auto&) {
|
||||||
|
if(!anim.playing) dead_anim.push_back(ent);
|
||||||
|
});
|
||||||
|
|
||||||
|
for(auto ent : dead_anim) {
|
||||||
|
world->remove<Sprite>(ent);
|
||||||
|
world->remove<Animation>(ent);
|
||||||
|
world->remove<SpriteEffect>(ent);
|
||||||
|
remove_from_world(ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::spawn_attack(World& world, int attack_id) {
|
||||||
|
using enum ritual::Element; // for FIRE vs LIGHTNING
|
||||||
|
auto& the_belt = world.get_the<ritual::Belt>();
|
||||||
|
|
||||||
|
dbc::check(the_belt.has(attack_id), "STOP passing invalid attack IDs to the system.");
|
||||||
|
|
||||||
|
auto& ritual = the_belt.get(attack_id);
|
||||||
|
|
||||||
|
auto effect = ritual.element == FIRE ? "burning_animation" : "lightning_animation";
|
||||||
|
|
||||||
|
auto effect_id = world.entity();
|
||||||
|
world.set<Sprite>(effect_id, {effect, 256, 256, 1.0});
|
||||||
|
world.set<Temporary>(effect_id, {true});
|
||||||
|
|
||||||
|
auto shader = shaders::get(ritual.element == FIRE ? "flame" : "lightning");
|
||||||
|
world.set<SpriteEffect>(effect_id, {100, shader});
|
||||||
|
|
||||||
|
auto anim = animation::load(effect);
|
||||||
|
anim.play();
|
||||||
|
world.set<Animation>(effect_id, anim);
|
||||||
|
|
||||||
|
drop_item(effect_id);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,4 +71,7 @@ namespace System {
|
||||||
|
|
||||||
paths.compute_paths(walls);
|
paths.compute_paths(walls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clear_attack();
|
||||||
|
void spawn_attack(World& world, int attack_id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,11 @@ TEST_CASE("animation utility API", "[animation]") {
|
||||||
animation::init();
|
animation::init();
|
||||||
|
|
||||||
auto blanket = textures::get_sprite("ritual_crafting_area");
|
auto blanket = textures::get_sprite("ritual_crafting_area");
|
||||||
auto anim = animation::load("ritual_blanket");
|
auto anim = animation::load("ritual_crafting_area");
|
||||||
|
|
||||||
anim.play();
|
anim.play();
|
||||||
|
|
||||||
while(animation::apply(anim, blanket)) {
|
while(animation::apply(anim, *blanket.sprite)) {
|
||||||
fmt::println("animation: {}", anim.subframe);
|
fmt::println("animation: {}", anim.subframe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue