Ver Fonte

:sparkles: added working splash screen

Felix Bytow há 1 ano atrás
pai
commit
9bc7fb912f

+ 2 - 2
CMakeLists.txt

@@ -34,8 +34,8 @@ add_executable(Snake WIN32
     game/GameState.hxx
     game/GameStateManager.cxx game/GameStateManager.hxx
     game/LoadingState.cxx game/LoadingState.hxx
-    game/AssetManager.cxx
-    game/AssetManager.hxx
+    game/SplashState.cxx game/SplashState.hxx
+    game/AssetManager.cxx game/AssetManager.hxx
 )
 
 target_link_libraries(Snake PRIVATE

+ 2 - 2
SDLRenderer.cxx

@@ -2,11 +2,11 @@
 
 SDLRenderer::SDLRenderer(SDLWindow& window)
 {
-  renderer_ = SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED);
+  renderer_ = SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
   if (renderer_==nullptr) {
     throw SDLError("Failed to create renderer.");
   }
-  SDL_RenderSetVSync(renderer_, SDL_TRUE);
+  SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND);
   SDL_Log("Created renderer successfully.");
 }
 

+ 11 - 12
game/AssetManager.cxx

@@ -37,8 +37,8 @@ AssetManager::~AssetManager()
   if (loading_thread_.joinable()) {
     loading_thread_.join();
   }
-  for (auto& kv: texture_assets_) {
-    SDL_DestroyTexture(kv.second);
+  for (auto& kv: image_assets_) {
+    SDL_FreeSurface(kv.second);
     SDL_Log("Unloaded texture %s successfully.", kv.first.c_str());
   }
   for (auto& kv: font_assets_) {
@@ -53,23 +53,22 @@ void AssetManager::load_assets(std::filesystem::path const& asset_directory)
   for (auto const& entry: std::filesystem::directory_iterator(asset_directory)) {
     auto const path = entry.path().string();
     auto const ext = entry.path().extension();
+    auto const filename = entry.path().filename().string();
     if (ext==".png" || ext==".jpg") {
-      SDL_Surface* temp_surface = IMG_Load(path.c_str());
-      auto const texture = SDL_CreateTextureFromSurface(renderer_, temp_surface);
-      SDL_FreeSurface(temp_surface);
-      if (texture==nullptr) {
+      auto const surface = IMG_Load(path.c_str());
+      if (surface==nullptr) {
         throw SDLError{std::format("Failed to load texture {}.", path)};
       }
-      texture_assets_[path] = texture;
-      SDL_Log("Loaded texture %s successfully.", path.c_str());
+      image_assets_[filename] = surface;
+      SDL_Log("Loaded texture %s successfully.", filename.c_str());
     }
     else if (ext==".ttf") {
       auto const font = TTF_OpenFont(path.c_str(), 16);
       if (font==nullptr) {
         throw SDLError{std::format("Failed to load font {}.", path)};
       }
-      font_assets_[path] = font;
-      SDL_Log("Loaded font %s successfully.", path.c_str());
+      font_assets_[filename] = font;
+      SDL_Log("Loaded font %s successfully.", filename.c_str());
     }
     ++assets_loaded_;
   }
@@ -80,9 +79,9 @@ float AssetManager::get_progress() const
   return static_cast<float>(assets_loaded_)/static_cast<float>(total_assets_);
 }
 
-SDL_Texture* AssetManager::get_texture_asset(std::string const& filepath)
+SDL_Surface* AssetManager::get_image_asset(std::string const& filepath)
 {
-  return texture_assets_[filepath];
+  return image_assets_[filepath];
 }
 
 TTF_Font* AssetManager::get_font_asset(std::string const& filepath)

+ 4 - 3
game/AssetManager.hxx

@@ -4,13 +4,14 @@
 #define SNAKE_ASSETMANAGER_HXX
 
 #include "../SDLRenderer.hxx"
-#include <SDL_ttf.h>
 
 #include <atomic>
 #include <filesystem>
 #include <thread>
 #include <unordered_map>
 
+#include <SDL_ttf.h>
+
 class AssetManager final {
 
 public:
@@ -26,7 +27,7 @@ public:
 
   float get_progress() const;
 
-  SDL_Texture* get_texture_asset(std::string const& filepath);
+  SDL_Surface* get_image_asset(std::string const& filepath);
 
   TTF_Font* get_font_asset(std::string const& filepath);
 
@@ -41,7 +42,7 @@ private:
 
   SDLRenderer& renderer_;
 
-  std::unordered_map<std::string, SDL_Texture*> texture_assets_;
+  std::unordered_map<std::string, SDL_Surface*> image_assets_;
   std::unordered_map<std::string, TTF_Font*> font_assets_;
 };
 

+ 2 - 0
game/GameStateManager.cxx

@@ -23,6 +23,8 @@ GameState* GameStateManager::current()
     return nullptr; // TODO: handle all game states
   case GameStates::Loading:
     return &loading_;
+  case GameStates::Splash:
+    return &splash_;
   }
 }
 

+ 2 - 0
game/GameStateManager.hxx

@@ -4,6 +4,7 @@
 #define SNAKE_GAMESTATEMANAGER_HXX
 
 #include "LoadingState.hxx"
+#include "SplashState.hxx"
 
 #include <stack>
 
@@ -33,6 +34,7 @@ private:
   std::stack<GameStates> states_;
 
   LoadingState loading_;
+  SplashState splash_;
 };
 
 #endif // SNAKE_GAMESTATEMANAGER_HXX

+ 4 - 1
game/LoadingState.cxx

@@ -1,10 +1,13 @@
 #include "LoadingState.hxx"
 #include "AssetManager.hxx"
+#include "GameStateManager.hxx"
 
 void LoadingState::update(GameStateManager& gsm, std::chrono::milliseconds const delta_time)
 {
-  (void) gsm;
   (void) delta_time;
+  if (AssetManager::instance().get_progress()==1.0f) {
+    gsm.replace_state(GameStates::Splash);
+  }
 }
 
 void LoadingState::render(SDLRenderer& renderer)

+ 62 - 0
game/SplashState.cxx

@@ -0,0 +1,62 @@
+#include "SplashState.hxx"
+#include "AssetManager.hxx"
+#include "GameStateManager.hxx"
+
+using namespace std::chrono_literals;
+
+void SplashState::update(GameStateManager& gsm, std::chrono::milliseconds delta_time)
+{
+    time_in_state_ += delta_time;
+    auto const key_state = SDL_GetKeyboardState(nullptr);
+    if (time_in_state_>13'000ms || key_state[SDL_SCANCODE_SPACE] || key_state[SDL_SCANCODE_RETURN])
+      gsm.replace_state(GameStates::MainMenu);
+}
+
+void SplashState::render(SDLRenderer& renderer)
+{
+  if (logo_==nullptr) {
+    // right now this is required here as we need the renderer and the main thread
+    logo_ = SDL_CreateTextureFromSurface(
+        renderer,
+        AssetManager::instance().get_image_asset("logo.jpg")
+    );
+    SDL_SetTextureBlendMode(logo_, SDL_BLENDMODE_BLEND);
+  }
+
+  SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
+  SDL_RenderClear(renderer);
+
+  int w, h;
+  SDL_GetRendererOutputSize(renderer, &w, &h);
+
+  if (time_in_state_>=2'000ms && time_in_state_<4'000ms) {
+    auto const progress = static_cast<float>(time_in_state_.count()-2'000)/2'000.0f;
+    auto const alpha = static_cast<int>(std::lerp(SDL_ALPHA_TRANSPARENT, SDL_ALPHA_OPAQUE, progress));
+    SDL_SetTextureAlphaMod(logo_, alpha);
+    SDL_RenderCopy(renderer, logo_, nullptr, nullptr);
+  }
+  else if (time_in_state_>=4'000ms && time_in_state_<9'000ms) {
+    SDL_SetTextureAlphaMod(logo_, SDL_ALPHA_OPAQUE);
+    SDL_RenderCopy(renderer, logo_, nullptr, nullptr);
+  }
+  else if (time_in_state_>=9'000ms && time_in_state_<11'000ms) {
+    auto const progress = static_cast<float>(time_in_state_.count()-11'000)/2'000.0f;
+    auto const alpha = static_cast<int>(std::lerp(SDL_ALPHA_OPAQUE, SDL_ALPHA_TRANSPARENT, progress));
+    SDL_SetTextureAlphaMod(logo_, alpha);
+    SDL_RenderCopy(renderer, logo_, nullptr, nullptr);
+  }
+
+  SDL_RenderPresent(renderer);
+}
+
+void SplashState::on_enter()
+{
+  time_in_state_ = 0ms;
+  logo_ = nullptr;
+}
+
+void SplashState::on_leave()
+{
+  SDL_DestroyTexture(logo_);
+  logo_ = nullptr;
+}

+ 25 - 0
game/SplashState.hxx

@@ -0,0 +1,25 @@
+#pragma once
+
+#ifndef SNAKE_SPLASHSTATE_HXX
+#define SNAKE_SPLASHSTATE_HXX
+
+#include "GameState.hxx"
+
+#include <SDL.h>
+
+class SplashState final : public GameState {
+public:
+  void on_enter() override;
+
+  void on_leave() override;
+
+  void update(GameStateManager& gsm, std::chrono::milliseconds delta_time) override;
+
+  void render(SDLRenderer& renderer) override;
+
+private:
+  std::chrono::milliseconds time_in_state_;
+  SDL_Texture* logo_;
+};
+
+#endif // SNAKE_SPLASHSTATE_HXX

+ 1 - 0
main.cxx

@@ -28,6 +28,7 @@ void main_loop(SDLRenderer& renderer)
 
     auto const end = high_resolution_clock::now();
     auto const delta_time = duration_cast<milliseconds>(end-start);
+    start = end;
 
     state->update(gsm, delta_time);
     state->render(renderer);