I’m a beginner in SDL2 and I’m trying to make a window, with a GUI (where you can move a sprite around) and a CLI (a textbox where the game can return some text and the user can insert commands).
Let’s say the entire output textbox is 800x280 pixels and below is the input textbox which is 800x20 pixels. This means, each line of text should be 800x20 pixels, we can divide the entire output by 14 parts, or 14 textures. Maybe I can use a class
but I’m still looking for more efficient ways.
I don’t plan to make the textbox scrolling but when there is an input, all the boxes get incremented down (where the lowest box get moved up to the top).
This is my unfinished attempt (excuse my horrible code structure):
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <string>
// Screen dimensions, constants
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 900; // 600 for ground, 280 for output, 20 for input
SDL_Window* gWindow = NULL; // The window we'll be rendering to
SDL_Surface* gScreenSurface = NULL; // The surface contained by the window
SDL_Surface* gCurrentSurface = NULL; // Current displayed image
SDL_Surface* gTextSurface = NULL;
SDL_Renderer* gRenderer = NULL; // The renderer we'll be using
SDL_Texture* gTextOutput = NULL;
TTF_Font* gFont = NULL;
SDL_Color gTextColor = { 0, 0, 0, 0xFF };
SDL_Rect rcSprite, rcGround, rcTextInput;
SDL_Rect rcTOB0, rcTOB1, rcTOB2, rcTOB3, rcTOB4, rcTOB5, rcTOB6, rcTOB7,
rcTOB8, rcTOB9, rcTOB10, rcTOB11, rcTOB12, rcTOB13;
SDL_Rect rcTextOutputBox[14] = { rcTOB0, rcTOB1, rcTOB2, rcTOB3, rcTOB4, rcTOB5,
rcTOB6, rcTOB7, rcTOB8, rcTOB9, rcTOB10,
rcTOB11, rcTOB12, rcTOB13 };
int iTOBInc = 0;
void init();
void loadMedia();
void quit();
void output(std::string text);
void output(std::string text)
{
SDL_Surface* gTextSurface = TTF_RenderText_Solid(gFont, text.c_str(), gTextColor);
if (gTextSurface != NULL)
{
gTextOutput = SDL_CreateTextureFromSurface(gRenderer, gTextSurface);
if (gTextOutput == NULL)
{
throw "Unable to render texture! SDL ERROR: ";
}
SDL_RenderCopy(gRenderer, gTextOutput, NULL, &rcTextOutputBox[iTOBInc]);
SDL_RenderCopyEx(gRenderer, gTextOutput, &rcTextOutputBox[iTOBInc], NULL, 0.0, NULL, SDL_FLIP_NONE);
}
else
{
throw "Unable to render text surface! SDL_ttf Error: ";
}
}
void init()
{
if(SDL_Init(SDL_INIT_VIDEO) > 0)
{
throw "SDL failed to initialise! ERROR: ";
}
else
{
gWindow = SDL_CreateWindow("Caventure",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
SCREEN_WIDTH,
SCREEN_HEIGHT,
SDL_WINDOW_SHOWN);
if(gWindow == NULL)
{
throw "Window failed to initialise! ERROR: ";
}
else
{
gScreenSurface = SDL_GetWindowSurface(gWindow);
}
gRenderer = SDL_CreateRenderer(gWindow,
-1,
SDL_RENDERER_ACCELERATED);
if (gRenderer == NULL)
{
throw "Renderer could not be initialised! ERROR: ";
}
else
{
SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0x00);
if (TTF_Init() == -1)
{
throw "TTF could not be initialised! ERROR: ";
}
}
}
}
void loadMedia()
{
// Ground rendering
rcGround.x = 0;
rcGround.y = 0;
rcGround.w = 800;
rcGround.h = 600;
// Sprite rendering
rcSprite.x = 400;
rcSprite.y = 300;
rcSprite.w = 4;
rcSprite.h = 4;
// TextOutput box rendering
for (int i = 0; i < 14; i++)
{
rcTextOutputBox[i].x = 0;
rcTextOutputBox[i].y = 20 * i + 600;
rcTextOutputBox[i].w = 800;
rcTextOutputBox[i].h = 20;
}
// TextInput box rendering
rcTextInput.x = 0;
rcTextInput.y = 880;
rcTextInput.w = 800;
rcTextInput.h = 20;
gFont = TTF_OpenFont("resources/consolas.ttf", 14);
if (gFont == NULL)
{
throw "Failed to load font! ERROR: ";
}
SDL_SetTextInputRect(&rcTextInput);
}
void quit()
{
// Destroy window
SDL_DestroyWindow(gWindow);
SDL_DestroyRenderer(gRenderer);
TTF_CloseFont(gFont);
gWindow = NULL;
gRenderer = NULL;
gFont = NULL;
// Quit SDL subsystems
TTF_Quit();
SDL_Quit();
}
int main()
{
try
{
init();
loadMedia();
bool quit = false;
bool renderText = false;
SDL_Event event;
std::string inputText = "";
std::string inputCmd = "";
SDL_StartTextInput();
while(!quit)
{
while(SDL_PollEvent(&event) != 0)
{
if(event.type == SDL_QUIT)
{
quit = true;
}
else if(event.type == SDL_KEYDOWN)
{
switch(event.key.keysym.sym)
{
case SDLK_UP:
rcSprite.y -= 5;
break;
case SDLK_DOWN:
rcSprite.y += 5;
break;
case SDLK_LEFT:
rcSprite.x -= 5;
break;
case SDLK_RIGHT:
rcSprite.x += 5;
break;
}
}
else if (event.type == SDL_TEXTINPUT)
{
inputText += event.text.text;
}
else if (event.key.keysym.sym == SDLK_BACKSPACE && inputText.length() > 0)
{
inputText.pop_back();
}
else if (event.key.keysym.sym == SDLK_RETURN && inputText.size() != 0)
{
inputCmd = inputText.c_str();
renderText = true;
inputText = "";
}
}
if (rcSprite.x < 0 ||
rcSprite.y < 0 ||
rcSprite.y > rcGround.h ||
rcSprite.x > rcGround.w)
{
rcSprite.x = 400;
rcSprite.y = 300;
}
SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0x00);
SDL_RenderClear(gRenderer);
SDL_RenderFillRect(gRenderer, &rcGround);
SDL_BlitSurface(gCurrentSurface, NULL, gScreenSurface, &rcGround);
SDL_RenderFillRect(gRenderer, &rcTextInput);
SDL_BlitSurface(gCurrentSurface, NULL, gScreenSurface, &rcTextInput);
SDL_SetRenderDrawColor(gRenderer, 0x40, 0x40, 0x40, 0x40);
for (int i = 0; i < 14; i++)
{
SDL_RenderFillRect(gRenderer, &rcTextOutputBox[i]);
SDL_BlitSurface(gCurrentSurface, NULL, gScreenSurface, &rcTextOutputBox[i]);
if (i == iTOBInc &&
renderText &&
inputCmd != "")
{
output(inputCmd);
iTOBInc++;
renderText = false;
}
if (SDL_GetError() != NULL || TTF_GetError() != NULL)
{
throw "ERROR: ";
}
}
SDL_SetRenderDrawColor(gRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
SDL_RenderDrawLine(gRenderer, 0, 600, 800, 600);
SDL_RenderDrawLine(gRenderer, 0, 880, 800, 880);
SDL_RenderFillRect(gRenderer, &rcSprite);
SDL_BlitSurface(gCurrentSurface, NULL, gScreenSurface, &rcSprite);
SDL_RenderPresent(gRenderer);
}
SDL_StopTextInput();
}
catch (const std::string& msg)
{
printf("%s", msg.c_str());
if (SDL_GetError() != NULL)
{
printf("%s", SDL_GetError());
}
else if (TTF_GetError() != NULL)
{
printf("%s", TTF_GetError());
}
else
{
printf("%s", "NULL");
}
quit();
exit(EXIT_FAILURE);
}
quit();
return 0;
}
Right now, this code doesn’t work, it just returns a SIGABRT
regarding a const char*
.