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

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

相关文章

  • 详解CLion配置openMP的方法

    详解CLion配置openMP的方法

    这篇文章主要介绍了CLion配置openMP的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • C++控制台实现简单人机对弈井字棋

    C++控制台实现简单人机对弈井字棋

    这篇文章主要为大家详细介绍了C++控制台实现简单人机对弈井字棋,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • 从汇编看c++中extern关键字的使用

    从汇编看c++中extern关键字的使用

    本篇文章介绍了,在c++中extern关键字的使用概述,需要的朋友参考下
    2013-05-05
  • 详解c++ libuv工作队列

    详解c++ libuv工作队列

    这篇文章主要介绍了c++ libuv工作队列的相关资料,帮助大家更好的理解和使用libuv,感兴趣的朋友可以了解下
    2021-02-02
  • C语言枚举的使用以及作用

    C语言枚举的使用以及作用

    这篇文章主要介绍了C语言枚举的使用以及使用,阅读下面内容我们将掌握枚举的相关概念、掌握枚举的几种用法、掌握枚举在实际产品中的用法,需要的朋友可以参考一下
    2022-03-03
  • 最新VScode C/C++ 环境配置的详细教程

    最新VScode C/C++ 环境配置的详细教程

    这篇文章主要介绍了最新VScode C/C++ 环境配置的详细教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • Pthread 并发编程线程自底向上深入解析

    Pthread 并发编程线程自底向上深入解析

    这篇文章主要为大家介绍了Pthread 并发编程线程自底向上深入解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • C++插入排序算法实例详解

    C++插入排序算法实例详解

    这篇文章主要为大家详细介绍了C++插入排序算法实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • c++利用vector创建二维数组的几种方法总结

    c++利用vector创建二维数组的几种方法总结

    这篇文章主要介绍了c++利用vector创建二维数组的几种方法总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • C语言程序设计之指针的应用详解

    C语言程序设计之指针的应用详解

    为了让大家能够更准确的了解C语言中指针的使用,本文为大家准备了四个指针相关的例题,文中的示例代码讲解详细,感兴趣的小伙伴可以学习一下
    2022-11-11

最新评论