Adding animations to our Minesweeper game can greatly enhance the user experience. Let's explore how we can implement animations for cell clearing and mine explosions using SDL2.
First, we'll create a simple animation framework:
#include <SDL.h>
#include <functional>
#include <vector>
class Animation {
public:
Animation(
int duration,
std::function<void(float)> updateFunc) :
Duration{duration},
UpdateFunc{updateFunc}, ElapsedTime{0} {}
bool Update(int deltaTime) {
ElapsedTime += deltaTime;
float progress =
std::min(1.0f,
static_cast<float>(ElapsedTime) /
Duration);
UpdateFunc(progress);
return ElapsedTime >= Duration;
}
private:
int Duration;
std::function<void(float)> UpdateFunc;
int ElapsedTime;
};
class AnimationManager {
public:
void AddAnimation(Animation animation) {
Animations.push_back(std::move(animation));
}
void Update(int deltaTime) {
Animations.erase(
std::remove_if(
Animations.begin(), Animations.end(),
[deltaTime](Animation &anim) {
return anim.Update(deltaTime);
}),
Animations.end());
}
private:
std::vector<Animation> Animations;
};
Now, let's modify our MinesweeperCell
class to include an animation when clearing:
class MinesweeperCell : public Engine::Button {
public:
// ... existing code ...
void ClearCell() {
if (isCleared) return;
isCleared = true;
SetIsDisabled(true);
// Start clearing animation
AnimationManager.AddAnimation(Animation(
500, // Duration in milliseconds
[this](float progress) {
// Interpolate between initial and
// cleared color
SDL_Color initialColor =
Config::BUTTON_COLOR;
SDL_Color targetColor =
Config::BUTTON_CLEARED_COLOR;
Color.r = initialColor.r +
(targetColor.r - initialColor.r) *
progress;
Color.g = initialColor.g +
(targetColor.g - initialColor.g) *
progress;
Color.b = initialColor.b +
(targetColor.b - initialColor.b) *
progress;
}));
ReportEvent(UserEvents::CELL_CLEARED);
}
void Update(int deltaTime) {
AnimationManager.Update(deltaTime);
}
private:
AnimationManager AnimationManager;
};
For mine explosions, we can create a more dramatic animation:
class MinesweeperCell : public Engine::Button {
public:
// ... existing code ...
void ExplodeMine() {
if (isExploded) return;
isExploded = true;
SetIsDisabled(true);
AnimationManager.AddAnimation(Animation(
1000, // Duration in milliseconds
[this](float progress) {
// Pulsating red effect
float intensity =
(std::sin(progress * 10) + 1) / 2;
Color.r = 255;
Color.g = Color.b = static_cast<Uint8>(
255 * (1 - intensity));
// Expand cell size
float scale = 1 + progress * 0.2f;
SetSize(static_cast<int>(OriginalWidth *
scale),
static_cast<int>(
OriginalHeight * scale));
}));
ReportEvent(UserEvents::MINE_EXPLODED);
}
private:
bool isExploded{false};
int OriginalWidth, OriginalHeight;
};
To integrate these animations into our game loop, we need to update our main game class:
class MinesweeperGame {
public:
void Update(int deltaTime) {
for (auto &cell : Grid.Children) {
cell.Update(deltaTime);
}
}
void Render(SDL_Surface *Surface) {
Grid.Render(Surface);
}
private:
MinesweeperGrid Grid;
};
And in our main loop:
int main() {
// ... initialization code ...
Uint32 lastTime = SDL_GetTicks();
while (Running) {
// ... event handling ...
Uint32 currentTime = SDL_GetTicks();
int deltaTime = currentTime - lastTime;
lastTime = currentTime;
Game.Update(deltaTime);
Game.Render(WindowSurface);
SDL_UpdateWindowSurface(Window);
SDL_Delay(16); // Cap at roughly 60 FPS
}
// ... cleanup code ...
}
These animations add visual flair to our Minesweeper game. The cell clearing animation provides a smooth transition, while the mine explosion creates a dramatic effect.
Remember to adjust the animation durations and effects to suit your game's style and pacing. You can also extend this system to include other animations, such as flagging cells or revealing the entire board at game end.
Answers to questions are automatically generated and may not have been reviewed.
Building a two-dimensional grid of interactive minesweeper cells