##第一个小项目 大一学生寒假项目
最终实现效果如图
一.以C语言实现个人小项目
在我们快速学完了一个高级编程语言,就应该写一个小项目来加以巩固自己的学习成果。
所以今天,我们来尝试写一写greedy snake,对于大学生来说也是可以加强能力的存在。事不宜迟,我们开始吧。(Tips:我会加入一些外部链接,这些于编写遇到的问题有关,可以帮助你更好的解决)
外部文件的引入以及一些定义
#include <stdio.h>
#include <graphics.h> //与绘图文件有关
#include <conio.h> //两个都需要
#include <stdlib.h>
#define SNAKE_NUM 500 //宏定义 规定好蛇的最大节数,以防溢出
enum DIR //枚举初始化方向
{UP,DOWN,LEFT,RIGHT,
};
解释:
我们在第四行引入的头文件是为了产生一个生成随机数的种子(所以我们引入了该头文件)
5函数名称: rand
函数原型: int rand(void);
函数功能: 产生0到32767间的随机整数(0到0x7fff之间)
函数返回: 随机整数
对于第二,第三行的头文件引入。
我在这里放好了它的下载官网,官网有下载说明,不复杂的
easyx文档 点击此处
二.蛇和食物的结构体定义
struct Snake
{int size; //蛇的长度int dir; //蛇的方向int speed; //蛇的速度POINT coor[SNAKE_NUM]; //坐标
}snake;struct food
{int x;int y;int r; //食物大小bool flag; //为后面判断食物是否被吃做准备DWORO color; //规定食物的颜色
}food;
在这里,我们注意一下 POINT coor[] 是我们引入了相应的头文件,以有表示二维坐标的
函数。 后续创建实体的写法是---> snake[i].x = 坐标点 其中i是它的大小。
并且,这里的坐标轴与数学中的有些不同,y轴的正方向是朝下的,所以我们想让蛇向上移动最好是snake[i].y -= speed .这样蛇就是上移。
对于food(食物处),我们定义的x,y是为后续的随机坐标做好准备的。
三.数据的初始化
void GameInit()
{//init;初始化 graph 图形窗口initgraph(640,480);//设置随机数种子 GetTickCount 获取系统开机到当前运行所经过的时间srand(GetTickCount());//初始化蛇 一开始就有n节 n随你定义 这里我是3snake.size = 3;snake.speed = 5;snake.dir = RIGHT;//循环 这里是初始化蛇的位置 for (int i = 0; i < snake.size; i++) {snake.coor[i].x = 40 - 10*isnake.coor[i].y = 10;}//初始化食物,rand()是随机函数 来自头文件<stdlib.h>//一般把时间作为种子,时间在变化food.x = rand() % 640; // % n ---> 可以得到 0-(n-1)的数food.y = rand() % 480;food.color = RGB(rand() % 256, rand() % 256, rand() % 256);food.r = rand() % 10 + 5; //随机这个食物的半径 最小半径为5food.flag = true;
}
初始化的循环是为了 让蛇排列成行。 下标为0 是蛇头
四.图的绘制
void GameDraw()
{//双缓冲绘图 ---> 原因是会卡频//设置背景颜色setbkcolor(RGB(28,115,119)); //颜色可以按自己喜好的来cleardevice(); //清空绘图设备//绘制蛇setfillcolor(GREEN);for (int i = 0; i < snake.size; i++) {solidcircle(snake.coor[i].x,snake.coor[i].y, 5); //半径为5}//绘制食物if (food.flag){solidcircle(food.x, food.y, food.r);}EndBatchDraw();
}
1.头文件graphics.h包含cleardevice()函数,该函数在图形模式下清除屏幕并将当前位置设置为(0,0)。清除屏幕包括用当前背景色填充屏幕。
2.if条件判断是为了判断食物是否被吃 后刷新食物。
3.EndBatchDraw();---相关官方文档在这easyx文档
五.蛇的移动
//移动蛇
void snakemove()
{//移动是什么发生改变;坐标改变for (int i = snake.size; 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+10 <= 0) {snake.coor[0].y = 640;}//超出了上面的边界break;}switch (snake.dir){case DOWN:snake.coor[0].y+=snake.speed;if (snake.coor[0].y - 10 >= 640) {snake.coor[0].y = 0;}break;}switch (snake.dir){case LEFT:snake.coor[0].x-=snake.speed;if (snake.coor[0].x + 10 <= 0) {snake.coor[0].x = 640;}break;}switch (snake.dir){case RIGHT:snake.coor[0].x+=snake.speed;if (snake.coor[0].x - 10 >= 640) {snake.coor[0].x = 0;}break;}}
六.keyborad (键盘控制)
//通过按键操作
void keyControl()
{//判断有无按键if (_kbhit()) //检测是否有按键{//72 80 75 77 上下左右键值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;}}
}
对于_kbhit() --->判断是否keyboard被按动 并返回bool值
_getch() ---> 获取按键信息
七.吃食物
void EatFood()
{if (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;food.color = RGB(rand() % 256, rand() % 256, rand() % 256);food.r = rand() % 10 + 5;food.flag = true;}/*可以加一个分数,吃一个食物,加n分*/
}
TIPS:
这里的两个if判断语句
对于第一个if,是判定什么时候什么范围,蛇将这个食物给吃了 可以同过点到圆心的距离
即 d <= r(该圆半径) (d为距离---为两个圆心的距离) 此时,两个圆相交便为吃了,否则,玩家的操作系数上升,不适宜游玩。
对于第二个if,是为了刷新被吃掉的食物。
八.游戏暂停
void stop()
{//判断是否键盘按动 和 是否为空格键if (_kbhit() && _getch() == ' '){while(1){if (_getch() == ' '){break;}}}
}
原理是:我们写一个死循环 while(1)---来占用进程,以达到暂停效果。 里面还有一个if判断是为了用户在按下一次的空格键下,打破循环,继续游戏。
二.主函数实现最终效果
int main()
{//在主函数调用,出现初始化结果GameInit();//防止闪退while(1) {GameDraw();snakemove();stop();keyControl();EatFood();Sleep(30); //防止太快移动,延迟}return 0;
}
注意,这里的stop()---游戏暂停函数一定要放到前面,否则会达不到暂停的效果。
在我们完成这些代码,F5调试时,可能有些人的蛇无法移动。
解决方案是:在shell(控制面板)来操控蛇的移动。就是那个黑框框。
也有不用在这个shell里面操作的方案,但是操作起来有些麻烦,所以还是用这个简单些的方案好一些。
最后我将这个project的源码放入,感兴趣的同学也可以通过这以上的代码自行输入,会有很多感悟。
谢谢。