控制台版 贪吃蛇

简介: 控制台版 贪吃蛇

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

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

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

#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;
}


相关文章
|
Java
往控制台打字(实例:游戏开局输入名字)
往控制台打字(实例:游戏开局输入名字)
43 0
autojs普通版控制台美化
autojs普通版控制台美化
850 0
|
3月前
|
定位技术 C++ Windows
第一人称射击游戏 C++控制台版(未完成)
第一人称射击游戏 C++控制台版(未完成)
第一人称射击游戏 C++控制台版(未完成)
|
3月前
|
C++
俄罗斯方块 C++控制台版
俄罗斯方块 C++控制台版
|
3月前
|
Python
Python实现用鼠标截图功能
Python实现用鼠标截图功能
95 0
|
3月前
|
存储
控制台版 赛车游戏
控制台版 赛车游戏
|
C语言 C++ Windows
【c++】设置控制台窗口字体颜色和背景色(system和SetConsoleTextAttribute函数 )(内含超好玩的c++游戏链接)
【c++】设置控制台窗口字体颜色和背景色(system和SetConsoleTextAttribute函数 )(内含超好玩的c++游戏链接)
524 0
【c++】设置控制台窗口字体颜色和背景色(system和SetConsoleTextAttribute函数 )(内含超好玩的c++游戏链接)
|
C语言
C项目(贪吃蛇)
C项目(贪吃蛇)
57 0
|
C语言
C/关于N子棋小游戏的创建
C/关于N子棋小游戏的创建