初学C++,利用C的基础完成了控制台程序贪吃蛇。代码如下
#include <ctime>
#include <conio.h>
#include <iostream>
#include <windows.h>
#include <process.h>
#define random(x) (rand()%x)
using namespace std;
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
struct Snake {
COORD P;
short direc;
}snakebd[1000];
short length=0; // 蛇身长度
short score = 0; // 当前得分
short record = 0; // 得分最高记录
bool pause = false; // 暂停标识
bool GameOver = false; // 游戏结束标识
bool interrupt = false; // moving函数暂停标识
int chr; // 按键键码
short level; // 难度
COORD FoodPost; // 食物坐标
COORD Eat[100]; // 用来记录吃下的还在身体内的食物坐标
short eati = 0; // 对应Eat,记录吃下还在身体内的食物数量
bool operator==(COORD p1, COORD p2) // 重载运算符==用于类型COORD
{
if (p1.X == p2.X&&p1.Y == p2.Y) return true;
else return false;
}
void printxy(short x, short y,const char ch[]) // 在(x,y)处打印字符
{
COORD c;
c.X = x; c.Y = y;
SetConsoleCursorPosition(handle, c);
cout << ch;
}
void printxy(short x, short y, short n) // 重载printxy 在(x,y)处打印整型
{
COORD c;
c.X = x; c.Y = y;
SetConsoleCursorPosition(handle, c);
cout << n;
}
void color(int a) // 改变输出的颜色
{
SetConsoleTextAttribute(handle, a);
/*
1 深蓝色
2 深绿色
3 深青色
4 深红色
5 深粉色
6 黄色
7 深白色
8 灰色
9 浅蓝色
10 浅绿色
11 浅青色
12 浅红色
13 浅粉色
14 浅黄色
15 浅白色
*/
}
void food() // 生成食物
{
bool err;
do
{
err = false;
srand((unsigned int)(clock()));
FoodPost.X = random(40) + 2;
srand((unsigned int)(clock()));
FoodPost.Y = random(25) + 2;
for (int i = 0; i < length; i++)
{
if (snakebd[i].P == FoodPost)
{
err = true;
break;
}
}
} while (err);
color(14);
printxy(2* FoodPost.X-2, FoodPost.Y-1, "●");
color(4);
}
void initialize() // 初始化及绘制界面
{
int i;
score = 0;
level = 1;
length = 3;
eati = 0;
memset(Eat, NULL, sizeof(Eat));
snakebd[0] = { 20,14,3 };
snakebd[1] = { 21,14,3 };
snakebd[2] = { 22,14,3 };
color(2);
for (i = 1; i <= 27; i++)
{
if (i == 1 || i == 27)
cout << "██████████████████████████████████████████" << endl;
else if (i == 14)
{
cout << "█ ";
color(4);
cout << "●██";
color(2);
cout << " █" << endl;
}
else
cout << "█ █" << endl;
}
color(6);
printxy(84, 0, "█████████████████");
printxy(84, 1, "█ █");
printxy(84, 2, "█ 改变窗口尺寸会导致光标出现 █");
printxy(84, 3, "█ 按'V'可以隐藏光标 █");
printxy(84, 4, "█ 请切换输入法至英文输入 █");
printxy(84, 5, "█ 否则无法控制 █");
printxy(84, 6, "█ █");
printxy(84, 7, "█████████████████");
printxy(84, 8, "█ █ █");
printxy(84, 9, "█ 最高分:"); cout << record ;
printxy(100, 9, "█ 得分: "); cout << score ;
printxy(116, 9, "█");
printxy(84, 10, "█ █ █");
printxy(84, 11, "█████████████████");
printxy(84, 12, "█ █");
printxy(84, 13, "█ WASD:控制方向 Space:暂停 █");
printxy(84, 14, "█ 长按可加速移动 █");
printxy(84, 15, "█ 分数越高难度越大 █");
printxy(84, 16, "█ 贪吃蛇 制作:YJH █");
printxy(84, 17, "█ █");
printxy(84, 18, "█████████████████");
printxy(40, 27, "Level-"); cout <<level;
color(4);
food();
}
bool JGameover() // 判断是否结束游戏
{
if (snakebd[0].P.X == 1 || snakebd[0].P.X == 42 || snakebd[0].P.Y == 1 || snakebd[0].P.Y == 27) // 边界判断
return true;
for (int i = 2; i < length; i++)
{
if (snakebd[0].P == snakebd[i].P)
return true;
}
return false;
}
void moving(void*) // 实现画面的移动
{
COORD TailPost;
while (1)
{
if (!pause&&!GameOver)
{
interrupt = false;
TailPost = snakebd[length - 1].P ;
for (int i = length; i > 1; i--)
{
snakebd[i - 1].direc = snakebd[i - 2].direc;
snakebd[i - 1].P = snakebd[i - 2].P;
} // 每次移动,第n块蛇身体信息跟随前一块(第n-1)蛇身体的信息,故每次只需改变蛇头的方向和位置即可
if (chr == 119 && snakebd[0].direc != 4) { snakebd[0].direc = 2; }
else if (chr == 97 && snakebd[0].direc != 1 ) { snakebd[0].direc = 3; }
else if (chr == 115 && snakebd[0].direc != 2 ) { snakebd[0].direc = 4; }
else if (chr == 100 && snakebd[0].direc != 3 ) { snakebd[0].direc = 1; } // 蛇头的转向
switch (snakebd[0].direc) // 蛇头的位置移动
{
case 1:
snakebd[0].P.X++; break;
case 2:
snakebd[0].P.Y--; break;
case 3:
snakebd[0].P.X--; break;
case 4:
snakebd[0].P.Y++;
}
if (JGameover()) // 判断游戏结束
{
printxy(0, 27, "GAME OVER!!");
printxy(0, 28, "Press R to Play Again...");
GameOver = true;
}
else
{
if (TailPost == Eat[0]) // 如果此刻尾巴的位置是曾经吃掉食物的位置,则“长”一块身体
{
length++;
snakebd[length - 1].P = TailPost;
for (int j = 0; j < eati; j++)
{
Eat[j] = Eat[j + 1];
}
eati--;
}
else printxy(2 *TailPost.X - 2, TailPost.Y - 1, " ");
printxy(2 * snakebd[1].P.X - 2, snakebd[1].P.Y - 1, "█");
printxy(2 * snakebd[0].P.X - 2, snakebd[0].P.Y - 1, "●"); // 否则擦掉尾巴,重画蛇头
if (snakebd[0].P == FoodPost) // 如果吃到了食物,则记录食物的位置
{
score++;
Eat[eati++] = FoodPost;
score / 3 + 1 > 23 ? level = 23 : level = score / 3 + 1;
color(6);
printxy(46, 27, level);
printxy(112, 9, score);
if (score > record)
printxy(96, 9, record = score);
food();
}
chr = 0;
for (int k = 1; k <= 26-level; k++) // 等待刷新,如果有按键则跳过等待,实现加速功能
{
if (chr == 119 || chr == 97 || chr == 115 || chr == 100) break;
Sleep(20);
}
}
}
else interrupt = true;
}
}
int main()
{
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(handle, &CursorInfo); // 获取控制台光标信息
CursorInfo.bVisible = false;
SetConsoleCursorInfo(handle, &CursorInfo); // 隐藏控制台光标
initialize();
_beginthread(moving, 0, NULL); // 新建一个线程用以循环显示画面的变化
while (1) // 循环监听键盘消息
{
if (_kbhit())
{
chr = _getch();
if (chr == 32 && GameOver == false)
{
if (!pause ) // 空格暂停游戏
{
pause = !pause;
while (!interrupt) {}
printxy(99, 23, "PAUSE!"); // 这里是等待moving函数进入pause,避免子线程同时调用printxy
}
else
{
printxy(99, 23, " ");
pause = !pause;
}
}
else if (chr == 114 && GameOver == true) // R键重新开始
{
system("cls");
initialize();
GameOver = false;
}
else if (chr == 118) // 隐藏控制台光标
SetConsoleCursorInfo(handle, &CursorInfo);
}
}
return 0;
}
运行效果图: