From be09120d1d044b476ef8b516efbdb526f20d9e2d Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Wed, 3 Feb 2021 01:35:58 -0500 Subject: Added animation system --- CMakeLists.txt | 2 +- src/animation_system.cpp | 122 +++++++++++++++++++++++++++++++++++++++++++++++ src/animation_system.h | 35 ++++++++++++++ src/camera_system.cpp | 2 +- src/game.cpp | 4 +- src/game.h | 18 ++----- src/main.cpp | 24 +++++----- src/party.cpp | 37 +++++++------- src/renderer.cpp | 6 +-- src/sprite.cpp | 110 ------------------------------------------ src/sprite.h | 46 +++++------------- src/system.h | 3 +- src/transform_system.cpp | 8 ++-- 13 files changed, 220 insertions(+), 197 deletions(-) create mode 100644 src/animation_system.cpp create mode 100644 src/animation_system.h delete mode 100644 src/sprite.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fd88ff..f7b6790 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,12 +35,12 @@ add_executable(tanetane src/main.cpp src/renderer.cpp src/mixer.cpp - src/sprite.cpp src/party.cpp src/game.cpp src/map.cpp src/transform_system.cpp src/camera_system.cpp + src/animation_system.cpp ) set_property(TARGET tanetane PROPERTY CXX_STANDARD 17) diff --git a/src/animation_system.cpp b/src/animation_system.cpp new file mode 100644 index 0000000..3320e1d --- /dev/null +++ b/src/animation_system.cpp @@ -0,0 +1,122 @@ +#include "animation_system.h" +#include +#include +#include +#include "game.h" +#include "vector.h" +#include "util.h" + +void AnimationSystem::initSprite(int spriteId, std::string_view filename, Renderer& renderer) { + std::ifstream datafile(filename.data()); + if (!datafile.is_open()) { + throw std::invalid_argument(std::string("Could not find sprite datafile: ") + std::string(filename)); + } + + Sprite& sprite = game_.getSprite(spriteId); + sprite.isAnimated = true; + + char ch; + std::string line; + + std::string imagename; + datafile >> imagename; + sprite.textureId = renderer.loadImageFromFile(imagename); + + std::string framefilename; + datafile >> framefilename; + + std::ifstream framefile(framefilename); + if (!framefile.is_open()) { + throw std::invalid_argument("Could not find frame datafile: " + framefilename); + } + + vec2i cellSize; + framefile >> cellSize.w(); + framefile >> ch; //, + framefile >> cellSize.h(); + std::getline(framefile, line); // cell size + + int framesPerRow; + framefile >> framesPerRow; + std::getline(framefile, line); // frames per row + + int numFrames; + framefile >> numFrames; + std::getline(framefile, line); // frames + std::getline(framefile, line); // blank + + for (int i=0; i> f.size.w(); + framefile >> ch; //, + framefile >> f.size.h(); + framefile >> ch; //, + framefile >> f.center.x(); + framefile >> ch; //, + framefile >> f.center.y(); + std::getline(framefile, line); // blank + + f.srcRect.x = (i % framesPerRow) * cellSize.w(); + f.srcRect.y = (i / framesPerRow) * cellSize.h(); + f.srcRect.w = f.size.w(); + f.srcRect.h = f.size.h(); + + sprite.frames.push_back(std::move(f)); + } + + std::string animLine; + std::getline(datafile, animLine); // blank + while (std::getline(datafile, animLine)) { + std::regex re(R"(([a-z]+)\[([a-z_]+)\]: ([0-9,]+))"); + std::smatch m; + std::regex_match(animLine, m, re); + + std::vector frames; + auto framestrs = splitStr>(m[3], ","); + for (const std::string& f : framestrs) { + frames.push_back(std::stoi(f)); + } + + int animId = sprite.animations.size(); + sprite.animations.push_back(std::move(frames)); + + Direction dir = directionFromString(std::string(m[2])); + sprite.nameDirToAnim[m[1]][dir] = animId; + } + + updateAnimation(spriteId); +} + +void AnimationSystem::tick(double dt) { + animTimer_.accumulate(dt); + while (animTimer_.step()) { + for (Sprite& sprite : game_.getSprites() | game_.spriteView()) { + sprite.animationFrame++; + if (sprite.animationFrame >= sprite.animations[sprite.animationId].size()) { + sprite.animationFrame = 0; + } + } + } +} + +void AnimationSystem::setSpriteDirection(int spriteId, Direction dir) { + Sprite& sprite = game_.getSprite(spriteId); + if (sprite.dir != dir) { + sprite.dir = dir; + updateAnimation(spriteId); + } +} + +void AnimationSystem::setSpriteAnimation(int spriteId, std::string_view name) { + Sprite& sprite = game_.getSprite(spriteId); + if (sprite.animationName != name) { + sprite.animationName = name; + updateAnimation(spriteId); + } +} + +void AnimationSystem::updateAnimation(int spriteId) { + Sprite& sprite = game_.getSprite(spriteId); + sprite.animationId = sprite.nameDirToAnim[sprite.animationName][sprite.dir]; + sprite.animationFrame = 0; +} diff --git a/src/animation_system.h b/src/animation_system.h new file mode 100644 index 0000000..2e13784 --- /dev/null +++ b/src/animation_system.h @@ -0,0 +1,35 @@ +#ifndef ANIMATION_SYSTEM_H_CCCC7CB8 +#define ANIMATION_SYSTEM_H_CCCC7CB8 + +#include +#include "direction.h" +#include "system.h" +#include "timer.h" + +class Game; +class Renderer; + +class AnimationSystem : public System { +public: + + static constexpr SystemKey Key = SystemKey::Animation; + + AnimationSystem(Game& game) : game_(game) {} + + void tick(double dt) override; + + void initSprite(int spriteId, std::string_view filename, Renderer& renderer); + + void setSpriteDirection(int spriteId, Direction dir); + + void setSpriteAnimation(int spriteId, std::string_view name); + +private: + + void updateAnimation(int spriteId); + + Game& game_; + Timer animTimer_ {1000/5};//30fps * 1000 t/s;; +}; + +#endif /* end of include guard: ANIMATION_SYSTEM_H_CCCC7CB8 */ diff --git a/src/camera_system.cpp b/src/camera_system.cpp index abeba86..4709d01 100644 --- a/src/camera_system.cpp +++ b/src/camera_system.cpp @@ -9,7 +9,7 @@ void CameraSystem::tick(double dt) { const Map& map = game_.getMap(); vec2i mapBounds = map.getMapSize() * map.getTileSize(); - pos_ = follow.loc() - (fov_ / 2); + pos_ = follow.loc - (fov_ / 2); if (pos_.x() < 0) { pos_.x() = 0; diff --git a/src/game.cpp b/src/game.cpp index bb20d74..4ea7fd2 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1,8 +1,8 @@ #include "game.h" -int Game::addSprite(Sprite sprite) { +int Game::emplaceSprite() { int id = sprites_.size(); - sprites_.push_back(std::move(sprite)); + sprites_.emplace_back(); spriteIds_.push_back(id); return id; } diff --git a/src/game.h b/src/game.h index f89c707..f8e4b0d 100644 --- a/src/game.h +++ b/src/game.h @@ -38,7 +38,7 @@ public: }); } - int addSprite(Sprite sprite); + int emplaceSprite(); const Sprite& getSprite(int id) const { return sprites_.at(id); @@ -58,18 +58,10 @@ public: }); } - void setSpriteState(int id, std::string state) { - sprites_[id].setState(std::move(state)); - } - - void setSpriteDirection(int id, Direction dir) { - sprites_[id].setDirection(dir); - } - - void tickSpriteAnim() { - for (Sprite& sprite : sprites_) { - sprite.tickAnim(); - } + auto spriteView() { + return ranges::views::transform([&] (int id) -> Sprite& { + return sprites_.at(id); + }); } void setMap(std::unique_ptr map) { map_ = std::move(map); } diff --git a/src/main.cpp b/src/main.cpp index a092114..ea27511 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,26 +8,34 @@ #include "mixer.h" #include "transform_system.h" #include "camera_system.h" +#include "animation_system.h" void loop(Renderer& renderer, Mixer& mixer) { Game game; game.emplaceSystem(); game.emplaceSystem(); + game.emplaceSystem(); Input keystate; auto map = std::make_unique("../res/map1.tmx", renderer); game.setMap(std::move(map)); - int lucasSprite = game.addSprite(Sprite("../res/lucas_anim.txt", renderer)); - int kumaSprite = game.addSprite(Sprite("../res/kuma_anim.txt", renderer)); - int dusterSprite = game.addSprite(Sprite("../res/duster_anim.txt", renderer)); - int boneySprite = game.addSprite(Sprite("../res/boney_anim.txt", renderer)); - + int lucasSprite = game.emplaceSprite(); game.getSystem().initSprite(lucasSprite, {32, 32}); + game.getSystem().initSprite(lucasSprite, "../res/lucas_anim.txt", renderer); + + int kumaSprite = game.emplaceSprite(); game.getSystem().initSprite(kumaSprite, {32, 32}); + game.getSystem().initSprite(kumaSprite, "../res/kuma_anim.txt", renderer); + + int dusterSprite = game.emplaceSprite(); game.getSystem().initSprite(dusterSprite, {32, 32}); + game.getSystem().initSprite(dusterSprite, "../res/duster_anim.txt", renderer); + + int boneySprite = game.emplaceSprite(); game.getSystem().initSprite(boneySprite, {32, 32}); + game.getSystem().initSprite(boneySprite, "../res/boney_anim.txt", renderer); Party party; party.addMember(game, lucasSprite); @@ -41,7 +49,6 @@ void loop(Renderer& renderer, Mixer& mixer) { renderer.render(game); Timer inputTimer(33); - Timer animTimer(1000/5);//30fps * 1000 t/s; size_t lastTime = SDL_GetTicks(); @@ -72,11 +79,6 @@ void loop(Renderer& renderer, Mixer& mixer) { party.move(game, mixer, keystate); } - animTimer.accumulate(frameTime); - while (animTimer.step()) { - game.tickSpriteAnim(); - } - for (System& system : game.systems()) { system.tick(frameTime); } diff --git a/src/party.cpp b/src/party.cpp index b717281..8b0c34e 100644 --- a/src/party.cpp +++ b/src/party.cpp @@ -2,6 +2,7 @@ #include "consts.h" #include "mixer.h" #include "transform_system.h" +#include "animation_system.h" void Party::addMember(Game& game, int spriteId) { int index = members_.size(); @@ -12,12 +13,12 @@ void Party::addMember(Game& game, int spriteId) { if (index > 0) { const Sprite& sprite = game.getSprite(spriteId); - newMember.movement = std::deque(PARTY_FRAME_DELAY * index, {.pos = sprite.loc(), .dir = sprite.getDirection()}); + newMember.movement = std::deque(PARTY_FRAME_DELAY * index, {.pos = sprite.loc, .dir = sprite.dir}); } members_.push_back(std::move(newMember)); - game.setSpriteState(spriteId, "still"); + game.getSystem().setSpriteAnimation(spriteId, "still"); } void Party::move(Game& game, Mixer& mixer, const Input& keystate) { @@ -33,7 +34,7 @@ void Party::move(Game& game, Mixer& mixer, const Input& keystate) { } else { if (state_ == State::Normal) { for (int i = 0; i < members_.size(); i++) { - game.setSpriteState(members_[i].spriteId, "still"); + game.getSystem().setSpriteAnimation(members_[i].spriteId, "still"); } } @@ -72,16 +73,16 @@ void Party::move(Game& game, Mixer& mixer, const Input& keystate) { lastDir_ = dir; const Sprite& p1 = game.getSprite(members_[0].spriteId); - vec2i pLoc = p1.loc(); + vec2i pLoc = p1.loc; if (state_ == State::Crouching) { for (int i = 0; i < members_.size(); i++) { - game.setSpriteDirection(members_[i].spriteId, dir); + game.getSystem().setSpriteDirection(members_[i].spriteId, dir); } return; } else { - game.setSpriteDirection(members_[0].spriteId, dir); + game.getSystem().setSpriteDirection(members_[0].spriteId, dir); } int speed = MOVEMENT_SPEED; @@ -89,7 +90,7 @@ void Party::move(Game& game, Mixer& mixer, const Input& keystate) { speed *= 2; } else { for (int i = 0; i < members_.size(); i++) { - game.setSpriteState(members_[i].spriteId, "walk"); + game.getSystem().setSpriteAnimation(members_[i].spriteId, "walk"); } } @@ -101,9 +102,9 @@ void Party::move(Game& game, Mixer& mixer, const Input& keystate) { const vec2i UL_COL_BOX = { 8, 8 }; const vec2i DR_COL_BOX = { 4, 0 }; - vec2i oldColPosUL = (p1.loc() - UL_COL_BOX) / map.getTileSize(); + vec2i oldColPosUL = (p1.loc - UL_COL_BOX) / map.getTileSize(); vec2i newColPosUL = (pLoc - UL_COL_BOX) / map.getTileSize(); - vec2i oldColPosDR = (p1.loc() + DR_COL_BOX) / map.getTileSize(); + vec2i oldColPosDR = (p1.loc + DR_COL_BOX) / map.getTileSize(); vec2i newColPosDR = (pLoc + DR_COL_BOX) / map.getTileSize(); if (dirHasDir(dir, Direction::right) && @@ -111,7 +112,7 @@ void Party::move(Game& game, Mixer& mixer, const Input& keystate) { for (int y = newColPosUL.y(); y <= newColPosDR.y(); y++) { if (map.isBlocked(newColPosDR.x(), y)) { blocked = true; - pLoc.x() = p1.loc().x();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1; + pLoc.x() = p1.loc.x();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1; break; } } @@ -122,7 +123,7 @@ void Party::move(Game& game, Mixer& mixer, const Input& keystate) { for (int y = newColPosUL.y(); y <= newColPosDR.y(); y++) { if (map.isBlocked(newColPosUL.x(), y)) { blocked = true; - pLoc.x() = p1.loc().x();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1; + pLoc.x() = p1.loc.x();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1; break; } } @@ -133,7 +134,7 @@ void Party::move(Game& game, Mixer& mixer, const Input& keystate) { for (int x = newColPosUL.x(); x <= newColPosDR.x(); x++) { if (map.isBlocked(x, newColPosDR.y())) { blocked = true; - pLoc.y() = p1.loc().y();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1; + pLoc.y() = p1.loc.y();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1; break; } } @@ -144,7 +145,7 @@ void Party::move(Game& game, Mixer& mixer, const Input& keystate) { for (int x = newColPosUL.x(); x <= newColPosDR.x(); x++) { if (map.isBlocked(x, newColPosUL.y())) { blocked = true; - pLoc.y() = p1.loc().y();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1; + pLoc.y() = p1.loc.y();//(newColPosDR * map.getTileSize() - (collisionBox / 2)).x() - 1; break; } } @@ -156,14 +157,14 @@ void Party::move(Game& game, Mixer& mixer, const Input& keystate) { } // Move everything - if (pLoc != p1.loc()) { + if (pLoc != p1.loc) { game.getSystem().moveSprite(members_[0].spriteId, pLoc); for (int i = 1; i < members_.size(); i++) { const Sprite& pNext = game.getSprite(members_[i].spriteId); const Movement& posdir = members_[i].movement.front(); game.getSystem().moveSprite(members_[i].spriteId, posdir.pos); - game.setSpriteDirection(members_[i].spriteId, posdir.dir); + game.getSystem().setSpriteDirection(members_[i].spriteId, posdir.dir); members_[i].movement.pop_front(); members_[i].movement.push_back({.pos = pLoc, .dir = dir}); @@ -178,7 +179,7 @@ void Party::beginCrouch(Game& game) { state_ = State::Crouching; for (int i = 0; i < members_.size(); i++) { - game.setSpriteState(members_[i].spriteId, "crouch"); + game.getSystem().setSpriteAnimation(members_[i].spriteId, "crouch"); } } } @@ -188,7 +189,7 @@ void Party::endCrouch(Game& game) { state_ = State::Running; for (int i = 0; i < members_.size(); i++) { - game.setSpriteState(members_[i].spriteId, "run"); + game.getSystem().setSpriteAnimation(members_[i].spriteId, "run"); // Halve the movement buffer for the followers. if (i > 0) { @@ -212,7 +213,7 @@ void Party::stopRunning(Game& game) { // Double the movement buffer for the followers. for (int i = 1; i < members_.size(); i++) { std::deque newMove; - vec2i lastPos = game.getSprite(members_[i].spriteId).loc(); + vec2i lastPos = game.getSprite(members_[i].spriteId).loc; while (!members_[i].movement.empty()) { Movement m1 = members_[i].movement.front(); diff --git a/src/renderer.cpp b/src/renderer.cpp index d9ce396..92083db 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -112,10 +112,10 @@ void Renderer::render(Game& game) { SDL_RenderCopy(ren_.get(), renLay1_.get(), nullptr, nullptr); for (const Sprite& sprite : game.getSystem().getSpritesByY() | game.spriteView()) { - const SpriteFrame& frame = sprite.getFrame(); + const SpriteFrame& frame = sprite.frames.at(sprite.animations.at(sprite.animationId).at(sprite.animationFrame)); const SDL_Rect& src = frame.srcRect; - SDL_Rect dest { sprite.loc().x() - frame.center.x(), sprite.loc().y() - frame.center.y(), frame.size.w(), frame.size.h() }; - SDL_RenderCopy(ren_.get(), textures_.at(sprite.getTextureId()).get(), &src, &dest); + SDL_Rect dest { sprite.loc.x() - frame.center.x(), sprite.loc.y() - frame.center.y(), frame.size.w(), frame.size.h() }; + SDL_RenderCopy(ren_.get(), textures_.at(sprite.textureId).get(), &src, &dest); } SDL_RenderCopy(ren_.get(), renLay0_.get(), nullptr, nullptr); diff --git a/src/sprite.cpp b/src/sprite.cpp deleted file mode 100644 index b84f4ce..0000000 --- a/src/sprite.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "sprite.h" -#include -#include -#include -#include -#include "util.h" - -Sprite::Sprite(std::string_view filename, Renderer& renderer) { - std::ifstream datafile(filename.data()); - if (!datafile.is_open()) { - throw std::invalid_argument(std::string("Could not find sprite datafile: ") + std::string(filename)); - } - - char ch; - std::string line; - - std::string imagename; - datafile >> imagename; - textureId_ = renderer.loadImageFromFile(imagename); - - std::string framefilename; - datafile >> framefilename; - - std::ifstream framefile(framefilename); - if (!framefile.is_open()) { - throw std::invalid_argument("Could not find frame datafile: " + framefilename); - } - - vec2i cellSize; - framefile >> cellSize.w(); - framefile >> ch; //, - framefile >> cellSize.h(); - std::getline(framefile, line); // cell size - - int framesPerRow; - framefile >> framesPerRow; - std::getline(framefile, line); // frames per row - - int numFrames; - framefile >> numFrames; - std::getline(framefile, line); // frames - std::getline(framefile, line); // blank - - for (int i=0; i> f.size.w(); - framefile >> ch; //, - framefile >> f.size.h(); - framefile >> ch; //, - framefile >> f.center.x(); - framefile >> ch; //, - framefile >> f.center.y(); - std::getline(framefile, line); // blank - - f.srcRect.x = (i % framesPerRow) * cellSize.w(); - f.srcRect.y = (i / framesPerRow) * cellSize.h(); - f.srcRect.w = f.size.w(); - f.srcRect.h = f.size.h(); - - frames_.push_back(std::move(f)); - } - - std::string animLine; - std::getline(datafile, animLine); // blank - while (std::getline(datafile, animLine)) { - std::regex re(R"(([a-z]+)\[([a-z_]+)\]: ([0-9,]+))"); - std::smatch m; - std::regex_match(animLine, m, re); - - std::vector frames; - auto framestrs = splitStr>(m[3], ","); - for (const std::string& f : framestrs) { - frames.push_back(std::stoi(f)); - } - - int animId = animations_.size(); - animations_.push_back(std::move(frames)); - - Direction dir = directionFromString(std::string(m[2])); - stateDirToAnim_[m[1]][dir] = animId; - } - - updateAnimation(); -} - -void Sprite::setDirection(Direction dir) { - if (curDir_ != dir) { - curDir_ = dir; - updateAnimation(); - } -} - -void Sprite::setState(std::string state) { - if (state_ != state) { - state_ = state; - updateAnimation(); - } -} - -void Sprite::updateAnimation() { - curAnim_ = stateDirToAnim_[state_][curDir_]; - curFrame_ = 0; -} - -void Sprite::tickAnim() { - curFrame_++; - if (curFrame_ >= animations_[curAnim_].size()) { - curFrame_ = 0; - } -} \ No newline at end of file diff --git a/src/sprite.h b/src/sprite.h index dc26fad..e842192 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -17,39 +17,19 @@ struct SpriteFrame { class Sprite { public: - Sprite(std::string_view filename, Renderer& renderer); - - int getTextureId() const { - return textureId_; - } - - const vec2i& loc() const { return loc_; } - - vec2i& loc() { return loc_; } - - const SpriteFrame& getFrame() const { return frames_.at(animations_[curAnim_][curFrame_]); } - - void setDirection(Direction dir); - - Direction getDirection() const { return curDir_; } - - void setState(std::string state); - - void tickAnim(); - -private: - - void updateAnimation(); - - int textureId_; - vec2i loc_ { 0, 0 }; - Direction curDir_ = Direction::down; - std::string state_; - int curAnim_ = 0; - int curFrame_ = 0; - std::vector frames_; - std::vector> animations_; - std::map> stateDirToAnim_; + // Transform + vec2i loc { 0, 0 }; + + // Animation + bool isAnimated = false; + int textureId; + Direction dir = Direction::down; + std::string animationName; + int animationId = 0; + int animationFrame = 0; + std::vector frames; + std::vector> animations; + std::map> nameDirToAnim; }; #endif /* end of include guard: SPRITE_H_70503825 */ diff --git a/src/system.h b/src/system.h index 0fb1bcd..c601bc7 100644 --- a/src/system.h +++ b/src/system.h @@ -3,7 +3,8 @@ enum class SystemKey { Transform, - Camera + Camera, + Animation }; class System { diff --git a/src/transform_system.cpp b/src/transform_system.cpp index e53fb7d..0e602e0 100644 --- a/src/transform_system.cpp +++ b/src/transform_system.cpp @@ -3,17 +3,17 @@ void TransformSystem::initSprite(int spriteId, vec2i loc) { Sprite& sprite = game_.getSprite(spriteId); - sprite.loc() = loc; + sprite.loc = loc; spritesByY_.emplace(loc.y(), spriteId); } void TransformSystem::moveSprite(int spriteId, vec2i newLoc) { Sprite& sprite = game_.getSprite(spriteId); - bool changedY = (sprite.loc().y() != newLoc.y()); + bool changedY = (sprite.loc.y() != newLoc.y()); if (changedY) { - spritesByY_.erase(std::make_tuple(sprite.loc().y(), spriteId)); + spritesByY_.erase(std::make_tuple(sprite.loc.y(), spriteId)); } - sprite.loc() = newLoc; + sprite.loc = newLoc; if (changedY) { spritesByY_.emplace(newLoc.y(), spriteId); } -- cgit 1.4.1