From 33dfe3e6bd636277593eaa7a57328eba7fcfc2f5 Mon Sep 17 00:00:00 2001 From: ThePythonator <49040244+ThePythonator@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:21:45 +0100 Subject: [PATCH] feat: Add support for animations Created AnimationHandler and MultiTileAnimationHandler for managing animations which use indicies from a spritesheet and/or rendering transformations (such as rotation or mirroring). --- CMakeLists.txt | 1 + include/framework/Animation.hpp | 56 +++++++++++++++++++++++++++++++ include/game/GameStages.hpp | 6 ++++ src/framework/Animation.cpp | 43 ++++++++++++++++++++++++ src/game/GameStages.cpp | 58 +++++++++++++++++++++++++++++++++ 5 files changed, 164 insertions(+) create mode 100644 include/framework/Animation.hpp create mode 100644 src/framework/Animation.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1178fa5..d2576ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ set(FRAMEWORK_SOURCES "Font.cpp" "Image.cpp" "Spritesheet.cpp" + "Animation.cpp" "Colour.cpp" diff --git a/include/framework/Animation.hpp b/include/framework/Animation.hpp new file mode 100644 index 0000000..aab48b2 --- /dev/null +++ b/include/framework/Animation.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include + +#include "Maths.hpp" +#include "Spritesheet.hpp" + +namespace Framework { + struct AnimationFrame { + uint16_t index; + float duration; + Framework::SpriteTransform transform; + }; + + // Multiple frames make an animation + typedef std::vector Animation; + + // Basic interface class + class AnimationInterface { + public: + virtual void update(float dt) = 0; + virtual void render(Framework::vec2 position) = 0; + }; + + class AnimationHandler : public AnimationInterface { + public: + AnimationHandler(const Framework::Spritesheet& _spritesheet, Animation _animation, float time_offset = 0.0f, Framework::vec2 _position_offset = Framework::VEC_NULL); + + void update(float dt); + void render(Framework::vec2 position); + + private: + const Framework::Spritesheet& spritesheet; + Animation animation; + Framework::vec2 position_offset; + + float current_time = 0.0f; + + uint16_t current_index = 0; + }; + + typedef std::map MultiTileAnimation; + + class MultiTileAnimationHandler : public AnimationInterface { + public: + MultiTileAnimationHandler(const Framework::Spritesheet& _spritesheet, MultiTileAnimation _animation, float time_offset = 0.0f, Framework::vec2 _position_offset = Framework::VEC_NULL); + + void update(float dt); + void render(Framework::vec2 position); + + private: + std::vector animations; + }; +} diff --git a/include/game/GameStages.hpp b/include/game/GameStages.hpp index 5cbbb62..7ee12ac 100644 --- a/include/game/GameStages.hpp +++ b/include/game/GameStages.hpp @@ -6,12 +6,18 @@ #include "MenuStages.hpp" +#include "Animation.hpp" + class GameStage : public Framework::BaseStage { public: void start(); bool update(float dt); void render(); + +private: + std::unique_ptr spinning_sword; + std::unique_ptr big_animation; }; class PausedStage : public Framework::BaseStage { diff --git a/src/framework/Animation.cpp b/src/framework/Animation.cpp new file mode 100644 index 0000000..da8305b --- /dev/null +++ b/src/framework/Animation.cpp @@ -0,0 +1,43 @@ +#include "Animation.hpp" + +namespace Framework { + AnimationHandler::AnimationHandler(const Framework::Spritesheet& _spritesheet, Animation _animation, float time_offset, Framework::vec2 _position_offset) : spritesheet(_spritesheet) { + animation = _animation; + current_time = time_offset; + position_offset = _position_offset; + } + + void AnimationHandler::update(float dt) { + current_time += dt; + float t = animation.at(current_index).duration; + while (current_time >= t) { + current_time -= t; + current_index++; + current_index %= animation.size(); + t = animation.at(current_index).duration; + } + } + + void AnimationHandler::render(Framework::vec2 position) { + const AnimationFrame& current_frame = animation.at(current_index); + spritesheet.sprite(current_frame.index, position + position_offset, current_frame.transform); + } + + MultiTileAnimationHandler::MultiTileAnimationHandler(const Framework::Spritesheet& _spritesheet, MultiTileAnimation _animation, float time_offset, Framework::vec2 _position_offset) { + for (const auto& [position, animation] : _animation) { + animations.push_back(AnimationHandler(_spritesheet, animation, time_offset, position + _position_offset)); + } + } + + void MultiTileAnimationHandler::update(float dt) { + for (AnimationHandler& animation_handler : animations) { + animation_handler.update(dt); + } + } + + void MultiTileAnimationHandler::render(Framework::vec2 position) { + for (AnimationHandler& animation_handler : animations) { + animation_handler.render(position); + } + } +} diff --git a/src/game/GameStages.cpp b/src/game/GameStages.cpp index 969c463..593f06f 100644 --- a/src/game/GameStages.cpp +++ b/src/game/GameStages.cpp @@ -8,11 +8,66 @@ void GameStage::start() { // Start transition transition->open(); + + // Create animation + Framework::Animation demo_animation = { + {0, 0.5f, Framework::SpriteTransform::NONE}, + {0, 0.5f, Framework::SpriteTransform::ROTATE_90_CW}, + {0, 0.5f, Framework::SpriteTransform::ROTATE_180_CW}, + {0, 0.5f, Framework::SpriteTransform::ROTATE_270_CW} + }; + // Create animation handler + spinning_sword = std::make_unique(graphics_objects->spritesheets[GRAPHICS_OBJECTS::SPRITESHEETS::MAIN_SPRITESHEET], demo_animation); + + // Create big animation + Framework::MultiTileAnimation big_multi_animation = { + { + {0, 0}, + { + {4, 1.0f, Framework::SpriteTransform::NONE}, + {8, 0.4f, Framework::SpriteTransform::NONE}, + {12, 0.2f, Framework::SpriteTransform::NONE}, + {8, 0.4f, Framework::SpriteTransform::NONE}, + } + }, + { + {16, 0}, + { + {5, 1.0f, Framework::SpriteTransform::NONE}, + {9, 0.4f, Framework::SpriteTransform::NONE}, + {13, 0.2f, Framework::SpriteTransform::NONE}, + {9, 0.4f, Framework::SpriteTransform::NONE}, + } + }, + { + {32, 0}, + { + {6, 1.0f, Framework::SpriteTransform::NONE}, + {10, 0.4f, Framework::SpriteTransform::NONE}, + {14, 0.2f, Framework::SpriteTransform::NONE}, + {10, 0.4f, Framework::SpriteTransform::NONE}, + } + }, + { + {48, 0}, + { + {7, 1.0f, Framework::SpriteTransform::NONE}, + {11, 0.4f, Framework::SpriteTransform::NONE}, + {15, 0.2f, Framework::SpriteTransform::NONE}, + {11, 0.4f, Framework::SpriteTransform::NONE}, + } + }, + }; + // Create animation handler + big_animation = std::make_unique(graphics_objects->spritesheets[GRAPHICS_OBJECTS::SPRITESHEETS::MAIN_SPRITESHEET], big_multi_animation); } bool GameStage::update(float dt) { transition->update(dt); + spinning_sword->update(dt); + big_animation->update(dt); + if (input->just_down(Framework::KeyHandler::Key::ESCAPE) || input->just_down(Framework::KeyHandler::Key::P)) { finish(new PausedStage(this), false); } @@ -25,6 +80,9 @@ void GameStage::render() { graphics_objects->spritesheets[GRAPHICS_OBJECTS::SPRITESHEETS::MAIN_SPRITESHEET].sprite(0, Framework::Vec(128, 64)); + spinning_sword->render({ 64, 64 }); + big_animation->render({ 64, 96 }); + transition->render(); }