【C++项目实战】贪吃蛇小游戏
一、引言
贪吃蛇,这款经典的电子游戏,自1976年诞生以来,一直受到全球玩家的喜爱。它的规则简单,玩法直观,但同时也充满了挑战性。在这篇文章中,我们将一起探索如何开发一个贪吃蛇游戏,无论是作为编程新手的练手项目,还是作为游戏开发爱好者的娱乐之作,都是一个不错的选择。

二、贪吃蛇游戏基本规则
游戏规则
- 游戏目标:控制一条蛇在屏幕上移动,吃掉出现的食物,每吃一个食物,蛇的长度就会增加一节。
- 控制方式:通过键盘的上下左右键控制蛇的移动方向。
- 游戏结束条件:蛇撞到屏幕边界或者自己的身体时,游戏结束。
基本思路
- 使用数组或链表来表示蛇的身体。
- 通过改变蛇头的位置来控制蛇的移动。
- 使用随机数来生成食物的位置。
- 检测蛇头与食物、墙壁和自身身体的碰撞。
三、代码
首先绘制一个范围,确定面积多大
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#include<conio.h>//设置地图边界
#define W 120
#define H 30//测试Ui界面多大
void text_ui()
{for (int i = 0;i < H; i++){for (int j = 0;j < W; j++){printf("=");}printf("\n");}
}int main()
{text_ui();return 0;}
得到结果如下:

整个游戏复制以下代码运行即可:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#include<conio.h>//设置地图边界
#define W 120
#define H 30//设置贪吃蛇结构体
typedef struct _body
{int x; //坐标xint y; //坐标y
}BODY;typedef struct snake
{BODY list[W*H]; //贪吃蛇最大沾满整个地图int size; //实际身体个数。蛇头 @,蛇身 *BODY food; //食物 #COORD coord; //定位光标int dx; //移动方向int dy; //移动方向BODY tail; //记录蛇尾(用于清除蛇尾痕迹)int score; //记录分数
}SNAKE;//食物函数
void init_food(BODY* food)
{//设置随机数种子srand(time(NULL));//设置食物坐标food->x = rand() % (W - 1) +1;food->y = rand() % (H - 1) +1;
}//初始化蛇的函数
void init_snake(SNAKE *snake)
{//设置蛇头与蛇尾的位置snake->list[0].x = W / 2;snake->list[0].y = H / 2;snake->list[1].x = W / 2 - 1;snake->list[1].y = H / 2 - 1;snake->size = 2;//设置移动方向snake->dx = 1;snake->dy = 0;snake->score = 0;//设置食物位置init_food(&(snake->food));
}
//展示UI函数
void show_ui(SNAKE* snake)
{//显示蛇for (int i = 0;i < snake->size;i++){snake->coord.X = snake->list[i].x;snake->coord.Y = snake->list[i].y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);if (i == 0) //显示蛇头{printf("@");}else{printf("*");}}//显示食物snake->coord.X = snake->food.x;snake->coord.Y = snake->food.y;//定位光标位置SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);printf("#");//清除蛇尾痕迹snake->coord.X = snake->tail.x;snake->coord.Y = snake->tail.y;//定位光标位置SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);printf(" ");}
void show_wall()
{for (int i = 0;i <= H; i++){for (int j = 0;j <= W; j++){if (i == 0 || j == 0 || i == H || j == W){printf("+");}else{printf(" ");}}printf("\n");}
}
/*
测试Ui界面多大
void text_ui()
{for (int i = 0;i < H; i++){for (int j = 0;j < W; j++){printf("=");}printf("\n");}
}
*/void hide_cur()//隐藏光标
{CONSOLE_CURSOR_INFO cci;cci.dwSize = sizeof(cci);cci.bVisible = FALSE;SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
}void move_snake(SNAKE* snake) //移动函数
{snake->tail = snake->list[snake->size - 1];for (int i = snake->size - 1; i > 0; i--){snake->list[i] = snake->list[i - 1];}snake -> list[0].x += snake->dx;snake -> list[0].y += snake->dy;
}void contorl_snake(SNAKE *snake)
{char key=0;while (_kbhit()) //判断是否按下按键,按下不等于0{key = _getch();}switch (key){case'w':snake -> dy = -1;snake -> dx = 0;break;case's':snake->dy = 1;snake->dx = 0;break;case'a':snake->dy = 0;snake->dx = -1;break;case'd':snake->dy = 0;snake->dx = 1;break;}}void snake_eat_food(SNAKE* snake) //吃食物函数
{if (snake->list[0].x== snake->food.x && snake->list[0].y == snake->food.y){snake->size++;init_food(&(snake->food));snake->score += 10;}
}void game_over(SNAKE *snake)
{snake->coord.X = 110;snake->coord.Y = 65;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake->coord);printf("游戏结束,总分数:%d\n", snake->score);
}void start_game(SNAKE *snake)
{while (snake->list[0].x>0 && snake->list[0].x<W &&snake->list[0].y>0 && snake->list[0].y < H){show_ui(snake); //显示蛇和食物contorl_snake(snake); //控制蛇snake_eat_food(snake); //判断是否吃到食物move_snake(snake); //移动蛇Sleep(100); //延时100毫秒}game_over(snake);
}int main(int argc, char* argv[])
{hide_cur(); //隐藏光标//text_ui();SNAKE* snake = (SNAKE*)malloc(sizeof(SNAKE));init_snake(snake); //初始化show_wall(); //显示墙start_game(snake);free(snake);while (1);return 0;
}
代码解析
-
头文件包含:
stdio.h:标准输入输出头文件。string.h:字符串操作头文件。stdlib.h:标准库头文件,包含内存分配等函数。time.h:时间操作头文件,用于生成随机数。windows.h:Windows API 头文件,用于控制台操作。conio.h:控制台输入输出头文件,用于非缓冲输入。
-
宏定义:
W和H分别定义了游戏地图的宽度和高度。
-
结构体定义:
BODY:用于存储蛇的身体部分的坐标。SNAKE:包含蛇的身体、食物、方向、分数等信息。
-
函数定义:
init_food:初始化食物的位置。init_snake:初始化蛇的位置和方向。show_ui:显示蛇和食物。show_wall:显示游戏的边界。hide_cur:隐藏控制台光标。move_snake:根据蛇的方向移动蛇的身体。contorl_snake:通过键盘输入控制蛇的移动方向。snake_eat_food:判断蛇是否吃到食物,并更新蛇的长度和分数。game_over:游戏结束时显示分数。start_game:游戏的主循环,控制游戏的流程。
-
主函数:
main:程序的入口点,初始化游戏,隐藏光标,显示边界,并开始游戏循环。
相关文章:
【C++项目实战】贪吃蛇小游戏
一、引言 贪吃蛇,这款经典的电子游戏,自1976年诞生以来,一直受到全球玩家的喜爱。它的规则简单,玩法直观,但同时也充满了挑战性。在这篇文章中,我们将一起探索如何开发一个贪吃蛇游戏,无论是作为…...
Python基于matplotlib实现树形图的绘制
在Python中,你可以使用matplotlib库来绘制树形图(Tree Diagram)。虽然matplotlib本身没有专门的树形图绘制函数,但你可以通过组合不同的图形元素(如线条和文本)来实现这一点。 以下是一个简单的示例&#…...
【UE5 C++课程系列笔记】21——弱指针的简单使用
目录 概念 声明和初始化 转换为共享指针 打破循环引用 弱指针使用警告 概念 在UE C 中,弱指针(TWeakPtr )也是一种智能指针类型,主要用于解决循环引用问题以及在不需要强引用保证对象始终有效的场景下,提供一种可…...
【游戏设计原理】46 - 魔杖
幻想,人们可以通过多种形式来引发,比如文字,图片,绘画,语言等,但游戏与以上这些形式的区别,正如游戏与其他艺术形式的区别一样,游戏作为一种艺术和娱乐形式,其独特之处在…...
【路径跟踪】PIDMPC
路径跟踪(Path Tracking)是指在实际行驶过程中,根据预先规划好的路径进行控制,能够沿着设定的路径行驶。常见的路径跟踪算法包括基于模型的控制方法(如PID控制器)、模型预测控制(Model Predicti…...
Spring源码分析之事件机制——观察者模式(二)
目录 获取监听器的入口方法 实际检索监听器的核心方法 监听器类型检查方法 监听器的注册过程 监听器的存储结构 过程总结 Spring源码分析之事件机制——观察者模式(一)-CSDN博客 Spring源码分析之事件机制——观察者模式(二ÿ…...
热备份路由HSRP及配置案例
✍作者:柒烨带你飞 💪格言:生活的情况越艰难,我越感到自己更坚强;我这个人走得很慢,但我从不后退。 📜系列专栏:网路安全入门系列 目录 一,HSRP的相关概念二,…...
仿生的群体智能算法总结之三(十种)
群体智能算法是一类通过模拟自然界中的群体行为来解决复杂优化问题的方法。以下是30种常见的群体智能算法,本文汇总第21-30种。接上文 : 编号 算法名称(英文) 算法名称(中文) 年份 作者 1 Ant Colony Optimization (ACO) 蚁群优化算法 1991 Marco Dorigo 2 Particle Swar…...
CentOS 7系统 OpenSSH和OpenSSL版本升级指南
文章目录 CentOS 7系统 OpenSSH和OpenSSL版本升级指南环境说明当前系统版本当前组件版本 现存安全漏洞升级目标版本升级准备工作OpenSSL升级步骤1. 下载和解压2. 编译安装3. 配置环境 OpenSSH升级步骤1. 下载和解压2. 编译安装3. 创建systemd服务配置4. 更新SSH配置文件5. 设置…...
【专题】2024年出口跨境电商促销趋势白皮书报告汇总PDF洞察(附原数据表)
原文链接:https://tecdat.cn/?p38722 在当今全球化加速演进、数字经济蓬勃发展的大背景下,跨境电商行业正以前所未有的态势重塑国际贸易格局,成为各方瞩目的焦点领域。 根据亚马逊发布的《2024年出口跨境电商促销趋势白皮书》,…...
【Ubuntu】不能连上网络
1. ping路由器的IP地址 ping 192.168.1.1 如果ping不通的话,可能是网络故障导致的。需要重启配置ip地址。配置文件 sudo vi /etc/network/interface 2. ping 8.8.8.8 如果ping不通的话,可能是路由器不能链接往外网; 或者路由器显示了当…...
CSS3 框大小
CSS3 框大小 CSS3 是网页设计和开发中不可或缺的一部分,它为开发者提供了更多样化、更灵活的样式和布局选择。在 CSS3 中,框大小(Box Sizing)是一个重要的概念,它决定了元素内容的宽度和高度以及元素整体的大小。本文将详细介绍 CSS3 框大小的概念、用法以及最佳实践。 …...
联发科MTK6771/MT6771安卓核心板规格参数介绍
MT6771,也被称为Helio P60,是联发科技(MediaTek)推出的一款中央处理器(CPU)芯片,可运行 android9.0 操作系统的 4G AI 安卓智能模块。MT6771芯片采用了12纳米工艺制造,拥有八个ARM Cortex-A73和Cortex-A53核心,主频分别…...
python中的时间模块--datetime模块、time模块
python中的时间模块 一.datetime模块二.time模块 一.datetime模块 引入时间模块 from datetime import datetime获取当前时间 print(datetime.today()) # 前的日期和时间 print(datetime.now()) # 当前的日期和时间 print(datetime.now().year) # 当前的年份 print(datetime…...
CV 处理全流程:从数据采集到模型部署的整个过程,体现全面性
CV 处理全流程:从数据采集到模型部署的整个过程,体现全面性 Numpy广播 OpenCV - Python归一化提取ROI(感兴趣区域)分离和合并通道 Pytorch 基础算子自动梯度计算 CV 全流程图像数据采集1. 确认目标2. 分析过程(使用目标-手段分析法࿰…...
OWASP ZAP之API 请求基础知识
ZAP API 提供对 ZAP 大部分核心功能的访问,例如主动扫描器和蜘蛛。ZAP API 在守护进程模式和桌面模式下默认启用。如果您使用 ZAP 桌面,则可以通过访问以下屏幕来配置 API: Tools -> Options -> API。 ZAP 需要 API 密钥才能通过 REST API 执行特定操作。必须在所有 …...
南京观海微电子----GH7009国宇测试盒使用
1. SPI接线 针对7009: 2. 国宇上位机代码准备 在主函数首尾两端加入IO2时序控制的代码、以及国语SPI有效位控制的代码(请注意7009和其他700x使用的有效位控制不一致,需要用哪一款加入哪一行即可): 三、国宇SPI读的使…...
mysql及其兼容语法数据库对于注释的特殊要求
我司大部分数据库使用MS-SQL,其中使用大量–开头的行注释,由于业务需要,切换到了Starrocks数据库(兼容mysql语法)后发现使用with开头子查询的时候,大量报错,单独执行内部的子查询又正常…...
数据去重与重复数据的高效处理策略
在实际业务中,数据去重是一个非常常见的需求,特别是在日志数据、用户操作记录或交易记录等领域。去重不仅仅是删除重复数据,更重要的是按照业务规则保留最有价值的数据记录。 本文将探讨如何在 SQL 中高效地处理重复数据,通过 DI…...
Spring Boot自动装配代码详解
概述 Spring Boot自动装配是其核心特性之一,它能够根据项目中添加的依赖自动配置Spring应用程序。通过自动装配,开发人员可以减少大量的配置工作,快速搭建起一个可用的Spring应用。 关键组件和注解 SpringBootApplication注解 这是Spring Bo…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
