经典贪吃蛇游戏 - 用 C 语言实现控制台版
在本篇博客中,我们将一起来实现经典的贪吃蛇游戏,使用 C 语言编写,并在控制台中运行。这个小游戏会让你回忆起童年的经典游戏体验。我们将从游戏的初始化开始,逐步实现游戏的各个功能,包括蛇的移动、食物的生成、得分的计算等等。最后,我们将整个游戏逻辑串起来,形成一个完整的贪吃蛇游戏。
初始化游戏
首先,让我们来初始化游戏的一些必要组件。我们会使用 Windows API 来实现控制台窗口大小和颜色的设置,以及键盘输入的获取。以下是初始化函数的代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <conio.h>
#include <windows.h>#define WIDTH 40
#define HEIGHT 20
#define INITIAL_LENGTH 3enum Direction {UP,DOWN,LEFT,RIGHT
};struct Node {int x;int y;struct Node* next;
};// ...(省略部分代码)...int main() {system("mode con cols=50 lines=30"); // 设置控制台窗口大小system("title 经典贪吃蛇游戏"); // 设置控制台窗口标题init();// ...(省略部分代码)...releaseSnake();return 0;
}
在初始化函数 init()
中,我们初始化了游戏的一些状态,包括蛇的初始位置、食物的位置、得分等。同时,我们使用 srand()
函数来设置随机数种子,以便在每次游戏开始时都能生成不同的食物位置。
绘制游戏界面
游戏界面的绘制是一个重要的部分。我们会使用 gotoxy()
函数来设置光标位置,以及 setTextColor()
函数来设置文本颜色。以下是绘制函数的代码:
void gotoxy(int x, int y) {COORD coord;coord.X = x;coord.Y = y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}void setTextColor(int color) {SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}// ...(省略部分代码)...void draw() {// 设置控制台光标位置gotoxy(0, 0);// 画上边界for (int i = 0; i < WIDTH + 2; i++) {printf("=");}printf("\n");// 画中间部分和蛇身for (int i = 0; i < HEIGHT; i++) {printf("|");for (int j = 0; j < WIDTH; j++) {// ...(省略部分代码)...}printf("|\n");}// 画下边界for (int i = 0; i < WIDTH + 2; i++) {printf("=");}printf("\n");// 显示得分和提示信息printf("得分: %d\n", score);printf("使用 W, A, S, D 控制移动\n");printf("按下空格键开始下一局\n");
}
在绘制函数中,我们使用了循环来画出游戏界面的各个部分,包括边界、蛇头、食物、蛇身、得分和提示信息。不同的元素使用不同的颜色来区分,以增强游戏的可视化效果。
处理用户输入
用户输入是控制游戏进行的关键。我们使用 _kbhit()
和 _getch()
函数来获取键盘输入,并根据用户的操作来改变蛇的移动方向。以下是处理输入的代码:
void clearInputBuffer() {while (_kbhit()) {_getch();}
}// ...(省略部分代码)...void input() {if (_kbhit()) {int key = _getch();clearInputBuffer(); // 清空输入缓冲区switch (key) {// ...(省略部分代码)...}}
}
在处理输入函数中,我们使用了 switch
语句来根据不同的按键进行相应的操作,包括控制蛇的移动方向、结束游戏等。
移动蛇和游戏逻辑
游戏逻辑的核心在于蛇的移动和吃食物。我们使用一个链表来表示蛇的身体,每个节点表示一个身体部位。以下是移动函数和游戏逻辑的代码:
// ...(省略部分代码)...void move() {// 移动蛇头struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));newNode->next = NULL;switch (direction) {// ...(省略部分代码)...}// ...(省略部分代码)...// 添加新节点到头部newNode->next = head;head = newNode;// 检查是否撞到自己的身体struct Node* current = head->next;while (current != NULL) {if (current->x == head->x && current->y == head->y) {gameover = true;break;}current = current->next;}
}// ...(省略部分代码)...int main() {// ...(省略部分代码)...while (!gameover) {draw();input();if (gameover && !startNextRound) {// ...(省略部分代码)...}if (!gameover) {move();Sleep(100); // 控制游戏速度}if (startNextRound) {// ...(省略部分代码)...}}// ...(省略部分代码)...
}
在移动函数中,我们首先根据当前的移动方向计算蛇头的新位置,并创建一个新的节点来表示蛇头。然后,我们检查是否吃到了食物,如果吃到了就增加得分并重新生成食物。如果没有吃到食物,我们删除蛇尾的节点,实现蛇的移动。最后,我们检查蛇是否撞到了自己的身体,如果是则游戏结束。
完整游戏逻辑
在 main()
函数中,我们将上述的各个功能整合在一起,形成了一个完整的游戏逻辑。游戏会不断地循环进行,直到游戏结束或玩家主动退出。游戏结束时,会显示最终得分,并等待用户按下任意键退出。
总结
通过本篇博客,我们成功地实现了一个简单的控制台版贪吃蛇游戏。从游戏的初始化、绘制界面、处理用户输入,到蛇的移动和游戏逻辑,我们逐步构建了一个完整的游戏框架。这个小游戏不仅可以带我们回忆童年的游戏经历,还可以锻炼我们编程的逻辑思维和综合能力。希望本篇博客对你有所帮助,如果你有任何问题或建议,欢迎在评论区留言!
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <conio.h>
#include <windows.h>#define WIDTH 40
#define HEIGHT 20
#define INITIAL_LENGTH 3enum Direction {UP,DOWN,LEFT,RIGHT
};struct Node {int x;int y;struct Node* next;
};enum Direction direction;
struct Node* head;
struct Node* tail;
int foodX, foodY;
int score;
bool gameover;
bool startNextRound; // 用于标记是否开始下一局游戏void gotoxy(int x, int y) {COORD coord;coord.X = x;coord.Y = y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}void setTextColor(int color) {SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}void clearInputBuffer() {while (_kbhit()) {_getch();}
}void init() {gameover = false;direction = RIGHT;head = (struct Node*)malloc(sizeof(struct Node));head->x = WIDTH / 2;head->y = HEIGHT / 2;head->next = NULL;tail = head;for (int i = 1; i < INITIAL_LENGTH; i++) {struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));newNode->x = head->x - i;newNode->y = head->y;newNode->next = NULL;tail->next = newNode;tail = newNode;}score = 0;srand((unsigned int)time(NULL));foodX = rand() % WIDTH;foodY = rand() % HEIGHT;startNextRound = false;
}void releaseSnake() {while (head != NULL) {struct Node* temp = head;head = head->next;free(temp);}
}void draw() {// 设置控制台光标位置gotoxy(0, 0);// 画上边界for (int i = 0; i < WIDTH + 2; i++) {printf("=");}printf("\n");// 画中间部分和蛇身for (int i = 0; i < HEIGHT; i++) {printf("|");for (int j = 0; j < WIDTH; j++) {if (i == head->y && j == head->x) {setTextColor(12); // 颜色为12,红色printf("@"); // 蛇头setTextColor(15); // 颜色为15,白色} else if (i == foodY && j == foodX) {setTextColor(14); // 颜色为14,黄色printf("@"); // 食物setTextColor(15); // 颜色为15,白色} else {struct Node* current = head->next;bool printed = false;while (current != NULL) {if (current->x == j && current->y == i) {setTextColor(10); // 颜色为10,绿色printf("#"); // 蛇身setTextColor(15); // 颜色为15,白色printed = true;break;}current = current->next;}if (!printed) {printf(" ");}}}printf("|\n");}// 画下边界for (int i = 0; i < WIDTH + 2; i++) {printf("=");}printf("\n");// 显示得分和提示信息printf("得分: %d\n", score);printf("使用 W, A, S, D 控制移动\n");printf("按下空格键开始下一局\n");
}void input() {if (_kbhit()) {int key = _getch();clearInputBuffer(); // 清空输入缓冲区switch (key) {case 'w':direction = UP;break;case 's':direction = DOWN;break;case 'a':direction = LEFT;break;case 'd':direction = RIGHT;break;case 'x':gameover = true;break;case ' ':if (gameover) {startNextRound = true;}break;default:break;}}
}void move() {// 移动蛇头struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));newNode->next = NULL;switch (direction) {case UP:newNode->x = head->x;newNode->y = (head->y - 1 + HEIGHT) % HEIGHT; // 从另一面出现break;case DOWN:newNode->x = head->x;newNode->y = (head->y + 1) % HEIGHT; // 从另一面出现break;case LEFT:newNode->x = (head->x - 1 + WIDTH) % WIDTH; // 从另一面出现newNode->y = head->y;break;case RIGHT:newNode->x = (head->x + 1) % WIDTH; // 从另一面出现newNode->y = head->y;break;default:break;}// 检查是否吃到食物if (newNode->x == foodX && newNode->y == foodY) {score++;foodX = rand() % WIDTH;foodY = rand() % HEIGHT;}else {// 删除蛇的尾部节点struct Node* temp = head;while (temp->next->next != NULL) {temp = temp->next;}free(temp->next);temp->next = NULL;}// 添加新节点到头部newNode->next = head;head = newNode;// 检查是否撞到自己的身体struct Node* current = head->next;while (current != NULL) {if (current->x == head->x && current->y == head->y) {gameover = true;break;}current = current->next;}
}int main() {system("mode con cols=50 lines=30"); // 设置控制台窗口大小system("title 经典贪吃蛇游戏"); // 设置控制台窗口标题init();while (!gameover) {draw();input();if (gameover && !startNextRound) {setTextColor(12); // 颜色为12,红色gotoxy(WIDTH / 2 - 4, HEIGHT / 2);printf("游戏结束");setTextColor(15); // 颜色为15,白色gotoxy(WIDTH / 2 - 6, HEIGHT / 2 + 1);printf("最终得分:%d", score);}if (!gameover) {move();Sleep(100); // 控制游戏速度}if (startNextRound) {releaseSnake();init();gameover = false;startNextRound = false;}}setTextColor(15); // 颜色为15,白色gotoxy(WIDTH / 2 - 4, HEIGHT / 2 + 2);printf("按下任意键退出");_getch();releaseSnake();return 0;
}
相关文章:
经典贪吃蛇游戏 - 用 C 语言实现控制台版
在本篇博客中,我们将一起来实现经典的贪吃蛇游戏,使用 C 语言编写,并在控制台中运行。这个小游戏会让你回忆起童年的经典游戏体验。我们将从游戏的初始化开始,逐步实现游戏的各个功能,包括蛇的移动、食物的生成、得分的…...

安灯Andon系统的应用与优势
安灯系统是一款与硬件相结合,实时了解机台与工位状态,让异常的信息得到快速、高效的解决的系统软件,同时记录每次异常报警的种类、响应时间和处理问题用时,提供改善生产管理和人员考核的数据参考,实现透明、快速的生产…...
2023年的C++基础笔记
C 基本语法 对象 - 对象具有状态和行为。例如:一只狗的状态 - 颜色、名称、品种,行为 - 摇动、叫唤、吃。对象是类的实例。 类 - 类可以定义为描述对象行为/状态的模板/蓝图。 方法 - 从基本上说,一个方法表示一种行为。一个类可以包含多个…...

综合能源系统(6)——综合能源综合评估技术
综合能源系统关键技术与典型案例 何泽家,李德智主编 综合能源系统是多种能源系统非线性耦合的、多时间与空间尺度耦合的“源-网-荷一储”一体化系统,通过能源耦合、多能互补,能够实现能源的高效利用,并提高新能源的利用水平。对…...

华为OD机试真题 Java 实现【寻找相同子串】【2023 B卷 100分】,附详细解题思路
目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(A卷B卷&#…...

软件外包开发的桌面客户端开发
跨平台桌面客户端开发工具允许开发者在多个操作系统上构建应用程序,从而实现一次编码、多平台运行的目标。以下是几个常见的跨平台桌面客户端开发工具以及它们的特点,希望对大家有所帮助。北京木奇移动技术有限公司,专业的软件外包开发公司&a…...

PAT(Advanced Level) Practice(with python)——1058 A+B in Hogwarts
Code A,B map(str,input().split()) g1,s1,k1 A.split(.) g2,s2,k2 B.split(.) g int(g1)int(g2) s int(s1)int(s2) k int(k1)int(k2) if k>29:s1k%29 if s>17:g1s%17 print(str(g).str(s).str(k))...
【ES】笔记-ES6的函数rest参数用法
es6中引入了rest参数,样式形如…xxx,用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的一个变量是一个数组,该变量将多余的参数放入数组中。例如: function add(...value){console.log(value);…...

【MOOC】北京理工大学Python网络爬虫与信息提取慕课答案-综合挑出了一些很难评的慕课测验题
1 Requests库中的get()方法最常用,下面哪个说法正确?…...

【论文阅读】基于深度学习的时序预测——Crossformer
系列文章链接 论文一:2020 Informer:长时序数据预测 论文二:2021 Autoformer:长序列数据预测 论文三:2022 FEDformer:长序列数据预测 论文四:2022 Non-Stationary Transformers:非平…...

谷粒商城第十一天-完善商品分组(主要添上关联属性)
目录 一、总述 二、前端部分 2.1 改良前端获取分组列表接口及其调用 2.2 添加关联的一整套逻辑 三、后端部分 四、总结 一、总述 前端部分和之前的商品品牌添加分类差不多。 也是修改一下前端的分页获取列表的接口,还有就是加上关联的那一套逻辑,…...

C++笔记之函数参数列表中设置默认值
C笔记之函数参数列表中设置默认值 code review! 代码 #include <iostream>// 函数声明时设置默认值 void printInfo(std::string name "Unknown", int age 0);int main() {printInfo(); // 使用默认参数值printInfo("Alice", 25);…...

Verilog求log10和log2近似
Verilog求log10和log2近似 Verilog求10对数近似方法,整数部分用位置index代替,小数部分用查找表实现 参考: Verilog写一个对数计算模块Log2(x) FPGA实现对数log2和10*log10...
二叉树小结
二叉树 树的遍历(如何遍历,如何利用特性问题) 前序遍历(中前后) 递归 class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> res new ArrayList<>();inorder(root, res);return res…...

vue二进制下载
封装axios,/api/request import axios from axios import store from /store import Vue from vue import { Message, MessageBox } from element-uiimport { getToken } from /utils/authaxios.defaults.headers[Content-Type] application/json;charsetutf-8 co…...

c++QT文件操作
1 介绍 QT的文件操作来源于其抽象基类QIODevice,中用于处理输入输出设备。提供了统一的接口来处理不同类型的数据源,如文件、套接字、缓冲区等。QIODevice 主要用于读取和写入数据,无论数据来自何种源头,都可以通过 QIODevice 统一…...

Jmeter —— jmeter设置HTTP信息头管理器模拟请求头
HTTP信息头管理器 HTTP信息头管理器是在有需要模拟请求头部的时候进行设置的,添加方式 是 右击线程组 -- 配置元件 -- HTTP信息头管理器 可以通过抓包工具或者F12获取http请求的header头部信息;如下图: 复制并点击jmeter中的从剪贴板添加&am…...
vue 图片转pdf
尝试了集中图片转pdf的方式, (1)最终较为优秀的一种是使用jspdf将图片转为pdf,支持JPG/JPEG/PNG/BMP/TIF/TIFF图片格式转换,详见我的另一篇文章: https://blog.csdn.net/Ann_52547/article/details/1322149…...

20.5 HTML 媒体
1. video视频标签 video视频标签: 是HTML中用于在网页上嵌入视频的元素.常用的视频标签属性: - src属性: 指定视频文件的URL地址. - controls属性: 用于显示视频播放控件(如播放按钮, 进度条等), 使用户能够控制视频的播放. - width和height: 指定视频的宽度和高度. - autopla…...

科大讯飞分类算法挑战赛2023的一些经验总结
引言: ResNet是he kaiming大佬的早年神作,当年直接刷榜各大图像分类任务。ResNet是一种残差网络,咱们可以把它理解为一个子网络,这个子网络经过堆叠可以构成一个很深的网络,而ResNext在其基础上,进行了一定修改完善&am…...

如何在没有 iTunes 的情况下备份 iPhone
我可以在没有 iTunes 的情况下将 iPhone 备份到电脑吗?虽然 iTunes 曾经是备份 iPhone 的主要方法,但它并不是 iOS 用户唯一的备份选项。您可以选择多种方便的替代方案来备份 iPhone,无需使用 iTunes。您可以在这里获得更灵活、更人性化的备份…...

QQ邮箱发送验证码(Springboot)
一、邮箱发送服务准备 在qq邮箱的设置中选择账号下开启服务。 开启时可能会有短信验证,开启后显示验证码之类的一串英文,复制保存起来,在配置文件中会使用到。 二、后端依赖及配置 依赖 在pom.yml文件中添加相关依赖,redis的…...
QuickJS 如何发送一封邮件 ?
参阅:bellard.org : QuickJS 如何使用 qjs 执行 js 脚本 在 QuickJS 中发送邮件需要依赖外部库或调用系统命令,因为 QuickJS 本身不包含 SMTP 功能。以下是两种实现方法: 方法 1:调用系统命令(推荐) 使…...

VUE前端实现自动打包成压缩文件
VUE前端实现自动打包成压缩文件 背景思路实现打包代码实现 尾巴 背景 做前端开发的兄弟们都经历过每次开发完成之后发包需要进行打包,然后将打包文件压缩。每次打好包了都得手动压缩一遍,就有点繁琐。今天我们就使用一种命令行自动压缩的方法࿰…...

pygame开发的坦克大战
使用Python和Pygame开发的精美坦克大战游戏。这个游戏包含玩家控制的坦克、敌方坦克、各种障碍物、爆炸效果和完整的游戏机制。 游戏说明 这个坦克大战游戏包含以下功能: 游戏特点 玩家控制:使用方向键移动坦克,空格键射击 敌人AI&#x…...
AI图片售卖:是暴利新风口还是虚幻泡沫?哪些平台适合售卖AI图片
还记得去年大火的Midjourney吗?今年4月,Midjourney又发布了备受期待的V7版本,带来了更高的图像质量和创新功能。使用Midjourney、Stable Diffusion、DALLE等AI图片生成工具,创作者只需输入关键词即可获得高质量的原创图片。这一变…...

【HarmonyOS Next之旅】DevEco Studio使用指南(三十一) -> 同步云端代码至DevEco Studio工程
目录 1 -> 同步云函数/云对象 1.1 -> 同步单个云函数/云对象 1.2 -> 批量同步云函数/云对象 2 -> 同步云数据库 2.1 -> 同步单个对象类型 2.2 -> 批量同步对象类型 3 -> 一键同步云侧代码 1 -> 同步云函数/云对象 说明 对于使用DevEco Studio…...

【k8s】k8s集群搭建
k8s集群搭建 一、环境准备1.1 集群类型1.2 安装方式1.3 主机规划1.4 环境配置1.4.1 说明1.4.2 初始化1.4.3 关闭防火墙和禁止防火墙开机启动1.4.4 设置主机名1.4.5 主机名解析1.4.6 时间同步1.4.7 关闭selinux1.4.8 关闭swap分区1.4.9 将桥接的IPv4流量传递到iptables的链1.4.1…...

计算机网络领域所有CCF-A/B/C类期刊汇总!
本期小编统计了【计算机网络】领域CCF推荐所有期刊的最新影响因子,分区、年发文量以及投稿经验,供大家参考! CCF-A类 1 IEEE Journal on Selected Areas in Communications 【影响因子】13.8 【期刊分区】JCR1区,中科院1区TOP …...
uniapp map组件的基础与实践
UniApp 中的 map 组件用于在应用中展示地图,并且支持在地图上添加标记、绘制线条和多边形等功能。以下是一些基本用法: 1. 基本结构 首先,确保你在页面的 .vue 文件中引入了 map 组件。以下是创建一个简单地图的基本代码结构: <template><view class="con…...