c/c++游戏编程之控制台贪吃蛇(二)
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_WIDTH和MAP_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++游戏编程之控制台贪吃蛇(二)相关推荐
- c/c++游戏编程之控制台贪吃蛇(一)
c/c++游戏编程之控制台贪吃蛇(一) c/c++游戏编程之控制台贪吃蛇(二) 欢迎你开启了c++的游戏编程世界之旅 如果你还未学过c++基本语法,请先学习基本语法再来学习游戏编程噢~. 对这样的&q ...
- 用python自带的tkinter做游戏(一)—— 贪吃蛇 篇
用python自带的tkinter做游戏(一)-- 贪吃蛇 篇 本人新手,刚自学python满半年,现分享下心得,希望各位老手能指点一二,也希望和我一样的新手能共勉,谢谢~ 大家都知道用python做 ...
- JAVA基于J2ME的手机游戏开发和实现——贪吃蛇
随着通信技术的发展和手机的普及,手机游戏的开发技术越来越为人们所关注.以J2ME为开发平台,利用Java提供强大工具,不但可以在手机上实现静态HTML技术所无法实现的计算处理.数据存储.与服务器的通信 ...
- java小游戏代码压缩包_java 贪吃蛇小游戏 源码下载
[实例简介] 使用java程序设计语言制作的一个贪吃蛇小游戏.游戏的控制模块应该做到易懂.易操作,以给玩家一个很好的游戏环境.在这个游戏的设计中,牵涉到图形界面的显示与更新.数据的收集与更新,还要应用 ...
- [娱乐]一款浅陋的C++控制台贪吃蛇小游戏
因为时间有限,没能尽可能完善这一款贪吃蛇,不过能做出来还是很有意思的 1 #include<bits/stdc++.h> 2 #include<windows.h> 3 #in ...
- 基于WIN32 API界面编程实现的贪吃蛇游戏
1 设计目的和任务 本次期末大作业采用课程设计的形式进行,作为<Windows编程>课程的期末考核.要求综合运用Windows编程的相关知识,完成大作业的相关内容,并撰写设计报告.其目的和 ...
- C++控制台贪吃蛇小游戏详细教程
游戏截图 开始动画: 游戏过程: 游戏架构设计 该游戏的玩法简单,玩家通过键盘方向键控制蛇上下左右移动,吃到食物得分并增长,碰到墙或者自己的身体则死亡,游戏结束. 整个游戏其实就是一 ...
- 【C++】经典项目控制台贪吃蛇小游戏详细教程
[小游戏]贪吃蛇GreedySnake 本文将讲解如何使用c++面向对象方法编写控制台版贪吃蛇小游戏 项目github地址:游戏源码链接 游戏下载:GreedySnake 本人属初学者,水平所限,难免 ...
- 【编程6】贪吃蛇游戏(python+pygame)
效果图~新鲜出炉 开始界面 游戏中 结束界面 一.pygame模块概览 模块名称 功能 pygame.cdrom 访问光驱 pygame.cursors 加载光标 pygame.display 访问显 ...
最新文章
- SOA+AIOT=无限可能,上汽零束 AIOT 沙龙上海站火热报名中
- matlab dsearchn,cKDTree与dsearchn
- [JavaWeb-HTML]HTML标签(大部分常用标签介绍)
- ZOJ.3551.Bloodsucker(期望DP)
- bzoj1853: [Scoi2010]幸运数字 dp+容斥原理
- BITED数学建模七日谈之一:参加全国大学生数学建模比赛前你需要积累哪些
- Java中常见定时任务的实现方式
- SDN的机遇与挑战 让宽带利用率与硬件不再是难题
- HDOJ:1533-Going Home(最小费用流)
- 11001-软件架构设计风格及visio使用
- atmega328p引脚图_ATMEGA328P-AU 8位AVR微控制器
- 抖音企业号开发功能技术搭建
- 黑盒测试简介与其测试方法
- accuracy(准确率), precision(精密度), recall(召回率), specificity(特异性), and F1-score(F1分数):分别是什么意思?
- Google Play In-app Billing API version is less than 3.
- PointNet网络结构详细解析
- What's the differece between high price houses and low price houses of airbnb?
- arduino(19 ):使用ESP32连接 PS3 蓝牙手柄,需要在windows 上先连接成功,然后在修改mac地址,才可以连接成功,但是目前正在测试中,需要在windows上配对成功。
- linux usb驱动——OTG数据线与普通数据线区别
- Hadoop大数据平台开发与案例分析
热门文章
- 快速排序算法-c语言实现,快速排序算法实现(C语言)(转)
- vue 模拟双色球机选
- 图像溯源,图血缘关系总结
- 未来对于35岁以后程序员的职业规划
- Wireshark抓包工具使用教程以及常用抓包规则 ——Powered By 死性不改
- 周润发:《上海滩》成就史上最酷最帅“许文强”
- 苹果4s参数_iPhone 12为何回归扁平中框:并非为怀旧 苹果道出缘由(全文)_苹果 iPhone 12 Pro Max_手机新闻...
- 魅族MAX4 pro 手机无声音
- C++20之Concpet(概念部分,之二)
- 微信第三方平台【一】获取验证票据 component_verify_ticket,授权结果接收