Pārlūkot izejas kodu

:sparkles: added dependency to boost and refactored the button class

Felix Bytow 1 gadu atpakaļ
vecāks
revīzija
0714f504ba
8 mainītis faili ar 151 papildinājumiem un 281 dzēšanām
  1. 11 4
      CMakeLists.txt
  2. 3 5
      SDL.hxx
  3. 4 6
      SDLRenderer.hxx
  4. 3 5
      SDLWindow.hxx
  5. 63 5
      game/AssetManager.hxx
  6. 3 5
      game/GameStateManager.hxx
  7. 57 245
      game/ui/Button.cxx
  8. 7 6
      game/ui/Button.hxx

+ 11 - 4
CMakeLists.txt

@@ -6,25 +6,31 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_CXX_EXTENSIONS OFF)
 
 include(FetchContent)
+FetchContent_Declare(
+    Boost
+    GIT_REPOSITORY https://github.com/boostorg/boost.git
+    GIT_TAG a7090e8ce184501cfc9e80afa6cafb5bfd3b371c # boost-1.74.0
+    FIND_PACKAGE_ARGS 1.74.0 REQUIRED COMPONENTS serialization program_options system
+)
 FetchContent_Declare(
     SDL2
     GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
     GIT_TAG 15ead9a40d09a1eb9972215cceac2bf29c9b77f6 # release-2.28.5
-    FIND_PACKAGE_ARGS NAMES SDL2
+    FIND_PACKAGE_ARGS
 )
 FetchContent_Declare(
     SDL2_image
     GIT_REPOSITORY https://github.com/libsdl-org/SDL_image.git
     GIT_TAG 28b9daa15a59aa2829cd29944ca9ffbf049d7667 # release-2.8.1
-    FIND_PACKAGE_ARGS NAMES SDL2_image
+    FIND_PACKAGE_ARGS
 )
 FetchContent_Declare(
     SDL2_ttf
     GIT_REPOSITORY https://github.com/libsdl-org/SDL_ttf.git
     GIT_TAG 89d1692fd8fe91a679bb943d377bfbd709b52c23 # release-2.20.2
-    FIND_PACKAGE_ARGS NAMES SDL2_ttf
+    FIND_PACKAGE_ARGS
 )
-FetchContent_MakeAvailable(SDL2 SDL2_image SDL2_ttf)
+FetchContent_MakeAvailable(SDL2 SDL2_image SDL2_ttf Boost)
 
 add_executable(Snake WIN32
     main.cxx
@@ -45,4 +51,5 @@ target_link_libraries(Snake PRIVATE
     SDL2::SDL2 SDL2::SDL2main
     SDL2_image::SDL2_image
     SDL2_ttf::SDL2_ttf
+    Boost::headers Boost::serialization Boost::program_options
 )

+ 3 - 5
SDL.hxx

@@ -10,6 +10,8 @@
 #include <stdexcept>
 #include <string_view>
 
+#include <boost/noncopyable.hpp>
+
 /**
  * @class SDLError
  * @brief Represents an SDL error.
@@ -33,16 +35,12 @@ public:
  * This class provides a convenient way to initialize SDL and clean up resources when they are no longer needed.
  * Only one instance of this class can exist at a time.
  */
-class SDL final {
+class SDL final : private boost::noncopyable {
 public:
   explicit SDL(std::uint32_t flags = SDL_INIT_EVERYTHING);
 
   ~SDL() noexcept;
 
-  SDL(SDL const&) = delete;
-
-  SDL& operator=(SDL const&) = delete;
-
   static SDL& instance() noexcept;
 
   static SDL& require(std::uint32_t flags) noexcept;

+ 4 - 6
SDLRenderer.hxx

@@ -5,16 +5,14 @@
 
 #include "SDLWindow.hxx"
 
-class SDLRenderer final {
+#include <boost/noncopyable.hpp>
+
+class SDLRenderer final : private boost::noncopyable {
 public:
-  SDLRenderer(SDLWindow& window);
+  explicit SDLRenderer(SDLWindow& window);
 
   ~SDLRenderer() noexcept;
 
-  SDLRenderer(SDLRenderer const&) = delete;
-
-  SDLRenderer& operator=(SDLRenderer const&) = delete;
-
   SDLRenderer(SDLRenderer&& src) noexcept;
 
   SDLRenderer& operator=(SDLRenderer&& src) noexcept;

+ 3 - 5
SDLWindow.hxx

@@ -7,16 +7,14 @@
 
 #include <string_view>
 
-class SDLWindow final {
+#include <boost/noncopyable.hpp>
+
+class SDLWindow final : private boost::noncopyable {
 public:
   SDLWindow(std::string_view title, int x, int y, int w, int h, std::uint32_t flags = 0u);
 
   ~SDLWindow() noexcept;
 
-  SDLWindow(SDLWindow const&) = delete;
-
-  SDLWindow& operator=(SDLWindow const&) = delete;
-
   SDLWindow(SDLWindow&& src) noexcept;
 
   SDLWindow& operator=(SDLWindow&& src) noexcept;

+ 63 - 5
game/AssetManager.hxx

@@ -7,20 +7,19 @@
 
 #include <atomic>
 #include <filesystem>
+#include <optional>
 #include <thread>
 #include <unordered_map>
 
 #include <SDL_ttf.h>
 
-class AssetManager final {
+#include <boost/noncopyable.hpp>
+
+class AssetManager final : private boost::noncopyable {
 
 public:
   explicit AssetManager(SDLRenderer& renderer);
 
-  AssetManager(AssetManager const&) = delete;
-
-  AssetManager& operator=(AssetManager const&) = delete;
-
   ~AssetManager();
 
   static AssetManager& instance() noexcept;
@@ -47,4 +46,63 @@ private:
   std::unordered_map<std::string, TTF_Font*> font_assets_;
 };
 
+template<typename T>
+struct AssetTraits;
+
+template<>
+struct AssetTraits<SDL_Texture*> final {
+  using type = SDL_Texture*;
+
+  static type get(std::string const& name)
+  {
+    return AssetManager::instance().get_texture_asset(name);
+  }
+};
+
+template<>
+struct AssetTraits<TTF_Font*> final {
+  using type = TTF_Font*;
+
+  static type get(std::string const& name)
+  {
+    return AssetManager::instance().get_font_asset(name);
+  }
+};
+
+template<typename T>
+class Asset final {
+  using traits = AssetTraits<T>;
+
+public:
+  explicit Asset(std::string name)
+      :name_{std::move(name)}
+  {
+  }
+
+  Asset(Asset const&) = default;
+
+  Asset& operator=(Asset const&) = default;
+
+  operator T& () { // NOLINT(*-explicit-constructor)
+    evaluate();
+    return asset_.value();
+  }
+
+  operator T const& () const { // NOLINT(*-explicit-constructor)
+    evaluate();
+    return asset_.value();
+  }
+
+private:
+  void evaluate() const
+  {
+    if (!asset_.has_value()) {
+      asset_ = traits::get(name_);
+    }
+  }
+
+  std::string name_;
+  mutable std::optional<T> asset_;
+};
+
 #endif // SNAKE_ASSETMANAGER_HXX

+ 3 - 5
game/GameStateManager.hxx

@@ -10,6 +10,8 @@
 
 #include <stack>
 
+#include <boost/noncopyable.hpp>
+
 enum class GameStates {
   Loading,
   Splash,
@@ -18,16 +20,12 @@ enum class GameStates {
   GameOver,
 };
 
-class GameStateManager final {
+class GameStateManager final : private boost::noncopyable {
 public:
   GameStateManager();
 
   ~GameStateManager();
 
-  GameStateManager(GameStateManager const&) = delete;
-
-  GameStateManager& operator=(GameStateManager) = delete;
-
   GameState* current();
 
   GameState* parent();

+ 57 - 245
game/ui/Button.cxx

@@ -1,94 +1,32 @@
 #include "Button.hxx"
 
-#include "../AssetManager.hxx"
-
 #include "../../SDLRenderer.hxx"
 
 #include <cassert>
 
 namespace {
-  SDL_Rect const UPPER_LEFT{
-      .x = 0,
-      .y = 0,
-      .w = 6,
-      .h = 5,
-  };
-
-  SDL_Rect const UPPER_EDGE{
-      .x = 6,
-      .y = 0,
-      .w = 37,
-      .h = 5,
-  };
-
-  SDL_Rect const UPPER_RIGHT{
-      .x = 43,
-      .y = 0,
-      .w = 6,
-      .h = 5,
-  };
-
-  SDL_Rect const LEFT_EDGE{
-      .x = 0,
-      .y = 5,
-      .w = 6,
-      .h = 35,
-  };
-
-  SDL_Rect const CENTER{
-      .x = 6,
-      .y = 5,
-      .w = 37,
-      .h = 35,
-  };
-
-  SDL_Rect const RIGHT_EDGE{
-      .x = 43,
-      .y = 5,
-      .w = 6,
-      .h = 35,
-  };
-
-  SDL_Rect const LOWER_LEFT_UP{
-      .x = 0,
-      .y = 40,
-      .w = 6,
-      .h = 9,
-  };
-
-  SDL_Rect const LOWER_EDGE_UP{
-      .x = 6,
-      .y = 40,
-      .w = 37,
-      .h = 9,
+  SDL_Rect const TEXTURE_RECTS_UP[] = {
+      {.x = 0, .y = 0, .w = 6, .h = 5}, // UPPER_LEFT
+      {.x = 6, .y = 0, .w = 37, .h = 5}, // UPPER_EDGE
+      {.x = 43, .y = 0, .w = 6, .h = 5}, // UPPER_RIGHT
+      {.x = 0, .y = 5, .w = 6, .h = 35}, // LEFT_EDGE
+      {.x = 6, .y = 5, .w = 37, .h = 35}, // CENTER
+      {.x = 43, .y = 5, .w = 6, .h = 35}, // RIGHT_EDGE
+      {.x = 0, .y = 40, .w = 6, .h = 9}, // LOWER_LEFT
+      {.x = 6, .y = 40, .w = 37, .h = 9}, // LOWER_EDGE
+      {.x = 43, .y = 40, .w = 6, .h = 9}, // LOWER_RIGHT
   };
 
-  SDL_Rect const LOWER_RIGHT_UP{
-      .x = 43,
-      .y = 40,
-      .w = 6,
-      .h = 9,
-  };
-
-  SDL_Rect const LOWER_LEFT_DOWN{
-      .x = 0,
-      .y = 40,
-      .w = 6,
-      .h = 5,
-  };
-
-  SDL_Rect const LOWER_EDGE_DOWN{
-      .x = 6,
-      .y = 40,
-      .w = 37,
-      .h = 5,
-  };
-
-  SDL_Rect const LOWER_RIGHT_DOWN{
-      .x = 43,
-      .y = 40,
-      .w = 6,
-      .h = 5,
+  SDL_Rect const TEXTURE_RECTS_DOWN[] = {
+      {.x = 0, .y = 0, .w = 6, .h = 5}, // UPPER_LEFT
+      {.x = 6, .y = 0, .w = 37, .h = 5}, // UPPER_EDGE
+      {.x = 43, .y = 0, .w = 6, .h = 5}, // UPPER_RIGHT
+      {.x = 0, .y = 5, .w = 6, .h = 35}, // LEFT_EDGE
+      {.x = 6, .y = 5, .w = 37, .h = 35}, // CENTER
+      {.x = 43, .y = 5, .w = 6, .h = 35}, // RIGHT_EDGE
+      {.x = 0, .y = 40, .w = 6, .h = 5}, // LOWER_LEFT
+      {.x = 6, .y = 40, .w = 37, .h = 5}, // LOWER_EDGE
+      {.x = 43, .y = 40, .w = 6, .h = 5}, // LOWER_RIGHT
   };
 
   SDL_Rect calculate_text_rect(SDL_Rect const& area, SDL_Texture* texture)
@@ -119,22 +57,15 @@ namespace {
 }
 
 Button::Button(std::string title, int x, int y, int w, int h)
-    :title_{std::move(title)}, x_{x}, y_{y}, w_{w}, h_{h}, pressed_{false}
+    :title_{std::move(title)}, x_{x}, y_{y}, w_{w}, h_{h}, pressed_{false},
+     up_{"blue_button_up.png"},
+     down_{"blue_button_down.png"},
+     font_{"kenney_pixel.ttf"}
 {
   assert(w_>=12);
   assert(h_>=14);
 
   on_click_ = [] { };
-
-  up_ = std::async(std::launch::deferred, [] {
-    return AssetManager::instance().get_texture_asset("blue_button_up.png");
-  });
-  down_ = std::async(std::launch::deferred, [] {
-    return AssetManager::instance().get_texture_asset("blue_button_down.png");
-  });
-  font_ = std::async(std::launch::deferred, [] {
-    return AssetManager::instance().get_font_asset("kenney_pixel.ttf");
-  });
 }
 
 void Button::set_pressed(bool const pressed)
@@ -149,166 +80,47 @@ bool Button::is_pressed() const
 
 void Button::render(SDLRenderer& renderer)
 {
-  auto const text = TTF_RenderUTF8_Solid(font_.get(), title_.c_str(), {0, 0, 0, SDL_ALPHA_OPAQUE});
+  auto const text = TTF_RenderUTF8_Solid(font_, title_.c_str(), {0, 0, 0, SDL_ALPHA_OPAQUE});
   auto const text_ure = SDL_CreateTextureFromSurface(renderer, text);
   SDL_FreeSurface(text);
 
+  SDL_Texture* texture;
+  SDL_Rect const* texture_rects;
+  SDL_Rect target_rects[9];
   if (pressed_) {
-    auto const down = down_.get();
-
-    SDL_Rect const upper_left{
-        .x = x_,
-        .y = y_+4,
-        .w = 6,
-        .h = 5,
-    };
-    SDL_RenderCopy(renderer, down, &::UPPER_LEFT, &upper_left);
-
-    SDL_Rect const upper_edge{
-        .x = x_+6,
-        .y = y_+4,
-        .w = w_-12,
-        .h = 5,
-    };
-    SDL_RenderCopy(renderer, down, &::UPPER_EDGE, &upper_edge);
-
-    SDL_Rect const upper_right{
-        .x = x_+w_-6,
-        .y = y_+4,
-        .w = 6,
-        .h = 5,
-    };
-    SDL_RenderCopy(renderer, down, &::UPPER_RIGHT, &upper_right);
-
-    SDL_Rect const left_edge{
-        .x = x_,
-        .y = y_+9,
-        .w = 6,
-        .h = h_-14,
-    };
-    SDL_RenderCopy(renderer, down, &::LEFT_EDGE, &left_edge);
-
-    SDL_Rect const center{
-        .x = x_+6,
-        .y = y_+9,
-        .w = w_-12,
-        .h = h_-14,
-    };
-    SDL_RenderCopy(renderer, down, &::CENTER, &center);
-
-    SDL_Rect const text_rect = ::calculate_text_rect(center, text_ure);
-    SDL_RenderCopy(renderer, text_ure, nullptr, &text_rect);
-
-    SDL_Rect const right_edge{
-        .x = x_+w_-6,
-        .y = y_+9,
-        .w = 6,
-        .h = h_-14,
-    };
-    SDL_RenderCopy(renderer, down, &::RIGHT_EDGE, &right_edge);
-
-    SDL_Rect const lower_left{
-        .x = x_,
-        .y = y_+h_-5,
-        .w = 6,
-        .h = 5,
-    };
-    SDL_RenderCopy(renderer, down, &::LOWER_LEFT_DOWN, &lower_left);
-
-    SDL_Rect const lower_edge{
-        .x = x_+6,
-        .y = y_+h_-5,
-        .w = w_-12,
-        .h = 5,
-    };
-    SDL_RenderCopy(renderer, down, &::LOWER_EDGE_DOWN, &lower_edge);
-
-    SDL_Rect const lower_right{
-        .x = x_+w_-6,
-        .y = y_+h_-5,
-        .w = 6,
-        .h = 5,
-    };
-    SDL_RenderCopy(renderer, down, &::LOWER_RIGHT_DOWN, &lower_right);
+    texture = down_;
+    texture_rects = ::TEXTURE_RECTS_DOWN;
+
+    target_rects[0] = {.x = x_, .y = y_+4, .w = 6, .h = 5};
+    target_rects[1] = {.x = x_+6, .y = y_+4, .w = w_-12, .h = 5};
+    target_rects[2] = {.x = x_+w_-6, .y = y_+4, .w = 6, .h = 5};
+    target_rects[3] = {.x = x_, .y = y_+9, .w = 6, .h = h_-14};
+    target_rects[4] = {.x = x_+6, .y = y_+9, .w = w_-12, .h = h_-14};
+    target_rects[5] = {.x = x_+w_-6, .y = y_+9, .w = 6, .h = h_-14};
+    target_rects[6] = {.x = x_, .y = y_+h_-5, .w = 6, .h = 5};
+    target_rects[7] = {.x = x_+6, .y = y_+h_-5, .w = w_-12, .h = 5};
+    target_rects[8] = {.x = x_+w_-6, .y = y_+h_-5, .w = 6, .h = 5};
   }
   else {
-    auto const up = up_.get();
-
-    SDL_Rect const upper_left{
-        .x = x_,
-        .y = y_,
-        .w = 6,
-        .h = 5,
-    };
-    SDL_RenderCopy(renderer, up, &::UPPER_LEFT, &upper_left);
-
-    SDL_Rect const upper_edge{
-        .x = x_+6,
-        .y = y_,
-        .w = w_-12,
-        .h = 5,
-    };
-    SDL_RenderCopy(renderer, up, &::UPPER_EDGE, &upper_edge);
-
-    SDL_Rect const upper_right{
-        .x = x_+w_-6,
-        .y = y_,
-        .w = 6,
-        .h = 5,
-    };
-    SDL_RenderCopy(renderer, up, &::UPPER_RIGHT, &upper_right);
-
-    SDL_Rect const left_edge{
-        .x = x_,
-        .y = y_+5,
-        .w = 6,
-        .h = h_-14,
-    };
-    SDL_RenderCopy(renderer, up, &::LEFT_EDGE, &left_edge);
-
-    SDL_Rect const center{
-        .x = x_+6,
-        .y = y_+5,
-        .w = w_-12,
-        .h = h_-14,
-    };
-    SDL_RenderCopy(renderer, up, &::CENTER, &center);
-
-    SDL_Rect const text_rect = ::calculate_text_rect(center, text_ure);
-    SDL_RenderCopy(renderer, text_ure, nullptr, &text_rect);
-
-    SDL_Rect const right_edge{
-        .x = x_+w_-6,
-        .y = y_+5,
-        .w = 6,
-        .h = h_-14,
-    };
-    SDL_RenderCopy(renderer, up, &::RIGHT_EDGE, &right_edge);
+    texture = up_;
+    texture_rects = ::TEXTURE_RECTS_UP;
+
+    target_rects[0] = {.x = x_, .y = y_, .w = 6, .h = 5};
+    target_rects[1] = {.x = x_+6, .y = y_, .w = w_-12, .h = 5};
+    target_rects[2] = {.x = x_+w_-6, .y = y_, .w = 6, .h = 5};
+    target_rects[3] = {.x = x_, .y = y_+5, .w = 6, .h = h_-14};
+    target_rects[4] = {.x = x_+6, .y = y_+5, .w = w_-12, .h = h_-14};
+    target_rects[5] = {.x = x_+w_-6, .y = y_+5, .w = 6, .h = h_-14};
+    target_rects[6] = {.x = x_, .y = y_+h_-9, .w = 6, .h = 9};
+    target_rects[7] = {.x = x_+6, .y = y_+h_-9, .w = w_-12, .h = 9};
+    target_rects[8] = {.x = x_+w_-6, .y = y_+h_-9, .w = 6, .h = 9};
+  }
 
-    SDL_Rect const lower_left{
-        .x = x_,
-        .y = y_+h_-9,
-        .w = 6,
-        .h = 9,
-    };
-    SDL_RenderCopy(renderer, up, &::LOWER_LEFT_UP, &lower_left);
+  for (int n = 0; n<9; ++n)
+    SDL_RenderCopy(renderer, texture, texture_rects+n, target_rects+n);
 
-    SDL_Rect const lower_edge{
-        .x = x_+6,
-        .y = y_+h_-9,
-        .w = w_-12,
-        .h = 9,
-    };
-    SDL_RenderCopy(renderer, up, &::LOWER_EDGE_UP, &lower_edge);
-
-    SDL_Rect const lower_right{
-        .x = x_+w_-6,
-        .y = y_+h_-9,
-        .w = 6,
-        .h = 9,
-    };
-    SDL_RenderCopy(renderer, up, &::LOWER_RIGHT_UP, &lower_right);
-  }
+  SDL_Rect const text_rect = ::calculate_text_rect(target_rects[4], text_ure);
+  SDL_RenderCopy(renderer, text_ure, nullptr, &text_rect);
 
   SDL_DestroyTexture(text_ure);
 }

+ 7 - 6
game/ui/Button.hxx

@@ -7,9 +7,10 @@
 #include <SDL_ttf.h>
 
 #include <functional>
-#include <future>
 #include <string>
 
+#include "../AssetManager.hxx"
+
 class SDLRenderer;
 
 class Button final {
@@ -18,7 +19,7 @@ public:
 
   void set_pressed(bool pressed);
 
-  bool is_pressed() const;
+  [[nodiscard]] bool is_pressed() const;
 
   void update();
 
@@ -32,7 +33,7 @@ public:
 
   void resize(int w, int h);
 
-  SDL_Rect get_bounding_box() const;
+  [[nodiscard]] SDL_Rect get_bounding_box() const;
 
 private:
   std::string title_;
@@ -40,9 +41,9 @@ private:
   bool pressed_;
   std::function<void()> on_click_;
 
-  std::shared_future<SDL_Texture*> up_;
-  std::shared_future<SDL_Texture*> down_;
-  std::shared_future<TTF_Font*> font_;
+  Asset<SDL_Texture*> up_;
+  Asset<SDL_Texture*> down_;
+  Asset<TTF_Font*> font_;
 };
 
 #endif // SNAKE_BUTTON_HXX