Looking to build a terminal based game.
I am assuming X-Term like terminal.
This means:
- I can use the X-Term control codes to clear and move around the screen.
- I can use
tcsetattr
functions to make standard input none buffered (I think).
I know this game loop runs as basically a busy wait. I will sove that at the select()
command by adding the appropriate timeout there (I just need to calculate an appropriate value).
This is the shell for my game (I have not implemented game logic). I wanted to make sure I am not messing stuff up around display and terminal manipulation before I go any further.
#include <iostream>
#include <signal.h>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
static volatile sig_atomic_t done = 0;
extern "C" void handle_done(int signum)
{
if (!done) {
done = signum;
}
}
class Game
{
static constexpr int width = 40;
static constexpr int height = 20;
static constexpr char clearScreen[] = "\033[2J";
static constexpr char movetoZeroZero[] = "\033[0;0H";
static constexpr char hideCursor[] = "\033[?25l";
static constexpr char showCursor[] = "\033[?25h";
termios originalConfig;
bool gameOver;
char getChar(int x, int y)
{
if (y == 0 || y == height -1 || x == 0 || x == width -1 ) {
return '#';
}
return ' ';
}
void draw(bool dirty)
{
std::cout << movetoZeroZero
<< "Snake V1\n";
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
std::cout << getChar(x, y);
}
std::cout << "\n";
}
std::cout << std::flush;
}
bool input()
{
if (done) {
gameOver = true;
}
fd_set input;
FD_ZERO(&input);
FD_SET(STDIN_FILENO, &input);
timeval timeout{0, 0};
if (select(STDIN_FILENO+1, &input, nullptr, nullptr, &timeout) > 0) {
char key = std::cin.get();
if (key == 'Q') {
gameOver = true;
}
}
return false;
}
bool logic()
{
return false;
}
int install_done(const int signum)
{
struct sigaction act {0};
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = handle_done;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1) {
return errno;
}
return 0;
}
public:
Game()
: gameOver(false)
{
if (install_done(SIGINT) || install_done(SIGTERM) || install_done(SIGHUP)) {
throw std::runtime_error("Fail: Installing handlers\n");
}
termios config;
if (!isatty(STDIN_FILENO) || tcgetattr(STDIN_FILENO, &originalConfig) || tcgetattr(STDIN_FILENO, &config)) {
throw std::runtime_error("Fail: Setting up input stream\n");
}
config.c_lflag &= ~(ICANON | ECHO);
config.c_cc[VMIN] = 1; /* Blocking input */
config.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &config);
}
~Game()
{
tcsetattr(STDIN_FILENO, TCSAFLUSH, &originalConfig);
}
void run()
{
std::cout << clearScreen
<< hideCursor;
bool dirty = true;
while (!gameOver)
{
draw(dirty);
dirty = input();
dirty = logic() || dirty;
}
std::cout << showCursor;
}
};
int main()
{
Game game;
game.run();
}