Arena works better now and I can give a list of sprites to work as fixtures in a scene.
This commit is contained in:
parent
59ba73baa0
commit
71e3c97cf0
8 changed files with 169 additions and 36 deletions
|
|
@ -14,11 +14,15 @@
|
||||||
"boss": {
|
"boss": {
|
||||||
"start_pos": "boss5",
|
"start_pos": "boss5",
|
||||||
"scale": 0.6,
|
"scale": 0.6,
|
||||||
"mid_cell": true
|
"mid_cell": true,
|
||||||
}
|
"sprite": "rat_king_boss"
|
||||||
|
},
|
||||||
|
"fixtures": [
|
||||||
|
{"name": "torch_crappy", "scale": 0.8, "cell": "torch1"},
|
||||||
|
{"name": "torch_crappy", "scale": 0.8, "cell": "torch2"}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false},
|
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false},
|
||||||
{"_type": "Sprite", "name": "rat_king_boss", "width": 720, "height": 720, "scale": 0.8, "stationary": true},
|
|
||||||
{"_type": "Sound", "attack": "Marmot_Scream_1", "death": "Creature_Death_1"}
|
{"_type": "Sound", "attack": "Marmot_Scream_1", "death": "Creature_Death_1"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
{"_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": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||||
{"_type": "Personality", "hearing_distance": 5, "tough": false},
|
{"_type": "Personality", "hearing_distance": 5, "tough": false},
|
||||||
{"_type": "Sprite", "name": "gold_savior", "width": 256, "height": 256, "width": 256, "height": 256, "scale": 1.0},
|
{"_type": "Sprite", "name": "gold_savior", "scale": 1.0},
|
||||||
{"_type": "Sound", "attack": "Sword_Hit_2", "death": "Humanoid_Death_1"}
|
{"_type": "Sound", "attack": "Sword_Hit_2", "death": "Humanoid_Death_1"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
{"_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": "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": "Personality", "hearing_distance": 5, "tough": true},
|
||||||
{"_type": "Sprite", "name": "armored_knight", "width": 256, "height": 256, "width": 256, "height": 256, "scale": 1.0},
|
{"_type": "Sprite", "name": "armored_knight", "scale": 1.0},
|
||||||
{"_type": "Sound", "attack": "Sword_Hit_2", "death": "Humanoid_Death_1"}
|
{"_type": "Sound", "attack": "Sword_Hit_2", "death": "Humanoid_Death_1"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": true},
|
{"_type": "Motion", "dx": 0, "dy": 0, "random": true},
|
||||||
{"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
{"_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": "Personality", "hearing_distance": 5, "tough": true},
|
||||||
{"_type": "Sprite", "name": "axe_ranger", "width": 256, "height": 256, "scale": 1.0},
|
{"_type": "Sprite", "name": "axe_ranger", "scale": 1.0},
|
||||||
{"_type": "Sound", "attack": "Sword_Hit_2", "death": "Ranger_1"}
|
{"_type": "Sound", "attack": "Sword_Hit_2", "death": "Ranger_1"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
{"_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": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
|
||||||
{"_type": "Personality", "hearing_distance": 5, "tough": false},
|
{"_type": "Personality", "hearing_distance": 5, "tough": false},
|
||||||
{"_type": "Sprite", "name": "rat_with_sword", "width": 256, "height": 256, "scale": 1.0},
|
{"_type": "Sprite", "name": "rat_with_sword", "scale": 1.0},
|
||||||
{"_type": "Sound", "attack": "Small_Rat", "death": "Creature_Death_1"}
|
{"_type": "Sound", "attack": "Small_Rat", "death": "Creature_Death_1"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -83,7 +83,7 @@
|
||||||
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
|
{"_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": "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": "Personality", "hearing_distance": 5, "tough": true},
|
||||||
{"_type": "Sprite", "name": "hairy_spider", "width": 256, "height": 256, "scale": 1.0},
|
{"_type": "Sprite", "name": "hairy_spider", "scale": 1.0},
|
||||||
{"_type": "Sound", "attack": "Spider_1", "death": "Spider_2"}
|
{"_type": "Sound", "attack": "Spider_1", "death": "Spider_2"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
37
boss/ui.cpp
37
boss/ui.cpp
|
|
@ -15,8 +15,8 @@ namespace boss {
|
||||||
$scene(world->get<components::BossFight>($boss_id)),
|
$scene(world->get<components::BossFight>($boss_id)),
|
||||||
$combat_ui(true)
|
$combat_ui(true)
|
||||||
{
|
{
|
||||||
auto& sprite = $world->get<components::Sprite>($boss_id);
|
std::string sprite_name = $scene.boss["sprite"];
|
||||||
$boss_sprite = textures::get_sprite(sprite.name);
|
$boss_sprite = textures::get_sprite(sprite_name);
|
||||||
|
|
||||||
// floor is std::optional
|
// floor is std::optional
|
||||||
if($scene.floor) {
|
if($scene.floor) {
|
||||||
|
|
@ -25,14 +25,21 @@ namespace boss {
|
||||||
|
|
||||||
$player_sprite = textures::get_sprite($scene.player["sprite"]);
|
$player_sprite = textures::get_sprite($scene.player["sprite"]);
|
||||||
|
|
||||||
dbc::check(animation::has(sprite.name), "add boss animation to animations.json");
|
dbc::check(animation::has(sprite_name), "add boss animation to animations.json");
|
||||||
$boss_anim = animation::load(sprite.name);
|
$boss_anim = animation::load(sprite_name);
|
||||||
|
|
||||||
$torch_left = textures::get_sprite("torch_crappy");
|
for(auto& fixture : $scene.fixtures) {
|
||||||
$torch_left_anim = animation::load("torch_crappy");
|
std::string name = fixture["name"];
|
||||||
$torch_right.texture = $torch_left.texture;
|
auto st = textures::get_sprite(name);
|
||||||
$torch_right.sprite = std::make_shared<sf::Sprite>(*$torch_right.texture);
|
// clone the sprite so it can be positioned
|
||||||
$torch_right_anim = animation::load("torch_crappy");
|
st.sprite = std::make_shared<sf::Sprite>(*st.texture);
|
||||||
|
|
||||||
|
auto anim = animation::load(name);
|
||||||
|
float scale = fixture["scale"];
|
||||||
|
std::string cell = fixture["cell"];
|
||||||
|
|
||||||
|
$fixtures.emplace_back(st, anim, cell, scale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UI::init() {
|
void UI::init() {
|
||||||
|
|
@ -57,8 +64,9 @@ namespace boss {
|
||||||
position_sprite($floor_sprite, $scene.floor_pos, 1.0, false);
|
position_sprite($floor_sprite, $scene.floor_pos, 1.0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
position_sprite($torch_left, "torch1", 1.0, false);
|
for(auto& fixture : $fixtures) {
|
||||||
position_sprite($torch_right, "torch2", 1.0, false);
|
position_sprite(fixture.st, fixture.cell, fixture.scale, false);
|
||||||
|
}
|
||||||
|
|
||||||
$arena.init();
|
$arena.init();
|
||||||
|
|
||||||
|
|
@ -103,10 +111,9 @@ namespace boss {
|
||||||
window.draw(*$boss_sprite.sprite);
|
window.draw(*$boss_sprite.sprite);
|
||||||
window.draw(*$player_sprite.sprite);
|
window.draw(*$player_sprite.sprite);
|
||||||
|
|
||||||
window.draw(*$torch_left.sprite);
|
for(auto& fixture : $fixtures) {
|
||||||
window.draw(*$torch_right.sprite);
|
window.draw(*fixture.st.sprite);
|
||||||
|
}
|
||||||
// $arena.debug_layout(window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UI::mouse(float x, float y, Modifiers mods) {
|
bool UI::mouse(float x, float y, Modifiers mods) {
|
||||||
|
|
|
||||||
12
boss/ui.hpp
12
boss/ui.hpp
|
|
@ -7,6 +7,13 @@
|
||||||
#include "gui/combat_ui.hpp"
|
#include "gui/combat_ui.hpp"
|
||||||
#include "components.hpp"
|
#include "components.hpp"
|
||||||
|
|
||||||
|
struct AnimatedFixture {
|
||||||
|
textures::SpriteTexture st;
|
||||||
|
components::Animation anim;
|
||||||
|
std::string cell;
|
||||||
|
float scale;
|
||||||
|
};
|
||||||
|
|
||||||
namespace boss {
|
namespace boss {
|
||||||
using std::shared_ptr;
|
using std::shared_ptr;
|
||||||
using namespace DinkyECS;
|
using namespace DinkyECS;
|
||||||
|
|
@ -25,10 +32,7 @@ namespace boss {
|
||||||
components::Animation $boss_anim;
|
components::Animation $boss_anim;
|
||||||
sf::Vector2f $boss_pos;
|
sf::Vector2f $boss_pos;
|
||||||
|
|
||||||
SpriteTexture $torch_left;
|
std::vector<AnimatedFixture> $fixtures;
|
||||||
SpriteTexture $torch_right;
|
|
||||||
components::Animation $torch_left_anim;
|
|
||||||
components::Animation $torch_right_anim;
|
|
||||||
|
|
||||||
UI(shared_ptr<World> world, Entity boss_id);
|
UI(shared_ptr<World> world, Entity boss_id);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "components.hpp"
|
#include "components.hpp"
|
||||||
|
|
||||||
#include "point.hpp"
|
#include "point.hpp"
|
||||||
#include "easings.hpp"
|
#include "easings.hpp"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,12 +76,18 @@ namespace components {
|
||||||
int hp = 10;
|
int hp = 10;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Sprite {
|
||||||
|
string name;
|
||||||
|
float scale;
|
||||||
|
};
|
||||||
|
|
||||||
struct BossFight {
|
struct BossFight {
|
||||||
std::string background;
|
std::string background;
|
||||||
std::optional<std::string> floor;
|
std::optional<std::string> floor;
|
||||||
std::string floor_pos;
|
std::string floor_pos;
|
||||||
json player;
|
json player;
|
||||||
json boss;
|
json boss;
|
||||||
|
json fixtures;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Combat {
|
struct Combat {
|
||||||
|
|
@ -105,12 +111,6 @@ namespace components {
|
||||||
std::vector<std::string> events;
|
std::vector<std::string> events;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Sprite {
|
|
||||||
string name;
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Sound {
|
struct Sound {
|
||||||
std::string attack;
|
std::string attack;
|
||||||
std::string death;
|
std::string death;
|
||||||
|
|
@ -149,8 +149,8 @@ namespace components {
|
||||||
using ComponentMap = std::unordered_map<std::string, ReflFuncSignature>;
|
using ComponentMap = std::unordered_map<std::string, ReflFuncSignature>;
|
||||||
|
|
||||||
ENROLL_COMPONENT(Tile, display, foreground, background);
|
ENROLL_COMPONENT(Tile, display, foreground, background);
|
||||||
ENROLL_COMPONENT(BossFight, background, floor, floor_pos, player, boss);
|
ENROLL_COMPONENT(BossFight, background, floor, floor_pos, player, boss, fixtures);
|
||||||
ENROLL_COMPONENT(Sprite, name, width, height);
|
ENROLL_COMPONENT(Sprite, name, scale);
|
||||||
ENROLL_COMPONENT(Curative, hp);
|
ENROLL_COMPONENT(Curative, hp);
|
||||||
ENROLL_COMPONENT(LightSource, strength, radius);
|
ENROLL_COMPONENT(LightSource, strength, radius);
|
||||||
ENROLL_COMPONENT(Position, location.x, location.y);
|
ENROLL_COMPONENT(Position, location.x, location.y);
|
||||||
|
|
|
||||||
|
|
@ -669,7 +669,7 @@ void System::spawn_attack(World& world, int attack_id, DinkyECS::Entity enemy) {
|
||||||
auto effect = ritual.element == FIRE ? "burning_animation" : "lightning_animation";
|
auto effect = ritual.element == FIRE ? "burning_animation" : "lightning_animation";
|
||||||
|
|
||||||
auto effect_id = world.entity();
|
auto effect_id = world.entity();
|
||||||
world.set<Sprite>(effect_id, {effect, 256, 256});
|
world.set<Sprite>(effect_id, {effect, 1.0f});
|
||||||
world.set<Temporary>(effect_id, {true});
|
world.set<Temporary>(effect_id, {true});
|
||||||
|
|
||||||
auto shader = shaders::get(ritual.element == FIRE ? "flame" : "lightning");
|
auto shader = shaders::get(ritual.element == FIRE ? "flame" : "lightning");
|
||||||
|
|
|
||||||
117
tests/animation2.cpp
Normal file
117
tests/animation2.cpp
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include <string>
|
||||||
|
#include <coroutine>
|
||||||
|
#include <chrono>
|
||||||
|
#include "rand.hpp"
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include "components.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include "stats.hpp"
|
||||||
|
#include "simplefsm.hpp"
|
||||||
|
#include "textures.hpp"
|
||||||
|
#include "animation.hpp"
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
using components::Animation;
|
||||||
|
using TheClock = std::chrono::steady_clock;
|
||||||
|
using TimeDelta = TheClock::duration;
|
||||||
|
using TimePoint = std::chrono::time_point<TheClock>;
|
||||||
|
const TimeDelta MIN_TICK = 200ms;
|
||||||
|
|
||||||
|
struct AnimationState {
|
||||||
|
Animation anim;
|
||||||
|
TimeDelta wait = 1s;
|
||||||
|
sf::Vector2f scale{1.0, 1.0};
|
||||||
|
sf::Vector2f pos{0.0, 0.0};
|
||||||
|
sf::IntRect rect{{0, 0}, {300, 300}};
|
||||||
|
|
||||||
|
void step() {
|
||||||
|
anim.step(scale, pos, rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply(textures::SpriteTexture& st) {
|
||||||
|
animation::apply(anim, *st.sprite, pos);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AnimationQueue {
|
||||||
|
std::vector<AnimationState> active;
|
||||||
|
TimePoint last_tick;
|
||||||
|
TimeDelta wait_for = MIN_TICK;
|
||||||
|
size_t in_queue = 0;
|
||||||
|
size_t chunk = 1000;
|
||||||
|
|
||||||
|
TimeDelta delta() {
|
||||||
|
return TheClock::now() - last_tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() {
|
||||||
|
last_tick = TheClock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t add(Animation& anim, int initial_wait) {
|
||||||
|
active.emplace_back(anim, TheClock::duration(initial_wait));
|
||||||
|
return active.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimationState& get(size_t id) {
|
||||||
|
return active.at(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void render() {
|
||||||
|
wait_for = MIN_TICK;
|
||||||
|
auto dt = delta();
|
||||||
|
|
||||||
|
for(size_t i = 0; i < chunk; i++) {
|
||||||
|
in_queue++;
|
||||||
|
auto& a = active[in_queue % active.size()];
|
||||||
|
|
||||||
|
if(!a.anim.playing) continue;
|
||||||
|
|
||||||
|
if(a.wait < wait_for) {
|
||||||
|
wait_for = a.wait;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(a.wait < dt) {
|
||||||
|
// fmt::println("play animation: {} total ", active.size());
|
||||||
|
a.step();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE("simple coroutine animation test", "[coro]") {
|
||||||
|
AnimationQueue queue;
|
||||||
|
Animation anim;
|
||||||
|
anim.play();
|
||||||
|
|
||||||
|
for(int i = 0; i < 100; i++) {
|
||||||
|
int time = Random::uniform(16, 32);
|
||||||
|
size_t id = queue.add(anim, time);
|
||||||
|
auto& what = queue.get(id);
|
||||||
|
|
||||||
|
REQUIRE(what.wait == TheClock::duration(time));
|
||||||
|
}
|
||||||
|
|
||||||
|
dbc::check(queue.active.size() > 0, "zero size queue after adding is impossible.");
|
||||||
|
queue.tick();
|
||||||
|
Stats stats;
|
||||||
|
|
||||||
|
for(int i = 0; i < 10000; i++) {
|
||||||
|
auto start = stats.time_start();
|
||||||
|
auto delta = queue.delta();
|
||||||
|
|
||||||
|
queue.render();
|
||||||
|
|
||||||
|
if(delta > queue.wait_for) {
|
||||||
|
queue.add(anim, Random::uniform(100, 5000));
|
||||||
|
queue.tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.sample_time(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::print("stdev: {}, mean: {}\r", stats.stddev(), stats.mean());
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue