Upgrade Buttons
Upgrades are a very important feature for any clicker game that exists. They give a sense of acomplishment and progress during gameplay. I had a hard time finding a good layout for the user interface of the game. In the end, I decided to just go for it and change things that I do not like.
I created a new header and a source file to acommodate the code of the upgrade button. Then, I added its logic.
upgradebutton.h
class UpgradeButton
{
private:
float x, y;
float width, height;
float verticalPOS;
float maxVerticalMovement;
SDL_Surface *upgradeButtonImageDefault;
SDL_Surface *upgradeButtonImageHovered;
SDL_Surface *upgradeButtonImageClicked;
SDL_Renderer *renderer;
public:
UpgradeButton( std::string defaultImageLocation, std::string hoveredImageLocation,
std::string clickedImageLocation, SDL_Renderer *getRenderer,
int _x, int _y, int _maxVerticalMovement);
~UpgradeButton();
bool IsButtonHovered(int getMouseX, int getMouseY);
void DisplayButton(int mouseX, int mouseY, bool isButtonClicked);
};
upgradebutton.cpp
bool UpgradeButton::IsButtonHovered(int getMouseX, int getMouseY)
{
if (getMouseX >= x && getMouseX <= x + width)
{
if (getMouseY >= y && getMouseY <= y + height)
{
return true;
}
}
return false;
}
UpgradeButton::UpgradeButton( std::string defaultImageLocation, std::string hoveredImageLocation,
std::string clickedImageLocation, SDL_Renderer *getRenderer,
int _x, int _y, int _maxVerticalMovement)
{
renderer = getRenderer;
auto loadDefaultImage = std::async( std::launch::async, IMG_Load,
defaultImageLocation.c_str());
auto loadHoveredImage = std::async( std::launch::async, IMG_Load,
hoveredImageLocation.c_str());
auto loadClickedImage = std::async( std::launch::async, IMG_Load,
clickedImageLocation.c_str());
loadDefaultImage.wait();
loadHoveredImage.wait();
loadClickedImage.wait();
upgradeButtonImageDefault = loadDefaultImage.get();
upgradeButtonImageHovered = loadHoveredImage.get();
upgradeButtonImageClicked = loadClickedImage.get();
if (upgradeButtonImageClicked == nullptr || upgradeButtonImageDefault == nullptr || upgradeButtonImageHovered == nullptr)
{
std::cout << "Failed to load a button" << std::endl;
exit(-1);
}
x = _x;
y = _y;
maxVerticalMovement = _maxVerticalMovement;
}
void UpgradeButton::DisplayButton(int mouseX, int mouseY, bool isButtonClicked)
{
SDL_Surface *convertedImage;
float sizePercentage = 0.2;
if (isButtonClicked)
{
convertedImage = SDL_ConvertSurface( upgradeButtonImageDefault,
upgradeButtonImageDefault->format);
}
else if (IsButtonHovered(mouseX, mouseY))
{
convertedImage = SDL_ConvertSurface( upgradeButtonImageHovered,
upgradeButtonImageHovered->format);
if (verticalPOS < maxVerticalMovement)
{
verticalPOS += 20;
}
}
else
{
convertedImage = SDL_ConvertSurface( upgradeButtonImageDefault,
upgradeButtonImageDefault->format);
verticalPOS = 0;
}
SDL_Texture *buttonImageTexture = SDL_CreateTextureFromSurface(renderer, convertedImage);
const SDL_FRect buttonImageHolder = { static_cast<float>(x),
static_cast<float>(y) - verticalPOS,
static_cast<float>(convertedImage->w),
static_cast<float>(convertedImage->h)};
width = convertedImage->w;
height = convertedImage->h;
SDL_RenderTexture(renderer, buttonImageTexture, nullptr, &buttonImageHolder);
SDL_DestroyTexture(buttonImageTexture);
SDL_DestroySurface(convertedImage);
}
UpgradeButton::~UpgradeButton()
{
SDL_DestroySurface(upgradeButtonImageDefault);
SDL_DestroySurface(upgradeButtonImageHovered);
SDL_DestroySurface(upgradeButtonImageClicked);
}
The button’s texture changes based on its condition.Also, I am using the VerticalPOS
and maxVerticalMovement
variables to
control it’s animation. The animation is played when the button is hovered.
Then, I created 2 more files create the interface of button in a type of grid. I named the files ‘upgradegrid.h’ and upgradegrid.cpp
. The purpose of the files
is to generate the buttons and handle their states. It might be a better idea to create a new class for each upgrade and use
some type of tag.
upgradegrid.h
class Upgrades{
public:
Upgrades( std::string defaultImageButtonLocation, std::string hoveredImageButtonLocation,
std::string clickedImageButtonLocation, int getWindowWidth, int getWindowHeight,
float initialX, float initialY, float gap, SDL_Renderer *getRenderer);
~Upgrades();
void ShowUpgradeButtons(int _x, int _y, bool _isClicked);
private:
static UpgradeButton *upgradeWorkers;
static UpgradeButton *summonWorkers;
static UpgradeButton *upgradeFighters;
static UpgradeButton *summonFighters;
static UpgradeButton *upgradeCursor;
};
upgradegrid.cpp
#include "upgradegrid.h"
UpgradeButton *Upgrades::summonFighters = nullptr;
UpgradeButton *Upgrades::summonWorkers = nullptr;
UpgradeButton *Upgrades::upgradeCursor = nullptr;
UpgradeButton *Upgrades::upgradeFighters = nullptr;
UpgradeButton *Upgrades::upgradeWorkers = nullptr;
Upgrades::Upgrades( std::string defaultImageButtonLocation, std::string hoveredImageButtonLocation,
std::string clickedImageButtonLocation, int getWindowWidth, int getWindowHeight,
float initialX, float initialY, float gap, SDL_Renderer *getRenderer)
{
summonFighters = new UpgradeButton( defaultImageButtonLocation,
hoveredImageButtonLocation,
clickedImageButtonLocation,
getRenderer,
initialX,
initialY,
150);
summonWorkers = new UpgradeButton( defaultImageButtonLocation,
hoveredImageButtonLocation,
clickedImageButtonLocation,
getRenderer,
initialX + gap,
initialY,
150);
upgradeFighters = new UpgradeButton( defaultImageButtonLocation,
hoveredImageButtonLocation,
clickedImageButtonLocation,
getRenderer,
initialX + gap * 2,
initialY,
150);
upgradeWorkers = new UpgradeButton( defaultImageButtonLocation,
hoveredImageButtonLocation,
clickedImageButtonLocation,
getRenderer,
initialX + gap * 3,
initialY,
150);
upgradeCursor = new UpgradeButton( defaultImageButtonLocation,
hoveredImageButtonLocation,
clickedImageButtonLocation,
getRenderer,
initialX + gap * 4,
initialY,
150);
}
Upgrades::~Upgrades()
{
delete(upgradeWorkers);
delete(upgradeFighters);
delete(upgradeCursor);
delete(summonFighters);
delete(summonWorkers);
}
void Upgrades::ShowUpgradeButtons(int _mouseX, int _mouseY, bool _isClicked)
{
summonFighters->DisplayButton(_mouseX, _mouseY, _isClicked);
summonWorkers->DisplayButton(_mouseX, _mouseY, _isClicked);
upgradeFighters->DisplayButton(_mouseX, _mouseY, _isClicked);
upgradeWorkers->DisplayButton(_mouseX, _mouseY, _isClicked);
upgradeCursor->DisplayButton(_mouseX, _mouseY, _isClicked);
}
To make everything work, I updated the gameplay and main files.
gameplay.h
public:
static Upgrades *upgrades;
gameplay.cpp
upgrades = new Upgrades( defaultUpgradedButtonImageLocation,
hoveredUpgradeButtonImageLocation,
clickedUpgradeButtonImageLocation,
windowWidth,
windowHeight,
static_cast<float>(windowWidth * 0.22),
static_cast<float>(windowHeight * 0.9),
200,
getRenderer );
The only thing, is to update the gameplay class on the main file to instantiate the upgrade buttons:
main.cpp
std::string generateBackgroundImagePath = Game::execpath + std::string("/Contents/Resources/graphics/enviroment/Game_Enviroment_Alternative_Sky.jpg");
std::string generateMenuSfxPath = Game::execpath + std::string("/Contents/Resources/audio/menulightup/lightup.wav");
std::string generateUpgradeButtonDefaultPath = Game::execpath + std::string("/Contents/Resources/graphics/interface/UpgradeButton.png");
std::string generateUpgradeButtonClickedPath = Game::execpath + std::string("/Contents/Resources/graphics/interface/UpgradeButtonClicked.png");
std::string generateUpgradeButtonHoveredPath = Game::execpath + std::string("/Contents/Resources/graphics/interface/UpgradeButtonLightUp.png");
Game::gameplay = new Gameplay( generateFont2Path,
generateBackgroundImagePath,
generateMenuSfxPath,
Game::windowWidth,
Game::windowHeight,
Game::renderer,
generateUpgradeButtonDefaultPath,
generateUpgradeButtonHoveredPath,
generateUpgradeButtonClickedPath );