Building Interactive Buttons

Animating Button Clicks in SDL

How would I implement a button that triggers an animation when clicked?

Abstract art representing computer programming

Implementing a button that triggers an animation when clicked is a great way to provide visual feedback to the user. We can achieve this by extending our existing Button class and incorporating a simple animation system. Let's walk through the process step by step.

First, let's create an AnimatedButton class that inherits from our Button class:

#include <SDL.h>
#include <chrono>
#include <vector>

class AnimatedButton : public Button {
public:
  AnimatedButton(int x, int y, int w, int h)
    : Button{x, y, w, h} {}

  void HandleLeftClick() override {
    Button::HandleLeftClick();
    StartAnimation();
  }

  void Render(SDL_Surface* Surface) override {
    Button::Render(Surface);
    if (isAnimating) {
      RenderAnimation(Surface);
    }
  }

  void Update() {
    if (isAnimating) { UpdateAnimation(); }
  }

private:
  void StartAnimation() {
    isAnimating = true;
    animationStart =
        std::chrono::steady_clock::now();
    currentFrame = 0;
  }

  void UpdateAnimation() {
    auto now = std::chrono::steady_clock::now();
    auto elapsed =
        std::chrono::duration_cast<
          std::chrono::milliseconds>(
            now - animationStart)
        .count();

    if (elapsed > animationDuration) {
      isAnimating = false;
    } else {
      currentFrame = (elapsed / frameDuration) %
                     frames.size();
    }
  }

  void RenderAnimation(SDL_Surface* Surface) {
    // Render current animation frame
    SDL_BlitSurface(frames[currentFrame],
                    nullptr, Surface, &Rect);
  }

  bool isAnimating{false};
  std::chrono::steady_clock::time_point
  animationStart;
  std::vector<SDL_Surface*> frames;
  int currentFrame{0};
  int animationDuration{1000}; // milliseconds
  int frameDuration{
      100}; // milliseconds per frame
};

In this AnimatedButton class, we've added:

  1. An isAnimating flag to track the animation state.
  2. A vector of SDL_Surface pointers to store animation frames.
  3. StartAnimation(), UpdateAnimation(), and RenderAnimation() methods to manage the animation.
  4. An overridden HandleLeftClick() method that starts the animation when the button is clicked.
  5. An Update() method that should be called in your game loop to update the animation state.

Now, let's see how we can use this AnimatedButton in our main game loop:

#include <SDL.h>
#include <SDL_image.h>

int main(int argc, char* argv[]) {
  SDL_Init(SDL_INIT_VIDEO);
  IMG_Init(IMG_INIT_PNG);

  Window GameWindow;
  AnimatedButton MyButton{50, 50, 100, 50};

  // Load animation frames
  std::vector<std::string> framePaths = {
      "frame1.png", "frame2.png", "frame3.png",
      "frame4.png"};
  for (const auto& path : framePaths) {
    MyButton.AddFrame(IMG_Load(path.c_str()));
  }

  SDL_Event Event;
  bool shouldQuit{false};
  while (!shouldQuit) {
    while (SDL_PollEvent(&Event)) {
      MyButton.HandleEvent(Event);
      if (Event.type == SDL_QUIT) {
        shouldQuit = true;
      }
    }

    MyButton.Update(); // Update animation state

    GameWindow.Render();
    MyButton.Render(GameWindow.GetSurface());
    GameWindow.Update();

    SDL_Delay(16); // Cap at ~60 FPS
  }

  IMG_Quit();
  SDL_Quit();
  return 0;
}

In this main loop, we create an AnimatedButton, load its animation frames, and ensure we call its Update() method each frame to progress the animation.

To make this example more concrete, let's consider a "power-up" button that plays a glowing animation when clicked:

class PowerUpButton : public AnimatedButton {
public:
  PowerUpButton(int x, int y, int w, int h)
    : AnimatedButton{x, y, w, h} {
    LoadAnimationFrames();
  }

  void HandleLeftClick() override {
    AnimatedButton::HandleLeftClick();
    std::cout << "Power-up activated!\n";
    // Add game logic for power-up here
  }

private:
  void LoadAnimationFrames() {
    std::vector<std::string> framePaths = {
        "powerup_glow1.png",
        "powerup_glow2.png",
        "powerup_glow3.png",
        "powerup_glow4.png"};
    for (const auto& path : framePaths) {
      AddFrame(IMG_Load(path.c_str()));
    }
  }
};

This PowerUpButton loads specific "glow" animation frames and includes some game logic when clicked. By using this approach, you can create visually appealing, interactive buttons that provide immediate feedback to the user through animations!

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

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