diff options
| -rw-r--r-- | res/lucas.png | bin | 1663 -> 5334 bytes | |||
| -rw-r--r-- | res/lucas_anim.txt | 18 | ||||
| -rw-r--r-- | src/direction.h | 30 | ||||
| -rw-r--r-- | src/game.h | 14 | ||||
| -rw-r--r-- | src/main.cpp | 18 | ||||
| -rw-r--r-- | src/party.cpp | 33 | ||||
| -rw-r--r-- | src/party.h | 1 | ||||
| -rw-r--r-- | src/renderer.cpp | 5 | ||||
| -rw-r--r-- | src/sprite.cpp | 79 | ||||
| -rw-r--r-- | src/sprite.h | 24 | ||||
| -rw-r--r-- | src/util.h | 39 |
11 files changed, 252 insertions, 9 deletions
| diff --git a/res/lucas.png b/res/lucas.png index 9c5a067..4ac7157 100644 --- a/res/lucas.png +++ b/res/lucas.png | |||
| Binary files differ | |||
| diff --git a/res/lucas_anim.txt b/res/lucas_anim.txt new file mode 100644 index 0000000..ff37580 --- /dev/null +++ b/res/lucas_anim.txt | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | ../res/lucas.png | ||
| 2 | 17,26 | ||
| 3 | still[down]: 1 | ||
| 4 | still[left]: 4 | ||
| 5 | still[down_left]: 7 | ||
| 6 | still[down_right]: 10 | ||
| 7 | still[up]: 13 | ||
| 8 | still[right]: 16 | ||
| 9 | still[up_left]: 19 | ||
| 10 | still[up_right]: 22 | ||
| 11 | walk[down]: 0,1,2 | ||
| 12 | walk[left]: 3,4,5 | ||
| 13 | walk[down_left]: 6,7,8 | ||
| 14 | walk[down_right]: 9,10,11 | ||
| 15 | walk[up]: 12,13,14 | ||
| 16 | walk[right]: 15,16,17 | ||
| 17 | walk[up_left]: 18,19,20 | ||
| 18 | walk[up_right]: 21,22,23 \ No newline at end of file | ||
| diff --git a/src/direction.h b/src/direction.h new file mode 100644 index 0000000..0679a00 --- /dev/null +++ b/src/direction.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #ifndef DIRECTION_H_AB66A90E | ||
| 2 | #define DIRECTION_H_AB66A90E | ||
| 3 | |||
| 4 | #include <string> | ||
| 5 | #include <stdexcept> | ||
| 6 | |||
| 7 | enum class Direction { | ||
| 8 | up, | ||
| 9 | up_right, | ||
| 10 | right, | ||
| 11 | down_right, | ||
| 12 | down, | ||
| 13 | down_left, | ||
| 14 | left, | ||
| 15 | up_left | ||
| 16 | }; | ||
| 17 | |||
| 18 | inline Direction directionFromString(std::string_view str) { | ||
| 19 | if (str == "up") return Direction::up; | ||
| 20 | if (str == "up_right") return Direction::up_right; | ||
| 21 | if (str == "right") return Direction::right; | ||
| 22 | if (str == "down_right") return Direction::down_right; | ||
| 23 | if (str == "down") return Direction::down; | ||
| 24 | if (str == "down_left") return Direction::down_left; | ||
| 25 | if (str == "left") return Direction::left; | ||
| 26 | if (str == "up_left") return Direction::up_left; | ||
| 27 | throw std::invalid_argument("Invalid direction: " + std::string(str)); | ||
| 28 | } | ||
| 29 | |||
| 30 | #endif /* end of include guard: DIRECTION_H_AB66A90E */ | ||
| diff --git a/src/game.h b/src/game.h index abb70ce..a8626fa 100644 --- a/src/game.h +++ b/src/game.h | |||
| @@ -40,6 +40,20 @@ public: | |||
| 40 | 40 | ||
| 41 | void moveSprite(int id, vec2i newLoc); | 41 | void moveSprite(int id, vec2i newLoc); |
| 42 | 42 | ||
| 43 | void setSpriteWalking(int id, bool walking) { | ||
| 44 | sprites_[id].setWalking(walking); | ||
| 45 | } | ||
| 46 | |||
| 47 | void setSpriteDirection(int id, Direction dir) { | ||
| 48 | sprites_[id].setDirection(dir); | ||
| 49 | } | ||
| 50 | |||
| 51 | void tickSpriteAnim() { | ||
| 52 | for (Sprite& sprite : sprites_) { | ||
| 53 | sprite.tickAnim(); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 43 | private: | 57 | private: |
| 44 | 58 | ||
| 45 | std::vector<Sprite> sprites_; | 59 | std::vector<Sprite> sprites_; |
| diff --git a/src/main.cpp b/src/main.cpp index 5a4373b..1d3d58e 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -7,18 +7,23 @@ void loop(Renderer& renderer) { | |||
| 7 | Game game; | 7 | Game game; |
| 8 | Input keystate; | 8 | Input keystate; |
| 9 | 9 | ||
| 10 | int kumaSprite = game.addSprite(Sprite("../res/kumatora.png", renderer, 17, 31)); | 10 | //int kumaSprite = game.addSprite(Sprite("../res/kumatora.png", renderer, 17, 31)); |
| 11 | int lucasSprite = game.addSprite(Sprite("../res/lucas.png", renderer, 17, 27)); | 11 | int lucasSprite = game.addSprite(Sprite("../res/lucas_anim.txt", renderer)); |
| 12 | int lucasSprite2 = game.addSprite(Sprite("../res/lucas_anim.txt", renderer)); | ||
| 12 | 13 | ||
| 13 | Party party; | 14 | Party party; |
| 14 | party.addMember(game, lucasSprite); | 15 | party.addMember(game, lucasSprite); |
| 15 | party.addMember(game, kumaSprite); | 16 | party.addMember(game, lucasSprite2); |
| 17 | //party.addMember(game, kumaSprite); | ||
| 16 | 18 | ||
| 17 | renderer.render(game); | 19 | renderer.render(game); |
| 18 | 20 | ||
| 19 | size_t inputDt = 50; | 21 | size_t inputDt = 50; |
| 20 | size_t inputAcc = 0; | 22 | size_t inputAcc = 0; |
| 21 | 23 | ||
| 24 | size_t animDt = 1000/5;//30fps * 1000 t/s; | ||
| 25 | size_t animAcc = 0; | ||
| 26 | |||
| 22 | size_t lastTime = SDL_GetTicks(); | 27 | size_t lastTime = SDL_GetTicks(); |
| 23 | 28 | ||
| 24 | for (;;) { | 29 | for (;;) { |
| @@ -46,6 +51,13 @@ void loop(Renderer& renderer) { | |||
| 46 | party.move(game, keystate); | 51 | party.move(game, keystate); |
| 47 | } | 52 | } |
| 48 | 53 | ||
| 54 | animAcc += frameTime; | ||
| 55 | while (animAcc > animDt) { | ||
| 56 | animAcc -= animDt; | ||
| 57 | |||
| 58 | game.tickSpriteAnim(); | ||
| 59 | } | ||
| 60 | |||
| 49 | renderer.render(game); | 61 | renderer.render(game); |
| 50 | } | 62 | } |
| 51 | } | 63 | } |
| diff --git a/src/party.cpp b/src/party.cpp index 1ff4bb4..19b9557 100644 --- a/src/party.cpp +++ b/src/party.cpp | |||
| @@ -12,6 +12,7 @@ void Party::addMember(Game& game, int spriteId) { | |||
| 12 | 12 | ||
| 13 | for (int i = 0; i < PARTY_FRAME_DELAY * index; i++) { | 13 | for (int i = 0; i < PARTY_FRAME_DELAY * index; i++) { |
| 14 | newMember.nextPosition.push_back(sprite.loc()); | 14 | newMember.nextPosition.push_back(sprite.loc()); |
| 15 | newMember.nextDirection.push_back(sprite.getDirection()); | ||
| 15 | } | 16 | } |
| 16 | } | 17 | } |
| 17 | 18 | ||
| @@ -25,35 +26,67 @@ void Party::move(Game& game, const Input& keystate) { | |||
| 25 | 26 | ||
| 26 | const Sprite& p1 = game.getSprite(members_[0].spriteId); | 27 | const Sprite& p1 = game.getSprite(members_[0].spriteId); |
| 27 | vec2i pLoc = p1.loc(); | 28 | vec2i pLoc = p1.loc(); |
| 29 | Direction dir = Direction::left; | ||
| 28 | 30 | ||
| 29 | if (keystate.up) | 31 | if (keystate.up) |
| 30 | { | 32 | { |
| 31 | pLoc.y() -= MOVEMENT_SPEED; | 33 | pLoc.y() -= MOVEMENT_SPEED; |
| 34 | dir = Direction::up; | ||
| 32 | } | 35 | } |
| 33 | 36 | ||
| 34 | if (keystate.down) | 37 | if (keystate.down) |
| 35 | { | 38 | { |
| 36 | pLoc.y() += MOVEMENT_SPEED; | 39 | pLoc.y() += MOVEMENT_SPEED; |
| 40 | dir = Direction::down; | ||
| 37 | } | 41 | } |
| 38 | 42 | ||
| 39 | if (keystate.left) | 43 | if (keystate.left) |
| 40 | { | 44 | { |
| 41 | pLoc.x() -= MOVEMENT_SPEED; | 45 | pLoc.x() -= MOVEMENT_SPEED; |
| 46 | |||
| 47 | if (dir == Direction::up) { | ||
| 48 | dir = Direction::up_left; | ||
| 49 | } else if (dir == Direction::down) { | ||
| 50 | dir = Direction::down_left; | ||
| 51 | } else { | ||
| 52 | dir = Direction::left; | ||
| 53 | } | ||
| 42 | } | 54 | } |
| 43 | 55 | ||
| 44 | if (keystate.right) | 56 | if (keystate.right) |
| 45 | { | 57 | { |
| 46 | pLoc.x() += MOVEMENT_SPEED; | 58 | pLoc.x() += MOVEMENT_SPEED; |
| 59 | |||
| 60 | if (dir == Direction::up) { | ||
| 61 | dir = Direction::up_right; | ||
| 62 | } else if (dir == Direction::down) { | ||
| 63 | dir = Direction::down_right; | ||
| 64 | } else { | ||
| 65 | dir = Direction::right; | ||
| 66 | } | ||
| 47 | } | 67 | } |
| 48 | 68 | ||
| 49 | if (keystate.up || keystate.down || keystate.left || keystate.right) { | 69 | if (keystate.up || keystate.down || keystate.left || keystate.right) { |
| 70 | for (int i = 0; i < members_.size(); i++) { | ||
| 71 | game.setSpriteWalking(members_[i].spriteId, true); | ||
| 72 | } | ||
| 73 | |||
| 50 | game.moveSprite(members_[0].spriteId, pLoc); | 74 | game.moveSprite(members_[0].spriteId, pLoc); |
| 75 | game.setSpriteDirection(members_[0].spriteId, dir); | ||
| 51 | 76 | ||
| 52 | for (int i = 1; i < members_.size(); i++) { | 77 | for (int i = 1; i < members_.size(); i++) { |
| 53 | const Sprite& pNext = game.getSprite(members_[i].spriteId); | 78 | const Sprite& pNext = game.getSprite(members_[i].spriteId); |
| 54 | members_[i].nextPosition.push_back(pLoc); | 79 | members_[i].nextPosition.push_back(pLoc); |
| 55 | game.moveSprite(members_[i].spriteId, members_[i].nextPosition.front()); | 80 | game.moveSprite(members_[i].spriteId, members_[i].nextPosition.front()); |
| 56 | members_[i].nextPosition.pop_front(); | 81 | members_[i].nextPosition.pop_front(); |
| 82 | |||
| 83 | members_[i].nextDirection.push_back(dir); | ||
| 84 | game.setSpriteDirection(members_[i].spriteId, members_[i].nextDirection.front()); | ||
| 85 | members_[i].nextDirection.pop_front(); | ||
| 86 | } | ||
| 87 | } else { | ||
| 88 | for (int i = 0; i < members_.size(); i++) { | ||
| 89 | game.setSpriteWalking(members_[i].spriteId, false); | ||
| 57 | } | 90 | } |
| 58 | } | 91 | } |
| 59 | } \ No newline at end of file | 92 | } \ No newline at end of file |
| diff --git a/src/party.h b/src/party.h index 2a6ffc9..9b0aa89 100644 --- a/src/party.h +++ b/src/party.h | |||
| @@ -17,6 +17,7 @@ private: | |||
| 17 | struct PartyMember { | 17 | struct PartyMember { |
| 18 | int spriteId; | 18 | int spriteId; |
| 19 | std::deque<vec2i> nextPosition; | 19 | std::deque<vec2i> nextPosition; |
| 20 | std::deque<Direction> nextDirection; | ||
| 20 | }; | 21 | }; |
| 21 | 22 | ||
| 22 | std::vector<PartyMember> members_; | 23 | std::vector<PartyMember> members_; |
| diff --git a/src/renderer.cpp b/src/renderer.cpp index 8053de0..77f0a0c 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp | |||
| @@ -49,8 +49,9 @@ void Renderer::render(Game& game) { | |||
| 49 | SDL_RenderClear(ren_.get()); | 49 | SDL_RenderClear(ren_.get()); |
| 50 | 50 | ||
| 51 | for (const Sprite& sprite : game.getSpritesByY() | game.spriteView()) { | 51 | for (const Sprite& sprite : game.getSpritesByY() | game.spriteView()) { |
| 52 | SDL_Rect dest { sprite.loc().x(), sprite.loc().y(), sprite.size().w(), sprite.size().h() }; | 52 | SDL_Rect src { sprite.getFrame() * sprite.size().w(), 0, sprite.size().w(), sprite.size().h() }; |
| 53 | SDL_RenderCopy(ren_.get(), textures_.at(sprite.getTextureId()).get(), nullptr, &dest); | 53 | SDL_Rect dest { sprite.loc().x(), sprite.loc().y(), sprite.size().w() * 4, sprite.size().h() * 4 }; |
| 54 | SDL_RenderCopy(ren_.get(), textures_.at(sprite.getTextureId()).get(), &src, &dest); | ||
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | SDL_SetRenderTarget(ren_.get(), nullptr); | 57 | SDL_SetRenderTarget(ren_.get(), nullptr); |
| diff --git a/src/sprite.cpp b/src/sprite.cpp index c8c4656..cc196ae 100644 --- a/src/sprite.cpp +++ b/src/sprite.cpp | |||
| @@ -1,7 +1,80 @@ | |||
| 1 | #include "sprite.h" | 1 | #include "sprite.h" |
| 2 | #include <SDL_image.h> | 2 | #include <SDL_image.h> |
| 3 | #include <fstream> | ||
| 4 | #include <list> | ||
| 5 | #include <regex> | ||
| 6 | #include "util.h" | ||
| 3 | 7 | ||
| 4 | Sprite::Sprite(std::string_view filename, Renderer& renderer, int width, int height) { | 8 | Sprite::Sprite(std::string_view filename, Renderer& renderer) { |
| 5 | textureId_ = renderer.loadImageFromFile(filename); | 9 | std::ifstream datafile(filename.data()); |
| 6 | size_ = { width*4, height*4 }; | 10 | if (!datafile.is_open()) { |
| 11 | throw std::invalid_argument(std::string("Could not find sprite datafile: ") + std::string(filename)); | ||
| 12 | } | ||
| 13 | |||
| 14 | char ch; | ||
| 15 | |||
| 16 | std::string imagename; | ||
| 17 | datafile >> imagename; | ||
| 18 | textureId_ = renderer.loadImageFromFile(imagename); | ||
| 19 | |||
| 20 | datafile >> size_.w(); | ||
| 21 | datafile >> ch; //, | ||
| 22 | datafile >> size_.h(); | ||
| 23 | |||
| 24 | std::string animLine; | ||
| 25 | std::getline(datafile, animLine); // blank | ||
| 26 | while (std::getline(datafile, animLine)) { | ||
| 27 | std::regex re(R"(([a-z]+)\[([a-z_]+)\]: ([0-9,]+))"); | ||
| 28 | std::smatch m; | ||
| 29 | std::regex_match(animLine, m, re); | ||
| 30 | |||
| 31 | std::vector<int> frames; | ||
| 32 | auto framestrs = splitStr<std::list<std::string>>(m[3], ","); | ||
| 33 | for (const std::string& f : framestrs) { | ||
| 34 | frames.push_back(std::stoi(f)); | ||
| 35 | } | ||
| 36 | |||
| 37 | int animId = animations_.size(); | ||
| 38 | animations_.push_back(std::move(frames)); | ||
| 39 | |||
| 40 | Direction dir = directionFromString(std::string(m[2])); | ||
| 41 | |||
| 42 | if (m[1] == "still") { | ||
| 43 | stillAnims_[dir] = animId; | ||
| 44 | } else { | ||
| 45 | walkingAnims_[dir] = animId; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | updateAnimation(); | ||
| 50 | } | ||
| 51 | |||
| 52 | void Sprite::setDirection(Direction dir) { | ||
| 53 | if (curDir_ != dir) { | ||
| 54 | curDir_ = dir; | ||
| 55 | updateAnimation(); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | void Sprite::setWalking(bool walking) { | ||
| 60 | if (isWalking_ != walking) { | ||
| 61 | isWalking_ = walking; | ||
| 62 | updateAnimation(); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | void Sprite::updateAnimation() { | ||
| 67 | if (isWalking_) { | ||
| 68 | curAnim_ = walkingAnims_[curDir_]; | ||
| 69 | } else { | ||
| 70 | curAnim_ = stillAnims_[curDir_]; | ||
| 71 | } | ||
| 72 | curFrame_ = 0; | ||
| 73 | } | ||
| 74 | |||
| 75 | void Sprite::tickAnim() { | ||
| 76 | curFrame_++; | ||
| 77 | if (curFrame_ >= animations_[curAnim_].size()) { | ||
| 78 | curFrame_ = 0; | ||
| 79 | } | ||
| 7 | } \ No newline at end of file | 80 | } \ No newline at end of file |
| diff --git a/src/sprite.h b/src/sprite.h index 1f917d9..5e8003b 100644 --- a/src/sprite.h +++ b/src/sprite.h | |||
| @@ -1,14 +1,17 @@ | |||
| 1 | #ifndef SPRITE_H_70503825 | 1 | #ifndef SPRITE_H_70503825 |
| 2 | #define SPRITE_H_70503825 | 2 | #define SPRITE_H_70503825 |
| 3 | 3 | ||
| 4 | #include <map> | ||
| 4 | #include <string_view> | 5 | #include <string_view> |
| 6 | #include <vector> | ||
| 7 | #include "direction.h" | ||
| 5 | #include "renderer.h" | 8 | #include "renderer.h" |
| 6 | #include "vector.h" | 9 | #include "vector.h" |
| 7 | 10 | ||
| 8 | class Sprite { | 11 | class Sprite { |
| 9 | public: | 12 | public: |
| 10 | 13 | ||
| 11 | Sprite(std::string_view filename, Renderer& renderer, int width, int height); | 14 | Sprite(std::string_view filename, Renderer& renderer); |
| 12 | 15 | ||
| 13 | int getTextureId() const { | 16 | int getTextureId() const { |
| 14 | return textureId_; | 17 | return textureId_; |
| @@ -22,11 +25,30 @@ public: | |||
| 22 | 25 | ||
| 23 | vec2i& size() { return size_; } | 26 | vec2i& size() { return size_; } |
| 24 | 27 | ||
| 28 | int getFrame() const { return animations_[curAnim_][curFrame_]; } | ||
| 29 | |||
| 30 | void setDirection(Direction dir); | ||
| 31 | |||
| 32 | Direction getDirection() const { return curDir_; } | ||
| 33 | |||
| 34 | void setWalking(bool walking); | ||
| 35 | |||
| 36 | void tickAnim(); | ||
| 37 | |||
| 25 | private: | 38 | private: |
| 26 | 39 | ||
| 40 | void updateAnimation(); | ||
| 41 | |||
| 27 | int textureId_; | 42 | int textureId_; |
| 28 | vec2i loc_ { 0, 0 }; | 43 | vec2i loc_ { 0, 0 }; |
| 29 | vec2i size_; | 44 | vec2i size_; |
| 45 | Direction curDir_ = Direction::down; | ||
| 46 | bool isWalking_ = false; | ||
| 47 | int curAnim_ = 0; | ||
| 48 | int curFrame_ = 0; | ||
| 49 | std::vector<std::vector<int>> animations_; | ||
| 50 | std::map<Direction, int> stillAnims_; | ||
| 51 | std::map<Direction, int> walkingAnims_; | ||
| 30 | }; | 52 | }; |
| 31 | 53 | ||
| 32 | #endif /* end of include guard: SPRITE_H_70503825 */ | 54 | #endif /* end of include guard: SPRITE_H_70503825 */ |
| diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..8cdad3d --- /dev/null +++ b/src/util.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #ifndef UTIL_H_4AC35025 | ||
| 2 | #define UTIL_H_4AC35025 | ||
| 3 | |||
| 4 | #include <string> | ||
| 5 | #include <iterator> | ||
| 6 | |||
| 7 | template <class OutputIterator> | ||
| 8 | void splitStr( | ||
| 9 | std::string input, | ||
| 10 | std::string delimiter, | ||
| 11 | OutputIterator out) { | ||
| 12 | while (!input.empty()) { | ||
| 13 | int divider = input.find(delimiter); | ||
| 14 | if (divider == std::string::npos) { | ||
| 15 | *out = input; | ||
| 16 | out++; | ||
| 17 | |||
| 18 | input = ""; | ||
| 19 | } else { | ||
| 20 | *out = input.substr(0, divider); | ||
| 21 | out++; | ||
| 22 | |||
| 23 | input = input.substr(divider+delimiter.length()); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | template <class Container> | ||
| 29 | Container splitStr( | ||
| 30 | std::string input, | ||
| 31 | std::string delimiter) { | ||
| 32 | Container result; | ||
| 33 | |||
| 34 | splitStr(input, delimiter, std::back_inserter(result)); | ||
| 35 | |||
| 36 | return result; | ||
| 37 | } | ||
| 38 | |||
| 39 | #endif /* end of include guard: UTIL_H_4AC35025 */ | ||
