近期入门学习C++ 做的一个小项目练手
Gitee开源地址
单文件源码300行
使用了双缓冲技术来消除system(“cls”)刷新时带来的闪烁
多线程监听按键,拒绝阻塞
学习了对象的继承等
新人初学练手项目
代码结构观感较差,仅仅实现了功能
大佬勿喷
下载后编译运行即可
欢迎小伙伴尝试~
#include <iostream>
#include <windows.h>
#include <vector>
#include <ctime>
#include <unordered_set>
#include <thread>
#include <conio.h>
using namespace std;
const int ROW = 22;//行
const int COL = 42;//列
enum dir
{
UP, DOWN, LEFT, RIGHT
};
class Refresh
{
protected:
HANDLE hOutput;
HANDLE hOutBuf;
COORD coord;
char data[4000];
//双缓冲处理显示
DWORD bytes = 0;
public:
Refresh()
{
//获取默认标准显示缓冲区句柄
coord = { 0, 0 };
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//创建新的缓冲区
hOutBuf = CreateConsoleScreenBuffer(
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CONSOLE_TEXTMODE_BUFFER,
NULL
);
//设置新的缓冲区为活动显示缓冲
SetConsoleActiveScreenBuffer(hOutBuf);
//隐藏两个缓冲区的光标
CONSOLE_CURSOR_INFO cci;
cci.bVisible = 0;
cci.dwSize = 1;
SetConsoleCursorInfo(hOutput, &cci);
SetConsoleCursorInfo(hOutBuf, &cci);
}
void refreshFrame()
{
ReadConsoleOutputCharacterA(hOutput, data, 4000, coord, &bytes);
WriteConsoleOutputCharacterA(hOutBuf, data, 4000, coord, &bytes);
FillConsoleOutputCharacterA(hOutput, ' ', bytes, coord, &bytes);
//FlushFileBuffers(hOutput);//清空暂存缓存区
SetConsoleCursorPosition(hOutput, { 0, 0 });//设置光标位置
}
void setCursorPosition(int x, int y)
{
SetConsoleCursorPosition(hOutput, { (short)(x), (short)y });
}
void loadMap()
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (j == 0 || j == COL - 1)
{
setCursorPosition(2 * j, i);
printf("■");
}
else if (i == 0 || i == ROW - 1)
{
printf("■");
}
}
}
setCursorPosition(0, ROW);
printf("得分:");
}
};
class Snake : public Refresh
{
protected:
struct b
{
int x, y;
};
std::vector<struct b> body;
public:
Snake()
{
body.push_back({ COL / 2, ROW / 2 });//头
body.push_back({ COL / 2 - 1, ROW / 2 });//身体
body.push_back({ COL / 2 - 2, ROW / 2 });//身体
}
void printSnake()
{
setCursorPosition(2 * body[0].x, body[0].y);
printf("■");
for (int i = 1; i < body.size(); ++i)
{
setCursorPosition(2 * body[i].x, body[i].y);
printf("□");
}
}
void moveSnake(int dir)
{
for (int i = body.size() - 1; i >= 1; --i)
{
body[i] = body[i - 1];
}
switch (dir)
{
case UP:
body[0].y--;
break;
case DOWN:
body[0].y++;
break;
case LEFT:
body[0].x--;
break;
case RIGHT:
body[0].x++;
break;
}
}
void eatSnake()
{
body.push_back({ body.back().x, body.back().y });
}
};
class Game final : public Snake
{
public:
static bool isgameOver;
int score;
static int dir;
protected:
/*
struct pair_hash
{
inline size_t operator()(const std::pair<int, int>& p) const
{
return p.first * p.second;
}
};
*/
struct pair_hash
{
template <class T1, class T2>
size_t operator()(pair<T1, T2> const& pair) const
{
size_t h1 = hash<T1>()(pair.first); //用默认的 hash 处理 pair 中的第一个数据 X1
size_t h2 = hash<T2>()(pair.second);//用默认的 hash 处理 pair 中的第二个数据 X2
return h1 ^ h2;
}
};
std::unordered_set<std::pair<int, int>, pair_hash> s;
public:
Game()
{
srand((unsigned int)time(NULL));
score = 0;
}
inline int getRandinRange(int a, int b)
{
return (rand() % (b - a + 1)) + a;
}
void generateFood()
{
int x, y;
do
{
x = getRandinRange(1, COL - 2);
y = getRandinRange(1, ROW - 2);
} while (isOnSnake(x, y) || s.count({ x, y }));
s.insert({ x, y });
}
void setFood()
{
for (auto it = s.begin(); it != s.end(); ++it)
{
setCursorPosition(2 * it->first, it->second);
printf("●");
}
}
bool isOnSnake(int x, int y)
{
for (const b& b : body)
{
if (b.x == x && b.y == y) return true;
}
return false;
}
void updateScore()
{
setCursorPosition(6, ROW);
printf("%d", score);
}
bool gameOver()
{
int x = body[0].x;
int y = body[0].y;
if (x == 0 || y == 0 || x >= COL - 1 || y >= ROW - 1) return true;
for (int i = 1; i < body.size(); ++i) if (x == body[i].x && y == body[i].y) return true;
return false;
}
bool hasFood(int x, int y)
{
if (s.count({ x, y })) return true;
else return false;
}
void judge()
{
int x = body[0].x;
int y = body[0].y;
if (hasFood(x, y))
{
s.erase({ x, y });
score += 10;
eatSnake();
generateFood();
}
}
};
int Game::dir = RIGHT;
bool Game::isgameOver = false;
void listenKey()//按键监听线程
{
while (1)
{
if (Game::isgameOver) ExitThread(0);
int key = getch();
switch (key)
{
case 72:
if (Game::dir == UP || Game::dir == DOWN);
else Game::dir = UP;
break;
case 80:
if (Game::dir == UP || Game::dir == DOWN);
else Game::dir = DOWN;
break;
case 75:
if (Game::dir == LEFT || Game::dir == RIGHT);
else Game::dir = LEFT;
break;
case 77:
if (Game::dir == LEFT || Game::dir == RIGHT);
else Game::dir = RIGHT;
break;
}
}
}
int main()
{
system("title 儒");
system("mode con cols=84 lines=23");
gameBegin:
Game::dir = RIGHT;
Game::isgameOver = false;
Game g;
std::thread task(listenKey);
task.detach();
g.loadMap();
g.printSnake();
g.generateFood();
g.setFood();
g.updateScore();
g.refreshFrame();
while (1)
{
g.loadMap();
g.moveSnake(Game::dir);
g.printSnake();
g.judge();
if (g.gameOver())
{
Game::isgameOver = true;
g.setCursorPosition(COL - 5, ROW / 2);
printf("GAME OVER !");
g.setCursorPosition(COL - 5, ROW / 2 + 1);
printf("Wanna retry?(y/n)");
g.refreshFrame();
while (1)
{
int key = getch();
if (key == 121 || key == 89) goto gameBegin;
else if (key == 110 || key == 78) break;
}
}
g.setFood();
g.updateScore();
g.refreshFrame();
this_thread::sleep_for(chrono::milliseconds(50));//移动速度
}
system("pause");
return 0;
}