文章目录
头文件
1.写出图形化界面
2.绘制🐍
3.移动🐍
4.绘制球球
5.🐍吃球球
6.加上音乐(灵魂)
7.主函数
8.完整代码
小结
头文件
三个都是现学的,easyx图形库直接去网上搜下载安装到编译器就🆗,很容易上手,我习惯先去阅读官方文档,有没见过的查阅学习。
#include<stdio.h>//标准io #include<conio.h>//控制键盘操作 #include<graphics.h>//easyx图形库,创建图形化窗口 #include<stdlib.h>//本次主要用来生成随机数 #include<mmstream.h>//最后这两个,就是花里胡哨,可以播放音乐,升华 #pragma comment(lib ,"winmm.lib")
1.写出图形化界面
注释都写好了
#include<stdio.h> #include<graphics.h>//引入图形库 //需要知识:结构体,循环,函数,easyx图形库,结构体数组 int main() { initgraph(640,480);//init graph 初始化 图形窗口 while (1)//卡死 { } return 0; }
效果:
好ok,就是这样,第一步就完成了。加个背景颜色优化一下
//设置背景颜色 setbkcolor(RGB(82,143,22)); //清空绘图设备 cleardevice();
效果:
🆗,完成
2.绘制🐍
说一下,后来背景颜色我要调了一下,配色白痴。。。。。。浪费了可长时间,然后细节代码里都写注释了。
1,好习惯
2,方便学习和增删改查
#include<stdio.h> #include<graphics.h>//引入图形库 //需要知识:结构体,循环,函数,easyx图形库,结构体数组 //宏定义🐍的最大节数,宏定义建议大写,好的编码习惯 #define SNAKE_MAX 400 //结构体定义🐍的结构 struct Snake { int size;//节数 int speed;//速度 int dir;//方向 //坐标,定义好的结构体(x,y) POINT coor[SNAKE_MAX];//通过宏定义定义🐍的最大400节,想开挂,直接去修改宏定义 }snake; //数据初始化,用函数模块化编程思想 void Gameinit() { //init graph 初始化 图形窗口 initgraph(640, 480); //初始化🐍,开始游戏有3节 snake.size = 3; snake.speed = 10; snake.dir; //通过循环初始化出3段🐍 for (int i = snake.size; i >= 0; i--) { snake.coor[i].x = 10*i+40;//x坐标慢慢调试,合适位置 snake.coor[i].y = 30; } } //绘制,也单独写一个函数 void Gamedraw() { //设置背景颜色 setbkcolor(RGB( 193, 205, 193)); cleardevice(); //循环绘制3段🐍 setfillcolor(GREEN); for (int i = 0; i < snake.size; i++) { solidcircle(snake.coor[i].x, snake.coor[i].y, 5);//三个参数,x坐标,y坐标,半径 } EndBatchDraw(); } int main() { Gameinit(); Gamedraw(); while (1)//卡死 { } return 0; }
简简单单,无压力。最浪费我时间的就是逻辑判断和码字速度,需要多锻炼。😏
🆗看下效果:
3.移动🐍
初步效果:
很简单,只需要循环让三节在x轴++就实现了,但是有很多问题。
//移动🐍 void snakeMove() { for (int i = 0; i < snake.size; i++) { snake.coor[i].x++; } }
用枚举加按键改变方向,这里要引入#include<conio.h>//控制键盘操作这个头文件,这里需要注意_getch()是一个阻塞函数,我们通过函数_kbhit()判断有没有按键来解决,有按键就返回真就移动,这里入坑了,🆗没问题
#include<stdio.h> #include<conio.h>//控制键盘操作 #include<graphics.h>//引入图形界面的头文件 //需要知识:结构体,循环,函数,easyx图形库,结构体数组 //宏定义🐍的最大节数,宏定义建议大写,好的编码习惯 #define SNAKE_MAX 400 //🐍的方向通过枚举实现 enum DIR { UP, DOWN, LEFT, RIGHT, }; //结构体定义🐍的结构 struct Snake { int size;//节数 int speed;//速度 int dir;//方向 //坐标,定义好的结构体(x,y) POINT coor[SNAKE_MAX];//通过宏定义定义🐍的最大400节,想开挂,直接去修改宏定义 }snake; //数据初始化,用函数模块化编程思想 void Gameinit() { //init graph 初始化 图形窗口 initgraph(640, 480); //初始化🐍,开始游戏有3节 snake.size = 3; snake.speed = 10; //默认往右走 snake.dir=RIGHT; //通过循环初始化出3段🐍 for (int i = snake.size; i >= 0; i--) { snake.coor[i].x = 10*i+40;//x坐标慢慢调试,合适位置 snake.coor[i].y = 30; } } //绘制,也单独写一个函数 void Gamedraw() { //设置背景颜色 setbkcolor(RGB(193, 205, 193)); cleardevice(); //循环绘制3段🐍 setfillcolor(GREEN); for (int i = 0; i < snake.size; i++) { solidcircle(snake.coor[i].x, snake.coor[i].y, 5);//三个参数,x坐标,y坐标,半径 } EndBatchDraw(); } //移动🐍 void snakeMove() { for (int i = 0; i < snake.size; i++) { switch (snake.dir) { case UP: snake.coor[i].y-=snake.speed; break; case DOWN: snake.coor[i].y+=snake.speed; break; case LEFT: snake.coor[i].x-=snake.speed; break; case RIGHT: snake.coor[i].x+=snake.speed; break; } } } //通过按键改变蛇的方向(上下左右),需要头文件#include<conio.h>//控制键盘操作 //72 80 75 77上下左右键对应的数字,可以通过这些数字确定键 //_getch()是一个阻塞函数,我们通过函数_kbhit()判断有没有按键来解决,有按键就返回真,就移动 void keyControl() { if (_kbhit()) { switch (_getch()) { case 'W': case'w': case 72: //移动上面已经写好了,在这就直接用 snake.dir = UP; break; case 'S': case's': case 80: snake.dir = DOWN; break; case 'a': case'A': case 75: snake.dir = LEFT; break; case 'D': case'd': case 77: snake.dir = RIGHT; break; } } } int main() { Gameinit(); while (1)//卡死 { Gamedraw(); snakeMove(); keyControl(); Sleep(10); } return 0; }
实现效果:
可以看到这……遇到Bug了。
分析了一下,你看它能左右能上下,什么怪物,所以,先让身体跟着头移动
//移动🐍 void snakeMove() { //-1防止数组越界 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; break; case DOWN: snake.coor[0].y+= snake.speed; break; case LEFT: snake.coor[0].x-= snake.speed; break; case RIGHT: snake.coor[0].x+= snake.speed; break; } }
果然,现在还有问题,发现它是没有头的开了挂的🐍,向上直接能变成向下,左右也是
这时候再用 if 判断语句轻松实现上了不能下,左了不能右,试一下:
//如果不👇那就👆,乖乖听话 if (snake.dir != DOWN) { snake.dir = UP; } break; case 'S': case's': case 80: //如果不👆那就👇,乖乖听话 if (snake.dir != UP) { snake.dir = DOWN; } break; case 'a': case'A': case 75: //如果不👉那就👈,乖乖听话 if (snake.dir != RIGHT) { snake.dir = LEFT; } break; case 'D': case'd': case 77: //如果不👈那就👉,乖乖听话 if (snake.dir != LEFT) { snake.dir = RIGHT; } break; }
好,🆗啊,简简单单。看下效果:
Good,好像有点样了
但是这个一出墙就不见了,我技术菜,我想让它无敌,上边进下边出,坐进又出。怎么实现?思考一下……
对移动部分的部分的函数做以下判断即可
普通模式: 如果蛇头在y轴坐标小于等于0,就说明已经碰到墙,可以直接结束游戏(不适合我)
开挂模式:如果撞墙把它坐标修改一下就实现了(适合我)
switch (snake.dir) { case UP: snake.coor[0].y-= snake.speed; //蛇头在y轴<等你0了,说明撞到墙了 if (snake.coor[0].y -5 <= 0) { snake.coor[0].y = 480; } break; case DOWN: snake.coor[0].y+= snake.speed; if (snake.coor[0].y +5>= 480) { snake.coor[0].y = 0; } break; case LEFT: snake.coor[0].x-= snake.speed; if (snake.coor[0].x +5 <=0) { snake.coor[0].x = 640; } break; case RIGHT: snake.coor[0].x+= snake.speed; if (snake.coor[0].x-5>= 640) { snake.coor[0].x = 0; } break; }
4.绘制球球
首先定义食物的结构体
//结构体定义食物的结构 struct food { int x;//食物坐标x int y;//食物坐标y int r;//食物半径r bool flag; //布尔类型,1没有吃 0吃了 DWORD color; //color }food;
然后进行初始化(随机数终于派上用场了😎)
//food初始化(用到随机函数)虚拟随机(如果没有随机数种子则产生的一样) //设置种子需要头文件stdlib.h food.x = rand() % 640;//初始化x food.y = rand() % 480;//初始化y food.color = RGB(rand() % 256, rand() % 256, rand() % 256);//分别初始化rgb的三个元素 food.r = rand() % 10 + 2;//半径随机 food.flag = true;//开局就生成食物
然后绘制,很简单,只需要判断是1就画
//绘制食物 if (food.flag) { solidcircle(food.x, food.y, food.r); }
好,🆗
5.🐍吃球球
//判断吃食物 void eat() { 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++; }
吃完再生成
if (!food.flag) { food.x = rand() % 640;//随机生成一个整数,如果没有随机数种子,每次都是固定的。 food.y = rand() % 480;//设置种子需要头文件stdlib.h food.color = RGB(rand() % 256, rand() % 256, rand() % 256); food.r = rand() % 10 + 5; food.flag = true; } }
6.加上音乐(灵魂)
头文件
#include<mmstream.h> #pragma comment(lib ,"winmm.lib")//多媒体设备接口
这些都是固定的,是不是很简单呢
//播放贪吃蛇MP3,固定格式 mciSendString("open 贪吃蛇.mp3 alias BEIJING", 0, 0, 0); mciSendString("play BEIJING repeat", 0, 0, 0);
7.主函数
int main() { Gameinit(); while (1) { Gamedraw(); snakeMove(); keyControl(); eat(); Sleep(60); } return 0;
8.完整代码
/* =====贪====吃=====🐍=======小=======游======戏====== @author:周棋洛 @版本号:1.0 @date:2021.7.9 */ #include<stdio.h> #include<conio.h>//控制键盘操作 #include<graphics.h>//引入图形界面的头文件 #include<stdlib.h> #include<Windows.h> #include<mmstream.h> #pragma comment(lib ,"winmm.lib")//多媒体设备接口 //需要知识:结构体,循环,函数,easyx图形库,结构体数组 //宏定义🐍的最大节数,宏定义建议大写,好的编码习惯 #define SNAKE_MAX 400 //🐍的方向通过枚举实现 enum DIR { UP, DOWN, LEFT, RIGHT, }; //结构体定义🐍的结构 struct Snake { int size;//节数 int speed;//速度 int dir;//方向 //坐标,定义好的结构体(x,y) POINT coor[SNAKE_MAX];//通过宏定义定义🐍的最大400节,想开挂,直接去修改宏定义 }snake; //结构体定义食物的结构 struct food { int x;//食物坐标x int y;//食物坐标y int r;//食物半径r bool flag; //布尔类型,1没有吃 0吃了 DWORD color; //color }food; //数据初始化,用函数模块化编程思想 void Gameinit() { //播放贪吃蛇MP3,固定格式 mciSendString("open 贪吃蛇.mp3 alias BEIJING", 0, 0, 0); mciSendString("play BEIJING repeat", 0, 0, 0); //init graph 初始化 图形窗口 initgraph(640, 480); //设置随机数种子(开机到目前的时间的毫秒) srand(GetTickCount()); //初始化🐍,开始游戏有3节 snake.size = 3; snake.speed = 10; //默认往右走 snake.dir=RIGHT; //通过循环初始化出3段🐍 for (int i = snake.size; i >= 0; i--) { snake.coor[i].x = 10*i+40;//x坐标慢慢调试,合适位置 snake.coor[i].y = 30; } //food初始化(用到随机函数)虚拟随机(如果没有随机数种子则产生的一样) //设置种子需要头文件stdlib.h food.x = rand() % 600;//初始化x food.y = rand() % 440;//初始化y food.color = RGB(rand() % 256, rand() % 256, rand() % 256);//分别初始化rgb的三个元素 food.r = rand() % 15 + 20;//半径随机 food.flag = true;//开局就生成食物 } //绘制,也单独写一个函数 void Gamedraw() { //设置背景颜色 setbkcolor(RGB(193, 205, 193)); cleardevice(); //循环绘制3段🐍 setfillcolor(GREEN); for (int i = 0; i < snake.size; i++) { solidcircle(snake.coor[i].x, snake.coor[i].y, 5);//三个参数,x坐标,y坐标,半径 } //绘制食物 if (food.flag) { solidcircle(food.x, food.y, food.r); } EndBatchDraw(); } //移动🐍 void snakeMove() { //-1防止数组越界 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; //蛇头在y轴<等你0了,说明撞到墙了 if (snake.coor[0].y -5 <= 0) { snake.coor[0].y = 480; } break; case DOWN: snake.coor[0].y+= snake.speed; if (snake.coor[0].y +5>= 480) { snake.coor[0].y = 0; } break; case LEFT: snake.coor[0].x-= snake.speed; if (snake.coor[0].x +5 <=0) { snake.coor[0].x = 640; } break; case RIGHT: snake.coor[0].x+= snake.speed; if (snake.coor[0].x-5>= 640) { snake.coor[0].x = 0; } break; } } //通过按键改变蛇的方向(上下左右),需要头文件#include<conio.h>//控制键盘操作 //72 80 75 77上下左右键对应的数字,可以通过这些数字确定键 //_getch()是一个阻塞函数,我们通过函数_kbhit()判断有没有按键来解决,有按键就返回真,就移动 void keyControl() { if (_kbhit()) { switch (_getch()) { case 'W': case'w': case 72: //移动上面已经写好了,在这就直接用 //如果不👇那就👆,乖乖听话 if (snake.dir != DOWN) { snake.dir = UP; } break; case 'S': case's': case 80: //如果不👆那就👇,乖乖听话 if (snake.dir != UP) { snake.dir = DOWN; } break; case 'a': case'A': case 75: //如果不👉那就👈,乖乖听话 if (snake.dir != RIGHT) { snake.dir = LEFT; } break; case 'D': case'd': case 77: //如果不👈那就👉,乖乖听话 if (snake.dir != LEFT) { snake.dir = RIGHT; } break; //游戏暂停⏸ case ' ': while (1) { if (_getch() == ' ') return; } break; } } } //判断吃食物 void eat() { 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++; } if (!food.flag) { food.x = rand() % 640;//随机生成一个整数,如果没有随机数种子,每次都是固定的。 food.y = rand() % 480;//设置种子需要头文件stdlib.h food.color = RGB(rand() % 256, rand() % 256, rand() % 256); food.r = rand() % 10 + 5; food.flag = true; } } int main() { Gameinit(); while (1) { Gamedraw(); snakeMove(); keyControl(); eat(); Sleep(60); } return 0; }
好,🆗到这里呢,一个简单的小游戏就完成了。实践之后才发现原来你觉得很简单的东西也是一点一点实现的。还是那句话,不积小流无以成江海小流,不积跬步无以至千里。加油吧,总之这个实战的逻辑很简单。可以试着来自己体验一下,后续可以自己做优化。你会有很多感悟和收获。撒花★,°:.☆( ̄▽ ̄)/$:.°★ 。
小结
对与C又有了新的理解,封装函数,模块化设计,化繁为简,一步步实现简单的最后复杂问题就迎刃而解。
感觉C特别强,一个字,牛,虽然我现在学的浅,随着深入学习,我觉得C很棒,感觉你随便给一些合理的需求,C都可以满足你,呃~~,图形化啊,这个,这个,可以借助库嘛,哈哈
编写程序前,弄清你的需求,不盲目,有需求后,弄清逻辑关系,想到代码的封装以及重要的逻辑处理和编写,锻炼思维,秃头警告⚠
编码过程中,多多少少碰到Bug,这就是修炼之路,先自己想实践看看可不可以解决,不行借助外力干掉bug(我们既是bug的生产者,也是bug的终结者),对代码负责,对自己负责。哈哈
通过有趣的栗子激发自己,促进学习。生活很苦,但你很甜。
🆗,不bb了,打🐏( ̄o ̄) . z Z