0
点赞
收藏
分享

微信扫一扫

控制台版 贪吃蛇


视频:​​https://www.bilibili.com/video/BV14z4y1r7wX?p=5​​

代码:​​https://github.com/OneLoneCoder/Videos​​

一、控制台相关设置(直接复制)

1、右击控制台属性,布局。调整控制台宽度120,高度40。

2、设置工程属性,字符集Unicode。

控制台版 贪吃蛇_#include

#include<iostream>
using namespace std;

#include<Windows.h>

int nScreenWidth = 120;
int nScreenHeight = 40;

int main()
{
//创建屏幕缓冲区
wchar_t * screen = new wchar_t[nScreenWidth*nScreenHeight];
for (int i = 0; i < nScreenWidth*nScreenHeight; i++) screen[i] = L' ';
HANDLE hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
SetConsoleActiveScreenBuffer(hConsole);
DWORD dwBytesWritten = 0;


while(1)
{


//显示
WriteConsoleOutputCharacter(hConsole, screen, nScreenWidth*nScreenHeight, { 0,0 }, &dwBytesWritten);
}

return 0;
}

二、游戏部分

2.1 蛇的数据结构

使用 x,y 表示蛇的一节身体, 使用列表存储 ,使用整形变量nSnakeDirection 表示蛇运动方向。

struct sSnakeSegment
{
int x;
int y;
};

//使用列表定义蛇,并初始化
list<sSnakeSegment> snake = { {60,15},{61,15},{62,15},{63,15},{64,15},{65,15},{66,15},{67,15},{68,15},{69,15} };
int nSnakeDirection = 3; //上0 右1 下2 左3 (顺时针)

2.2 绘制蛇 、食物

//清屏
for (int i = 0; i < nScreenWidth*nScreenHeight; i++) screen[i] = L' ';

//绘制状态栏
for (int i = 0; i < nScreenWidth; i++)
{
screen[i] = L'=';
screen[2 * nScreenWidth + i] = L'=';
}
wsprintf(&screen[nScreenWidth + 5], L"www.OneLoneCoder.com - S N A K E ! ! SCORE: %d", nScore);


//绘制蛇的身体
for (auto s : snake)
screen[s.y*nScreenWidth + s.x] = bDead ? L'+' : L'O'; //'O'表示蛇的身体,当游戏结束时,身体变成+
//绘制蛇头
screen[snake.front().y*nScreenWidth + snake.front().x] = bDead ? L'X' : L'@';

//绘制食物
screen[nFoodY*nScreenWidth + nFoodX] = L'%';

2.3 处理用户输入

 

        //计时 & 输入
this_thread::sleep_for(200ms);



// Get Input,
bKeyRight = (0x8000 & GetAsyncKeyState((unsigned char)('\x27'))) != 0;
bKeyLeft = (0x8000 & GetAsyncKeyState((unsigned char)('\x25'))) != 0;

if (bKeyRight && !bKeyRightOld)
{
nSnakeDirection++;
if (nSnakeDirection == 4) nSnakeDirection = 0;
}
if (bKeyLeft && !bKeyLeftOld)
{
nSnakeDirection--;
if (nSnakeDirection == -1) nSnakeDirection = 3;
}

2.4 更新蛇的位置

//更新蛇的位置
switch (nSnakeDirection)
{
case 0: //上
snake.push_front({ snake.front().x,snake.front().y - 1 });
break;
case 1: //右
snake.push_front({ snake.front().x+1,snake.front().y });
break;
case 2: //下
snake.push_front({ snake.front().x,snake.front().y +1 });
break;
case 3: //左
snake.push_front({ snake.front().x-1,snake.front().y });
break;
}
//删除最后一个(蛇尾)
snake.pop_back();
//碰撞检测
if (snake.front().x < 0 || snake.front().x >= nScreenWidth)
bDead = true;
if (snake.front().y < 3 || snake.front().y >= nScreenHeight)
bDead = true;

2.5 碰撞检测

//边界碰撞检测
if (snake.front().x < 0 || snake.front().x >= nScreenWidth)
bDead = true;
if (snake.front().y < 3 || snake.front().y >= nScreenHeight)
bDead = true;

//食物碰撞检测
if (snake.front().x == nFoodX && snake.front().y == nFoodY)
{
nScore++;
while (screen[nFoodY*nScreenWidth + nFoodX] != ' ') //重新生成食物
{
nFoodX = rand() % nScreenWidth;
nFoodY = (rand() % (nScreenHeight - 3)) + 3;
}
for (int i = 0; i < 5; i++)
snake.push_back({ snake.back().x,snake.back().y });
}
// 蛇身体碰撞检测
for (list<sSnakeSegment>::iterator i = snake.begin(); i != snake.end(); i++)
if (i != snake.begin() && i->x == snake.front().x && i->y == snake.front().y) //头与身体重叠
bDead = true;

2.6 游戏状态(死亡后重新开始)

当蛇死亡时,游戏更新的循环退出,等待按键空格

while(1)
{
while (!bDead)
{
//游戏更新
}
//等待空格重新开始
while ((0x8000 & GetAsyncKeyState((unsigned char)('\x20'))) == 0);
}

 

2.7 其他优化

取消硬延迟,调整上下,左右的延迟

// Get Input 
auto t1 = chrono::system_clock::now();
//由于控制台横高宽度不同,这里让左右延迟120ms,上下200ms
while ((chrono::system_clock::now() - t1) < ((nSnakeDirection % 2 == 1) ? 120ms : 200ms))
{
bKeyRight = (0x8000 & GetAsyncKeyState((unsigned char)('\x27'))) != 0;
bKeyLeft = (0x8000 & GetAsyncKeyState((unsigned char)('\x25'))) != 0;
}

 

三、完整代码

 

#include<iostream>
#include<list>
#include <thread>
#include<chrono>
using namespace std;

#include<Windows.h>

int nScreenWidth = 120;
int nScreenHeight = 40;
struct sSnakeSegment
{
int x;
int y;
};

int main()
{
//创建屏幕缓冲区
wchar_t * screen = new wchar_t[nScreenWidth*nScreenHeight];
for (int i = 0; i < nScreenWidth*nScreenHeight; i++) screen[i] = L' ';
HANDLE hConsole = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
SetConsoleActiveScreenBuffer(hConsole);
DWORD dwBytesWritten = 0;

while (1)
{
//使用列表定义蛇,并初始化
list<sSnakeSegment> snake = { {60,15},{61,15},{62,15},{63,15},{64,15},{65,15},{66,15},{67,15},{68,15},{69,15} };

int nFoodX = 30;
int nFoodY = 15;
int nScore = 0;
int nSnakeDirection = 3; //上0 右1 下2 左3 (顺时针)
bool bDead = false;
bool bKeyLeft = false, bKeyRight = false, bKeyLeftOld = false, bKeyRightOld = false;

while (!bDead)
{
//计时 & 输入
//this_thread::sleep_for(200ms);

// Get Input
auto t1 = chrono::system_clock::now();
//由于控制台横高宽度不同,这里让左右延迟120ms,上下200ms
while ((chrono::system_clock::now() - t1) < ((nSnakeDirection % 2 == 1) ? 120ms : 200ms))
{
bKeyRight = (0x8000 & GetAsyncKeyState((unsigned char)('\x27'))) != 0;
bKeyLeft = (0x8000 & GetAsyncKeyState((unsigned char)('\x25'))) != 0;
}



if (bKeyRight && !bKeyRightOld)
{
nSnakeDirection++;
if (nSnakeDirection == 4) nSnakeDirection = 0;
}
if (bKeyLeft && !bKeyLeftOld)
{
nSnakeDirection--;
if (nSnakeDirection == -1) nSnakeDirection = 3;
}

//游戏逻辑

//更新蛇的位置
switch (nSnakeDirection)
{
case 0: //上
snake.push_front({ snake.front().x,snake.front().y - 1 });
break;
case 1: //右
snake.push_front({ snake.front().x + 1,snake.front().y });
break;
case 2: //下
snake.push_front({ snake.front().x,snake.front().y + 1 });
break;
case 3: //左
snake.push_front({ snake.front().x - 1,snake.front().y });
break;
}

//边界碰撞检测
if (snake.front().x < 0 || snake.front().x >= nScreenWidth)
bDead = true;
if (snake.front().y < 3 || snake.front().y >= nScreenHeight)
bDead = true;

//食物碰撞检测
if (snake.front().x == nFoodX && snake.front().y == nFoodY)
{
nScore++;
while (screen[nFoodY*nScreenWidth + nFoodX] != ' ') //重新生成食物
{
nFoodX = rand() % nScreenWidth;
nFoodY = (rand() % (nScreenHeight - 3)) + 3;
}
for (int i = 0; i < 5; i++)
snake.push_back({ snake.back().x,snake.back().y });
}
// 蛇身体碰撞检测
for (list<sSnakeSegment>::iterator i = snake.begin(); i != snake.end(); i++)
if (i != snake.begin() && i->x == snake.front().x && i->y == snake.front().y) //头与身体重叠
bDead = true;

//删除最后一个(蛇尾)
snake.pop_back();


//显示
//清屏
for (int i = 0; i < nScreenWidth*nScreenHeight; i++) screen[i] = L' ';

//绘制状态栏
for (int i = 0; i < nScreenWidth; i++)
{
screen[i] = L'=';
screen[2 * nScreenWidth + i] = L'=';
}
wsprintf(&screen[nScreenWidth + 5], L"www.OneLoneCoder.com - S N A K E ! ! SCORE: %d", nScore);


//绘制蛇的身体
for (auto s : snake)
screen[s.y*nScreenWidth + s.x] = bDead ? L'+' : L'O'; //'O'表示蛇的身体,当游戏结束时,身体变成+
//绘制蛇头
screen[snake.front().y*nScreenWidth + snake.front().x] = bDead ? L'X' : L'@';

//绘制食物
screen[nFoodY*nScreenWidth + nFoodX] = L'%';

if (bDead)
wsprintf(&screen[15 * nScreenWidth + 40], L" PRESS 'SPACE' TO PLAY AGAIN ");
WriteConsoleOutputCharacter(hConsole, screen, nScreenWidth*nScreenHeight, { 0,0 }, &dwBytesWritten);

}
while ((0x8000 & GetAsyncKeyState((unsigned char)('\x20'))) == 0);


}

return 0;
}

 

举报

相关推荐

0 条评论