C语言编写贪吃蛇游戏

​ 完成了一下c语言入门完成的必修课——贪吃蛇游戏。边学边做也搞了一天,所以记录一下自己的学习过程吧!

工具

​ 因为我本来是用vscode进行编码的,发现不可以,查询才发现,vscode采用mingw,而制作图形界面的easyx使用的是支持的是vs的一些内容。

​ 而我只是想制作贪吃蛇,对easyx没太多要求,能运行就行,加上安装vs2019并不复杂,并且需easyx装到vs2019里极其方变,下载然后打开,在install界面选择vs2019即可。就装了一个vs2019。

​ 但使用过程只能说相当自闭。

​ vs2019:https://visualstudio.microsoft.com/zh-hans/vs/

​ easyx:https://easyx.cn/

游戏框架

​ 需要一条可以根据按键运动的蛇,只需要使得蛇头在按键的作用下左右上下移动即可,蛇的其他部分只需要移动到前一节蛇的位置。需要不断出现的食物,当蛇碰到食物时,食物消失并刷新出新食物。当蛇碰到自己时游戏结束。

需要准备的元素:方向常量;蛇的结构体;食物的结构体

函数设定:初始化函数;绘图函数;按键控制函数;移动函数;吃食物判定函数;死亡判定;主函数;

最终结果:

游戏初始

可以先定义一个最大长度:

#define SNAKE_NUM 500//蛇的最大节数

struct Snake {int size;//蛇的长度int dir;//蛇的方向int speed;// 蛇的速度int score;POINT coor[SNAKE_NUM];//POINT是定义好的一个结构体,内含long x;long y
}snake;

其中方向,需要进行枚举:

enum DIR {//表示方向UP,DOWN,LEFT,RIGHT,
};

这与#define UP #define LEFT 是等价的,就是定义一些常量为了后续使用

而后是定义食物的结构:

struct FOOD {int x;int y;int r;bool flag; //食物是否被吃DWORD color;//食物颜色
}food;

因为只有一个food所以没有必要用POINT进行二次引用了、

定义好了结构体后就要对其进行初始化了,即对这些内容进行赋值操作:

void GameInit(){initgraph(640, 480);snake.size = 3;snake.speed = 10;snake.dir = RIGHT;//给定一个方向snake.score= 0;//将蛇的位置先赋值了for (int i = snake.size  - 1; i >= 0 ; i--) {snake.coor[i].x = 40 - 10 * i;snake.coor[i].y = 10;}//初始化食物srand(time(0));//记得需要#include <time.h>food.x = rand() % 640;food.y = rand() % 480;food.color = RGB(rand() % 256, rand() % 256, rand() % 256);food.r = rand() % 10 + 5;food.flag = true;
}

记得一定要记好哪一个是宽度哪一个是高度,因为这个小细节改bug至少一个小时

绘图过程

绘图就是将移动蛇的过程动态化,这里需要说明的是,我们需要采用双缓冲绘图:

双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度。

如果不使用双缓冲绘图,那么很容易造成闪烁效果:

void GameDraw() {BeginBatchDraw();//双缓冲绘图setbkcolor(RGB(28,115,119));//设置背景颜色cleardevice();//清屏操作//绘制蛇setfillcolor(YELLOW);for (int i = 0; i < snake.size; i++) {solidcircle(snake.coor[i].x, snake.coor[i].y, 5);}EndBatchDraw();//绘制食物setfillcolor(food.color);if (food.flag) {solidcircle(food.x, food.y, food.r);}//写上分数char temp[20] = "";_stprintf_s(temp, "分数:%d",snake.score);outtextxy(20, 20, temp);EndBatchDraw();
}

这里倒没什么好说的,唯一值得说一下的就是在outtextxy有蛮多限制的。

会报一个错误:

error C2665: “outtextxy”: 2 个重载中没有一个可以转换所有参数类型

为了使temp是在outtextxy是可接受的函数参数,需要在头文件的上方加上

#undef UNICODE
#undef _UNICODE

即将这个项目以 MBCS 编码编译

而我当时由于使用sprintf输入temp,vs2019不指出错误,只是单纯无法运行,去参考了easyx文档才改成了_stprintf_s解决了这个问题。废了相当长时间,简直拳头硬了。

蛇的移动

解决了绘图问题,我们就要开始真正移动我们的蛇了:

首先我们应该根据键盘操作确定蛇的移动方向:

键盘操作函数

void keyControl() {//判断有没有按键if (_kbhit()) {//头文件#include <conio.h>switch (_getch()) {case 'w':case 72:if (snake.dir != DOWN)snake.dir = UP; break;case 's':case 80:if (snake.dir != UP)snake.dir = DOWN; break;case 'a':case 75:if (snake.dir != RIGHT)snake.dir = LEFT; break;case 'd':case 77:if (snake.dir != LEFT)snake.dir = RIGHT; break;case ' '://游戏暂停while (1) {if (_getch() == ' ')return;}}}
}

_kbhit()就是按键的判断,如果没有进行按键,则返回一个0值。故若有按键,则执行if中的内容。

话说我还是感觉离谱,因为要使用easyx才安装了vs2019,但是一些基本操作比如getchar他非得改成_getch,叹息。

加了一个无法朝相反方向移动的判定,即如果向前运动则不能直接改变方向为向后。

case后的数字代表小键盘操作。

额外加了游戏暂停判定。如果键入空格,则进入死循环中直到再次键入空格。

移动蛇函数:

void snakemove() {for (int i = snake.size - 1; i > 0; i--) {snake.coor[i] = snake.coor[i - 1];}switch (snake.dir) {case UP: {snake.coor[0].y -= snake.speed;if (snake.coor[0].y <= 0) {//超出上界snake.coor[0].y = 480;}break;}case DOWN: {snake.coor[0].y += snake.speed;if (snake.coor[0].y >= 480) {snake.coor[0].y = 0;}break;}case LEFT: {snake.coor[0].x -= snake.speed;if (snake.coor[0].x <=0) {snake.coor[0].x = 640;}break;}case RIGHT: {snake.coor[0].x += snake.speed;if (snake.coor[0].x >= 640) {snake.coor[0].x = 0;}break;}}
}

我们可以直到,移动蛇其实就是移动蛇头,而后再将蛇身的每一节的位置改变为他的前一节。

第一个for循环就是为了第二个操作。

移动蛇头到边界时就使蛇从反方向出现,也可以当蛇碰到边界后死亡,这没什么问题。(但是食物生成偶尔在边界,我测试时为了不增加难度就改成了这样。手残党悲哀)

吃食物过程

void eatfood(){if (food.flag && (snake.coor[0].x >= food.x - food.r && snake.coor[0].x <= food.x + food.r) &&(snake.coor[0].y >= food.y - food.r && snake.coor[0].y <= food.y + food.r)) {food.flag = false;snake.size++;snake.score += 5;}//食物消失则重新初始化if (!food.flag){srand(GetTickCount());//GetTickCount()获取系统开机到现在所需毫秒数food.x = rand() % 640;food.y = rand() % 480;food.color = RGB(rand() % 256, rand() % 256, rand() % 256);food.r = rand() % 10 + 10;food.flag = true;}
}

吃食物也很好理解,即蛇头处于食物的范围中即可。

让我有点自闭的是,如果我再重新生成食物的时候仍把随机种子设为time(0),食物的y坐标始终变化不大,基本可以不操作也可以吃到食物。最终我也没拍出这个bug,只能参考其他人,改成了GetTickCount()这个函数。

游戏终止过程

就是蛇吃到它自己,则终止。

void EatSelf()
{for (int i = 1; i < snake.size; i++){if (snake.coor[0].x == snake.coor[i].x && snake.coor[0].y == snake.coor[i].y){outtextxy(200, 200, "Game over!");int a = _getch();  //保持停顿exit(0);}}
}

为什么要用int a = _getch(); 这句话呢,因为vs2019需要一个承载_getch()返回值的变量,我真是服了。

最后加上主函数就可以了

int main() {GameInit();while (1) {GameDraw();snakemove();keyControl();eatfood();EatSelf();Sleep(50);}int b = _getch();closegraph();return 0;
}

哦对了,需要说明一下Sleep,是使得执行过程停顿x毫秒,如果不休眠的话蛇向右运动的速度我们反应不过来,当然你也可以把休眠时间缩短,效果如图:

md不玩了,辣鸡游戏(并不)

那么最后展示一下整体代码吧:

整体代码

#undef UNICODE
#undef _UNICODE#include <stdio.h>
#include <graphics.h>
#include <conio.h>
#include <stdlib.h>
#include <time.h>
#define SNAKE_NUM 500//蛇的最大节数enum DIR {//表示方向UP,DOWN,LEFT,RIGHT,
};
//蛇的结构
struct Snake {int size;//蛇的长度int dir;//蛇的方向int speed;// 蛇的速度int score;POINT coor[SNAKE_NUM];//POINT是定义好的一个结构体,内含long x;long y
}snake;//食物的结构
struct FOOD {int x;int y;int r;bool flag; //食物是否被吃DWORD color;//食物颜色
}food;//数据初始化
void GameInit() {//init初始化graph图形窗口 SHOWCONSOLE显示控制台initgraph(640, 480/*,SHOWCONSOLE*/ );snake.size = 3;snake.speed = 10;snake.dir = RIGHT;snake.score= 0;for (int i = snake.size  - 1; i >= 0 ; i--) {snake.coor[i].x = 40 - 10 * i;snake.coor[i].y = 10;}//初始化食物srand(time(0));//GetTickCount()获取系统开机到现在所需毫秒数food.x = rand() % 640;food.y = rand() % 480;food.color = RGB(rand() % 256, rand() % 256, rand() % 256);food.r = rand() % 10 + 5;food.flag = true;
}void GameDraw() {//双缓冲绘图BeginBatchDraw();//设置背景颜色setbkcolor(RGB(28,115,119));cleardevice();//绘制蛇setfillcolor(YELLOW);for (int i = 0; i < snake.size; i++) {solidcircle(snake.coor[i].x, snake.coor[i].y, 5);}//绘制食物EndBatchDraw();setfillcolor(food.color);if (food.flag) {solidcircle(food.x, food.y, food.r);}char temp[20] = "";_stprintf_s(temp, "分数:%d",snake.score);outtextxy(20, 20, temp);EndBatchDraw();
}//移动蛇
void snakemove() {for (int i = snake.size - 1; i > 0; i--) {snake.coor[i] = snake.coor[i - 1];}switch (snake.dir) {case UP: {snake.coor[0].y -= snake.speed;if (snake.coor[0].y <= 0) {//超出上界snake.coor[0].y = 480;}break;}case DOWN: {snake.coor[0].y += snake.speed;if (snake.coor[0].y >= 480) {snake.coor[0].y = 0;}break;}case LEFT: {snake.coor[0].x -= snake.speed;if (snake.coor[0].x <=0) {snake.coor[0].x = 640;}break;}case RIGHT: {snake.coor[0].x += snake.speed;if (snake.coor[0].x >= 640) {snake.coor[0].x = 0;}break;}}
}
//吃食物
void eatfood(){if (food.flag && (snake.coor[0].x >= food.x - food.r && snake.coor[0].x <= food.x + food.r) &&(snake.coor[0].y >= food.y - food.r && snake.coor[0].y <= food.y + food.r)) {food.flag = false;snake.size++;snake.score += 5;}//食物消失则重新初始化if (!food.flag){srand(GetTickCount());//GetTickCount()获取系统开机到现在所需毫秒数food.x = rand() % 640;food.y = rand() % 480;food.color = RGB(rand() % 256, rand() % 256, rand() % 256);food.r = rand() % 10 + 10;food.flag = true;}
}//通过按键改变蛇的移动方向
void keyControl() {//判断有没有按键if (_kbhit()) {//头文件#include <conio.h>switch (_getch()) {case 'w':case 72:if (snake.dir != DOWN)snake.dir = UP; break;case 's':case 80:if (snake.dir != UP)snake.dir = DOWN; break;case 'a':case 75:if (snake.dir != RIGHT)snake.dir = LEFT; break;case 'd':case 77:if (snake.dir != LEFT)snake.dir = RIGHT; break;case ' '://游戏暂停while (1) {if (_getch() == ' ')return;}}}
}void EatSelf()
{for (int i = 1; i < snake.size; i++){if (snake.coor[0].x == snake.coor[i].x && snake.coor[0].y == snake.coor[i].y){outtextxy(200, 200, "Game over!");int a = _getch();  //保持停顿exit(0);}}
}int main() {GameInit();while (1) {GameDraw();snakemove();keyControl();eatfood();EatSelf();Sleep(50);}int b = _getch();closegraph();return 0;
}

总结

第一次完成一个小项目,乐趣还是很充分的,对c语言学习也更有兴趣了。

另一方面,做一个东西才发现自己的知识的薄弱,和平常做的各种题目的感觉完全不一样,太多太多你需要去学习的东西了,也就有了更大动力去学习吧。

但还是有一些遗憾,到底没有实现bgm的播放,死活放不出声音来,我也就没把代码逻辑放到上面了。

希望随着学习的深入能够回来再改改?

看有大佬15行写出一个贪吃蛇?我比他十倍还多一点,还是要继续学习呀,加油吧!

C语言编写贪吃蛇游戏相关推荐

  1. linux贪吃蛇c程序,Linux环境下C语言实现贪吃蛇游戏

    Linux环境下C语言实现贪吃蛇游戏 [liul@test snake]$ more snake.c #include #include #include #include #include #def ...

  2. 贪吃蛇c语言经典程序下载,C语言实现贪吃蛇游戏(单人版)

    本文实例为大家分享了C语言实现贪吃蛇游戏的具体代码,供大家参考,具体内容如下 相比于第一个:贪吃蛇游戏,功能更加丰富 #include"snake.h" //蛇的移动 void m ...

  3. Linux下编写贪吃蛇游戏

    Linux下编写贪吃蛇游戏 文章目录 Linux下编写贪吃蛇游戏 前言 一.贪吃蛇代码 二.运行贪吃蛇代码 前言 本程序需要ncurses库,ubuntu下安装ncurses可以执行下面命令: sud ...

  4. c语言贪吃蛇游戏的论文,基于c语言的贪吃蛇游戏论文.doc

    基于c语言的贪吃蛇游戏论文 第 PAGE \* Arabic \* MERGEFORMAT 15 页第 PAGE \* Arabic \* MERGEFORMAT 15 页 学校代码 学号 分 类 号 ...

  5. 详解Linux终端下编写“贪吃蛇”游戏

    大一学习C语言的时候就想要用Turbo C编写一个视频小游戏出来,种种原因后面搁浅了,现在借着学习Linux系统编程的劲头,编写了一个终端下可以运行的贪吃蛇游戏,其中此视频游戏用到的一些知识和操作系统 ...

  6. 纯C语言实现贪吃蛇游戏(VC6.0)

    转自C语言网 今天笔者为大家展示C语言写的贪吃蛇游戏,让大家玩一玩自己写的游戏~ 是纯C语言哦~VC6.0开发 无问题 首先,开始界面: 游戏界面如下: 代码如下: 笔者VC6.0下编写,测试无问题, ...

  7. 用C++语言实现贪吃蛇游戏

    最近更了个新的关于用C/C++写贪吃蛇的blog,感觉那个帮助会更大些,这篇中用DirectX技术不适合刚开始试着写游戏的同学,传送:https://blog.csdn.net/weixin_3781 ...

  8. 【C语言】贪吃蛇游戏的实现(一)

    (注意:本代码是在VC++6.0环境下编译的,在其他环境如codeblocks下运行可能会产生意想不到的问题,请尽量使用VC.至于为什么要使用VC编译,哦,我亲爱的朋友,这只有上帝才知道) 最近由于小 ...

  9. C语言之贪吃蛇游戏源码

    贪吃蛇游戏是我编程以来做的第一个游戏,很兴奋,我记得当时我一直最求代码数量的最小化,即以最少的代码实现功能,有的时候,对换行.括号之类的都下手,呵呵,现在想想都想笑,那些年让我纠结的代码门,你们现在还 ...

  10. python编写贪吃蛇游戏

    关于编写游戏,是博主非常向往的东西(博主喜爱游戏),编写游戏得一步一步的走!今天我简单的编写一下非常经典的游戏贪吃蛇!!!! 效果图: 首先引入pygame模块 pip install pygame ...

最新文章

  1. iOS封装HTTPS双向和单向验证
  2. ACM-最短路之中的一个个人的旅行——hdu2066
  3. ubuntu wps
  4. ⚡关于Eastmount博客「网络安全自学篇」系列重要通知!!!⚡
  5. C++中extern关键字使用 http://blog.csdn.net/sruru/article/details/7951019
  6. leetcode 473. Matchsticks to Square | 473. 火柴拼正方形(递归)
  7. Linux 每日一练习!!反单引号·(键盘上数字1左边)··
  8. [Todo] 乐观悲观锁,自旋互斥锁等等
  9. 简而言之,JUnit:另一个JUnit教程
  10. 进程锁 读写文件的小例子 C++代码
  11. 信息图:程序员/开发人员实际在用哪些工具
  12. EBGP邻居抖动问题
  13. Bad Hair Day(求数组中元素和它后面离它最近元素之间的元素个数)
  14. matlab中load seamout,matlab中的save与load函数
  15. java httpsession_JavaWeb:HttpSession
  16. python一个字符占几个字节_字符串在Python内部是如何省内存的
  17. 斯伦贝谢宣布2020年第三季度业绩
  18. python websockets 网络聊天室V1
  19. KOC十问:品牌缺钱的谎言,还是新瓶装旧酒?
  20. python京东抢购软件_福利来了,python 京东抢购茅台脚本(亲测可用)

热门文章

  1. 一款黑科技神器:uTools
  2. Easyrecovery激活码生成器下载地址?
  3. 使用 Abaqus 进行有限元分析
  4. 利用 USB转485通过Wireshark抓取MSTP数据
  5. 计算机组成:真正理解单周期处理器的“数据通路”
  6. 关于使用tess4j-OCR识别图片中文教程,亲测可用,不报错
  7. 【视频直播篇七】Aliplayer的使用
  8. android 圆形自定义进度条,自定义Android圆形进度条(附源码)
  9. 面试题-mysql优化
  10. 【C语言】筛选法求素数