I wrote a retro 2D game in C++17, with 16 pixel sprites. I need to create a semi-transparent dialog to show the player status during the game. Can you give me an example how to do it? I use SDL_TTF and SDL2. I already have a semi-transparent black underline, now we need to write the properties and their values in the form of a 2-column table. How to do ?
Will the shape under the text/table be square/rectangle-shaped or you want the shape to be an irregular shape?
If you’re okay with having a square/rectangle under your text, you can render a square/rectangle under your text using the SDL_FillRect function, with the alpha being half-transparent.
I didn’t explain myself well, unfortunately I don’t write well in English. What I ask is to create a semi-transparent window on which the player’s status is written in 2 columns: Level, life, arm, defense, attack, etc…
Is the dialog a texture?
Maybe this will work
thank you for your suggestion. As soon as I’m home, I’ll respond better by adding if it’s allowed by the forum, the figures of what I want to achieve and what I wrote.
Here I am.
This is what I want to achieve:
This is what I got:
void UI::drawCharacterScreen()
{
// CREATE A FRAME: creiamo una finestra secondaria dove visualizzare gli stati del player
int frameX = GamePanel::GetInstance()->tileSize * 2;
int frameY = GamePanel::GetInstance()->tileSize;
int frameWidth = GamePanel::GetInstance()->tileSize * 5;
int frameHeight = GamePanel::GetInstance()->tileSize * 10;
drawSubWindow(frameX, frameY, frameWidth, frameHeight);
int textX = frameX + 20;
int textY = frameY + GamePanel::GetInstance()->tileSize - 20;
int textXValue = textX + 160;
int lineHeight = 26;
// TEXT: stato player
std::string nameStatoPlayer;
//----------- NAMES
nameStatoPlayer = std::string("Livello");
SDL_Texture* ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(nameStatoPlayer, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &textX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
nameStatoPlayer = std::string("Vita");
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(nameStatoPlayer, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &textX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
nameStatoPlayer = std::string("Forza");
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(nameStatoPlayer, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &textX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
nameStatoPlayer = std::string("Destrezza");
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(nameStatoPlayer, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &textX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
nameStatoPlayer = std::string("Attacco");
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(nameStatoPlayer, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &textX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
nameStatoPlayer = std::string("Difesa");
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(nameStatoPlayer, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &textX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
nameStatoPlayer = std::string("Esperienza");
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(nameStatoPlayer, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &textX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
nameStatoPlayer = std::string("Next Level");
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(nameStatoPlayer, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &textX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
nameStatoPlayer = std::string("Soldi");
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(nameStatoPlayer, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &textX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
nameStatoPlayer = std::string("Arma");
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(nameStatoPlayer, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &textX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
nameStatoPlayer = std::string("Scudo");
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(nameStatoPlayer, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &textX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
//------------- VALUES
int tailX = (frameX + frameWidth) - 40;
// Reset textY
textY = frameY + GamePanel::GetInstance()->tileSize - 20;
std::string value;
value = std::to_string(GamePanel::GetInstance()->player->level);
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(value, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &tailX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
tailX = (frameX + frameWidth) - 60;
value = std::to_string(GamePanel::GetInstance()->player->life);
value.append("/");
value.append(std::to_string(GamePanel::GetInstance()->player->maxLife));
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(value, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &tailX, &textY);
SDL_DestroyTexture(ftextureDialogue);
tailX = (frameX + frameWidth) - 40;
textY += lineHeight + 6;
value = std::to_string(GamePanel::GetInstance()->player->strength);
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(value, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &tailX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
value = std::to_string(GamePanel::GetInstance()->player->dexterity);
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(value, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &tailX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
value = std::to_string(GamePanel::GetInstance()->player->attack);
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(value, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &tailX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
value = std::to_string(GamePanel::GetInstance()->player->defense);
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(value, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &tailX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
value = std::to_string(GamePanel::GetInstance()->player->exp);
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(value, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &tailX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
value = std::to_string(GamePanel::GetInstance()->player->nextLevelExp);
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(value, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &tailX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
value = std::to_string(GamePanel::GetInstance()->player->coin);
ftextureDialogue = TextureManager::GetInstance()->LoadTextStato(value, lineHeight, *text_color_white);
TextureManager::GetInstance()->DrawDialogue(ftextureDialogue, &tailX, &textY);
SDL_DestroyTexture(ftextureDialogue);
textY += lineHeight + 6;
int xIcon = tailX - 8;
SDL_Rect dstRect = { xIcon, textY, GamePanel::GetInstance()->tileSize / 2, GamePanel::GetInstance()->tileSize / 2 };
SDL_RenderCopy(GamePanel::GetInstance()->GetRenderer(), GamePanel::GetInstance()->player->currentWeapon->down1, NULL, &dstRect);
textY += lineHeight + 6;
dstRect = { xIcon, textY, GamePanel::GetInstance()->tileSize / 2, GamePanel::GetInstance()->tileSize / 2 };
SDL_RenderCopy(GamePanel::GetInstance()->GetRenderer(), GamePanel::GetInstance()->player->currentShield->down1, NULL, &dstRect);
}
// finestra dei dialoghi tra personaggi
void UI::drawSubWindow(int x, int y, int width, int height)
{
// definisci le dimensioni del rettangolo
SDL_Rect rect{ x, y, width, height };
// attiva temporaneamente la modalita' trasparente nel parametro alpha
SDL_SetRenderDrawBlendMode(GamePanel::GetInstance()->GetRenderer(), SDL_BLENDMODE_BLEND);
// sfondo del rettangolo di colore nero con bassa trasparenza
SDL_SetRenderDrawColor(GamePanel::GetInstance()->GetRenderer(), 0, 0, 0, 210);
// riempi lo sfondo del rettangolo con il colore scelto
SDL_RenderFillRect(GamePanel::GetInstance()->GetRenderer(), &rect);
// disattiva la modalita' trasparente nel parametro alpha
SDL_SetRenderDrawBlendMode(GamePanel::GetInstance()->GetRenderer(), SDL_BLENDMODE_NONE);
// linea del rettangolo di colore bianco opaco
SDL_SetRenderDrawColor(GamePanel::GetInstance()->GetRenderer(), 255, 255, 255, 255);
SDL_RenderDrawRect(GamePanel::GetInstance()->GetRenderer(), &rect);
}
Is there a better method to obtain the target (first figure attached) ?
How can I get rounded window corners ?
How can I get rounded window corners ?
SDL doesn’t provide tools for drawing curves as far as I know, so I implemented this algorithm for exactly this purpose. You’re welcome to use my code, though it’s fairly crude and does not handle aliasing. I think there’s also a bug that overfills some pixels in the corners if using the “fill” version of the functions. But it’s good enough for my purposes. Attached a picture of what the results look like in my program.
/* Draw a circle quadrant. Quad 0 = upper right, 1 = upper left, 2 = lower left, 3 = lower right */
static void draw_quadrant(SDL_Renderer *rend, int xinit, int yinit, int r, const uint8_t quad)
{
int x = 0;
int y = r;
int d = 1-r;
while (x <= y) {
switch(quad) {
case 0:
SDL_RenderDrawPoint(rend, xinit + x, yinit - y);
SDL_RenderDrawPoint(rend, xinit + y, yinit - x);
break;
case 1:
SDL_RenderDrawPoint(rend, xinit - x, yinit - y);
SDL_RenderDrawPoint(rend, xinit - y, yinit - x);
break;
case 2:
SDL_RenderDrawPoint(rend, xinit - x, yinit + y);
SDL_RenderDrawPoint(rend, xinit - y, yinit + x);
break;
case 3:
SDL_RenderDrawPoint(rend, xinit + x, yinit + y);
SDL_RenderDrawPoint(rend, xinit + y, yinit + x);
break;
}
if (d>0) {
/* Select SE coordinate */
d += (x<<1) - (y<<1) + 5;
y--;
} else {
d += (x<<1) + 3;
}
x++;
}
}
/* Draw and fill a quadrant. quad 0 = upper right, 1 = upper left, 2 = lower left, 3 = lower right*/
static void fill_quadrant(SDL_Renderer *rend, int xinit, int yinit, int r, const uint8_t quad)
{
int x = 0;
int y = r;
int d = 1-r;
int fill_x = 0;
while (x <= y) {
if (d>0) {
switch(quad) {
case 0:
SDL_RenderDrawLine(rend, xinit, yinit - y, xinit + x, yinit - y);
SDL_RenderDrawLine(rend, xinit + y, yinit, xinit + y, yinit - x);
break;
case 1:
SDL_RenderDrawLine(rend, xinit, yinit - y, xinit - x, yinit - y);
SDL_RenderDrawLine(rend, xinit - y, yinit, xinit - y, yinit - x);
break;
case 2:
SDL_RenderDrawLine(rend, xinit, yinit + y, xinit - x, yinit + y);
SDL_RenderDrawLine(rend, xinit - y, yinit, xinit - y, yinit + x);
break;
case 3:
SDL_RenderDrawLine(rend, xinit, yinit + y, xinit + x, yinit + y);
SDL_RenderDrawLine(rend, xinit + y, yinit, xinit + y, yinit + x);
break;
}
/* Select SE coordinate */
d += (x<<1) - (y<<1) + 5;
y--;
} else {
d += (x<<1) + 3;
}
x++;
}
fill_x = x;
SDL_Rect fill = {xinit, yinit, fill_x, fill_x};
switch (quad) {
case 0:
fill.y -= fill_x -1;
break;
case 1:
fill.x -= fill_x - 1;
fill.y -= fill_x - 1;
break;
case 2:
fill.x -= fill_x - 1;
break;
case 3:
break;
default:
break;
}
SDL_RenderFillRect(rend, &fill);
}
void geom_draw_rounded_rect(SDL_Renderer *rend, SDL_Rect *rect, int r)
{
int left_x = rect->x + r;
int right_x = rect->x + rect->w -r;
int upper_y = rect->y + r;
int lower_y = rect->y + rect->h - r;
SDL_Point ul = {left_x, upper_y};
SDL_Point ur = {right_x, upper_y};
SDL_Point ll = {left_x, lower_y};
SDL_Point lr = {right_x, lower_y};
draw_quadrant(rend, ur.x, ur.y, r, 0);
draw_quadrant(rend, ul.x, ul.y, r, 1);
draw_quadrant(rend, ll.x, ll.y, r, 2);
draw_quadrant(rend, lr.x, lr.y, r, 3);
/* top */
SDL_RenderDrawLine(rend, left_x, rect->y, right_x, rect->y);
/* bottom */
SDL_RenderDrawLine(rend, left_x, lower_y + r, right_x, lower_y + r);
/* left */
SDL_RenderDrawLine(rend, rect->x, upper_y, rect->x, lower_y);
/* right */
SDL_RenderDrawLine(rend, right_x + r, upper_y, right_x + r, lower_y);
}
void geom_fill_rounded_rect(SDL_Renderer *rend, SDL_Rect *rect, int r)
{
int left_x = rect->x + r;
int right_x = rect->x + rect->w - r;
int upper_y = rect->y + r;
int lower_y = rect->y + rect->h - r;
SDL_Point ul = {left_x, upper_y};
SDL_Point ur = {right_x, upper_y};
SDL_Point ll = {left_x, lower_y};
SDL_Point lr = {right_x, lower_y};
fill_quadrant(rend, ur.x, ur.y, r, 0);
fill_quadrant(rend, ul.x, ul.y, r, 1);
fill_quadrant(rend, ll.x, ll.y, r, 2);
fill_quadrant(rend, lr.x, lr.y, r, 3);
int d = r<<1; // decision criterion
SDL_Rect top = {left_x + 1, rect->y, rect->w - d - 1, r};
SDL_Rect bottom = {left_x + 1, lower_y, rect->w - d - 1, r + 1};
SDL_Rect left = {rect->x, upper_y + 1, r, rect->h - d - 1};
SDL_Rect right = {right_x, upper_y + 1, r + 1, rect->h - d - 1};
SDL_Rect middle = {left_x, upper_y, rect->w - d, rect->h - d};
SDL_RenderFillRect(rend, &top);
SDL_RenderFillRect(rend, &bottom);
SDL_RenderFillRect(rend, &right);
SDL_RenderFillRect(rend, &left);
SDL_RenderFillRect(rend, &middle);
}
Thanks for sharing your code. Am I allowed to use it in my ?
Yes, it’s yours to use as you wish. That code lives here if you would like to credit me, but you don’t need to.
SDL2_gfx does. For example the function roundedRectangleColor()
.
I’m assuming this screenshot is from some retro game. If so, such a frame used to be rendered simply using tiles. For a frame like the one in the screenshot, three tiles are enough — corner, border and fill. The whole thing was done by rendering these tiles with appropriate rotation and flip.
You can do the same, but instead of rendering dozens of tiles, you just need to render the corners with rotation, the border tile can be rendered as appropriately stretched, and the filling can be done by simply filling the rectangle with the appropriate color and with the appropriate alpha channel (or one tile stretched to cover the entire frame filling rectangle).
Limiting the number of tiles can be used for a simple frame, but for a more graphically complex one, more textures will be needed. For example, if the left border is to look different than the right one, you need two textures (flip cannot be used).
chvolow code looks fine to me. I think most games have corner in the texture to draw it like that