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++实现读写文件的示例代码,帮助大家更好的理解和学习c++,感兴趣的朋友可以了解下
    2020-08-08
  • C++检查某个文件或目录是否存在的函数

    C++检查某个文件或目录是否存在的函数

    这篇文章主要介绍了C++检查某个文件或目录是否存在的函数,是Windows应用程序设计中非常常见的实用技巧,需要的朋友可以参考下
    2014-10-10
  • C++ std::function详解

    C++ std::function详解

    类模版std::function是一种通用的多态函数包装器std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,本文详细的介绍一下,感兴趣的可以了解一下
    2021-10-10
  • C++ 点(.)和箭头(->)运算符用法小结

    C++ 点(.)和箭头(->)运算符用法小结

    在C++中,点运算符(.)用于访问类的成员变量和成员函数,而箭头运算符(->)用于通过指针访问类的成员变量和成员函数,本文就来详细的介绍一下如何使用,感兴趣的可以了解一下
    2024-01-01
  • C语言将数组中元素的数排序输出的相关问题解决

    C语言将数组中元素的数排序输出的相关问题解决

    这篇文章主要介绍了C语言将数组中元素的数排序输出的相关问题解决,文中的题目是将元素连接起来排成一个数并要求出这类结果中数最小的一个,需要的朋友可以参考下
    2016-03-03
  • C++实现简单版图书管理系统

    C++实现简单版图书管理系统

    这篇文章主要为大家详细介绍了C++实现简单版图书管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • c++引用传参和指针传参的区别及说明

    c++引用传参和指针传参的区别及说明

    这篇文章主要介绍了c++引用传参和指针传参的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • C++ 中

    C++ 中"emplace_back" 与 "push_back" 的区别

    这篇文章主要介绍了C++ 中"emplace_back" 与 "push_back" 的区别的相关资料,需要的朋友可以参考下
    2017-04-04
  • 基于C语言fflush()函数的使用详解

    基于C语言fflush()函数的使用详解

    本篇文章是对C语言中fflush()函数的使用进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • 关于C++中的友元函数的一些总结

    关于C++中的友元函数的一些总结

    以下是对C++中的友元函数进行了详细的总结介绍,需要的朋友可以过来参考下
    2013-09-09

最新评论