大一上课设——贪吃蛇(链表 + easyx)
大一上课设——贪吃蛇(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)相关推荐
- C语言实现贪吃蛇(easyx)
1.大一写的贪吃蛇,有兴趣的可以自取,为了大家移植方便没有写头文件,直接复制就可以运行,编译环境是vs2022. 但是运行之前首先要进行两个配置,不然会报错. 1>将字符集改为"使用多 ...
- C语言游戏之贪吃蛇--链表实现
早自习时突然想到怎么写贪吃蛇,话不多说,代码如下: 开发环境:vs2015 虽然开始还是出了很多指针问题...(很烦C语言指针)调试了很久也大概是可以畅快的玩了. C语言新手,有很多写得不好的地方望大 ...
- C语言实现的贪吃蛇(无EasyX,详解)
- 基于EasyX的贪吃蛇游戏(C语言)
贪吃蛇游戏EasyX图形库C语言 #前言 关于贪吃蛇游戏这种学生时代,用来应付一个期末设计的东西.网上各式各样用不同方式实现的核心代码已经烂大街了. 用esayX这种图形库,可以简单进行蛇体,游戏界面 ...
- Linux编程 --- 贪吃蛇小游戏
如何使用ncurse #include <curses.h>int main() {initscr();//ncurse 界面的初始化函数printw("This is curs ...
- 贪吃蛇_C语言_链表实现_SCAU课程设计
个人大一下课程设计作品,本文中直接附上源代码(通过visual stdio 2019 实现 ): 课题:贪吃蛇(链表实现~) 语言:C/C++ 运行环境(软件):Visual Stdio 2019( ...
- 基于C语言Ncurse库和链表的简单贪吃蛇小游戏
参考:基于C语言Ncurse库和链表的简单贪吃蛇小游戏 作者:三速何时sub20 发布时间:2020-09-29 10:23:51 网址:https://blog.csdn.net/weixin_44 ...
- c语言链表写贪吃蛇思路,C语言构建的链表贪吃蛇
用C语言链表写的贪吃蛇(程序设计时做的,做的不好大佬勿喷) 借助游戏内容分析贪吃蛇所需的功能主要包括这几块: 移动光标模块 打印地图模块和基本规则信息 读取最高分文件 打印初始蛇模块 打印时给予蛇的初 ...
- 【精选】基于EasyX的贪吃蛇小游戏
文章目录 前言 一.项目环境 1.Visual Studio 2019 2.EasyX 20200902 二.运行效果展示 0.加载游戏 1.主菜单 2.游戏设置 3.游戏说明 4.生存模式 5.无尽 ...
最新文章
- 在统一软件开发过程中使用UML
- 知识在于积累和分类,厚积薄发
- 乖离性暗机器人_乖离性百万亚瑟王国服超弩暗机器人如何打|乖离性百万亚瑟王国服超弩暗机器人平民打法攻略分享_好特教程...
- Windows 应用容器化
- 程序员锁死公司服务器,导致600万元资金打水漂。网友神回复
- 在SpringMVC中使用拦截器(interceptor)拦截CSRF***
- linux是用于汇编语言,深入理解程序设计:使用Linux汇编语言
- 通过朋友间推广APP,根本不可行
- oracle表空间可以更改吗,如何更改Oracle数据库表的表空间?
- 酷狗php 技术题目,广州酷狗php面试题(赋答案)
- import matplotlib.pyplot as plt
- SIP信令协议消息(四)
- javaweb实现即时消息推送功能
- CSPS2019Day2T1(Emiya家今天的饭)题解
- Spread 14.2.0 for WinForms
- “走进名企”——微软亚洲研究院
- C++ 第一阶段编程练习
- Unreal Engine 4 初学者教程:开始
- 为什么现在90后员工离职率那么高?
- BYOD是企业网络安全的噩梦还是变革?