Quellcode durchsuchen

:construction: working on line input for high scores

Felix Bytow vor 1 Jahr
Ursprung
Commit
60633c2256

+ 2 - 0
CMakeLists.txt

@@ -45,6 +45,8 @@ add_executable(Snake WIN32
     game/DummyState.cxx game/DummyState.hxx
     game/AssetManager.cxx game/AssetManager.hxx
     game/ui/Button.cxx game/ui/Button.hxx
+    game/ui/LineInput.cxx game/ui/LineInput.hxx
+    game/ui/UiColor.hxx game/ui/UiColor.cxx
 )
 
 target_link_libraries(Snake PRIVATE

BIN
assets/green_button_down.png


BIN
assets/green_button_up.png


BIN
assets/grey_button_down.png


BIN
assets/grey_button_up.png


BIN
assets/kenney_high.ttf


BIN
assets/kenney_rocket.ttf


BIN
assets/kenvector_future.ttf


BIN
assets/line_input.png


BIN
assets/red_button_down.png


BIN
assets/red_button_up.png


BIN
assets/yellow_button_down.png


BIN
assets/yellow_button_up.png


+ 1 - 1
game/AssetManager.cxx

@@ -68,7 +68,7 @@ void AssetManager::load_assets(std::filesystem::path const& asset_directory)
       SDL_Log("Loaded texture %s successfully.", filename.c_str());
     }
     else if (ext==".ttf") {
-      auto const font = TTF_OpenFont(path.c_str(), 16);
+      auto const font = TTF_OpenFont(path.c_str(), 42);
       if (font==nullptr) {
         throw SDLError{std::format("Failed to load font {}.", path)};
       }

+ 2 - 0
game/DummyState.cxx

@@ -13,6 +13,7 @@ void DummyState::on_enter(GameStateManager& gsm)
 void DummyState::update(GameStateManager& gsm, std::chrono::milliseconds delta_time)
 {
   pause_button_.update();
+  name_input_.update(delta_time);
 }
 
 void DummyState::render(SDLRenderer& renderer)
@@ -21,6 +22,7 @@ void DummyState::render(SDLRenderer& renderer)
   SDL_RenderClear(renderer);
 
   pause_button_.render(renderer);
+  name_input_.render(renderer);
 
   SDL_RenderPresent(renderer);
 }

+ 2 - 0
game/DummyState.hxx

@@ -5,6 +5,7 @@
 
 #include "GameState.hxx"
 #include "ui/Button.hxx"
+#include "ui/LineInput.hxx"
 
 class DummyState final : public GameState {
 public:
@@ -16,6 +17,7 @@ public:
 
 private:
   Button pause_button_{"Pause", 15, 15, 800, 80};
+  LineInput name_input_{15, 200, 800, 80, "Felix"};
 };
 
 #endif // SNAKE_DUMMYSTATE_HXX

+ 1 - 1
game/MenuState.hxx

@@ -18,7 +18,7 @@ private:
   static int constexpr BUTTON_HEIGHT = 80;
   static int constexpr BUTTON_WIDTH = 300;
 
-  Button new_game_button_{"New game", 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT};
+  Button new_game_button_{"New game", 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, UiColor::Green};
   Button continue_button_{"Continue", 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT};
   Button quit_button_{"Quit", 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT};
   bool escape_pressed_{false};

+ 8 - 5
game/ui/Button.cxx

@@ -56,15 +56,15 @@ namespace {
   }
 }
 
-Button::Button(std::string title, int x, int y, int w, int h)
+Button::Button(std::string title, int const x, int const y, int const w, int const h, UiColor const color)
     :title_{std::move(title)}, x_{x}, y_{y}, w_{w}, h_{h}, pressed_{false},
      visible_{true},
-     up_{"blue_button_up.png"},
-     down_{"blue_button_down.png"},
+     up_{ui_image("button_up", color)},
+     down_{ui_image("button_down", color)},
      font_{"kenney_pixel.ttf"}
 {
-  assert(w_>=12);
-  assert(h_>=14);
+  assert(w_>=MIN_WIDTH);
+  assert(h_>=MIN_HEIGHT);
 
   on_click_ = [] { };
 }
@@ -171,6 +171,9 @@ void Button::move(int const x, int const y)
 
 void Button::resize(int const w, int const h)
 {
+  assert(w>=MIN_WIDTH);
+  assert(h>=MIN_HEIGHT);
+
   w_ = w;
   h_ = h;
 }

+ 8 - 4
game/ui/Button.hxx

@@ -9,13 +9,17 @@
 #include <functional>
 #include <string>
 
-#include "../AssetManager.hxx"
+#include <boost/noncopyable.hpp>
 
-class SDLRenderer;
+#include "UiColor.hxx"
+#include "../AssetManager.hxx"
 
-class Button final {
+class Button final : private boost::noncopyable {
 public:
-  Button(std::string title, int x, int y, int w, int h);
+  static int constexpr MIN_WIDTH = 12;
+  static int constexpr MIN_HEIGHT = 14;
+
+  Button(std::string title, int x, int y, int w, int h, UiColor color = UiColor::Grey);
 
   void set_pressed(bool pressed);
 

+ 107 - 0
game/ui/LineInput.cxx

@@ -0,0 +1,107 @@
+#include "LineInput.hxx"
+
+#include <cassert>
+
+namespace {
+  SDL_Rect const TEXTURE_RECTS[] = {
+      {.x = 0, .y = 0, .w = 8, .h = 8}, // UPPER_LEFT
+      {.x = 8, .y = 0, .w = 179, .h = 8}, // UPPER_EDGE
+      {.x = 187, .y = 0, .w = 8, .h = 8}, // UPPER_RIGHT
+      {.x = 0, .y = 8, .w = 8, .h = 34}, // LEFT_EDGE
+      {.x = 8, .y = 8, .w = 179, .h = 34}, // CENTER
+      {.x = 187, .y = 8, .w = 8, .h = 34}, // RIGHT_EDGE
+      {.x = 0, .y = 42, .w = 8, .h = 7}, // LOWER_LEFT
+      {.x = 8, .y = 42, .w = 179, .h = 7}, // LOWER_EDGE
+      {.x = 187, .y = 42, .w = 8, .h = 7}, // LOWER_RIGHT
+  };
+
+  SDL_Rect calculate_text_rect(SDL_Rect const& area, SDL_Texture* texture)
+  {
+    int text_w, text_h;
+    SDL_QueryTexture(texture, nullptr, nullptr, &text_w, &text_h);
+
+    int const w = std::min(text_w, area.w-LineInput::MIN_WIDTH-8);
+    int const h = std::min(text_h, area.h-LineInput::MIN_HEIGHT-8);
+
+    return {
+        .x = area.x+12,
+        .y = area.y+(area.h-h)/2,
+        .w = area.w>=(w+LineInput::MIN_WIDTH+8) ? w : 0,
+        .h = area.h>=(h+LineInput::MIN_HEIGHT+8) ? h : 0,
+    };
+  }
+}
+
+LineInput::LineInput(int const x, int const y, int const w, int const h, std::string value)
+    :value_{std::move(value)}, x_{x}, y_{y}, w_{w}, h_{h}, focus_{false},
+     texture_{"line_input.png"},
+     font_{"kenney_pixel.ttf"}
+{
+  assert(w_>=MIN_WIDTH);
+  assert(h_>=MIN_HEIGHT);
+}
+
+void LineInput::move(int const x, int const y)
+{
+  x_ = x;
+  y_ = y;
+}
+
+void LineInput::resize(int const w, int const h)
+{
+  assert(w>=MIN_WIDTH);
+  assert(h>=MIN_HEIGHT);
+
+  w_ = w;
+  h_ = h;
+}
+
+SDL_Rect LineInput::get_bounding_box() const
+{
+  return {x_, y_, w_, h_};
+}
+
+void LineInput::set_focus(bool const focus)
+{
+  focus_ = focus;
+}
+
+bool LineInput::has_focus() const
+{
+  return focus_;
+}
+
+void LineInput::update(std::chrono::milliseconds const delta_time)
+{
+  blink_timer_ = (blink_timer_+delta_time)%6'000u;
+}
+
+void LineInput::render(SDLRenderer& renderer)
+{
+  auto const text = TTF_RenderUTF8_Solid(font_, value_.c_str(), {0, 0, 0, SDL_ALPHA_OPAQUE});
+  auto const text_ure = SDL_CreateTextureFromSurface(renderer, text);
+  SDL_FreeSurface(text);
+
+  SDL_Texture* const background = texture_;
+  SDL_Rect const target_rects[9] = {
+      {.x = x_, .y = y_, .w = 8, .h = 8},
+      {.x = x_+8, .y = y_, .w = w_-16, .h = 8},
+      {.x = x_+w_-8, .y = y_, .w = 8, .h = 8},
+      {.x = x_, .y = y_+8, .w = 8, .h = h_-15},
+      {.x = x_+8, .y = y_+8, .w = w_-16, .h = h_-15},
+      {.x = x_+w_-8, .y = y_+8, .w = 8, .h = h_-15},
+      {.x = x_, .y = y_+h_-7, .w = 8, .h = 7},
+      {.x = x_+8, .y = y_+h_-7, .w = w_-16, .h = 7},
+      {.x = x_+w_-8, .y = y_+h_-7, .w = 8, .h = 7},
+  };
+
+  for (int n = 0; n<9; ++n)
+    SDL_RenderCopy(renderer, background, ::TEXTURE_RECTS+n, target_rects+n);
+  auto const text_rect = ::calculate_text_rect(target_rects[4], text_ure);
+  SDL_Rect const text_texture_rect{
+      .x = 0, .y = 0, .w = text_rect.w, .h = text_rect.h,
+  };
+  SDL_RenderCopy(renderer, text_ure, &text_texture_rect, &text_rect);
+
+  SDL_DestroyTexture(text_ure);
+}

+ 45 - 0
game/ui/LineInput.hxx

@@ -0,0 +1,45 @@
+#pragma once
+
+#ifndef SNAKE_LINEINPUT_HXX
+#define SNAKE_LINEINPUT_HXX
+
+#include <chrono>
+
+#include <boost/noncopyable.hpp>
+
+#include "../AssetManager.hxx"
+
+class LineInput final : private boost::noncopyable {
+public:
+  static int constexpr MIN_WIDTH = 16;
+  static int constexpr MIN_HEIGHT = 15;
+
+  LineInput(int x, int y, int w, int h, std::string value = "");
+
+  void update(std::chrono::milliseconds delta_time);
+
+  void render(SDLRenderer& renderer);
+
+  void move(int x, int y);
+
+  void resize(int w, int h);
+
+  [[nodiscard]] SDL_Rect get_bounding_box() const;
+
+  // this does not remove the focus from other inputs!
+  void set_focus(bool focus);
+
+  [[nodiscard]] bool has_focus() const;
+
+private:
+  std::string value_;
+  int x_, y_, w_, h_;
+  bool focus_;
+
+  Asset<SDL_Texture*> texture_;
+  Asset<TTF_Font*> font_;
+
+  std::chrono::milliseconds blink_timer_{0};
+};
+
+#endif // SNAKE_LINEINPUT_HXX

+ 19 - 0
game/ui/UiColor.cxx

@@ -0,0 +1,19 @@
+#include "UiColor.hxx"
+
+#include <format>
+
+std::string ui_image(std::string_view name, UiColor const color)
+{
+  switch (color) {
+  case UiColor::Blue:
+    return std::format("blue_{}.png", name);
+  case UiColor::Green:
+    return std::format("green_{}.png", name);
+  case UiColor::Grey:
+    return std::format("grey_{}.png", name);
+  case UiColor::Red:
+    return std::format("red_{}.png", name);
+  case UiColor::Yellow:
+    return std::format("yellow_{}.png", name);
+  }
+}

+ 19 - 0
game/ui/UiColor.hxx

@@ -0,0 +1,19 @@
+#pragma once
+
+#ifndef SNAKE_UICOLOR_HXX
+#define SNAKE_UICOLOR_HXX
+
+#include <string>
+#include <string_view>
+
+enum class UiColor {
+  Blue,
+  Green,
+  Grey,
+  Red,
+  Yellow,
+};
+
+std::string ui_image(std::string_view name, UiColor color = UiColor::Grey);
+
+#endif // SNAKE_UICOLOR_HXX