Motor de sprites/animacions/surfaces i jugador centrat (v0.2.0)
This commit is contained in:
1 parent
7bb1edd08a
commit
de5a670fdb
34 files changed
+16137
-81
No files matched your search
@@ -0,0 +1,201 @@
|
||||
#include "core/rendering/sprite/animated_sprite.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
|
||||
#include "core/rendering/surface.hpp"
|
||||
#include "core/resources/resource_cache.hpp"
|
||||
#include "external/fkyaml_node.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
auto convertYamlFramesToRects(const fkyaml::node& frames_node, float frame_width, float frame_height, int frames_per_row, int max_tiles) -> std::vector<SDL_FRect> {
|
||||
std::vector<SDL_FRect> frames;
|
||||
SDL_FRect rect = {.x = 0.0F, .y = 0.0F, .w = frame_width, .h = frame_height};
|
||||
for (const auto& frame_index_node : frames_node) {
|
||||
const int NUM_TILE = frame_index_node.get_value<int>();
|
||||
if (NUM_TILE <= max_tiles) {
|
||||
rect.x = static_cast<float>(NUM_TILE % frames_per_row) * frame_width;
|
||||
rect.y = static_cast<float>(NUM_TILE / frames_per_row) * frame_height;
|
||||
frames.emplace_back(rect);
|
||||
}
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
auto parseAnimations(const fkyaml::node& yaml, float frame_width, float frame_height, int frames_per_row, int max_tiles) -> std::vector<AnimatedSprite::AnimationData> {
|
||||
std::vector<AnimatedSprite::AnimationData> animations;
|
||||
if (!yaml.contains("animations") || !yaml["animations"].is_sequence()) {
|
||||
return animations;
|
||||
}
|
||||
|
||||
for (const auto& anim_node : yaml["animations"]) {
|
||||
AnimatedSprite::AnimationData animation;
|
||||
|
||||
if (anim_node.contains("name")) {
|
||||
animation.name = anim_node["name"].get_value<std::string>();
|
||||
}
|
||||
if (anim_node.contains("frames") && anim_node["frames"].is_sequence()) {
|
||||
animation.frames = convertYamlFramesToRects(
|
||||
anim_node["frames"],
|
||||
frame_width,
|
||||
frame_height,
|
||||
frames_per_row,
|
||||
max_tiles);
|
||||
}
|
||||
if (anim_node.contains("speed")) {
|
||||
const auto& speed_node = anim_node["speed"];
|
||||
if (speed_node.is_sequence()) {
|
||||
for (const auto& s : speed_node) {
|
||||
animation.speeds.push_back(s.get_value<float>());
|
||||
}
|
||||
} else {
|
||||
auto spd = speed_node.get_value<float>();
|
||||
if (spd > 0.0F) { animation.speeds.assign(animation.frames.size(), spd); }
|
||||
}
|
||||
}
|
||||
if (anim_node.contains("loopFrom")) {
|
||||
animation.loop_from = anim_node["loopFrom"].get_value<int>();
|
||||
}
|
||||
|
||||
animations.push_back(animation);
|
||||
}
|
||||
return animations;
|
||||
}
|
||||
|
||||
struct SheetParams {
|
||||
float frame_width{0.0F};
|
||||
float frame_height{0.0F};
|
||||
int frames_per_row{1};
|
||||
int max_tiles{1};
|
||||
};
|
||||
|
||||
auto parseGlobalConfig(const fkyaml::node& yaml, std::shared_ptr<Surface>& surface) -> SheetParams {
|
||||
SheetParams params;
|
||||
if (yaml.contains("tileSetFile")) {
|
||||
surface = Resource::Cache::get().getSurface(yaml["tileSetFile"].get_value<std::string>());
|
||||
}
|
||||
if (yaml.contains("frameWidth")) {
|
||||
params.frame_width = static_cast<float>(yaml["frameWidth"].get_value<int>());
|
||||
}
|
||||
if (yaml.contains("frameHeight")) {
|
||||
params.frame_height = static_cast<float>(yaml["frameHeight"].get_value<int>());
|
||||
}
|
||||
if (surface && params.frame_width > 0.0F && params.frame_height > 0.0F) {
|
||||
params.frames_per_row = surface->getWidth() / static_cast<int>(params.frame_width);
|
||||
const int H_TILES = surface->getHeight() / static_cast<int>(params.frame_height);
|
||||
params.max_tiles = params.frames_per_row * H_TILES;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AnimatedSprite::AnimatedSprite(const AnimationResource& cached_data) {
|
||||
std::string yaml_content(cached_data.yaml_data.begin(), cached_data.yaml_data.end());
|
||||
try {
|
||||
auto yaml = fkyaml::node::deserialize(yaml_content);
|
||||
auto params = parseGlobalConfig(yaml, surface_);
|
||||
animations_ = parseAnimations(yaml, params.frame_width, params.frame_height, params.frames_per_row, params.max_tiles);
|
||||
buildNameIndex();
|
||||
|
||||
setWidth(params.frame_width);
|
||||
setHeight(params.frame_height);
|
||||
|
||||
if (!animations_.empty() && !animations_[0].frames.empty()) {
|
||||
setClip(animations_[0].frames[0]);
|
||||
}
|
||||
} catch (const fkyaml::exception& e) {
|
||||
std::cerr << "YAML parsing error in animation " << cached_data.name << ": " << e.what() << '\n';
|
||||
throw;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error loading animation " << cached_data.name << ": " << e.what() << '\n';
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimatedSprite::buildNameIndex() {
|
||||
animation_index_.clear();
|
||||
for (int i = 0; i < static_cast<int>(animations_.size()); ++i) {
|
||||
animation_index_[animations_[i].name] = i;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimatedSprite::animate(float delta_time) {
|
||||
if (animations_.empty()) { return; }
|
||||
auto& anim = animations_[current_animation_];
|
||||
if (anim.speeds.empty()) { return; }
|
||||
|
||||
anim.accumulated_time += delta_time;
|
||||
|
||||
const float TOTAL = std::accumulate(anim.speeds.begin(), anim.speeds.end(), 0.0F);
|
||||
|
||||
if (anim.accumulated_time >= TOTAL) {
|
||||
if (anim.loop_from < 0) {
|
||||
anim.current_frame = static_cast<int>(anim.frames.size()) - 1;
|
||||
anim.completed = true;
|
||||
setClip(anim.frames[anim.current_frame]);
|
||||
return;
|
||||
}
|
||||
float loop_start = 0.0F;
|
||||
for (int i = 0; i < anim.loop_from; ++i) { loop_start += anim.speeds[i]; }
|
||||
float loop_len = TOTAL - loop_start;
|
||||
anim.accumulated_time = loop_start + std::fmod(anim.accumulated_time - loop_start, loop_len);
|
||||
}
|
||||
|
||||
float cursor = 0.0F;
|
||||
for (int i = 0; i < static_cast<int>(anim.frames.size()); ++i) {
|
||||
cursor += anim.speeds[i];
|
||||
if (anim.accumulated_time < cursor) {
|
||||
anim.current_frame = i;
|
||||
setClip(anim.frames[i]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
anim.current_frame = static_cast<int>(anim.frames.size()) - 1;
|
||||
setClip(anim.frames[anim.current_frame]);
|
||||
}
|
||||
|
||||
auto AnimatedSprite::animationIsCompleted() -> bool {
|
||||
return animations_[current_animation_].completed;
|
||||
}
|
||||
|
||||
void AnimatedSprite::setCurrentAnimation(const std::string& name) {
|
||||
auto it = animation_index_.find(name);
|
||||
if (it == animation_index_.end()) {
|
||||
std::cout << "** Warning: could not find \"" << name << "\" animation" << '\n';
|
||||
return;
|
||||
}
|
||||
setCurrentAnimation(it->second);
|
||||
}
|
||||
|
||||
void AnimatedSprite::setCurrentAnimation(int index) {
|
||||
if (current_animation_ != index) {
|
||||
current_animation_ = index;
|
||||
animations_[current_animation_].current_frame = 0;
|
||||
animations_[current_animation_].accumulated_time = 0.0F;
|
||||
animations_[current_animation_].completed = false;
|
||||
setClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimatedSprite::update(float delta_time) {
|
||||
animate(delta_time);
|
||||
MovingSprite::update(delta_time);
|
||||
}
|
||||
|
||||
void AnimatedSprite::resetAnimation() {
|
||||
animations_[current_animation_].current_frame = 0;
|
||||
animations_[current_animation_].accumulated_time = 0.0F;
|
||||
animations_[current_animation_].completed = false;
|
||||
}
|
||||
|
||||
void AnimatedSprite::setCurrentAnimationFrame(int num) {
|
||||
if (num < 0 || num >= static_cast<int>(animations_[current_animation_].frames.size())) {
|
||||
num = 0;
|
||||
}
|
||||
animations_[current_animation_].current_frame = num;
|
||||
setClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "core/rendering/sprite/moving_sprite.hpp"
|
||||
#include "core/resources/resource_types.hpp"
|
||||
|
||||
class AnimatedSprite : public MovingSprite {
|
||||
public:
|
||||
struct AnimationData {
|
||||
std::string name;
|
||||
std::vector<SDL_FRect> frames;
|
||||
std::vector<float> speeds;
|
||||
int loop_from{-1};
|
||||
bool completed{false};
|
||||
int current_frame{0};
|
||||
float accumulated_time{0.0F};
|
||||
};
|
||||
|
||||
explicit AnimatedSprite(const AnimationResource& cached_data);
|
||||
~AnimatedSprite() override = default;
|
||||
|
||||
void update(float delta_time) override;
|
||||
|
||||
auto animationIsCompleted() -> bool;
|
||||
auto getCurrentAnimationSize() -> int { return static_cast<int>(animations_[current_animation_].frames.size()); }
|
||||
|
||||
void setCurrentAnimation(const std::string& name = "default");
|
||||
void setCurrentAnimation(int index = 0);
|
||||
void resetAnimation();
|
||||
void setCurrentAnimationFrame(int num);
|
||||
void animate(float delta_time);
|
||||
|
||||
private:
|
||||
void buildNameIndex();
|
||||
|
||||
std::vector<AnimationData> animations_;
|
||||
std::unordered_map<std::string, int> animation_index_;
|
||||
int current_animation_{0};
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
#include "core/rendering/sprite/moving_sprite.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "core/rendering/surface.hpp"
|
||||
|
||||
MovingSprite::MovingSprite() { Sprite::clear(); }
|
||||
|
||||
MovingSprite::MovingSprite(std::shared_ptr<Surface> surface)
|
||||
: Sprite(std::move(surface)) { Sprite::clear(); }
|
||||
|
||||
MovingSprite::MovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos)
|
||||
: Sprite(std::move(surface), pos),
|
||||
x_(pos.x),
|
||||
y_(pos.y) { Sprite::pos_ = pos; }
|
||||
|
||||
MovingSprite::MovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos, SDL_FlipMode flip)
|
||||
: Sprite(std::move(surface), pos),
|
||||
x_(pos.x),
|
||||
y_(pos.y),
|
||||
flip_(flip) { Sprite::pos_ = pos; }
|
||||
|
||||
void MovingSprite::clear() {
|
||||
x_ = 0.0F;
|
||||
y_ = 0.0F;
|
||||
vx_ = 0.0F;
|
||||
vy_ = 0.0F;
|
||||
ax_ = 0.0F;
|
||||
ay_ = 0.0F;
|
||||
flip_ = SDL_FLIP_NONE;
|
||||
Sprite::clear();
|
||||
}
|
||||
|
||||
void MovingSprite::move(float delta_time) {
|
||||
vx_ += ax_ * delta_time;
|
||||
vy_ += ay_ * delta_time;
|
||||
x_ += vx_ * delta_time;
|
||||
y_ += vy_ * delta_time;
|
||||
pos_.x = static_cast<int>(x_);
|
||||
pos_.y = static_cast<int>(y_);
|
||||
}
|
||||
|
||||
void MovingSprite::update(float delta_time) {
|
||||
move(delta_time);
|
||||
}
|
||||
|
||||
void MovingSprite::render() {
|
||||
surface_->render(static_cast<int>(pos_.x), static_cast<int>(pos_.y), &clip_, flip_);
|
||||
}
|
||||
|
||||
void MovingSprite::setPos(SDL_FRect rect) {
|
||||
x_ = rect.x;
|
||||
y_ = rect.y;
|
||||
pos_ = rect;
|
||||
}
|
||||
|
||||
void MovingSprite::setPos(float x, float y) {
|
||||
x_ = x;
|
||||
y_ = y;
|
||||
pos_.x = static_cast<int>(x_);
|
||||
pos_.y = static_cast<int>(y_);
|
||||
}
|
||||
|
||||
void MovingSprite::setPosX(float value) {
|
||||
x_ = value;
|
||||
pos_.x = static_cast<int>(x_);
|
||||
}
|
||||
|
||||
void MovingSprite::setPosY(float value) {
|
||||
y_ = value;
|
||||
pos_.y = static_cast<int>(y_);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "core/rendering/sprite/sprite.hpp"
|
||||
|
||||
class Surface;
|
||||
|
||||
class MovingSprite : public Sprite {
|
||||
public:
|
||||
MovingSprite();
|
||||
explicit MovingSprite(std::shared_ptr<Surface> surface);
|
||||
MovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos);
|
||||
MovingSprite(std::shared_ptr<Surface> surface, SDL_FRect pos, SDL_FlipMode flip);
|
||||
~MovingSprite() override = default;
|
||||
|
||||
void update(float delta_time) override;
|
||||
void render() override;
|
||||
void clear() override;
|
||||
|
||||
[[nodiscard]] auto getPosX() const -> float { return x_; }
|
||||
[[nodiscard]] auto getPosY() const -> float { return y_; }
|
||||
[[nodiscard]] auto getVelX() const -> float { return vx_; }
|
||||
[[nodiscard]] auto getVelY() const -> float { return vy_; }
|
||||
[[nodiscard]] auto getAccelX() const -> float { return ax_; }
|
||||
[[nodiscard]] auto getAccelY() const -> float { return ay_; }
|
||||
|
||||
void setPos(SDL_FRect rect);
|
||||
void setPos(float x, float y);
|
||||
void setPosX(float value);
|
||||
void setPosY(float value);
|
||||
void setVelX(float value) { vx_ = value; }
|
||||
void setVelY(float value) { vy_ = value; }
|
||||
void setAccelX(float value) { ax_ = value; }
|
||||
void setAccelY(float value) { ay_ = value; }
|
||||
|
||||
void setFlip(SDL_FlipMode flip) { flip_ = flip; }
|
||||
auto getFlip() -> SDL_FlipMode { return flip_; }
|
||||
void flip() { flip_ = (flip_ == SDL_FLIP_HORIZONTAL) ? SDL_FLIP_NONE : SDL_FLIP_HORIZONTAL; }
|
||||
|
||||
protected:
|
||||
void move(float delta_time);
|
||||
|
||||
float x_{0.0F};
|
||||
float y_{0.0F};
|
||||
float vx_{0.0F};
|
||||
float vy_{0.0F};
|
||||
float ax_{0.0F};
|
||||
float ay_{0.0F};
|
||||
SDL_FlipMode flip_{SDL_FLIP_NONE};
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "core/rendering/sprite/sprite.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "core/rendering/surface.hpp"
|
||||
|
||||
Sprite::Sprite() = default;
|
||||
|
||||
Sprite::Sprite(std::shared_ptr<Surface> surface, float x, float y, float w, float h)
|
||||
: surface_(std::move(surface)),
|
||||
pos_{.x = x, .y = y, .w = w, .h = h},
|
||||
clip_{.x = 0.0F, .y = 0.0F, .w = pos_.w, .h = pos_.h} {}
|
||||
|
||||
Sprite::Sprite(std::shared_ptr<Surface> surface, SDL_FRect rect)
|
||||
: surface_(std::move(surface)),
|
||||
pos_(rect),
|
||||
clip_{.x = 0.0F, .y = 0.0F, .w = pos_.w, .h = pos_.h} {}
|
||||
|
||||
Sprite::Sprite(std::shared_ptr<Surface> surface)
|
||||
: surface_(std::move(surface)),
|
||||
pos_{0.0F, 0.0F, static_cast<float>(surface_->getWidth()), static_cast<float>(surface_->getHeight())},
|
||||
clip_(pos_) {}
|
||||
|
||||
void Sprite::render() {
|
||||
surface_->render(static_cast<int>(pos_.x), static_cast<int>(pos_.y), &clip_);
|
||||
}
|
||||
|
||||
void Sprite::setPosition(float x, float y) {
|
||||
pos_.x = x;
|
||||
pos_.y = y;
|
||||
}
|
||||
|
||||
void Sprite::setPosition(SDL_FPoint p) {
|
||||
pos_.x = p.x;
|
||||
pos_.y = p.y;
|
||||
}
|
||||
|
||||
void Sprite::clear() {
|
||||
pos_ = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F};
|
||||
clip_ = {.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F};
|
||||
}
|
||||
|
||||
void Sprite::update(float delta_time) {
|
||||
(void)delta_time;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
class Surface;
|
||||
|
||||
class Sprite {
|
||||
public:
|
||||
Sprite();
|
||||
Sprite(std::shared_ptr<Surface> surface, float x, float y, float w, float h);
|
||||
Sprite(std::shared_ptr<Surface> surface, SDL_FRect rect);
|
||||
explicit Sprite(std::shared_ptr<Surface> surface);
|
||||
|
||||
virtual ~Sprite() = default;
|
||||
|
||||
virtual void update(float delta_time);
|
||||
virtual void render();
|
||||
virtual void clear();
|
||||
|
||||
[[nodiscard]] auto getX() const -> float { return pos_.x; }
|
||||
[[nodiscard]] auto getY() const -> float { return pos_.y; }
|
||||
[[nodiscard]] auto getWidth() const -> float { return pos_.w; }
|
||||
[[nodiscard]] auto getHeight() const -> float { return pos_.h; }
|
||||
[[nodiscard]] auto getPosition() const -> SDL_FRect { return pos_; }
|
||||
[[nodiscard]] auto getClip() const -> SDL_FRect { return clip_; }
|
||||
[[nodiscard]] auto getSurface() const -> std::shared_ptr<Surface> { return surface_; }
|
||||
auto getRect() -> SDL_FRect& { return pos_; }
|
||||
|
||||
void setX(float x) { pos_.x = x; }
|
||||
void setY(float y) { pos_.y = y; }
|
||||
void setWidth(float w) { pos_.w = w; }
|
||||
void setHeight(float h) { pos_.h = h; }
|
||||
void setPosition(float x, float y);
|
||||
void setPosition(SDL_FPoint p);
|
||||
void setPosition(SDL_FRect r) { pos_ = r; }
|
||||
void incX(float value) { pos_.x += value; }
|
||||
void incY(float value) { pos_.y += value; }
|
||||
|
||||
void setClip(SDL_FRect rect) { clip_ = rect; }
|
||||
void setClip(float x, float y, float w, float h) { clip_ = SDL_FRect{.x = x, .y = y, .w = w, .h = h}; }
|
||||
void setSurface(std::shared_ptr<Surface> surface) { surface_ = std::move(surface); }
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Surface> surface_{nullptr};
|
||||
SDL_FRect pos_{.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F};
|
||||
SDL_FRect clip_{.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 0.0F};
|
||||
};
|
||||
Reference in new issue
Block a user