Quellcode durchsuchen

:sparkles: the game can now be fully controlled with the keyboard, no mouse necessary

Felix Bytow vor 1 Jahr
Ursprung
Commit
832ec881d2
3 geänderte Dateien mit 94 neuen und 14 gelöschten Zeilen
  1. 0 2
      game/CreditsState.cxx
  2. 90 12
      game/MenuState.cxx
  3. 4 0
      game/MenuState.hxx

+ 0 - 2
game/CreditsState.cxx

@@ -2,8 +2,6 @@
 
 #include "GameStateManager.hxx"
 
-#include <algorithm>
-#include <numeric>
 #include <type_traits>
 
 CreditsState::CreditsState()

+ 90 - 12
game/MenuState.cxx

@@ -3,14 +3,23 @@
 #include "GameStateManager.hxx"
 #include "../SDLRenderer.hxx"
 
+#include <array>
+
 void MenuState::on_enter(GameStateManager& gsm)
 {
+  active_button_ = 0;
+
   game_.reset();
   if (auto const parent = gsm.parent(); parent!=nullptr) {
+    continue_button_.set_visible(true);
     if (auto const game = dynamic_cast<PlayingState*>(parent); game!=nullptr) {
       game_ = game;
+      active_button_ = 1;
     }
   }
+  else {
+    continue_button_.set_visible(false);
+  }
 
   new_game_button_.set_on_click([&gsm] {
     if (gsm.parent()!=nullptr)
@@ -52,21 +61,83 @@ void MenuState::on_event(GameStateManager& gsm, SDL_Event const& evt)
     case SDL_SCANCODE_ESCAPE:
       gsm.pop_state();
       break;
+    case SDL_SCANCODE_UP:
+      SDL_ShowCursor(SDL_DISABLE);
+      --active_button_;
+      if (active_button_<0)
+        active_button_ = 4;
+      else if (active_button_==1 && !continue_button_.is_visible())
+        active_button_ = 0;
+      break;
+    case SDL_SCANCODE_DOWN:
+      SDL_ShowCursor(SDL_DISABLE);
+      ++active_button_;
+      if (active_button_>4)
+        active_button_ = 0;
+      else if (active_button_==1 && !continue_button_.is_visible())
+        active_button_ = 2;
+      break;
+    case SDL_SCANCODE_RETURN:
+      switch (active_button_) {
+      case 0:
+        new_game_button_.trigger();
+        break;
+      case 1:
+        continue_button_.trigger();
+        break;
+      case 2:
+        high_score_button_.trigger();
+        break;
+      case 3:
+        credits_button_.trigger();
+        break;
+      case 4:
+        quit_button_.trigger();
+        break;
+      }
+      break;
+    }
+  }
+  else if (evt.type==SDL_MOUSEMOTION) {
+    SDL_ShowCursor(SDL_ENABLE);
+
+    auto const x = evt.motion.x;
+    auto const y = evt.motion.y;
+
+    std::array<Button*, 5> buttons{&new_game_button_, nullptr, &high_score_button_, &credits_button_, &quit_button_};
+    if (continue_button_.is_visible())
+      buttons[1] = &continue_button_;
+
+    for (std::size_t n = 0; n<buttons.size(); ++n) {
+      if (buttons[n]==nullptr)
+        continue;
+
+      auto const box = buttons[n]->get_bounding_box();
+      if (x>=box.x && x<=box.x+box.w && y>=box.y && y<=box.y+box.h) {
+        active_button_ = static_cast<int>(n);
+        break;
+      }
     }
   }
 }
 
-void MenuState::update(GameStateManager& gsm, std::chrono::milliseconds delta_time)
+void MenuState::update(GameStateManager& gsm, std::chrono::milliseconds const delta_time)
 {
   (void) delta_time;
 
-  continue_button_.set_visible(gsm.parent()!=nullptr);
+  auto adjust_button_size = [this, idx = 0](Button& btn) mutable {
+    if (idx++==active_button_) {
+      btn.resize(BUTTON_WIDTH, BUTTON_HEIGHT);
+    }
+    else {
+      btn.resize(BUTTON_WIDTH-ACTIVE_SIZE_DIFF, BUTTON_HEIGHT-ACTIVE_SIZE_DIFF);
+    }
+  };
 
-  new_game_button_.update();
-  continue_button_.update();
-  high_score_button_.update();
-  credits_button_.update();
-  quit_button_.update();
+  for (auto const btn: {&new_game_button_, &continue_button_, &high_score_button_, &credits_button_, &quit_button_}) {
+    adjust_button_size(*btn);
+    btn->update();
+  }
 }
 
 void MenuState::render(SDLRenderer& renderer)
@@ -79,13 +150,20 @@ void MenuState::render(SDLRenderer& renderer)
   int const x = (screen_w-BUTTON_WIDTH)/2;
   int y = (screen_h-(button_count*BUTTON_HEIGHT+(button_count-1)*20))/2;
 
-  new_game_button_.move(x, y);
+  auto const adjust_button_position = [](Button& btn, int x, int y) {
+    auto const box = btn.get_bounding_box();
+    auto const dw = BUTTON_WIDTH-box.w;
+    auto const dh = BUTTON_HEIGHT-box.h;
+    btn.move(x+dw/2, y+dh/2);
+  };
+
+  adjust_button_position(new_game_button_, x, y);
   if (continue_button_.is_visible()) {
-    continue_button_.move(x, y += BUTTON_HEIGHT+20);
+    adjust_button_position(continue_button_, x, y += BUTTON_HEIGHT+20);
   }
-  high_score_button_.move(x, y += BUTTON_HEIGHT+20);
-  credits_button_.move(x, y += BUTTON_HEIGHT+20);
-  quit_button_.move(x, y+BUTTON_HEIGHT+20);
+  adjust_button_position(high_score_button_, x, y += BUTTON_HEIGHT+20);
+  adjust_button_position(credits_button_, x, y += BUTTON_HEIGHT+20);
+  adjust_button_position(quit_button_, x, y+BUTTON_HEIGHT+20);
 
   SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
   SDL_RenderClear(renderer);

+ 4 - 0
game/MenuState.hxx

@@ -23,6 +23,8 @@ public:
   void render(SDLRenderer& renderer) override;
 
 private:
+  static int constexpr ACTIVE_SIZE_DIFF = 8;
+
   static int constexpr BUTTON_HEIGHT = 80;
   static int constexpr BUTTON_WIDTH = 350;
 
@@ -33,6 +35,8 @@ private:
   Button quit_button_{"Quit", 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, UiColor::Red};
 
   std::optional<PlayingState*> game_{};
+
+  int active_button_{0};
 };
 
 #endif // SNAKE_MENUSTATE_HXX