大一上课设——贪吃蛇(easyx + 链表

  • 目录
    • 0.素材准备
    • 1.设计思路
    • 2.创建开始界面
    • 3.绘制游戏界面
    • 4.初始化蛇身
    • 6.键盘信息获取
    • 7.蛇的移动
    • 8.死亡判断
    • 9.额外功能
      • 1.加减速
      • 2.历史最高分的记录
      • 3.音乐播放
    • 10.参考博客
    • 11.完整代码

目录

0.素材准备

该文章写于大一上学期末,是本人的总结与复习,如有不合理或错误的地方,也请诸位指正。
语言:c语言
ide:vs2019
图形库:easyx 下载地址:link.
以上为本程序的所需素材。

1.设计思路

2.创建开始界面

首先创造欢迎界面
效果图:

以下是代码:

//绘制开始界面
int welcomegraph(void)
{initgraph(850, 600);int t;float H = 190, S = 1, LIGHT = 0.7f;       //H 色相:S 饱和度:L 亮度wchar_t s[] = L"贪吃蛇";wchar_t s1[] = L"V1.0";wchar_t s2[] = L"by TMY";IMAGE img, img1, apple, apple1;MOUSEMSG m;//鼠标指针int event = 0, i;if (flag == 0){t = 1;}else{t = 0;}for (int i = 0; i < 256; i += 5){setbkcolor(RGB(i, i, i));       //设置背景色,原来默认黑色cleardevice();                 //清屏(取决于背景色)Sleep(t * 15);}for (int i = 0; i < 600; i++){LIGHT += 0.0005f;setlinecolor(HSLtoRGB(H, S, LIGHT));line(0, i, 849, i);}loadimage(&apple, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\apple.jpg"), 100, 100);   //图片背景透明loadimage(&apple1, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\applex.jpg"), 100, 100);SetWorkingImage(&apple);putimage(0, 0, &apple1, SRCINVERT);SetWorkingImage(NULL);putimage(600, 100, &apple1, SRCAND);putimage(600, 100, &apple, SRCPAINT);loadimage(&img, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\界面.jpg"), 100, 100);   //图片背景透明loadimage(&img1, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\界面1.jpg"), 100, 100);SetWorkingImage(&img);putimage(0, 0, &img1, SRCINVERT);SetWorkingImage(NULL);putimage(80, 100, &img1, SRCAND);putimage(80, 100, &img, SRCPAINT);setbkmode(TRANSPARENT);                //文本透明settextcolor(RGB(255, 0, 255));settextstyle(120, 60, _T("微软雅黑"));outtextxy(180, 80, s);settextstyle(30, 15, _T("微软雅黑"));settextcolor(BLACK);outtextxy(0, 550, s1);outtextxy(680, 550, s2);//以下代码参考于另一篇文章,详见文末setbkmode(TRANSPARENT);                //文本透明settextstyle(30, 15, _T("微软雅黑"));settextcolor(BLACK);                                                 drawtext(_T("开始游戏"), &R1, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域内输入文字,水平居中,垂直居中,单行显示drawtext(_T("游戏说明"), &R2, DT_CENTER | DT_VCENTER | DT_SINGLELINE);drawtext(_T("退出"), &R3, DT_CENTER | DT_VCENTER | DT_SINGLELINE);  setlinecolor(BLACK);rectangle(r[0][0], r[0][1], r[0][2], r[0][3]);rectangle(r[1][0], r[1][1], r[1][2], r[1][3]);rectangle(r[2][0], r[2][1], r[2][2], r[2][3]);while (true){m = GetMouseMsg();//获取一条鼠标消息switch (m.uMsg){case WM_MOUSEMOVE:setrop2(R2_XORPEN);setlinecolor(LIGHTCYAN);//线条颜色为亮青色setlinestyle(PS_SOLID, 3);//设置画线样式为实现,10磅setfillcolor(WHITE);//填充颜色为白色if (button_judge(m.x, m.y) != 0){if (event != button_judge(m.x, m.y)){event = button_judge(m.x, m.y);//记录这一次触发的按钮fillrectangle(r[event - 1][0], r[event - 1][1], r[event - 1][2], r[event - 1][3]);//有框填充矩形(X1,Y1,X2,Y2)banana(event);}}else{if (event != 0)//上次触发的按钮未被修正为原来的颜色{fillrectangle(r[event - 1][0], r[event - 1][1], r[event - 1][2], r[event - 1][3]);//两次同或为原来颜色event = 0;banana(0);}}break;case WM_LBUTTONDOWN:PlaySound(TEXT("click.wav"), 0, SND_FILENAME | SND_ASYNC);   //鼠标点击音效setrop2(R2_NOTXORPEN);//二元光栅——NOT(屏幕颜色 XOR 当前颜色)for (i = 0; i <= 10; i++){setlinecolor(RGB(25 * i, 25 * i, 25 * i));//设置圆颜色circle(m.x, m.y, 2 * i);Sleep(30);//停顿30mscircle(m.x, m.y, 2 * i);//抹去刚刚画的圆}//判断事件switch (button_judge(m.x, m.y)){//复原按钮原型case 1:return 1;FlushMouseMsgBuffer();//单击事件后清空鼠标消息break;case 2:return 2;FlushMouseMsgBuffer();//单击事件后清空鼠标消息break;case 3:closegraph();//关闭绘图环境exit(0);//正常退出default:FlushMouseMsgBuffer();//单击事件后清空鼠标消息//printf("\r\n(%d,%d)",m.x,m.y);//打印鼠标坐标,方便调试时确定区域break;}break;FlushMouseMsgBuffer();//清空鼠标消息缓存区}}
}

接下来我会一部分一部分的分析我的代码。
初始化背景、加载图片和输出文本。

 initgraph(850, 600);int t;float H = 190, S = 1, LIGHT = 0.7f;        //H 色相:S 饱和度:L 亮度wchar_t s[] = L"贪吃蛇";wchar_t s1[] = L"V1.0";wchar_t s2[] = L"by TMY";IMAGE img, img1, apple, apple1;MOUSEMSG m;//鼠标指针int event = 0, i;if (flag == 0){t = 1;}else{t = 0;}for (int i = 0; i < 256; i += 5){setbkcolor(RGB(i, i, i));       //设置背景色,原来默认黑色cleardevice();                 //清屏(取决于背景色)Sleep(t * 15);}for (int i = 0; i < 600; i++){LIGHT += 0.0005f;setlinecolor(HSLtoRGB(H, S, LIGHT));line(0, i, 849, i);}loadimage(&apple, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\apple.jpg"), 100, 100);   //图片背景透明loadimage(&apple1, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\applex.jpg"), 100, 100);SetWorkingImage(&apple);putimage(0, 0, &apple1, SRCINVERT);SetWorkingImage(NULL);putimage(600, 100, &apple1, SRCAND);putimage(600, 100, &apple, SRCPAINT);loadimage(&img, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\界面.jpg"), 100, 100);   //图片背景透明loadimage(&img1, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\界面1.jpg"), 100, 100);SetWorkingImage(&img);putimage(0, 0, &img1, SRCINVERT);SetWorkingImage(NULL);putimage(80, 100, &img1, SRCAND);putimage(80, 100, &img, SRCPAINT);setbkmode(TRANSPARENT);                //文本透明settextcolor(RGB(255, 0, 255));settextstyle(120, 60, _T("微软雅黑"));outtextxy(180, 80, s);settextstyle(30, 15, _T("微软雅黑"));settextcolor(BLACK);outtextxy(0, 550, s1);outtextxy(680, 550, s2);

flag是判断进入界面次数的标志,只有第一次进入欢迎界面会产生渐渐明亮的效果。
loadimage分别加载了apple和apple1,再将二者叠加放置,就可以实现背景颜色透明的效果。
代码中图片文件的加载使用的是文件地址的方式,这种方式并不好,还有其他方式可以实现,但我还并不了解,有兴趣的,可以自行百度。

绘制按钮,获取鼠标消息

setbkmode(TRANSPARENT);                //文本透明settextstyle(30, 15, _T("微软雅黑"));settextcolor(BLACK);                                                 drawtext(_T("开始游戏"), &R1, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域内输入文字,水平居中,垂直居中,单行显示drawtext(_T("游戏说明"), &R2, DT_CENTER | DT_VCENTER | DT_SINGLELINE);drawtext(_T("退出"), &R3, DT_CENTER | DT_VCENTER | DT_SINGLELINE);  setlinecolor(BLACK);rectangle(r[0][0], r[0][1], r[0][2], r[0][3]);rectangle(r[1][0], r[1][1], r[1][2], r[1][3]);rectangle(r[2][0], r[2][1], r[2][2], r[2][3]);while (true){m = GetMouseMsg();//获取一条鼠标消息switch (m.uMsg){case WM_MOUSEMOVE:setrop2(R2_XORPEN);setlinecolor(LIGHTCYAN);//线条颜色为亮青色setlinestyle(PS_SOLID, 3);//设置画线样式为实现,10磅setfillcolor(WHITE);//填充颜色为白色if (button_judge(m.x, m.y) != 0){if (event != button_judge(m.x, m.y)){event = button_judge(m.x, m.y);//记录这一次触发的按钮fillrectangle(r[event - 1][0], r[event - 1][1], r[event - 1][2], r[event - 1][3]);//有框填充矩形(X1,Y1,X2,Y2)banana(event);}}else{if (event != 0)//上次触发的按钮未被修正为原来的颜色{fillrectangle(r[event - 1][0], r[event - 1][1], r[event - 1][2], r[event - 1][3]);//两次同或为原来颜色event = 0;banana(0);}}break;case WM_LBUTTONDOWN:PlaySound(TEXT("click.wav"), 0, SND_FILENAME | SND_ASYNC);   //鼠标点击音效setrop2(R2_NOTXORPEN);//二元光栅——NOT(屏幕颜色 XOR 当前颜色)for (i = 0; i <= 10; i++){setlinecolor(RGB(25 * i, 25 * i, 25 * i));//设置圆颜色circle(m.x, m.y, 2 * i);Sleep(30);//停顿30mscircle(m.x, m.y, 2 * i);//抹去刚刚画的圆}//判断事件switch (button_judge(m.x, m.y)){//复原按钮原型case 1:return 1;FlushMouseMsgBuffer();//单击事件后清空鼠标消息break;case 2:return 2;FlushMouseMsgBuffer();//单击事件后清空鼠标消息break;case 3:closegraph();//关闭绘图环境exit(0);//正常退出default:FlushMouseMsgBuffer();//单击事件后清空鼠标消息//printf("\r\n(%d,%d)",m.x,m.y);//打印鼠标坐标,方便调试时确定区域break;}break;FlushMouseMsgBuffer();//清空鼠标消息缓存区}}
}

以上代码来自 link
这位作者写的很详细。
我在其中加入了banana这一函数,实现当鼠标移至按钮时,在指定位置产生一张香蕉图片,清除图片则是通过从背景中获取相同大小的图片,覆盖在原图片上来实现(这一功能我想了很久,easyx中似乎没有可以清除加载图片的函数,因此我通过这一方式实现,但这也要求我的背景必须是纯色,如果有读者了解其他方式,也希望您能指点一番,不胜感激)

3.绘制游戏界面

效果图:

代码如下

//地图绘制
void drawmap(void)
{int i;IMAGE box;setbkcolor(RGB(240,230,140));cleardevice();loadimage(&box, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\墙.jpg"), 40, 40);for (i = 0; i < 600; i += 40)  //生成墙{putimage(0, i, &box, SRCINVERT);putimage(480, i, &box, SRCINVERT);}for (i = 40; i < 480; i += 40){putimage(i, 0, &box, SRCINVERT);putimage(i, 560, &box, SRCINVERT);}
}
//分数
void score(void)
{IMAGE a;wchar_t s[] = L"分数";wchar_t s2[] = L"最高分";TCHAR s1[5];TCHAR m1[5];int m;m = getMAX();SetWorkingImage(NULL);getimage(&a, 600, 300, 200, 200);putimage(550, 120, &a);setbkmode(TRANSPARENT);                //文本透明settextcolor(BLACK);settextstyle(40, 20, _T("微软雅黑"));outtextxy(550, 120, s);settextstyle(40, 20, _T("微软雅黑"));outtextxy(700, 120, s2);_stprintf_s(s1, _T("%d"), number);       settextcolor(RED);settextstyle(60, 30, _T("微软雅黑"));outtextxy(550, 200, s1);_stprintf_s(m1, _T("%d"), m);settextcolor(GREEN);outtextxy(680, 200, m1);
}

4.初始化蛇身

代码:

void initsnake(void)
{snake* tail;int i;int k = 0;tail = (snake *)malloc(sizeof(snake));   //从蛇尾开始,头插法,以x,y设定开始的位置tail->next = NULL;tail->x = 290;           tail->y = 350;for (i = 1; i <= 2; i++)       //设置蛇身,长度为3{head = (snake *)malloc(sizeof(snake));  //初始化蛇头head->next = tail;                       //蛇头的下一位为蛇尾head->x = 290 + 20 * i;                 //设置蛇头位置head->y = 350;tail = head;                             //蛇头变成蛇尾,然后重复循环}while (tail != NULL)                            //从头到尾,输出蛇身{if (k == 0){setfillcolor(BLUE);}else{setfillcolor(RED);}fillrectangle(tail->x - 10, tail->y + 10, tail->x + 10, tail->y - 10);      //输出蛇身tail = tail->next;   k++;}
}
```通过创建链表来存储蛇每一节身体的坐标,通过遍历链表来打印蛇身(此处的蛇身我是通过easyx里的函数来绘制长方形,应该可以通过加载图片来使画面更加好看,感兴趣的读者可以自行尝试)
## 5.随机生成食物
代码
```cpp
void food(void)
{srand((unsigned int)time(NULL));int i, j;IMAGE apple, apple1;i = rand() % 22 + 2;j = rand() % 26 + 2;q = head;while (q->next == NULL){if (q->x == i * 20 && q->y == j * 20) //判断蛇身是否与食物重合{food();                          //重新创建食物}q = q->next;}foodxy = (snake*)malloc(sizeof(snake));if (foodxy == NULL) {printf("内存分配不成功!\n");}else{foodxy->x = i * 20;foodxy->y = j * 20;}loadimage(&apple, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\apple.jpg"), 20, 20);   //图片背景透明loadimage(&apple1, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\apple.jpg"), 20, 20);SetWorkingImage(&apple);putimage(0, 0, &apple1, SRCINVERT);SetWorkingImage(NULL);putimage(i * 20, j * 20, &apple1, SRCAND);putimage(i * 20, j * 20, &apple, SRCPAINT);
}

通过rand函数来随机生成两个指定范围内的随机数,乘以系数,得到食物的坐标,再判断食物的坐标是否与蛇的坐标重合,重合则再次调用food函数,重新生成一组随机数,当不与蛇的坐标重合时,则在指定位置加载食物图片。(在实际运行过程中,有较低概率会出现食物不生成,我推测可能是因为食物生成在界面以外,但找不到原因,如果有读者知道哪里出错了,希望您不吝赐教)

6.键盘信息获取

代码:

void keyboardControl()
{click = R;  //初始蛇向右移动while (1){if (GetAsyncKeyState(VK_UP) && click != D)            //GetAsyncKeyState函数用来判断函数调用时指定虚拟键的状态{click = U;           //如果蛇不是向下前进的时候,按上键,执行向上前进操作}else if (GetAsyncKeyState(VK_DOWN) && click != U)     //如果蛇不是向上前进的时候,按下键,执行向下前进操作{click = D;}else if (GetAsyncKeyState(VK_LEFT) && click != R)      //如果蛇不是向右前进的时候,按左键,执行向左前进{click = L;}else if (GetAsyncKeyState(VK_RIGHT) && click != L)     //如果蛇不是向左前进的时候,按右键,执行向右前进{click = R;}if (GetAsyncKeyState(VK_SPACE))      //按暂停键,执行pause暂停函数{while (1){Sleep(300); if (GetAsyncKeyState(VK_SPACE))      //按空格键暂停{break;}}}else if (GetAsyncKeyState(VK_F1))    //按F1键,加速{speed_up();}else if (GetAsyncKeyState(VK_F2))    //按F2键,减速{speed_down();}Sleep(sleeptime);snake_move();}
}

通过getasynckeystate函数判断按下的按钮,从而实现蛇方向的变化,暂停,加减速等功能。

7.蛇的移动

代码

//蛇的移动
void snake_move(void)
{snake* nexthead;nexthead = (snake*)malloc(sizeof(snake));switch (click){case U:nexthead->x = head->x;        //向上前进时,x坐标不动,y坐标-20nexthead->y = head->y - 20;nexthead->next = head;head = nexthead;q = head;if (nexthead->x - 10 == foodxy->x && nexthead->y - 10 == foodxy->y){SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);number += add;add += 2;sleeptime -= 5;PlaySound(TEXT("eat.wav"), 0, SND_FILENAME | SND_ASYNC);food();score();}else{SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);while (q->next->next != NULL){q = q->next;}putimage(q->next->x - 10, q->next->y - 10, &bk);//擦蛇尾free(q->next);q->next = NULL;}break;case D:nexthead->x = head->x;        //向下前进时,x坐标不动,y坐标+20nexthead->y = head->y + 20;nexthead->next = head;head = nexthead;q = head;if (nexthead->x - 10 == foodxy->x && nexthead->y - 10 == foodxy->y){SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);number += add;add += 2;sleeptime -= 5;PlaySound(TEXT("eat.wav"), 0, SND_FILENAME | SND_ASYNC);food();score();}else{SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);while (q->next->next != NULL){q = q->next;}putimage(q->next->x - 10, q->next->y - 10, &bk);//擦蛇尾free(q->next);q->next = NULL;}break;case L:nexthead->x = head->x - 20;        //向左前进时,y坐标不动,x坐标-20nexthead->y = head->y;nexthead->next = head;head = nexthead;q = head;if (nexthead->x - 10 == foodxy->x && nexthead->y - 10 == foodxy->y){SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);number += add;add += 2;sleeptime -= 5;PlaySound(TEXT("eat.wav"), 0, SND_FILENAME | SND_ASYNC);food();score();}else{SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);while (q->next->next != NULL){q = q->next;}putimage(q->next->x - 10, q->next->y - 10, &bk);//擦蛇尾free(q->next);q->next = NULL;}break;case R:nexthead->x = head->x + 20;        //向右前进时,y坐标不动,x坐标+20nexthead->y = head->y;nexthead->next = head;head = nexthead;q = head;if (nexthead->x - 10 == foodxy->x && nexthead->y - 10 == foodxy->y){SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);number += add;add += 2;sleeptime -= 5;PlaySound(TEXT("eat.wav"), 0, SND_FILENAME | SND_ASYNC);food();score();}else{SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);while (q->next->next != NULL){q = q->next;}putimage(q->next->x - 10, q->next->y - 10, &bk);//擦蛇尾free(q->next);q->next = NULL;}break;}dead();
}

通过键盘获取的信息来改变蛇的坐标,每改变一次,我都将蛇尾,蛇头用背景覆盖,在原蛇头处绘制蛇身,在蛇头前进方向前面绘制蛇头,再延迟一段时间,从而实现蛇的移动。

8.死亡判断

代码

//撞到自己
int eatself(void)
{snake* self;                   //定义self为蛇身上的一个节点self = head->next;        //self是蛇头之外的蛇身上的节点while (self != NULL){if (self->x == head->x && self->y == head->y)    //如果self和蛇身上的节点重合{return 1;       }self = self->next;}return 0;
}
//死亡判断
void dead(void)
{if (head->x >= 480 || head->x <= 40 || head->y <= 40 || head->y >= 560){deadflag = 1;}else if (eatself() == 1){deadflag = 2;}end(deadflag);
}

通过蛇头前面一个的坐标来进行碰撞判断,如果与蛇身或墙壁重合,则死亡,如与食物重合,则加分、加速、播放音乐、不擦除蛇尾,从而实现蛇身的增长。

9.额外功能

1.加减速

代码

//加速
void speed_up(void)
{if (sleeptime >= 50){sleeptime -= 10;}add += 2;
}
//减速
void speed_down(void)
{if (sleeptime <= 350){sleeptime += 20;}add -= 2;
}

2.历史最高分的记录

代码

//获取最高分
int getMAX(void)
{FILE* fp;int a;fp = fopen("MAX.txt", "r");fscanf(fp, "%d", &a);return a;
}
//写入最高分
void writeMAX(int score1)
{FILE *fp;int a;fp = fopen("MAX.txt", "r");fscanf(fp, "%d", &a);fclose(fp);if (score1 > a){fp = fopen("MAX.txt", "w+");fprintf(fp, "%d", score1);fclose(fp);}
}

因为只会c语言,我使用的是c语言中的fopen等函数,vs会报不安全,可以通过一些方法去除。

3.音乐播放

本程序中通过playsound和mcisendstring两个函数分别实现音效和背景音乐的播放。

10.参考博客

贪吃蛇链表实现 link

按钮的实现 link

11.完整代码

效果图




素材图片:

代码

#include <stdio.h>         //标准库
#include <easyx.h>            //图形库
#include <conio.h>            //接收键盘输入输出
#include <windows.h>          //sleep
#include <time.h>         //随机数
#include <mmsystem.h>     //音乐#pragma comment (lib, "winmm.lib")/*******         变量声明
*******/
typedef struct snake
{int x;int y;struct snake* next;
}snake;
snake *head, *q, *foodxy;
int number = 0, add = 10, deadflag = 0;
int click;
int flag = 0;
int sleeptime = 200;
int r[3][4] = { { 300,260,450,320 }, { 300,360,450,420 }, { 300,460,450,520 } };IMAGE bk;
RECT R1 = { r[0][0],r[0][1],r[0][2],r[0][3] };
RECT R2 = { r[1][0],r[1][1],r[1][2],r[1][3] };
RECT R3 = { r[2][0],r[2][1],r[2][2],r[2][3] };/*******       函数原型声明
*******/
int welcomegraph(void);
void banana(int x);
int button_judge(int x, int y);
void help(void);
void drawmap(void);
void score(void);
int getMAX(void);
void food(void);
void initsnake(void);
void snake_head(int x, int y);
void snake_body(int x, int y);
int eatself(void);
void dead(void);
void keyboardControl(void);
void snake_move(void);
void speed_up(void);
void speed_down(void);
void end(int deadflag);
void writeMAX(int score1);/*******       宏定义
*******/
#define U 1
#define D 2
#define L 3
#define R 4      //蛇的状态,U:上 ;D:下;L:左 R:右//主函数
int main(void)
{TCHAR fileName[] = L"play C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\bgm.wav";   //打开背景音乐mciSendString(fileName, NULL, 0, NULL);deadflag = 0;        //初始化死亡标志switch (welcomegraph()){case 1:cleardevice();drawmap();score();initsnake();food();keyboardControl();break;case 2:cleardevice();help();main();break;}
}
//绘制开始界面
int welcomegraph(void)
{initgraph(850, 600);int t;float H = 190, S = 1, LIGHT = 0.7f;       //H 色相:S 饱和度:L 亮度wchar_t s[] = L"贪吃蛇";wchar_t s1[] = L"V1.0";wchar_t s2[] = L"by TMY";IMAGE img, img1, apple, apple1;MOUSEMSG m;//鼠标指针int event = 0, i;if (flag == 0){t = 1;}else{t = 0;}for (int i = 0; i < 256; i += 5){setbkcolor(RGB(i, i, i));       //设置背景色,原来默认黑色cleardevice();                 //清屏(取决于背景色)Sleep(t * 15);}for (int i = 0; i < 600; i++){LIGHT += 0.0005f;setlinecolor(HSLtoRGB(H, S, LIGHT));line(0, i, 849, i);}loadimage(&apple, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\apple.jpg"), 100, 100);   //图片背景透明loadimage(&apple1, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\applex.jpg"), 100, 100);SetWorkingImage(&apple);putimage(0, 0, &apple1, SRCINVERT);SetWorkingImage(NULL);putimage(600, 100, &apple1, SRCAND);putimage(600, 100, &apple, SRCPAINT);loadimage(&img, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\界面.jpg"), 100, 100);   //图片背景透明loadimage(&img1, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\界面1.jpg"), 100, 100);SetWorkingImage(&img);putimage(0, 0, &img1, SRCINVERT);SetWorkingImage(NULL);putimage(80, 100, &img1, SRCAND);putimage(80, 100, &img, SRCPAINT);setbkmode(TRANSPARENT);                //文本透明settextcolor(RGB(255, 0, 255));settextstyle(120, 60, _T("微软雅黑"));outtextxy(180, 80, s);settextstyle(30, 15, _T("微软雅黑"));settextcolor(BLACK);outtextxy(0, 550, s1);outtextxy(680, 550, s2);setbkmode(TRANSPARENT);                //文本透明settextstyle(30, 15, _T("微软雅黑"));settextcolor(BLACK);                                                   drawtext(_T("开始游戏"), &R1, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域内输入文字,水平居中,垂直居中,单行显示drawtext(_T("游戏说明"), &R2, DT_CENTER | DT_VCENTER | DT_SINGLELINE);drawtext(_T("退出"), &R3, DT_CENTER | DT_VCENTER | DT_SINGLELINE);  setlinecolor(BLACK);rectangle(r[0][0], r[0][1], r[0][2], r[0][3]);rectangle(r[1][0], r[1][1], r[1][2], r[1][3]);rectangle(r[2][0], r[2][1], r[2][2], r[2][3]);while (true){m = GetMouseMsg();//获取一条鼠标消息switch (m.uMsg){case WM_MOUSEMOVE:setrop2(R2_XORPEN);setlinecolor(LIGHTCYAN);//线条颜色为亮青色setlinestyle(PS_SOLID, 3);//设置画线样式为实现,10磅setfillcolor(WHITE);//填充颜色为白色if (button_judge(m.x, m.y) != 0){if (event != button_judge(m.x, m.y)){event = button_judge(m.x, m.y);//记录这一次触发的按钮fillrectangle(r[event - 1][0], r[event - 1][1], r[event - 1][2], r[event - 1][3]);//有框填充矩形(X1,Y1,X2,Y2)banana(event);}}else{if (event != 0)//上次触发的按钮未被修正为原来的颜色{fillrectangle(r[event - 1][0], r[event - 1][1], r[event - 1][2], r[event - 1][3]);//两次同或为原来颜色event = 0;banana(0);}}break;case WM_LBUTTONDOWN:PlaySound(TEXT("click.wav"), 0, SND_FILENAME | SND_ASYNC);   //鼠标点击音效setrop2(R2_NOTXORPEN);//二元光栅——NOT(屏幕颜色 XOR 当前颜色)for (i = 0; i <= 10; i++){setlinecolor(RGB(25 * i, 25 * i, 25 * i));//设置圆颜色circle(m.x, m.y, 2 * i);Sleep(30);//停顿30mscircle(m.x, m.y, 2 * i);//抹去刚刚画的圆}//判断事件switch (button_judge(m.x, m.y)){//复原按钮原型case 1:return 1;FlushMouseMsgBuffer();//单击事件后清空鼠标消息break;case 2:return 2;FlushMouseMsgBuffer();//单击事件后清空鼠标消息break;case 3:closegraph();//关闭绘图环境exit(0);//正常退出default:FlushMouseMsgBuffer();//单击事件后清空鼠标消息//printf("\r\n(%d,%d)",m.x,m.y);//打印鼠标坐标,方便调试时确定区域break;}break;FlushMouseMsgBuffer();//清空鼠标消息缓存区}}
}
//香蕉
void banana(int x)
{IMAGE banana, banana1;loadimage(&banana, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\banana.jpg"), 50, 50);   //图片背景透明loadimage(&banana1, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\bananax.jpg"), 50, 50);SetWorkingImage(&banana);putimage(0, 0, &banana1, SRCINVERT);SetWorkingImage(NULL);if (x == 0){IMAGE b1, b2, b3;getimage(&b1, 0, 260, 60, 60);putimage(200, 260, &b1);getimage(&b2, 0, 360, 60, 60);putimage(200, 360, &b2);getimage(&b3, 0, 460, 60, 60);putimage(200, 460, &b3);}else if (x == 1){putimage(200, 260, &banana1, SRCAND);putimage(200, 260, &banana, SRCPAINT);}else if (x == 2){putimage(200, 360, &banana1, SRCAND);putimage(200, 360, &banana, SRCPAINT);}else if (x == 3){putimage(200, 460, &banana1, SRCAND);putimage(200, 460, &banana, SRCPAINT);}
}
//按钮判断函数
int button_judge(int x, int y)
{if (x > r[0][0] && x<r[0][2] && y>r[0][1] && y < r[0][3]){return 1;}if (x > r[1][0] && x<r[1][2] && y>r[1][1] && y < r[1][3]) { return 2;}if (x > r[2][0] && x<r[2][2] && y>r[2][1] && y < r[2][3]){return 3;}return 0;
}
//游戏说明
void help(void)
{float LIGHT = 0.7f;IMAGE h;char ch;setbkcolor(WHITE);loadimage(&h, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\help.png"), 860, 600);putimage(0, 0, &h, SRCINVERT);do{ch = _getch();if (ch == 'x' || ch == 'X'){break;}} while (1);flag = 1;
}
//地图绘制
void drawmap(void)
{int i;IMAGE box;setbkcolor(RGB(240,230,140));cleardevice();loadimage(&box, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\墙.jpg"), 40, 40);for (i = 0; i < 600; i += 40)  //生成墙{putimage(0, i, &box, SRCINVERT);putimage(480, i, &box, SRCINVERT);}for (i = 40; i < 480; i += 40){putimage(i, 0, &box, SRCINVERT);putimage(i, 560, &box, SRCINVERT);}
}
//分数
void score(void)
{IMAGE a;wchar_t s[] = L"分数";wchar_t s2[] = L"最高分";TCHAR s1[5];TCHAR m1[5];int m;m = getMAX();SetWorkingImage(NULL);getimage(&a, 600, 300, 200, 200);putimage(550, 120, &a);setbkmode(TRANSPARENT);                //文本透明settextcolor(BLACK);settextstyle(40, 20, _T("微软雅黑"));outtextxy(550, 120, s);settextstyle(40, 20, _T("微软雅黑"));outtextxy(700, 120, s2);_stprintf_s(s1, _T("%d"), number);       settextcolor(RED);settextstyle(60, 30, _T("微软雅黑"));outtextxy(550, 200, s1);_stprintf_s(m1, _T("%d"), m);settextcolor(GREEN);outtextxy(680, 200, m1);
}
//食物生成
void food(void)
{srand((unsigned int)time(NULL));int i, j;IMAGE apple, apple1;i = rand() % 22 + 2;j = rand() % 26 + 2;q = head;while (q->next == NULL){if (q->x == i * 20 && q->y == j * 20) //判断蛇身是否与食物重合{food();                          //重新创建食物}q = q->next;}foodxy = (snake*)malloc(sizeof(snake));if (foodxy == NULL) {printf("内存分配不成功!\n");}else{foodxy->x = i * 20;foodxy->y = j * 20;}loadimage(&apple, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\apple.jpg"), 20, 20);   //图片背景透明loadimage(&apple1, _T("C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\图片\\apple.jpg"), 20, 20);SetWorkingImage(&apple);putimage(0, 0, &apple1, SRCINVERT);SetWorkingImage(NULL);putimage(i * 20, j * 20, &apple1, SRCAND);putimage(i * 20, j * 20, &apple, SRCPAINT);
}
//初始化蛇
void initsnake(void)
{snake* tail;int i;int k = 0;tail = (snake *)malloc(sizeof(snake));   //从蛇尾开始,头插法,以x,y设定开始的位置tail->next = NULL;tail->x = 290;           tail->y = 350;for (i = 1; i <= 2; i++)       //设置蛇身,长度为3{head = (snake *)malloc(sizeof(snake));  //初始化蛇头head->next = tail;                       //蛇头的下一位为蛇尾head->x = 290 + 20 * i;                 //设置蛇头位置head->y = 350;tail = head;                             //蛇头变成蛇尾,然后重复循环}while (tail != NULL)                            //从头到尾,输出蛇身{if (k == 0){setfillcolor(BLUE);}else{setfillcolor(RED);}fillrectangle(tail->x - 10, tail->y + 10, tail->x + 10, tail->y - 10);      //输出蛇身tail = tail->next;   k++;}
}
//产生蛇头
void snake_head(int x, int y)
{setfillcolor(BLUE);fillrectangle(x - 10, y + 10, x + 10, y - 10);
}
//产生蛇身
void snake_body(int x, int y)
{setfillcolor(RED);fillrectangle(x - 10, y + 10, x + 10, y - 10);
}
//撞到自己
int eatself(void)
{snake* self;                   //定义self为蛇身上的一个节点self = head->next;        //self是蛇头之外的蛇身上的节点while (self != NULL){if (self->x == head->x && self->y == head->y)    //如果self和蛇身上的节点重合{return 1;       }self = self->next;}return 0;
}
//死亡判断
void dead(void)
{if (head->x >= 480 || head->x <= 40 || head->y <= 40 || head->y >= 560){deadflag = 1;}else if (eatself() == 1){deadflag = 2;}end(deadflag);
}
//蛇的移动
void snake_move(void)
{snake* nexthead;nexthead = (snake*)malloc(sizeof(snake));switch (click){case U:nexthead->x = head->x;        //向上前进时,x坐标不动,y坐标-20nexthead->y = head->y - 20;nexthead->next = head;head = nexthead;q = head;if (nexthead->x - 10 == foodxy->x && nexthead->y - 10 == foodxy->y){SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);number += add;add += 2;sleeptime -= 5;PlaySound(TEXT("eat.wav"), 0, SND_FILENAME | SND_ASYNC);food();score();}else{SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);while (q->next->next != NULL){q = q->next;}putimage(q->next->x - 10, q->next->y - 10, &bk);//擦蛇尾free(q->next);q->next = NULL;}break;case D:nexthead->x = head->x;        //向下前进时,x坐标不动,y坐标+20nexthead->y = head->y + 20;nexthead->next = head;head = nexthead;q = head;if (nexthead->x - 10 == foodxy->x && nexthead->y - 10 == foodxy->y){SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);number += add;add += 2;sleeptime -= 5;PlaySound(TEXT("eat.wav"), 0, SND_FILENAME | SND_ASYNC);food();score();}else{SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);while (q->next->next != NULL){q = q->next;}putimage(q->next->x - 10, q->next->y - 10, &bk);//擦蛇尾free(q->next);q->next = NULL;}break;case L:nexthead->x = head->x - 20;        //向左前进时,y坐标不动,x坐标-20nexthead->y = head->y;nexthead->next = head;head = nexthead;q = head;if (nexthead->x - 10 == foodxy->x && nexthead->y - 10 == foodxy->y){SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);number += add;add += 2;sleeptime -= 5;PlaySound(TEXT("eat.wav"), 0, SND_FILENAME | SND_ASYNC);food();score();}else{SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);while (q->next->next != NULL){q = q->next;}putimage(q->next->x - 10, q->next->y - 10, &bk);//擦蛇尾free(q->next);q->next = NULL;}break;case R:nexthead->x = head->x + 20;        //向右前进时,y坐标不动,x坐标+20nexthead->y = head->y;nexthead->next = head;head = nexthead;q = head;if (nexthead->x - 10 == foodxy->x && nexthead->y - 10 == foodxy->y){SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);number += add;add += 2;sleeptime -= 5;PlaySound(TEXT("eat.wav"), 0, SND_FILENAME | SND_ASYNC);food();score();}else{SetWorkingImage(NULL);getimage(&bk, 600, 0, 20, 20);putimage(head->next->x - 10, head->next->y - 10, &bk);   //擦蛇头snake_body(head->next->x, head->next->y);snake_head(head->x, head->y);while (q->next->next != NULL){q = q->next;}putimage(q->next->x - 10, q->next->y - 10, &bk);//擦蛇尾free(q->next);q->next = NULL;}break;}dead();
}
//输入
void keyboardControl()
{click = R;  //初始蛇向右移动while (1){if (GetAsyncKeyState(VK_UP) && click != D)            //GetAsyncKeyState函数用来判断函数调用时指定虚拟键的状态{click = U;           //如果蛇不是向下前进的时候,按上键,执行向上前进操作}else if (GetAsyncKeyState(VK_DOWN) && click != U)     //如果蛇不是向上前进的时候,按下键,执行向下前进操作{click = D;}else if (GetAsyncKeyState(VK_LEFT) && click != R)      //如果蛇不是向右前进的时候,按左键,执行向左前进{click = L;}else if (GetAsyncKeyState(VK_RIGHT) && click != L)     //如果蛇不是向左前进的时候,按右键,执行向右前进{click = R;}if (GetAsyncKeyState(VK_SPACE))      //按暂停键,执行pause暂停函数{while (1){Sleep(300); if (GetAsyncKeyState(VK_SPACE))      //按空格键暂停{break;}}}else if (GetAsyncKeyState(VK_F1))    //按F1键,加速{speed_up();}else if (GetAsyncKeyState(VK_F2))    //按F2键,减速{speed_down();}Sleep(sleeptime);snake_move();}
}
//加速
void speed_up(void)
{if (sleeptime >= 50){sleeptime -= 10;}add += 2;
}
//减速
void speed_down(void)
{if (sleeptime <= 350){sleeptime += 20;}add -= 2;
}
//结束
void end(int deadflag)
{if (deadflag != 0){writeMAX(number);TCHAR fileName[] = L"close C:\\Users\\Tianmy\\Desktop\\贪吃蛇\\bgm.wav";  //关闭背景音乐mciSendString(fileName, NULL, 0, NULL);PlaySound(TEXT("dead.wav"), 0, SND_FILENAME | SND_ASYNC);   //播放死亡音效float LIGHT = 0.7f;setbkcolor(WHITE);cleardevice();for (int i = 0; i < 600; i++){LIGHT += 0.0005f;setlinecolor(HSLtoRGB(190, 1, LIGHT));line(0, i, 849, i);}if (deadflag == 1){wchar_t s[] = L"您撞到墙了!";setbkmode(TRANSPARENT);                //文本透明settextcolor(BLACK);settextstyle(100, 50, _T("微软雅黑"));outtextxy(100, 200, s);}else{wchar_t s[] = L"您撞到自己了!";setbkmode(TRANSPARENT);                //文本透明settextcolor(BLACK);settextstyle(100, 50, _T("微软雅黑"));outtextxy(100, 200, s);}wchar_t s2[] = L"您的分数为";settextstyle(40, 20, _T("微软雅黑"));outtextxy(100, 300, s2);TCHAR s1[5];_stprintf_s(s1, _T("%d"), number);settextcolor(RED);settextstyle(80, 40, _T("微软雅黑"));outtextxy(350, 300, s1);wchar_t s3[] = L"按回车键继续……";settextcolor(BLACK);settextstyle(40, 20, _T("微软雅黑"));outtextxy(100, 500, s3);getchar();flag = 1;number = 0;sleeptime = 200;add = 10;main();}
}
//获取最高分
int getMAX(void)
{FILE* fp;int a;fp = fopen("MAX.txt", "r");fscanf(fp, "%d", &a);return a;
}
//写入最高分
void writeMAX(int score1)
{FILE *fp;int a;fp = fopen("MAX.txt", "r");fscanf(fp, "%d", &a);fclose(fp);if (score1 > a){fp = fopen("MAX.txt", "w+");fprintf(fp, "%d", score1);fclose(fp);}
}

大一上课设——贪吃蛇(链表 + easyx)相关推荐

  1. C语言实现贪吃蛇(easyx)

    1.大一写的贪吃蛇,有兴趣的可以自取,为了大家移植方便没有写头文件,直接复制就可以运行,编译环境是vs2022. 但是运行之前首先要进行两个配置,不然会报错. 1>将字符集改为"使用多 ...

  2. C语言游戏之贪吃蛇--链表实现

    早自习时突然想到怎么写贪吃蛇,话不多说,代码如下: 开发环境:vs2015 虽然开始还是出了很多指针问题...(很烦C语言指针)调试了很久也大概是可以畅快的玩了. C语言新手,有很多写得不好的地方望大 ...

  3. C语言实现的贪吃蛇(无EasyX,详解)

  4. 基于EasyX的贪吃蛇游戏(C语言)

    贪吃蛇游戏EasyX图形库C语言 #前言 关于贪吃蛇游戏这种学生时代,用来应付一个期末设计的东西.网上各式各样用不同方式实现的核心代码已经烂大街了. 用esayX这种图形库,可以简单进行蛇体,游戏界面 ...

  5. Linux编程 --- 贪吃蛇小游戏

    如何使用ncurse #include <curses.h>int main() {initscr();//ncurse 界面的初始化函数printw("This is curs ...

  6. 贪吃蛇_C语言_链表实现_SCAU课程设计

    个人大一下课程设计作品,本文中直接附上源代码(通过visual stdio 2019 实现  ): 课题:贪吃蛇(链表实现~) 语言:C/C++ 运行环境(软件):Visual Stdio 2019( ...

  7. 基于C语言Ncurse库和链表的简单贪吃蛇小游戏

    参考:基于C语言Ncurse库和链表的简单贪吃蛇小游戏 作者:三速何时sub20 发布时间:2020-09-29 10:23:51 网址:https://blog.csdn.net/weixin_44 ...

  8. c语言链表写贪吃蛇思路,C语言构建的链表贪吃蛇

    用C语言链表写的贪吃蛇(程序设计时做的,做的不好大佬勿喷) 借助游戏内容分析贪吃蛇所需的功能主要包括这几块: 移动光标模块 打印地图模块和基本规则信息 读取最高分文件 打印初始蛇模块 打印时给予蛇的初 ...

  9. 【精选】基于EasyX的贪吃蛇小游戏

    文章目录 前言 一.项目环境 1.Visual Studio 2019 2.EasyX 20200902 二.运行效果展示 0.加载游戏 1.主菜单 2.游戏设置 3.游戏说明 4.生存模式 5.无尽 ...

最新文章

  1. 在统一软件开发过程中使用UML
  2. 知识在于积累和分类,厚积薄发
  3. 乖离性暗机器人_乖离性百万亚瑟王国服超弩暗机器人如何打|乖离性百万亚瑟王国服超弩暗机器人平民打法攻略分享_好特教程...
  4. Windows 应用容器化
  5. 程序员锁死公司服务器,导致600万元资金打水漂。网友神回复
  6. 在SpringMVC中使用拦截器(interceptor)拦截CSRF***
  7. linux是用于汇编语言,深入理解程序设计:使用Linux汇编语言
  8. 通过朋友间推广APP,根本不可行
  9. oracle表空间可以更改吗,如何更改Oracle数据库表的表空间?
  10. 酷狗php 技术题目,广州酷狗php面试题(赋答案)
  11. import matplotlib.pyplot as plt
  12. SIP信令协议消息(四)
  13. javaweb实现即时消息推送功能
  14. CSPS2019Day2T1(Emiya家今天的饭)题解
  15. Spread 14.2.0 for WinForms
  16. “走进名企”——微软亚洲研究院
  17. C++ 第一阶段编程练习
  18. Unreal Engine 4 初学者教程:开始
  19. 为什么现在90后员工离职率那么高?
  20. BYOD是企业网络安全的噩梦还是变革?

热门文章

  1. 基于微信小程序的免费小说阅读平台小程序的设计与实现 毕业设计 毕设源码(1)小程序功能
  2. js文字转图片,使用画布绘制
  3. Linux基本命令---2
  4. C# 关于类的继承的总结
  5. 微信小程序支持windows PC版了
  6. 关于c语言杨辉三角编写的改进
  7. linux 下 格式化u盘 并分区 为fat32文件系统
  8. Centos7操作系统搭建Snipe-IT资产管理系统
  9. 【python】蓄水池
  10. 学生党无线蓝牙耳机推荐哪个,2022口碑最好的蓝牙耳机推荐