Absolutely! Implementing a button that changes shape when hovered or clicked can add a dynamic and interactive feel to your UI. We can achieve this by extending our existing Button
class and modifying its rendering logic. Let's walk through the process step by step.
First, let's create a ShapeChangingButton
class that inherits from our Button
 class:
#include <SDL.h>
#include <vector>
class ShapeChangingButton : public Button {
public:
ShapeChangingButton(int x, int y, int w,
int h)
: Button{x, y, w, h} {
normalShape = CreateRectShape(w, h);
hoverShape =
CreateCircleShape(std::min(w, h) / 2);
clickShape =
CreateStarShape(std::min(w, h) / 2);
}
void Render(SDL_Surface* Surface) override {
SDL_FillRect(Surface, nullptr,
SDL_MapRGB(Surface->format,
255, 255, 255));
const std::vector<SDL_Point>& currentShape =
GetCurrentShape();
RenderShape(Surface, currentShape);
}
protected:
void HandleMouseEnter() override {
Button::HandleMouseEnter();
currentState = ButtonState::Hover;
}
void HandleMouseExit() override {
Button::HandleMouseExit();
currentState = ButtonState::Normal;
}
void HandleLeftClick() override {
Button::HandleLeftClick();
currentState = ButtonState::Clicked;
}
private:
enum class ButtonState {
Normal,
Hover,
Clicked
};
std::vector<SDL_Point>
CreateRectShape(int w, int h) {
return {{0, 0}, {w, 0}, {w, h}, {0, h}};
}
std::vector<SDL_Point> CreateCircleShape(
int radius) {
std::vector<SDL_Point> points;
for (int i = 0; i < 360; i += 10) {
int x = static_cast<int>(
radius * cos(i * M_PI / 180));
int y = static_cast<int>(
radius * sin(i * M_PI / 180));
points.push_back(
{x + radius, y + radius});
}
return points;
}
std::vector<SDL_Point> CreateStarShape(
int radius) {
std::vector<SDL_Point> points;
for (int i = 0; i < 10; i++) {
double r =
(i % 2 == 0) ? radius : radius / 2;
int x = static_cast<int>(
r * cos(i * M_PI / 5));
int y = static_cast<int>(
r * sin(i * M_PI / 5));
points.push_back(
{x + radius, y + radius});
}
return points;
}
void RenderShape(
SDL_Surface* Surface,
const std::vector<SDL_Point>& shape) {
SDL_Point center = {Rect.x + Rect.w / 2,
Rect.y + Rect.h / 2};
std::vector<SDL_Point> translatedShape;
for (const auto& point : shape) {
translatedShape.push_back(
{point.x + center.x - Rect.w / 2,
point.y + center.y - Rect.h / 2});
}
SDL_Color color = GetCurrentColor();
for (size_t i = 0;
i < translatedShape.size(); i++) {
SDL_Point start = translatedShape[i];
SDL_Point end = translatedShape
[(i + 1) % translatedShape.size()];
SDL_DrawLine(Surface, start.x, start.y,
end.x, end.y, color);
}
}
const std::vector<SDL_Point>&
GetCurrentShape() const {
switch (currentState) {
case ButtonState::Hover:
return hoverShape;
case ButtonState::Clicked:
return clickShape;
default:
return normalShape;
}
}
SDL_Color GetCurrentColor() const {
switch (currentState) {
case ButtonState::Hover:
return {255, 0, 0, 255}; // Red
case ButtonState::Clicked:
return {0, 255, 0, 255}; // Green
default:
return {0, 0, 255, 255}; // Blue
}
}
std::vector<SDL_Point> normalShape;
std::vector<SDL_Point> hoverShape;
std::vector<SDL_Point> clickShape;
ButtonState currentState{ButtonState::Normal};
};
In this ShapeChangingButton
class, we've added:
SDL_Point
vectors.currentState
to track the button's state.HandleMouseEnter()
, HandleMouseExit()
, and HandleLeftClick()
methods to update the current state.Render()
method that draws the appropriate shape based on the current state.Now, let's see how we can use this ShapeChangingButton
in our main game loop:
#include <SDL.h>
int main(int argc, char* argv[]) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
ShapeChangingButton MyButton{50, 50, 100,
100};
SDL_Event Event;
bool shouldQuit{false};
while (!shouldQuit) {
while (SDL_PollEvent(&Event)) {
MyButton.HandleEvent(Event);
if (Event.type == SDL_QUIT) {
shouldQuit = true;
}
}
GameWindow.Render();
MyButton.Render(GameWindow.GetSurface());
GameWindow.Update();
SDL_Delay(16); // Cap at ~60 FPS
}
SDL_Quit();
return 0;
}
This main loop creates a ShapeChangingButton
and handles its events. The button will now change shape:
This implementation provides a visually engaging button that responds to user interactions by changing both its shape and color. You can further customize this by adding more shapes, adjusting colors, or even implementing smooth transitions between shapes for an even more dynamic effect.
Remember to include the necessary headers (like <cmath>
for trigonometric functions) and implement any missing utility functions (like SDL_DrawLine()
) that this code assumes are available.
Answers to questions are automatically generated and may not have been reviewed.
Explore techniques for building UI components that respond to user input