C++实现简易贪吃蛇游戏

 更新时间:2021年07月29日 11:11:19   作者:AAH。  
这篇文章主要为大家详细介绍了C++实现简易贪吃蛇游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

C++实现建议贪吃蛇(不会闪屏幕)

使用vs2013完成。记录踏上游戏开发的道路。

效果图

代码

// 2021.7.24.1贪吃蛇.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <list>
#include <numeric>  
#include <algorithm> 
#include <Windows.h>
#include <WinUser.h>
#include <cstdlib>
#include <ctime>
#include <vector>
using namespace std;

#define KEY_DOWN(vk_code) (GetAsyncKeyState(vk_code) & 0x8000 ? 1 : 0)
#define MAX_SNAKE_LEN 20
#define MAP_MAXIMUM_HEIGHT 20
#define MAP_MAXIMUM_WIDTH 20
#define MAX_NUMBER_FRUIT 5


struct sSnakeBody
{
 void setPostion(int x, int y)
 {
  nSnakeBodyX = x;
  nSnakeBodyY = y;
 }
 void setPostion(sSnakeBody* temp)
 {
  nSnakeBodyX = temp->nSnakeBodyX;
  nSnakeBodyY = temp->nSnakeBodyY;
 }
 int nSnakeBodyX;
 int nSnakeBodyY;
};
typedef list<sSnakeBody> LISTSNAKEBODY;

struct sSnake
{
 sSnake()
 {
  nSnakeHeadX = 1;
  nSnakeHeadY = 1;
  nSnakeDirection = 1;
  speed = 5;
 }
 bool isExit(sSnakeBody temp)
 {
  if (nSnakeHeadX==temp.nSnakeBodyX&&nSnakeHeadY==temp.nSnakeBodyY)
  {
   return true;
  }
  return false;
 }
 bool isExit(int x, int y)
 {
  if (nSnakeHeadX == x&&nSnakeHeadY == y)
  {
   return true;
  }
  return false;
 }
 void reduction(sSnakeBody temp)
 {
  nSnakeHeadX = 2 * nSnakeHeadXBk - temp.nSnakeBodyX;
  nSnakeHeadY = 2 * nSnakeHeadYBk - temp.nSnakeBodyY;
 }

 int nSnakeHeadX;
 int nSnakeHeadY;
 int nSnakeHeadXBk;
 int nSnakeHeadYBk;
 int nSnakeDirection;//0表示上,1表示右,2表示下,3表示左,顺时针
 int speed;//指的是几个循环前进一次
 LISTSNAKEBODY snakeBodyList[MAX_SNAKE_LEN];
};

struct sFruit
{
 int FruitX;
 int FruitY;
 sFruit()
 {
  FruitX = -1;
  FruitY = -1;
 }
 sFruit(int x, int y)
 {
  FruitX = x;
  FruitY = y;
 }
 bool isExit(sSnake snake)
 {
  if (FruitX == snake.nSnakeHeadX && FruitY == snake.nSnakeHeadY)
   return true;
  else return false;
 }
};

vector<sFruit> gFruitVector;

sSnake gSnake;

//显示地图
void showArrMap(int arrMap[MAP_MAXIMUM_HEIGHT][MAP_MAXIMUM_WIDTH])
{
 system("cls");
 for (int i = 0; i < MAP_MAXIMUM_HEIGHT; i++)
 {
  for (int j = 0; j < MAP_MAXIMUM_WIDTH; j++)
  {
   if (1 == arrMap[i][j] || gSnake.isExit(i,j))
   {
    cout << "■";
   }
   else if (arrMap[i][j] == 2)
   {
    cout << "●";
   }
   else
   {
    cout << "  ";
   }
  }
  if (4 == i)
  {
   cout << "\t得分:" << gSnake.snakeBodyList->size();
  }

  cout << endl;
 }
}

//将数据加载到地图上。返回值为加载后游戏是否失败
bool mapLoadData(int arrMap[MAP_MAXIMUM_HEIGHT][MAP_MAXIMUM_WIDTH])
{

 //加载水果的数据
 for (unsigned int i = 0; i < gFruitVector.size(); i++)
 {
  arrMap[gFruitVector[i].FruitX][gFruitVector[i].FruitY] = 2;
 }


 //加载蛇的身体数据
 for (LISTSNAKEBODY::iterator iter = gSnake.snakeBodyList->begin(); iter != gSnake.snakeBodyList->end(); iter++)
 {
  arrMap[iter->nSnakeBodyX][iter->nSnakeBodyY] = 1;
 }


 // 吃到第一个水果的时候,一定不是游戏失败
 if (gSnake.snakeBodyList->size() == 1 && gSnake.isExit(gSnake.snakeBodyList->front()))
 {
  return false;
 }


 if (arrMap[gSnake.nSnakeHeadX][gSnake.nSnakeHeadY] == 1)
  return true;
 return false;
}

//更新蛇的数据
void updateSnake(int &nProgramCounter)
{
 if (nProgramCounter > gSnake.speed)
 {
  gSnake.nSnakeHeadXBk = gSnake.nSnakeHeadX;
  gSnake.nSnakeHeadYBk = gSnake.nSnakeHeadY;

  if (gSnake.nSnakeDirection == 0)
  {
   gSnake.nSnakeHeadX--;

  }
  else if (gSnake.nSnakeDirection == 1)
  {
   gSnake.nSnakeHeadY++;
  }
  else if (gSnake.nSnakeDirection == 2)
  {
   gSnake.nSnakeHeadX++;
  }
  else if (gSnake.nSnakeDirection == 3)
  {
   gSnake.nSnakeHeadY--;
  }
  if (gSnake.snakeBodyList->size() != 0)
  {
   if (gSnake.isExit(gSnake.snakeBodyList->front()))
   {
    gSnake.reduction(gSnake.snakeBodyList->front());
   }
  }

  //蛇的身体移动
  if (gSnake.snakeBodyList->size() != 0)
  {
   sSnakeBody snakeBody;
   snakeBody.setPostion(gSnake.nSnakeHeadXBk, gSnake.nSnakeHeadYBk);
   gSnake.snakeBodyList->push_front(snakeBody);
   gSnake.snakeBodyList->pop_back();
  }
  nProgramCounter = 0;
 }
}

//绑定键盘事件
void bindKeyboardEvents()
{
 if (KEY_DOWN(VK_UP))
 {
  gSnake.nSnakeDirection = 0;
 }
 else if (KEY_DOWN(VK_RIGHT))
 {
  gSnake.nSnakeDirection = 1;
 }
 else if (KEY_DOWN(VK_DOWN))
 {
  gSnake.nSnakeDirection = 2;
 }
 else if (KEY_DOWN(VK_LEFT))
 {
  gSnake.nSnakeDirection = 3;
 }

 if (KEY_DOWN(VK_SHIFT))
 {
  gSnake.speed = 2;
 }
 else
 {
  gSnake.speed = 5;
 }
}

//随机生成 N 个水果,
void randomlyGeneratedFruit(int N, int arrMap[MAP_MAXIMUM_HEIGHT][MAP_MAXIMUM_WIDTH])
{
 int maxi = MAP_MAXIMUM_HEIGHT;
 int maxj = MAP_MAXIMUM_WIDTH;
 int AllCnt = 0;

 vector<int> randMap;

 for (int i = 0; i < maxi; i++)
 {
  for (int j = 0; j < maxj; j++)
  {
   if (0 == arrMap[i][j])
   {
    randMap.push_back(i*maxj + j);
    AllCnt++;
   }
  }
 }

 if (AllCnt < N)
 {
  N = AllCnt;
 }
 for (int i = 0; i < N; i++)
 {
  int temp = (rand() % AllCnt--);
  randMap.erase(randMap.begin() + temp);
  sFruit fruitTemp = sFruit(temp / maxi + 1, temp % (maxi - 2) + 1);
  gFruitVector.push_back(fruitTemp);
 }
}

//初始化地图
void initArrMap(int arrMap[MAP_MAXIMUM_HEIGHT][MAP_MAXIMUM_WIDTH])
{
 for (int i = 0; i < MAP_MAXIMUM_HEIGHT; i++)
 {
  for (int j = 0; j < MAP_MAXIMUM_WIDTH; j++)
  {
   if (i == 0 || j == 0 || i == MAP_MAXIMUM_HEIGHT - 1 || j == MAP_MAXIMUM_WIDTH - 1)
    arrMap[i][j] = 1;
   else
    arrMap[i][j] = 0;
  }
 }
}

//初始化水果数据
void initFruit(int arrMap[MAP_MAXIMUM_HEIGHT][MAP_MAXIMUM_WIDTH])
{
 randomlyGeneratedFruit(MAX_NUMBER_FRUIT, arrMap);
}


//蛇头吃到水果事件
void eatFruitEvent(int arrMap[MAP_MAXIMUM_HEIGHT][MAP_MAXIMUM_WIDTH])
{
 for (unsigned int i = 0; i < gFruitVector.size(); i++)
 {
  if (gFruitVector[i].isExit(gSnake))
  {
   sSnakeBody snakeBodyTemp;
   if (gSnake.snakeBodyList->size() == 0)
   {
    snakeBodyTemp.setPostion(gSnake.nSnakeHeadX, gSnake.nSnakeHeadY);
   }
   else
   {
    snakeBodyTemp.setPostion(&gSnake.snakeBodyList->back());
   }
   gSnake.snakeBodyList->push_back(snakeBodyTemp);
   gFruitVector.erase(gFruitVector.begin() + i);
   randomlyGeneratedFruit(1, arrMap);
  }
 }
}

int _tmain(int argc, _TCHAR* argv[])
{

 HANDLE hOutput;
 COORD coord = { 0, 0 };
 hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

 //创建新的缓冲区
 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);

 //双缓冲处理显示
 DWORD bytes = 0;
 char data[3200];

 //地图,需要将数据映射到地图上,再去渲染地图。
 int arrMap[MAP_MAXIMUM_HEIGHT][MAP_MAXIMUM_WIDTH];
 srand(time(NULL));

 //计数器。
 int nProgramCounter = 0;

 //初始化各个数据
 initArrMap(arrMap);
 initFruit(arrMap);

 while (true)
 {
  nProgramCounter++;
  initArrMap(arrMap);
  bindKeyboardEvents();
  updateSnake(nProgramCounter);
  eatFruitEvent(arrMap);
  bool bIsWin = mapLoadData(arrMap);
  showArrMap(arrMap);
  if (bIsWin)
  {
   cout << endl << "游戏失败!!!";
   ReadConsoleOutputCharacterA(hOutput, data, 3200, coord, &bytes);
   WriteConsoleOutputCharacterA(hOutBuf, data, 3200, coord, &bytes);
   break;
  }

  ReadConsoleOutputCharacterA(hOutput, data, 3200, coord, &bytes);
  WriteConsoleOutputCharacterA(hOutBuf, data, 3200, coord, &bytes);
 }
 system("pause");
 return 0;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • C++异常处理的方式总结

    C++异常处理的方式总结

    C++有一套独立的异常处理机制,相信大家一定听说过try,catch这两
    个词,今天就来做详细的介绍,文中通过代码示例给大家介绍的非常详细,具有一定参考价值,需要的朋友可以参考下
    2023-12-12
  • 深度剖析C++中的异常机制

    深度剖析C++中的异常机制

    异常是面向对象语言常用的一种处理错误的方式,当一个函数发现自己无法处理的错误时就可以抛出异常,本文我们将对C++ 异常机制进行深入剖析,感兴趣的同学跟着小编一起来看看吧
    2023-07-07
  • 浅析stl序列容器(map和set)的仿函数排序

    浅析stl序列容器(map和set)的仿函数排序

    有序的stl容器在工程中应用什么方便和广泛,但是当我们需要自己的排序的时候,可以用仿函数来设置它
    2013-09-09
  • 利用C语言实现三子棋游戏

    利用C语言实现三子棋游戏

    这篇文章主要为大家详细介绍了利用C语言实现三子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-02-02
  • VS2010 boost标准库开发环境安装教程

    VS2010 boost标准库开发环境安装教程

    这篇文章主要为大家详细介绍了VS2010 boost标准库开发环境的安装教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • C++实现十进制数转换为二进制数的数学算法

    C++实现十进制数转换为二进制数的数学算法

    这篇文章和大家分享一下我个人对十进制数转换为二进制数的想法,目前暂时更新只整数十进制的转换,后续会更新带有小数的进制转换,代码使用c++实现
    2021-09-09
  • 原码, 反码与补码基础知识详细介绍

    原码, 反码与补码基础知识详细介绍

    这篇文章讲解了计算机的原码, 反码和补码. 并且进行了深入探求了为何要使用反码和补码, 以及更进一步的论证了为何可以用反码, 补码的加法计算原码的减法,需要的朋友可以参考下
    2016-12-12
  • C语言中定义与声明有哪些区别

    C语言中定义与声明有哪些区别

    在C/C++中有一个基础且重要的知识,什么是声明?什么是定义?他们的区别是什么?本文将带你理清其中的区别
    2022-07-07
  • vs2019配置Qt5开发环境(图文教程)

    vs2019配置Qt5开发环境(图文教程)

    本文主要介绍了如何使用visual studi2019配置qt5开发环境,以及创建qt项目,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • C语言实现三子棋小游戏全程详解

    C语言实现三子棋小游戏全程详解

    完成一个三子棋的代码并不是很难,有困难且重要的是完成这个游戏代码所具备的思想,因为思想上的进步才是真正的进步,当我们有了这个思想上的武器,写出别的代码,难度就不会高
    2022-05-05

最新评论