Added The Recursive Backtracker Maze algorithm
This commit is contained in:
parent
3c2d01859a
commit
b586979c23
220
OneLoneCoder_Mazes.cpp
Normal file
220
OneLoneCoder_Mazes.cpp
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
OneLoneCoder.com - Recursive Backtracker Maze Algorithm
|
||||
"Get lost..." - @Javidx9
|
||||
|
||||
Disclaimer
|
||||
~~~~~~~~~~
|
||||
I don't care what you use this for. It's intended to be educational, and perhaps
|
||||
to the oddly minded - a little bit of fun. Please hack this, change it and use it
|
||||
in any way you see fit. BUT, you acknowledge that I am not responsible for anything
|
||||
bad that happens as a result of your actions. However, if good stuff happens, I
|
||||
would appreciate a shout out, or at least give the blog some publicity for me.
|
||||
Cheers!
|
||||
|
||||
Background
|
||||
~~~~~~~~~~
|
||||
I really like perfect algorithms. This one shows how to generate a maze that guarantees
|
||||
all cells can reach all other cells, it just may take some time to get there. I introduce
|
||||
stacks, and show how the algorithm generates the maze visually.
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
Twitter: @javidx9
|
||||
Blog: www.onelonecoder.com
|
||||
|
||||
Video:
|
||||
~~~~~~
|
||||
https://youtu.be/Y37-gB83HKE
|
||||
|
||||
Last Updated: 10/07/2017
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <stack>
|
||||
using namespace std;
|
||||
|
||||
#include "olcConsoleGameEngine.h"
|
||||
|
||||
class OneLoneCoder_Maze : public olcConsoleGameEngine
|
||||
{
|
||||
public:
|
||||
OneLoneCoder_Maze()
|
||||
{
|
||||
m_sAppName = L"MAZE";
|
||||
}
|
||||
|
||||
private:
|
||||
int m_nMazeWidth;
|
||||
int m_nMazeHeight;
|
||||
int *m_maze;
|
||||
|
||||
|
||||
// Some bit fields for convenience
|
||||
enum
|
||||
{
|
||||
CELL_PATH_N = 0x01,
|
||||
CELL_PATH_E = 0x02,
|
||||
CELL_PATH_S = 0x04,
|
||||
CELL_PATH_W = 0x08,
|
||||
CELL_VISITED = 0x10,
|
||||
};
|
||||
|
||||
|
||||
// Algorithm variables
|
||||
int m_nVisitedCells;
|
||||
stack<pair<int, int>> m_stack; // (x, y) coordinate pairs
|
||||
int m_nPathWidth;
|
||||
|
||||
|
||||
protected:
|
||||
// Called by olcConsoleGameEngine
|
||||
virtual bool OnUserCreate()
|
||||
{
|
||||
// Maze parameters
|
||||
m_nMazeWidth = 40;
|
||||
m_nMazeHeight = 25;
|
||||
m_maze = new int[m_nMazeWidth * m_nMazeHeight];
|
||||
memset(m_maze, 0x00, m_nMazeWidth * m_nMazeHeight * sizeof(int));
|
||||
m_nPathWidth = 3;
|
||||
|
||||
// Choose a starting cell
|
||||
int x = rand() % m_nMazeWidth;
|
||||
int y = rand() % m_nMazeHeight;
|
||||
m_stack.push(make_pair(x, y));
|
||||
m_maze[y * m_nMazeWidth + x] = CELL_VISITED;
|
||||
m_nVisitedCells = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called by olcConsoleGameEngine
|
||||
virtual bool OnUserUpdate(float fElapsedTime)
|
||||
{
|
||||
// Slow down for animation
|
||||
this_thread::sleep_for(10ms);
|
||||
|
||||
// Little lambda function to calculate index in a readable way
|
||||
auto offset = [&](int x, int y)
|
||||
{
|
||||
return (m_stack.top().second + y) * m_nMazeWidth + (m_stack.top().first + x);
|
||||
};
|
||||
|
||||
// Do Maze Algorithm
|
||||
if (m_nVisitedCells < m_nMazeWidth * m_nMazeHeight)
|
||||
{
|
||||
// Create a set of unvisted neighbours
|
||||
vector<int> neighbours;
|
||||
|
||||
// North neighbour
|
||||
if (m_stack.top().second > 0 && (m_maze[offset(0, -1)] & CELL_VISITED) == 0)
|
||||
neighbours.push_back(0);
|
||||
// East neighbour
|
||||
if (m_stack.top().first < m_nMazeWidth - 1 && (m_maze[offset(1, 0)] & CELL_VISITED) == 0)
|
||||
neighbours.push_back(1);
|
||||
// South neighbour
|
||||
if (m_stack.top().second < m_nMazeHeight - 1 && (m_maze[offset(0, 1)] & CELL_VISITED) == 0)
|
||||
neighbours.push_back(2);
|
||||
// West neighbour
|
||||
if (m_stack.top().first > 0 && (m_maze[offset(-1, 0)] & CELL_VISITED) == 0)
|
||||
neighbours.push_back(3);
|
||||
|
||||
// Are there any neighbours available?
|
||||
if (!neighbours.empty())
|
||||
{
|
||||
// Choose one available neighbour at random
|
||||
int next_cell_dir = neighbours[rand() % neighbours.size()];
|
||||
|
||||
// Create a path between the neighbour and the current cell
|
||||
switch (next_cell_dir)
|
||||
{
|
||||
case 0: // North
|
||||
m_maze[offset(0, -1)] |= CELL_VISITED | CELL_PATH_S;
|
||||
m_maze[offset(0, 0)] |= CELL_PATH_N;
|
||||
m_stack.push(make_pair((m_stack.top().first + 0), (m_stack.top().second - 1)));
|
||||
break;
|
||||
|
||||
case 1: // East
|
||||
m_maze[offset(+1, 0)] |= CELL_VISITED | CELL_PATH_W;
|
||||
m_maze[offset( 0, 0)] |= CELL_PATH_E;
|
||||
m_stack.push(make_pair((m_stack.top().first + 1), (m_stack.top().second + 0)));
|
||||
break;
|
||||
|
||||
case 2: // South
|
||||
m_maze[offset(0, +1)] |= CELL_VISITED | CELL_PATH_N;
|
||||
m_maze[offset(0, 0)] |= CELL_PATH_S;
|
||||
m_stack.push(make_pair((m_stack.top().first + 0), (m_stack.top().second + 1)));
|
||||
break;
|
||||
|
||||
case 3: // West
|
||||
m_maze[offset(-1, 0)] |= CELL_VISITED | CELL_PATH_E;
|
||||
m_maze[offset( 0, 0)] |= CELL_PATH_W;
|
||||
m_stack.push(make_pair((m_stack.top().first - 1), (m_stack.top().second + 0)));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
m_nVisitedCells++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No available neighbours so backtrack!
|
||||
m_stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === DRAWING STUFF ===
|
||||
|
||||
// Clear Screen by drawing 'spaces' everywhere
|
||||
Fill(0, 0, ScreenWidth(), ScreenHeight(), L' ');
|
||||
|
||||
// Draw Maze
|
||||
for (int x = 0; x < m_nMazeWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < m_nMazeHeight; y++)
|
||||
{
|
||||
// Each cell is inflated by m_nPathWidth, so fill it in
|
||||
for (int py = 0; py < m_nPathWidth; py++)
|
||||
for (int px = 0; px < m_nPathWidth; px++)
|
||||
{
|
||||
if (m_maze[y * m_nMazeWidth + x] & CELL_VISITED)
|
||||
Draw(x * (m_nPathWidth + 1) + px, y * (m_nPathWidth + 1) + py, PIXEL_SOLID, FG_WHITE); // Draw Cell
|
||||
else
|
||||
Draw(x * (m_nPathWidth + 1) + px, y * (m_nPathWidth + 1) + py, PIXEL_SOLID, FG_BLUE); // Draw Cell
|
||||
}
|
||||
|
||||
// Draw passageways between cells
|
||||
for (int p = 0; p < m_nPathWidth; p++)
|
||||
{
|
||||
if (m_maze[y * m_nMazeWidth + x] & CELL_PATH_S)
|
||||
Draw(x * (m_nPathWidth + 1) + p, y * (m_nPathWidth + 1) + m_nPathWidth); // Draw South Passage
|
||||
|
||||
if (m_maze[y * m_nMazeWidth + x] & CELL_PATH_E)
|
||||
Draw(x * (m_nPathWidth + 1) + m_nPathWidth, y * (m_nPathWidth + 1) + p); // Draw East Passage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw Unit - the top of the stack
|
||||
for (int py = 0; py < m_nPathWidth; py++)
|
||||
for (int px = 0; px < m_nPathWidth; px++)
|
||||
Draw(m_stack.top().first * (m_nPathWidth + 1) + px, m_stack.top().second * (m_nPathWidth + 1) + py, 0x2588, FG_GREEN); // Draw Cell
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
// Seed random number generator
|
||||
srand(clock());
|
||||
|
||||
// Use olcConsoleGameEngine derived app
|
||||
OneLoneCoder_Maze game;
|
||||
game.ConstructConsole(160, 100, 8, 8);
|
||||
game.Start();
|
||||
|
||||
return 0;
|
||||
}
|
||||
421
olcConsoleGameEngine.h
Normal file
421
olcConsoleGameEngine.h
Normal file
@ -0,0 +1,421 @@
|
||||
/*
|
||||
OneLoneCoder.com - Command Line Game Engine
|
||||
"Who needs a frame buffer?" - @Javidx9
|
||||
|
||||
Disclaimer
|
||||
~~~~~~~~~~
|
||||
I don't care what you use this for. It's intended to be educational, and perhaps
|
||||
to the oddly minded - a little bit of fun. Please hack this, change it and use it
|
||||
in any way you see fit. BUT, you acknowledge that I am not responsible for anything
|
||||
bad that happens as a result of your actions. However, if good stuff happens, I
|
||||
would appreciate a shout out, or at least give the blog some publicity for me.
|
||||
Cheers!
|
||||
|
||||
Background
|
||||
~~~~~~~~~~
|
||||
If you've seen any of my videos - I like to do things using the windows console. It's quick
|
||||
and easy, and allows you to focus on just the code that matters - ideal when you're
|
||||
experimenting. Thing is, I have to keep doing the same initialisation and display code
|
||||
each time, so this class wraps that up.
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
Twitter: @javidx9
|
||||
Blog: www.onelonecoder.com
|
||||
|
||||
Video:
|
||||
~~~~~~
|
||||
https://youtu.be/cWc0hgYwZyc
|
||||
|
||||
Last Updated: 10/07/2017
|
||||
|
||||
Usage:
|
||||
~~~~~~
|
||||
This class is abstract, so you must inherit from it. Override the OnUserCreate() function
|
||||
with all the stuff you need for your application (for thready reasons it's best to do
|
||||
this in this function and not your class constructor). Override the OnUserUpdate(float fElapsedTime)
|
||||
function with the good stuff, it gives you the elapsed time since the last call so you
|
||||
can modify your stuff dynamically. Both functions should return true, unless you need
|
||||
the application to close.
|
||||
|
||||
int main()
|
||||
{
|
||||
// Use olcConsoleGameEngine derived app
|
||||
OneLoneCoder_Example game;
|
||||
|
||||
// Create a console with resolution 160x100 characters
|
||||
// Each character occupies 8x8 pixels
|
||||
game.ConstructConsole(160, 100, 8, 8);
|
||||
|
||||
// Start the engine!
|
||||
game.Start();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Input is also handled for you - interrogate the m_keys[] array with the virtual
|
||||
keycode you want to know about. bPressed is set for the frame the key is pressed down
|
||||
in, bHeld is set if the key is held down, bReleased is set for the frame the key
|
||||
is released in.
|
||||
|
||||
The draw routines treat characters like pixels. By default they are set to white solid
|
||||
blocks - but you can draw any unicode character, using any of the colours listed below.
|
||||
|
||||
There may be bugs!
|
||||
|
||||
See my other videos for examples!
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
using namespace std;
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
enum COLOUR
|
||||
{
|
||||
FG_BLACK = 0x0000,
|
||||
FG_DARK_BLUE = 0x0001,
|
||||
FG_DARK_GREEN = 0x0002,
|
||||
FG_DARK_CYAN = 0x0003,
|
||||
FG_DARK_RED = 0x0004,
|
||||
FG_DARK_MAGENTA = 0x0005,
|
||||
FG_DARK_YELLOW = 0x0006,
|
||||
FG_GREY = 0x0007,
|
||||
FG_BLUE = 0x0009,
|
||||
FG_GREEN = 0x000A,
|
||||
FG_CYAN = 0x000B,
|
||||
FG_RED = 0x000C,
|
||||
FG_MAGENTA = 0x000D,
|
||||
FG_YELLOW = 0x000E,
|
||||
FG_WHITE = 0x000F,
|
||||
BG_BLACK = 0x0000,
|
||||
BG_DARK_BLUE = 0x0010,
|
||||
BG_DARK_GREEN = 0x0020,
|
||||
BG_DARK_CYAN = 0x0030,
|
||||
BG_DARK_RED = 0x0040,
|
||||
BG_DARK_MAGENTA = 0x0050,
|
||||
BG_DARK_YELLOW = 0x0060,
|
||||
BG_GREY = 0x0070,
|
||||
BG_BLUE = 0x0090,
|
||||
BG_GREEN = 0x00A0,
|
||||
BG_CYAN = 0x00B0,
|
||||
BG_RED = 0x00C0,
|
||||
BG_MAGENTA = 0x00D0,
|
||||
BG_YELLOW = 0x00E0,
|
||||
BG_WHITE = 0x00F0,
|
||||
};
|
||||
|
||||
enum PIXEL_TYPE
|
||||
{
|
||||
PIXEL_SOLID = 0x2588,
|
||||
PIXEL_HALF = 0x2592,
|
||||
};
|
||||
|
||||
|
||||
class olcConsoleGameEngine
|
||||
{
|
||||
public:
|
||||
olcConsoleGameEngine()
|
||||
{
|
||||
m_nScreenWidth = 80;
|
||||
m_nScreenHeight = 30;
|
||||
|
||||
m_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
m_keyNewState = new short[256];
|
||||
m_keyOldState = new short[256];
|
||||
memset(m_keyNewState, 0, 256 * sizeof(short));
|
||||
memset(m_keyOldState, 0, 256 * sizeof(short));
|
||||
|
||||
memset(m_keys, 0, 256 * sizeof(sKeyState));
|
||||
|
||||
|
||||
m_sAppName = L"Default";
|
||||
|
||||
}
|
||||
|
||||
int ConstructConsole(int width, int height, int fontw = 12, int fonth = 12)
|
||||
{
|
||||
m_nScreenWidth = width;
|
||||
m_nScreenHeight = height;
|
||||
|
||||
CONSOLE_FONT_INFOEX cfi;
|
||||
cfi.cbSize = sizeof(cfi);
|
||||
cfi.nFont = 0;
|
||||
cfi.dwFontSize.X = fontw;
|
||||
cfi.dwFontSize.Y = fonth;
|
||||
cfi.FontFamily = FF_DONTCARE;
|
||||
cfi.FontWeight = FW_NORMAL;
|
||||
wcscpy_s(cfi.FaceName, L"Consolas");
|
||||
|
||||
if (!SetCurrentConsoleFontEx(m_hConsole, false, &cfi))
|
||||
return Error(L"SetCurrentConsoleFontEx");
|
||||
|
||||
COORD coordLargest = GetLargestConsoleWindowSize(m_hConsole);
|
||||
if (m_nScreenHeight > coordLargest.Y)
|
||||
return Error(L"Game Height Too Big");
|
||||
if (m_nScreenWidth > coordLargest.X)
|
||||
return Error(L"Game Width Too Big");
|
||||
|
||||
COORD buffer = { (short)m_nScreenWidth, (short)m_nScreenHeight };
|
||||
if (!SetConsoleScreenBufferSize(m_hConsole, buffer))
|
||||
Error(L"SetConsoleScreenBufferSize");
|
||||
|
||||
m_rectWindow = { 0, 0, (short)m_nScreenWidth - 1, (short)m_nScreenHeight - 1 };
|
||||
if (!SetConsoleWindowInfo(m_hConsole, TRUE, &m_rectWindow))
|
||||
Error(L"SetConsoleWindowInfo");
|
||||
|
||||
m_bufScreen = new CHAR_INFO[m_nScreenWidth*m_nScreenHeight];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Draw(int x, int y, wchar_t c = 0x2588, short col = 0x000F)
|
||||
{
|
||||
if (x >= 0 && x < m_nScreenWidth && y >= 0 && y < m_nScreenHeight)
|
||||
{
|
||||
m_bufScreen[y * m_nScreenWidth + x].Char.UnicodeChar = c;
|
||||
m_bufScreen[y * m_nScreenWidth + x].Attributes = col;
|
||||
}
|
||||
}
|
||||
|
||||
void Fill(int x1, int y1, int x2, int y2, wchar_t c = 0x2588, short col = 0x000F)
|
||||
{
|
||||
Clip(x1, y1);
|
||||
Clip(x2, y2);
|
||||
for (int x = x1; x < x2; x++)
|
||||
for (int y = y1; y < y2; y++)
|
||||
Draw(x, y, c, col);
|
||||
}
|
||||
|
||||
void DrawString(int x, int y, wstring c, short col = 0x000F)
|
||||
{
|
||||
for (size_t i = 0; i < c.size(); i++)
|
||||
{
|
||||
m_bufScreen[y * m_nScreenWidth + x + i].Char.UnicodeChar = c[i];
|
||||
m_bufScreen[y * m_nScreenWidth + x + i].Attributes = col;
|
||||
}
|
||||
}
|
||||
|
||||
void Clip(int &x, int &y)
|
||||
{
|
||||
if (x < 0) x = 0;
|
||||
if (x >= m_nScreenWidth) x = m_nScreenWidth;
|
||||
if (y < 0) y = 0;
|
||||
if (y >= m_nScreenHeight) y = m_nScreenHeight;
|
||||
}
|
||||
|
||||
void DrawLine(int x1, int y1, int x2, int y2, wchar_t c = 0x2588, short col = 0x000F)
|
||||
{
|
||||
int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i;
|
||||
dx = x2 - x1;
|
||||
dy = y2 - y1;
|
||||
dx1 = abs(dx);
|
||||
dy1 = abs(dy);
|
||||
px = 2 * dy1 - dx1;
|
||||
py = 2 * dx1 - dy1;
|
||||
if (dy1 <= dx1)
|
||||
{
|
||||
if (dx >= 0)
|
||||
{
|
||||
x = x1;
|
||||
y = y1;
|
||||
xe = x2;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = x2;
|
||||
y = y2;
|
||||
xe = x1;
|
||||
}
|
||||
Draw(x, y, c, col);
|
||||
for (i = 0; x<xe; i++)
|
||||
{
|
||||
x = x + 1;
|
||||
if (px<0)
|
||||
px = px + 2 * dy1;
|
||||
else
|
||||
{
|
||||
if ((dx<0 && dy<0) || (dx>0 && dy>0))
|
||||
y = y + 1;
|
||||
else
|
||||
y = y - 1;
|
||||
px = px + 2 * (dy1 - dx1);
|
||||
}
|
||||
Draw(x, y, c, col);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dy >= 0)
|
||||
{
|
||||
x = x1;
|
||||
y = y1;
|
||||
ye = y2;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = x2;
|
||||
y = y2;
|
||||
ye = y1;
|
||||
}
|
||||
Draw(x, y, c, col);
|
||||
for (i = 0; y<ye; i++)
|
||||
{
|
||||
y = y + 1;
|
||||
if (py <= 0)
|
||||
py = py + 2 * dx1;
|
||||
else
|
||||
{
|
||||
if ((dx<0 && dy<0) || (dx>0 && dy>0))
|
||||
x = x + 1;
|
||||
else
|
||||
x = x - 1;
|
||||
py = py + 2 * (dx1 - dy1);
|
||||
}
|
||||
Draw(x, y, c, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
~olcConsoleGameEngine()
|
||||
{
|
||||
SetConsoleActiveScreenBuffer(m_hOriginalConsole);
|
||||
delete[] m_bufScreen;
|
||||
}
|
||||
|
||||
public:
|
||||
void Start()
|
||||
{
|
||||
m_bAtomActive = true;
|
||||
|
||||
// Star the thread
|
||||
thread t = thread(&olcConsoleGameEngine::GameThread, this);
|
||||
|
||||
// Wait for thread to be exited
|
||||
m_cvGameFinished.wait(unique_lock<mutex>(m_muxGame));
|
||||
|
||||
// Tidy up
|
||||
t.join();
|
||||
}
|
||||
|
||||
int ScreenWidth()
|
||||
{
|
||||
return m_nScreenWidth;
|
||||
}
|
||||
|
||||
int ScreenHeight()
|
||||
{
|
||||
return m_nScreenHeight;
|
||||
}
|
||||
|
||||
private:
|
||||
void GameThread()
|
||||
{
|
||||
// Create user resources as part of this thread
|
||||
if (!OnUserCreate())
|
||||
return;
|
||||
|
||||
auto tp1 = chrono::system_clock::now();
|
||||
auto tp2 = chrono::system_clock::now();
|
||||
|
||||
// Run as fast as possible
|
||||
while (m_bAtomActive)
|
||||
{
|
||||
// Handle Timing
|
||||
tp2 = chrono::system_clock::now();
|
||||
chrono::duration<float> elapsedTime = tp2 - tp1;
|
||||
tp1 = tp2;
|
||||
float fElapsedTime = elapsedTime.count();
|
||||
|
||||
// Handle Input
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
m_keyNewState[i] = GetAsyncKeyState(i);
|
||||
|
||||
m_keys[i].bPressed = false;
|
||||
m_keys[i].bReleased = false;
|
||||
|
||||
if (m_keyNewState[i] != m_keyOldState[i])
|
||||
{
|
||||
if (m_keyNewState[i] & 0x8000)
|
||||
{
|
||||
m_keys[i].bPressed = !m_keys[i].bHeld;
|
||||
m_keys[i].bHeld = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_keys[i].bReleased = true;
|
||||
m_keys[i].bHeld = false;
|
||||
}
|
||||
}
|
||||
|
||||
m_keyOldState[i] = m_keyNewState[i];
|
||||
}
|
||||
|
||||
|
||||
// Handle Frame Update
|
||||
if (!OnUserUpdate(fElapsedTime))
|
||||
m_bAtomActive = false;
|
||||
|
||||
// Update Title & Present Screen Buffer
|
||||
wchar_t s[128];
|
||||
swprintf_s(s, 128, L"OneLoneCoder.com - Console Game Engine - %s - FPS: %3.2f ", m_sAppName.c_str(), 1.0f / fElapsedTime);
|
||||
SetConsoleTitle(s);
|
||||
WriteConsoleOutput(m_hConsole, m_bufScreen, { (short)m_nScreenWidth, (short)m_nScreenHeight }, { 0,0 }, &m_rectWindow);
|
||||
}
|
||||
|
||||
m_cvGameFinished.notify_one();
|
||||
}
|
||||
|
||||
public:
|
||||
// User MUST OVERRIDE THESE!!
|
||||
virtual bool OnUserCreate() = 0;
|
||||
virtual bool OnUserUpdate(float fElapsedTime) = 0;
|
||||
|
||||
|
||||
protected:
|
||||
int m_nScreenWidth;
|
||||
int m_nScreenHeight;
|
||||
CHAR_INFO *m_bufScreen;
|
||||
atomic<bool> m_bAtomActive;
|
||||
condition_variable m_cvGameFinished;
|
||||
mutex m_muxGame;
|
||||
wstring m_sAppName;
|
||||
|
||||
struct sKeyState
|
||||
{
|
||||
bool bPressed;
|
||||
bool bReleased;
|
||||
bool bHeld;
|
||||
} m_keys[256];
|
||||
|
||||
|
||||
protected:
|
||||
int Error(wchar_t *msg)
|
||||
{
|
||||
wchar_t buf[256];
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL);
|
||||
SetConsoleActiveScreenBuffer(m_hOriginalConsole);
|
||||
wprintf(L"ERROR: %s\n\t%s\n", msg, buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE m_hOriginalConsole;
|
||||
CONSOLE_SCREEN_BUFFER_INFO m_OriginalConsoleInfo;
|
||||
HANDLE m_hConsole;
|
||||
SMALL_RECT m_rectWindow;
|
||||
short *m_keyOldState;
|
||||
short *m_keyNewState;
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user