效果图
说明
- 由于本人水平有限,算法并不完美,所以每次的运行结果都不尽相同,得分有高有低
- 本程序中使用ege图形库,所以如果你之前没有安装,需要先安装一下这个库。这个库相对于其它的图形库来说是比较简单的,很容易上手的。本程序只是使用了其中几个基础的绘图函数
- ege图形库的官网链接
源代码
#include <stdio.h>
// 这个就是 ege图形库的头文件
#include <graphics.h>
#include <time.h>
#include <conio.h>
#include <stdlib.h>
// 定义坐标
typedef struct {
int x;
int y;
}Pos;
// 定义蛇
struct Snake{
// 用数组来保存蛇身的每一块的坐标,最长100节
Pos xy[100];
int len; // 保存蛇身的长度
char dir; // 定义蛇的前进方向
}snake;
enum dir{up,down,left,right};
// 定义食物
struct Food{
Pos fdxy; // 食物的位置
int flag; // 蛇是否吃到了食物
int grade; // 定义食物的分数
}food;
// 定义一些全局变量
// 停顿时间,越小越快
int speed=300;
// 方块的大小
int size=10;
// 窗口的宽高
int width = 400;
int height = 300;
void initSnake() {
// 创建前三节
snake.xy[2].x = 0;
snake.xy[2].y = 0;
snake.xy[1].x = size;
snake.xy[1].y = 0;
snake.xy[0].x = 2*size;
snake.xy[0].y = 0;
snake.len = 3;
snake.dir = right;
}
void drawSnake() {
// 把蛇身绘制出来
setfillcolor(YELLOW);
bar(snake.xy[0].x, snake.xy[0].y, snake.xy[0].x + size, snake.xy[0].y + size);
bar(snake.xy[1].x, snake.xy[1].y, snake.xy[1].x + size, snake.xy[1].y + size);
for (int i = 2; i < snake.len; i++) {
//setcolor(BLACK);
setfillcolor(BLUE);
bar(snake.xy[i].x, snake.xy[i].y, snake.xy[i].x + size, snake.xy[i].y + size);
}
}
void moveSnake() {
// 除蛇头外的蛇身都向前移动
for (int i = snake.len - 1; i > 0; i--) {
snake.xy[i].x = snake.xy[i-1].x;
snake.xy[i].y = snake.xy[i-1].y;
}
// 根据前进方向决定蛇头的位置
switch (snake.dir) {
case up:
snake.xy[0].y -= size;
break;
case down:
snake.xy[0].y += size;
break;
case left:
snake.xy[0].x -= size;
break;
case right:
snake.xy[0].x += size;
break;
default:
break;
}
}
// 检测蛇是否撞到自己或墙
// 1:会撞到 0:撞不到
int check(Pos nextPos) {
int i;
for (i = 1; i < snake.len; i++) {
if (nextPos.x == snake.xy[i].x && nextPos.y == snake.xy[i].y) {
return 1;
}
}
if (nextPos.x<0 || nextPos.x>=width || nextPos.y<0 || nextPos.y>=height) {
return 1;
}
return 0;
}
//
void switchDir() {
Pos nextPos;
switch (snake.dir)
{
case up:
nextPos.x = snake.xy[0].x;
nextPos.y = snake.xy[0].y - size;
// 如果前面走不通或者食物出现在身后
if (check(nextPos) || food.fdxy.y > snake.xy[0].y) {
nextPos.x = snake.xy[0].x - size;
nextPos.y = snake.xy[0].y;
if (!check(nextPos)) {
snake.dir = left;
}
else {
snake.dir = right;
}
}
break;
case down:
nextPos.x = snake.xy[0].x;
nextPos.y = snake.xy[0].y + size;
if (check(nextPos) || food.fdxy.y < snake.xy[0].y) {
nextPos.x = snake.xy[0].x - size;
nextPos.y = snake.xy[0].y;
if (!check(nextPos)) {
snake.dir = left;
}
else {
snake.dir = right;
}
}
break;
case left:
nextPos.x = snake.xy[0].x - size;
nextPos.y = snake.xy[0].y;
if (check(nextPos) || food.fdxy.x > snake.xy[0].x) {
nextPos.x = snake.xy[0].x;
nextPos.y = snake.xy[0].y - size;
if (!check(nextPos)) {
snake.dir = up;
}
else {
snake.dir = down;
}
}
break;
case right:
nextPos.x = snake.xy[0].x + size;
nextPos.y = snake.xy[0].y;
if (check(nextPos) || food.fdxy.x < snake.xy[0].x) {
nextPos.x = snake.xy[0].x;
nextPos.y = snake.xy[0].y - size;
if (!check(nextPos)) {
snake.dir = up;
}
else {
snake.dir = down;
}
}
break;
}
}
// 根据食物的位置改变前进方向
void keyDown() {
// 获取按键
Pos nextPos;
if (snake.dir == left || snake.dir == right) {
if (food.fdxy.y < snake.xy[0].y) {
nextPos.x = snake.xy[0].x;
nextPos.y = snake.xy[0].y-size;
if (!check(nextPos)) {
snake.dir = up;
}
else {
snake.dir = down;
}
}
else if(food.fdxy.y > snake.xy[0].y){
nextPos.x = snake.xy[0].x;
nextPos.y = snake.xy[0].y+size;
if (!check(nextPos)) {
snake.dir = down;
}
else {
snake.dir = up;
}
}
else {
if (snake.xy[0].y <= 0) {
snake.dir = down;
}
if (snake.xy[0].y >= height-size) {
snake.dir = up;
}
switchDir();
}
}
else {
if (food.fdxy.x < snake.xy[0].x) {
nextPos.x = snake.xy[0].x - size;
nextPos.y = snake.xy[0].y;
if (!check(nextPos)) {
snake.dir = left;
}
else {
snake.dir = right;
}
}
else if(food.fdxy.x > snake.xy[0].x){
nextPos.x = snake.xy[0].x + size;
nextPos.y = snake.xy[0].y;
if (!check(nextPos)) {
snake.dir = right;
}
else {
snake.dir = left;
}
}
else {
if (snake.xy[0].x <= 0) {
snake.dir = right;
}
if (snake.xy[0].x >= width-size) {
snake.dir = left;
}
switchDir();
}
}
switchDir();
}
void initFood() {
// 随机数播种
srand(time(NULL));
// 生成随机坐标
food.fdxy.x = rand() % (width/size) * size;
food.fdxy.y = rand() % (height/size) * size;
// 判断食物是否与蛇身重合
for (int i = 0; i < snake.len; i++) {
if (snake.xy[i].x == food.fdxy.x && snake.xy[i].y == food.fdxy.y) {
// 重新生成坐标
food.fdxy.x = rand() % (width/size) * size;
food.fdxy.y = rand() % (height/size) * size;
}
}
// 蛇还没吃到食物
food.flag = 1;
}
void drawFood() {
setfillcolor(RED);
bar(food.fdxy.x, food.fdxy.y, food.fdxy.x+size, food.fdxy.y+size);
}
void eatFood() {
// 如果蛇头与食物重合,我们就认为蛇吃到了食物
if (snake.xy[0].x == food.fdxy.x && snake.xy[0].y == food.fdxy.y) {
// 蛇吃到了食物
food.flag = 0;
// 蛇身变长
snake.len++;
// 加分
food.grade += 10;
}
}
void showScore() {
LOGFONTA f;
getfont(&f); // 获取当前字体设置
f.lfHeight = 16;
setfont(&f);
char str[100] = "";
sprintf(str, "分数:%d", food.grade);
// 分数越高,速度越快,但不能小于50
if (speed > 50) {
speed -= 50 * (food.grade / 50);
}
setbkmode(TRANSPARENT);
setcolor(LIGHTGREEN);
outtextxy(300, 20, str);
}
// 判断蛇是否死亡,1代表死亡,0代表活着
int snakeDie() {
LOGFONTA f;
getfont(&f); // 获取当前字体设置
f.lfHeight = 48; // 设置字体高度为 48(包含行距)
f.lfWidth = 0;
strcpy(f.lfFaceName, "黑体"); // 设置字体为“黑体”
f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿
setfont(&f); // 设置字体样式
if (check(snake.xy[0])) {
outtextxy(width/2-120,height/2-24,"Game Over!!!");
return 1;
}
return 0;
}
int main() {
// 创建窗口
initgraph(width, height);
setcaption("欢迎来到贪吃蛇世界!!!");
// 设置背景颜色
setbkcolor(WHITE);
// 使用背景颜色清屏
cleardevice();
initSnake();
initFood();
while (1) {
drawSnake();
drawFood();
eatFood();
showScore();
if (snakeDie()) {
// 如果蛇死亡就结束循环
break;
}
// 如果蛇吃到了食物,重新生成食物
if (food.flag == 0) {
initFood();
}
keyDown();
moveSnake();
Sleep(speed);
// 刷新屏幕
cleardevice();
}
// conio.h 和 ege.h 中都定义了此函数,需要指明是那个文件中的
ege::getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
}