c/c++游戏编程之控制台贪吃蛇(一)
c/c++游戏编程之控制台贪吃蛇(二)

为了解决“闪屏”问题,我们不再使用system(“cls”)进行清屏,而是直接用空格符’
'清掉蛇尾即可,这就像裁剪(只对需要改变的区域进行更新,不对固定的地方进行多余的操作)。

总的来说:游戏画面的每一帧变化只有蛇头和蛇尾的位置变了,我们只需要在新蛇头的位置填上’O’,用空格符’ '覆盖旧蛇尾,而不需要每次都将整条蛇重新打印一遍。

所以,我们只要在游戏刚开始时将整条蛇打印一次就行了:

void Init() {speedx = 1; //游戏开始时蛇默认向右移动speedy = 0;pSnakeHead = new SnakeNode{ 10, 10, nullptr, nullptr }; //为蛇头申请一个空间pSnakeTail = pSnakeHead; //刚开始只有一节身体,蛇头即蛇尾PrintSnake();
}

RefreshSnakeHead()函数打印新蛇头:

void RefreshSnakeHead(int posx, int posy) {SnakeNode* pNewNode = new SnakeNode{ posx, posy, pSnakeHead, nullptr }; //在新位置形成新蛇头, 新蛇头next指针指向旧蛇头pSnakeHead->pLastNode = pNewNode; //旧蛇头last指针指向新蛇头pSnakeHead = pNewNode; //更新蛇头指针//打印新蛇头GotoPos(pSnakeHead->posx, pSnakeHead->posy);cout << 'O';
}

RefreshSnakeTail()函数用空格符’ '去掉蛇尾:

void RefreshSnakeTail() {//去掉蛇尾GotoPos(pSnakeTail->posx, pSnakeTail->posy);cout << ' ';SnakeNode* pTemp = pSnakeTail;pSnakeTail = pSnakeTail->pLastNode;pSnakeTail->pNextNode = nullptr;delete pTemp; //释放节点空间pTemp = nullptr; //为释放后的指针置空
}

main()函数不再调用PrintSnake()和system(“cls”):

int main() {Init();while (1) {Move();Sleep(100);}return 0;
}

到现在为止,游戏画面只有蛇哥自己,怎么能让它这么孤单?
我们先添加两个全局常量:MAP_WIDTHMAP_HEIGHT(地图宽度地图高度):

const int MAP_WIDTH = 100; //地图宽度
const int MAP_HEIGHT = 50; //地图高度

再定义一个名为PrintMap的函数为游戏画面添加地图框,我们用‘+’代表地图边界:

void PrintMap() {string tempStr = "+" + string(' ', MAP_WIDTH - 2) + "+\n";GotoPos(0, 1);for (int index = 2; index < MAP_HEIGHT; ++index) {cout << tempStr;}tempStr = string('+', MAP_WIDTH);cout << tempStr;GotoPos(0, 0);cout << tempStr;}

Init()函数里调用PrintMap

void Init() {speedx = 1; //游戏开始时蛇默认向右移动speedy = 0;pSnakeHead = new SnakeNode{ 10, 10, nullptr, nullptr }; //为蛇头申请一个空间pSnakeTail = pSnakeHead; //刚开始只有一节身体,蛇头即蛇尾PrintSnake();PrintMap();
}

运行效果截图:

现在定义一个名为Crash函数用来判定蛇头有没有跟墙壁相撞,如果撞上,则向主调函数返回true,并打印“Game Over”,休眠2秒钟后退出程序:

bool Crash() {//如果蛇头撞上地图边界,则返回trueif (pSnakeHead->posx >= MAP_WIDTH || pSnakeHead->posx <= 0 ||pSnakeHead->posy <= 0 || pSnakeHead->posy >= MAP_HEIGHT) {return true;}return false;
}

我们再定义一个释放链表空间的函数FreeList(),以在游戏结束时调用:

void FreeList() {SnakeNode* pTemp;while (pSnakeHead != NULL) {pTemp = pSnakeHead;pSnakeHead = pSnakeHead->pNextNode;delete pTemp;pTemp = nullptr;}
}

改写main()函数:

int main() {Init();while (1) {Move();if (Crash()) {GotoPos(MAP_WIDTH / 2, MAP_HEIGHT / 2); //在地图中央打印"Game  Over"cout << "Game  Over";FreeList();Sleep(2000);break;}Sleep(100);}return 0;
}

好了,现在该到我们的重磅嘉宾——食物出场了,先定义一个Food结构体和全局变量food

struct Food {int posx; //食物x坐标int posy; //食物y坐标char symbol; //食物符号
} food;

Init()函数里初始化食物状态,用字符’F’代表食物:

void Init() {srand(unsigned(time(NULL))); //初始化随机数种子speedx = 1; //游戏开始时蛇默认向右移动speedy = 0;//在地图边界内随机生成食物food.posx = 1 + rand() % (MAP_WIDTH - 1); food.posy = 1 + rand() % (MAP_HEIGHT - 1);food.symbol = 'F';pSnakeHead = new SnakeNode{ 10, 10, nullptr, nullptr }; //为蛇头申请一个空间pSnakeTail = pSnakeHead; //刚开始只有一节身体,蛇头即蛇尾PrintSnake();PrintMap();//打印食物GotoPos(food.posx, food.posy);cout << food.symbol;
}

定义一个判定食物是否被吃的函数EatFood(),如果被吃就刷新食物的位置:

(tips: 这里以及Init()函数里面都没有处理食物刷到蛇身上的情况,这里就交给你自己去解决啦,很简单的问题要相信自己(其实是作者自己懒罢了- _ -))

bool EatFood() {if (pSnakeHead->posx == food.posx && pSnakeHead->posy == food.posy) {food.posx = 1 + rand() % (MAP_WIDTH - 1);food.posy = 1 + rand() % (MAP_HEIGHT - 1);GotoPos(food.posx, food.posy);cout << food.symbol;return true;}return false;
}

改写Move()函数,如果吃到了食物,蛇就变长了,也就是说不删除蛇尾:

void Move() {char cinput;if (_kbhit()) {cinput = _getch(); //使用getch()需要 #include <conio.h>switch (cinput) {case 87: case 119: {speedx = 0;speedy = -1;break;}case 53: case 115: {speedx = 0;speedy = 1;break;}case 65: case 97: {speedx = -1;speedy = 0;break;}case 68: case 100: {speedx = 1;speedy = 0;break;}default: {break;}}}RefreshSnakeHead(pSnakeHead->posx + speedx, pSnakeHead->posy + speedy);if (!EatFood()) {RefreshSnakeTail();}}

所有代码:

//控制台贪吃蛇
#include <iostream>
#include <Windows.h>
#include <conio.h>
#include <ctime>
#include <cstdlib>
#include <string>using std::cout;
using std::string;struct SnakeNode {int posx;int posy;SnakeNode* pNextNode;SnakeNode* pLastNode;
} *pSnakeHead, *pSnakeTail;struct Food {int posx;int posy;char symbol;
} food;int speedx; //x轴速度
int speedy; //y轴速度const int MAP_WIDTH = 100; //地图宽度
const int MAP_HEIGHT = 50; //地图高度//设置光标位置
void GotoPos(int x, int y);
//打印蛇体
void PrintSnake();
//打印地图
void PrintMap();
//碰撞检测
bool Crash();
//判定食物是否被吃
bool EatFood();
//初始化游戏
void Init();
//释放链表
void FreeList();
//刷新蛇头节点
void RefreshSnakeHead(int posx, int posy);
//刷新蛇尾节点
void RefreshSnakeTail();
//移动控制
void Move();int main() {Init();while (1) {Move();if (Crash()) {GotoPos(MAP_WIDTH / 2, MAP_HEIGHT / 2); //在地图中央打印"Game  Over"cout << "Game  Over";FreeList();Sleep(2000);break;}Sleep(100);}return 0;
}void GotoPos(int x, int y) {HANDLE hout; //定义句柄COORD cor; //定义坐标hout = GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出句柄cor.X = x;cor.Y = y;SetConsoleCursorPosition(hout, cor); //设置光标位置
}void Init() {srand(unsigned(time(NULL))); //初始化随机数种子speedx = 1; //游戏开始时蛇默认向右移动speedy = 0;//在地图边界内随机生成食物food.posx = 1 + rand() % (MAP_WIDTH - 1); food.posy = 1 + rand() % (MAP_HEIGHT - 1);food.symbol = 'F';pSnakeHead = new SnakeNode{ 10, 10, nullptr, nullptr }; //为蛇头申请一个空间pSnakeTail = pSnakeHead; //刚开始只有一节身体,蛇头即蛇尾PrintSnake(); PrintMap();//打印食物GotoPos(food.posx, food.posy);cout << food.symbol;
}void PrintSnake() {SnakeNode* pTemp = pSnakeHead;while (pTemp != nullptr) {GotoPos(pTemp->posx, pTemp->posy);cout << 'O';pTemp = pTemp->pNextNode;}
}void RefreshSnakeHead(int posx, int posy) {SnakeNode* pNewNode = new SnakeNode{ posx, posy, pSnakeHead, nullptr }; //在新位置形成新蛇头, 新蛇头next指针指向旧蛇头pSnakeHead->pLastNode = pNewNode; //旧蛇头last指针指向新蛇头pSnakeHead = pNewNode; //更新蛇头指针//打印新蛇头GotoPos(pSnakeHead->posx, pSnakeHead->posy);cout << 'O';
}void RefreshSnakeTail() {//去掉蛇尾GotoPos(pSnakeTail->posx, pSnakeTail->posy);cout << ' ';SnakeNode* pTemp = pSnakeTail;pSnakeTail = pSnakeTail->pLastNode;pSnakeTail->pNextNode = nullptr;delete pTemp; //释放节点空间pTemp = nullptr; //为释放后的指针置空
}void Move() {char cinput;if (_kbhit()) {cinput = _getch(); //使用getch()需要 #include <conio.h>switch (cinput) {case 87: case 119: {speedx = 0;speedy = -1;break;}case 53: case 115: {speedx = 0;speedy = 1;break;}case 65: case 97: {speedx = -1;speedy = 0;break;}case 68: case 100: {speedx = 1;speedy = 0;break;}default: {break;}}}RefreshSnakeHead(pSnakeHead->posx + speedx, pSnakeHead->posy + speedy);if (!EatFood()) {RefreshSnakeTail();}}void PrintMap() {string tempStr = "+" + string(MAP_WIDTH - 2, ' ') + "+\n";GotoPos(0, 1);for (int index = 2; index < MAP_HEIGHT; ++index) {cout << tempStr;}tempStr = string(MAP_WIDTH, '+');cout << tempStr;GotoPos(0, 0);cout << tempStr;}bool Crash() {//如果蛇头撞上地图边界,则返回trueif (pSnakeHead->posx >= MAP_WIDTH || pSnakeHead->posx <= 0 ||pSnakeHead->posy <= 0 || pSnakeHead->posy >= MAP_HEIGHT) {return true;}return false;
}void FreeList() {SnakeNode* pTemp;while (pSnakeHead != nullptr) {pTemp = pSnakeHead;pSnakeHead = pSnakeHead->pNextNode;delete pTemp;pTemp = nullptr;}
}bool EatFood() {if (pSnakeHead->posx == food.posx && pSnakeHead->posy == food.posy) {food.posx = 1 + rand() % (MAP_WIDTH - 1);food.posy = 1 + rand() % (MAP_HEIGHT - 1);GotoPos(food.posx, food.posy);cout << food.symbol;return true;}return false;
}

运行效果截图:

好了,截至目前,我们基本上已经完成了所有主要功能的开发,还有一些功能如显示分数改变速度障碍物镜像穿墙等需要你自己动用聪明的大脑去扩展。

文章持续更新中!

求点赞、收藏!
作者水平有限,如果有误,欢迎指正!
编译环境:Visual Studio 2019

c/c++游戏编程之控制台贪吃蛇(二)相关推荐

  1. c/c++游戏编程之控制台贪吃蛇(一)

    c/c++游戏编程之控制台贪吃蛇(一) c/c++游戏编程之控制台贪吃蛇(二) 欢迎你开启了c++的游戏编程世界之旅 如果你还未学过c++基本语法,请先学习基本语法再来学习游戏编程噢~. 对这样的&q ...

  2. 用python自带的tkinter做游戏(一)—— 贪吃蛇 篇

    用python自带的tkinter做游戏(一)-- 贪吃蛇 篇 本人新手,刚自学python满半年,现分享下心得,希望各位老手能指点一二,也希望和我一样的新手能共勉,谢谢~ 大家都知道用python做 ...

  3. JAVA基于J2ME的手机游戏开发和实现——贪吃蛇

    随着通信技术的发展和手机的普及,手机游戏的开发技术越来越为人们所关注.以J2ME为开发平台,利用Java提供强大工具,不但可以在手机上实现静态HTML技术所无法实现的计算处理.数据存储.与服务器的通信 ...

  4. java小游戏代码压缩包_java 贪吃蛇小游戏 源码下载

    [实例简介] 使用java程序设计语言制作的一个贪吃蛇小游戏.游戏的控制模块应该做到易懂.易操作,以给玩家一个很好的游戏环境.在这个游戏的设计中,牵涉到图形界面的显示与更新.数据的收集与更新,还要应用 ...

  5. [娱乐]一款浅陋的C++控制台贪吃蛇小游戏

    因为时间有限,没能尽可能完善这一款贪吃蛇,不过能做出来还是很有意思的 1 #include<bits/stdc++.h> 2 #include<windows.h> 3 #in ...

  6. 基于WIN32 API界面编程实现的贪吃蛇游戏

    1 设计目的和任务 本次期末大作业采用课程设计的形式进行,作为<Windows编程>课程的期末考核.要求综合运用Windows编程的相关知识,完成大作业的相关内容,并撰写设计报告.其目的和 ...

  7. C++控制台贪吃蛇小游戏详细教程

    游戏截图     开始动画:    游戏过程:  游戏架构设计 该游戏的玩法简单,玩家通过键盘方向键控制蛇上下左右移动,吃到食物得分并增长,碰到墙或者自己的身体则死亡,游戏结束.  整个游戏其实就是一 ...

  8. 【C++】经典项目控制台贪吃蛇小游戏详细教程

    [小游戏]贪吃蛇GreedySnake 本文将讲解如何使用c++面向对象方法编写控制台版贪吃蛇小游戏 项目github地址:游戏源码链接 游戏下载:GreedySnake 本人属初学者,水平所限,难免 ...

  9. 【编程6】贪吃蛇游戏(python+pygame)

    效果图~新鲜出炉 开始界面 游戏中 结束界面 一.pygame模块概览 模块名称 功能 pygame.cdrom 访问光驱 pygame.cursors 加载光标 pygame.display 访问显 ...

最新文章

  1. SOA+AIOT=无限可能,上汽零束 AIOT 沙龙上海站火热报名中
  2. matlab dsearchn,cKDTree与dsearchn
  3. [JavaWeb-HTML]HTML标签(大部分常用标签介绍)
  4. ZOJ.3551.Bloodsucker(期望DP)
  5. bzoj1853: [Scoi2010]幸运数字 dp+容斥原理
  6. BITED数学建模七日谈之一:参加全国大学生数学建模比赛前你需要积累哪些
  7. Java中常见定时任务的实现方式
  8. SDN的机遇与挑战 让宽带利用率与硬件不再是难题
  9. HDOJ:1533-Going Home(最小费用流)
  10. 11001-软件架构设计风格及visio使用
  11. atmega328p引脚图_ATMEGA328P-AU 8位AVR微控制器
  12. 抖音企业号开发功能技术搭建
  13. 黑盒测试简介与其测试方法
  14. accuracy(准确率), precision(精密度), recall(召回率), specificity(特异性), and F1-score(F1分数):分别是什么意思?
  15. Google Play In-app Billing API version is less than 3.
  16. PointNet网络结构详细解析
  17. What's the differece between high price houses and low price houses of airbnb?
  18. arduino(19 ):使用ESP32连接 PS3 蓝牙手柄,需要在windows 上先连接成功,然后在修改mac地址,才可以连接成功,但是目前正在测试中,需要在windows上配对成功。
  19. linux usb驱动——OTG数据线与普通数据线区别
  20. Hadoop大数据平台开发与案例分析

热门文章

  1. 快速排序算法-c语言实现,快速排序算法实现(C语言)(转)
  2. vue 模拟双色球机选
  3. 图像溯源,图血缘关系总结
  4. 未来对于35岁以后程序员的职业规划
  5. Wireshark抓包工具使用教程以及常用抓包规则 ——Powered By 死性不改
  6. 周润发:《上海滩》成就史上最酷最帅“许文强”
  7. 苹果4s参数_iPhone 12为何回归扁平中框:并非为怀旧 苹果道出缘由(全文)_苹果 iPhone 12 Pro Max_手机新闻...
  8. 魅族MAX4 pro 手机无声音
  9. C++20之Concpet(概念部分,之二)
  10. 微信第三方平台【一】获取验证票据 component_verify_ticket,授权结果接收