Building a Modular UI System

Animated UI Transitions in SDL

How can I create animated transitions between different UI states or screens?

Abstract art representing computer programming

Creating animated transitions between UI states or screens can greatly enhance the user experience. In SDL, we can achieve this by implementing a simple animation system. Here's how you can create basic animated transitions:

First, let's define an Animation class:

#include <SDL.h>
#include <functional>

class Animation {
private:
  Uint32 startTime;
  Uint32 duration;
  std::function<void(float)> updateFunc;
  bool isComplete{false};

public:
  Animation(Uint32 durationMs,
            std::function<void(float)> update)
    : duration{durationMs},
      updateFunc{update} {}

  void Start() {
    startTime = SDL_GetTicks();
    isComplete = false;
  }

  void Update() {
    if (isComplete)
      return;

    Uint32 currentTime = SDL_GetTicks();
    Uint32 elapsedTime =
        currentTime - startTime;

    if (elapsedTime >= duration) {
      updateFunc(1.0f);
      isComplete = true;
    } else {
      float progress =
          static_cast<float>(elapsedTime) /
          duration;
      updateFunc(progress);
    }
  }

  bool IsComplete() const { return isComplete; }
};

Now, let's create a Screen base class and two example screens:

class Screen {
public:
  virtual void Update() = 0;
  virtual void Render(SDL_Surface* surface) = 0;
};

class MenuScreen : public Screen {
private:
  SDL_Rect bounds;
  float alpha{1.0f};

public:
  MenuScreen(int w, int h)
    : bounds{0, 0, w, h} {}

  void Update() override {}

  void Render(SDL_Surface* surface) override {
    SDL_Rect fillRect = bounds;
    fillRect.w =
        static_cast<int>(bounds.w * alpha);
    SDL_FillRect(surface, &fillRect,
                 SDL_MapRGB(surface->format,
                            200, 100, 100));
  }

  void SetAlpha(float a) { alpha = a; }
};

class GameScreen : public Screen {
private:
  SDL_Rect bounds;
  float alpha{0.0f};

public:
  GameScreen(int w, int h)
    : bounds{0, 0, w, h} {}

  void Update() override {}

  void Render(SDL_Surface* surface) override {
    SDL_Rect fillRect = bounds;
    fillRect.w =
        static_cast<int>(bounds.w * alpha);
    SDL_FillRect(surface, &fillRect,
                 SDL_MapRGB(surface->format,
                            100, 200, 100));
  }

  void SetAlpha(float a) { alpha = a; }
};

Finally, let's implement the main application loop with transitions:

#include <iostream>
#include <memory>

class Application {
private:
  std::unique_ptr<Screen> currentScreen;
  std::unique_ptr<Screen> nextScreen;
  std::unique_ptr<Animation> transition;
  bool isTransitioning{false};

public:
  Application(int w, int h) {
    currentScreen =
        std::make_unique<MenuScreen>(w, h);
  }

  void HandleEvent(const SDL_Event& event) {
    if (event.type == SDL_KEYDOWN &&
        event.key.keysym.sym == SDLK_SPACE) {
      if (!isTransitioning) {
        StartTransition(
            std::make_unique<GameScreen>(800,
              600));
      }
    }
  }

  void StartTransition(
      std::unique_ptr<Screen> newScreen) {
    nextScreen = std::move(newScreen);
    isTransitioning = true;

    transition = std::make_unique<Animation>(
        1000, [this](float progress) {
          dynamic_cast<MenuScreen*>(
                currentScreen.get())
              ->SetAlpha(1.0f - progress);
          dynamic_cast<GameScreen*>(
                nextScreen.get())
              ->SetAlpha(progress);
        });

    transition->Start();
  }

  void Update() {
    if (isTransitioning) {
      transition->Update();
      if (transition->IsComplete()) {
        currentScreen = std::move(nextScreen);
        isTransitioning = false;
      }
    }
    currentScreen->Update();
  }

  void Render(SDL_Surface* surface) {
    SDL_FillRect(surface, nullptr,
                 SDL_MapRGB(surface->format,
                            255, 255, 255));
    currentScreen->Render(surface);
    if (isTransitioning) {
      nextScreen->Render(surface);
    }
  }
};

int main(int argc, char* argv[]) {
  if (SDL_Init(SDL_INIT_VIDEO) < 0) {
    std::cerr << "SDL could not initialize! "
        "SDL_Error: "
        << SDL_GetError() << "\n";
    return 1;
  }

  SDL_Window* window = SDL_CreateWindow(
      "SDL Transitions",
      SDL_WINDOWPOS_UNDEFINED,
      SDL_WINDOWPOS_UNDEFINED, 800, 600,
      SDL_WINDOW_SHOWN);
  if (window == nullptr) {
    std::cerr << "Window could not be created! "
        "SDL_Error: "
        << SDL_GetError() << "\n";
    return 1;
  }

  SDL_Surface* screenSurface =
      SDL_GetWindowSurface(window);

  Application app(800, 600);

  SDL_Event e;
  bool quit = false;

  while (!quit) {
    while (SDL_PollEvent(&e) != 0) {
      if (e.type == SDL_QUIT) { quit = true; }
      app.HandleEvent(e);
    }

    app.Update();
    app.Render(screenSurface);
    SDL_UpdateWindowSurface(window);
  }

  SDL_DestroyWindow(window);
  SDL_Quit();

  return 0;
}

This implementation creates a simple transition between a menu screen and a game screen. The transition is triggered by pressing the space bar. The Animation class handles the progress of the transition, updating the alpha values of both screens.

In a real-world application, you'd want to expand on this basic framework:

  • Implement more complex animations (e.g., sliding, fading, scaling)
  • Use more sophisticated rendering techniques (e.g., SDL's hardware-accelerated rendering)
  • Create a more robust screen management system
  • Add support for reversible transitions

Remember to handle edge cases, such as rapid screen changes or interrupting an ongoing transition. Also, consider using easing functions to create more natural-looking animations.

This example provides a foundation for creating smooth transitions between different UI states or screens in your SDL application, enhancing the overall user experience.

This Question is from the Lesson:

Building a Modular UI System

Learn how to create a flexible and extensible UI system using C++ and SDL, focusing on component hierarchy and polymorphism.

Answers to questions are automatically generated and may not have been reviewed.

This Question is from the Lesson:

Building a Modular UI System

Learn how to create a flexible and extensible UI system using C++ and SDL, focusing on component hierarchy and polymorphism.

sdl2-promo.jpg
Part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, unlimited access

This course includes:

  • 46 Lessons
  • 100+ Code Samples
  • 91% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved