经典贪吃蛇游戏 - 用 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…...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
