C++ 图形界面的贪吃蛇。
2023-12-25 15:29:48
#include <graphics.h>
#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <conio.h>
#include <cstdio>
#include <time.h>
#define Width 40 //宽40格
#define Height 30 //高30格
#define Size 20 //格子大小:20
int blocks[Width][Height] = { 0 }; //用以表示所有方格
char moveDir; //表示运动方向
char oldmove; //旧的移动方向
int fx, fy; //food食物坐标
int addspeed = 15; //控制初始速度
int failure = 0; //用以判断游戏是否失败,失败赋值为 1
int score = 0; //记录分数
int max = 0; //记录最高分
int k = 1; //奖励机制初始值
char num[20]; //输出分数数组
char best[20]; //最高分数组
void initGame();
void show(); //图像绘制更新显示函数
void WithInput(); //与输入有关的更新
void WithoutInput(); //与输入无关的更新
void movesnake(); //蛇的移动函数
void readscore(); //读取分数
void writescore(); //写入分数
void ShowWord(); //界面文字
void drawmenu(); //主菜单
void gamestart(); //开始游戏
void seerule(); //查看规则
void back(); //退出游戏
int main() {
drawmenu();
return 0;
}
void initGame() {
int i;
initgraph(1000, 600); //①初始化绘图窗口,格子个数乘格子大小即为长度
setbkcolor(CYAN); //设置背景颜色
BeginBatchDraw(); //easyx中实现批绘图功能的函数,可以消除图像闪烁
cleardevice(); //使用当前背景色清空绘图设备
setlinecolor(WHITE); //划线颜色为白色
for (i = Size; i < Width * Size + 1; i += Size) { //用for循环绘制多条线
line(i, 0, i, Height * Size); //竖线坐标从x1(i,0)-> y1(i,Height * Size)
}
for (i = Size; i < Height * Size; i += Size) {
line(0, i, Width * Size, i); //横线坐标从x2(0,i) -> y2(Width * Size, i)
}
moveDir = oldmove = 'd'; //初始化默认蛇行进方向朝右
blocks[Width / 2][Height / 2] = 1; /*正中间画蛇头,蛇为赋值的方格
0则为空白区域*/
for (i = 1; i < 4; i++) {
blocks[Width / 2 - i][Height / 2] = i + 1; //y坐标不变,更改x坐标,在蛇头后方画蛇身
}
srand(time(0)); //srand函数产生一个时间种子,从而达到真正的随机效果
fx = rand() % (Width - 1) + 1; //随机数除39取余并+1,则为食物所生成的格子x坐标
fy = rand() % (Height - 1) + 1; //随机数除取余并+1,则为食物所生成的格子y坐标
}
void show() {
cleardevice();
for (int i = 0; i < Width; i++) { //遍历所有方格,更新蛇的颜色
for (int j = 1; j < Height; j++) {
if (blocks[i][j] != 0) {
setfillcolor(HSVtoRGB(blocks[i][j] * 10, 0.9, 1)); //HSV转RGB颜色,色调H 0°->360°,此处设置每长一个蛇身,提升10°色调,饱和度0.9,明度1
}
else {
setfillcolor(CYAN); //否则更新为正常背景颜色
}
fillrectangle(i * Size, j * Size, (i + 1) * Size, (j + 1) * Size); //填充,(参数为左上角和右下角坐标)
}
}
setfillcolor(LIGHTGREEN); //食物颜色
fillrectangle(fx * Size, fy * Size, (fx + 1) * Size, (fy + 1) * Size);
ShowWord();
FlushBatchDraw(); /*BeginBatchDraw执行后,任何绘图操作都将暂时不输出到绘图窗口上
直到执行 FlushBatchDraw 或 EndBatchDraw 才将之前的绘图输出。*/
}
void ShowWord() {
if (failure) {
settextcolor(LIGHTRED); //设置字体颜色
settextstyle(80, 0, _T("宋体")); //设置字体样式
setbkmode(TRANSPARENT);
outtextxy(240, 220, "游戏失败");
settextstyle(40, 0, _T("宋体"));
sprintf(num, "%d", score); //将int转换为char并存入num中
outtextxy(260, 360, "分数:"); //显示的字体及坐标
outtextxy(380, 360, num);
}
else { //②
settextcolor(BLACK); //设置分数颜色
settextstyle(25, 0, _T("隶书")); //设置字体样式
setbkmode(TRANSPARENT); //设置当前设备图案填充和文字输出时的背景模式,THANSPARENT为透明背景
sprintf(num, "%d", score); //将int转换为char并存入num中
outtextxy(860, 40, "分数:"); //显示的字体及坐标
outtextxy(940, 42, num);
outtextxy(845, 73, "最高分:");
sprintf(best, "%d", max);
outtextxy(940, 75, best);
}
}
void readscore() { //③
FILE* fpread = fopen("score.txt", "r"); //以只读的方式打开文件
if (fpread == NULL) {
return;
}
fscanf(fpread, "%d", &max);
fclose(fpread);
}
void writescore() {
FILE* fpWrite = fopen("score.txt", "w"); //"w"打开并写入文本文件.(若不存在则新建,反之,则从头写,覆盖原内容)
if (fpWrite == NULL) {
return;
}
fprintf(fpWrite, "%d", score);
fclose(fpWrite);
}
void WithInput() {
if (_kbhit() && failure == 0) {
//键盘输入控制蛇的方向
char input = _getch();
if (input == 'a' || input == 's' || input == 'd' || input == 'w') {
moveDir = input;
//设定规则:当向某一方向行动时,按下反向键不起作用(防止误触导致判断为咬到蛇身游戏失败),强制将按下的反向键变为当前运动方向
if (moveDir == 'a' && oldmove == 'd') {
moveDir = 'd';
}
else if (moveDir == 's' && oldmove == 'w') {
moveDir = 'w';
}
else if (moveDir == 'd' && oldmove == 'a') {
moveDir = 'a';
}
else if (moveDir == 'w' && oldmove == 's') {
moveDir = 's';
}
else {
oldmove = input;
}
movesnake();
}
}
}
void WithoutInput() {
if (failure) { //先判断是否失败,失败则返回
return;
}
//Sleep(100); //更新速度控制蛇的移动速度,但使用sleep会产生键盘控制与画面的延迟,所以不使用
//movesnake();
static int speed = 1; //设置一个静态变量speed,利用循环特定次数来触发movesnake函数
speed++;
if (speed == addspeed) { //每15帧移动一次,同时实现长按方向键加快蛇的移动速度
movesnake();
speed = 1;
}
}
void movesnake() {
int i, j;
//对蛇进行更新,蛇每个格数值都+1,以便后面预备一个数值为1的新蛇头,当吃到食物时,新蛇头改变颜色加入蛇身中
for (i = 0; i < Width; i++) {
for (j = 0; j < Height; j++) {
if (blocks[i][j] != 0) {
blocks[i][j]++;
}
}
}
int oldhx, oldhy, oldtx, oldty; //旧蛇头和旧蛇尾的坐标
int tailblock = 0;
for (i = 0; i < Width; i++) {
for (j = 0; j < Height; j++) {
if (tailblock < blocks[i][j]) {
tailblock = blocks[i][j]; //遍历找出最大值即为蛇尾
oldtx = i;
oldty = j;
}
if (blocks[i][j] == 2) { //寻找旧蛇头
oldhx = i;
oldhy = j;
}
}
}
//根据蛇的运动方向来赋值新蛇头的xy坐标
int newhx = oldhx, newhy = oldhy; //定义新蛇头
switch (moveDir) {
case'a':
newhx -= 1;
break;
case's':
newhy += 1;
break;
case'd':
newhx += 1;
break;
case'w':
newhy -= 1;
break;
}
//判断游戏是否失败
if (newhx >= Width || newhx < 0 || newhy >= Height || newhy < 0 || blocks[newhx][newhy] != 0) {
failure = 1;
return;
}
//令蛇头方格值为1
blocks[newhx][newhy] = 1;
//对吃到食物以及蛇尾的处理 ⑤
if (newhx == fx && newhy == fy) { //吃到食物,重新随机数生成新食物坐标
score = score + k; //吃到食物分数+k
if (score % 2 == 0) { //通过判断吃食物的数量来叠加奖励分
k = k + 1;
}
if (score > max) { //与最高分判断,条件成立才能覆盖最高分
writescore(); //写入最高分
}
//④
if (score) { //“加速”判断,通过判断分数增大的取值,加快蛇的运动速度,score不为0时进入该判断语句
if (score % 2 == 0) { //便于测试结果明显,设置每吃2个食物,蛇速度加快1帧
addspeed--;
}
}
fx = rand() % (Width - 1) + 1;
fy = rand() % (Height - 1) + 1;
}
else { //未吃到食物
blocks[oldtx][oldty] = 0;
}
}
void drawmenu() {
MOUSEMSG m; //定义结构体鼠标m
initgraph(600, 600); //定义画布大小
setbkcolor(CYAN); //设置背景颜色
cleardevice(); //使用当前背景色清空绘图设备,类似system(cls)的刷新
setfillcolor(WHITE); //方框颜色
fillrectangle(140, 50, 460, 170); //矩形左上角xy坐标和右下角xy坐标
fillrectangle(120, 220, 480, 350); //矩形左上角xy坐标和右下角xy坐标
fillrectangle(100, 400, 500, 540); //矩形左上角xy坐标和右下角xy坐标
settextcolor(BLACK); //设置字体颜色
settextstyle(50, 0, _T("楷体")); //设置字体格式
setbkmode(TRANSPARENT); //去除文字背景
outtextxy(200, 85, "开始游戏"); //文字输出位置的xy坐标
outtextxy(200, 260, "查看规则");
outtextxy(200, 445, "退出游戏");
while (1) {
m = GetMouseMsg(); //获取一个鼠标消息
if (m.x >= 140 && m.x <= 460 && m.y >= 50 && m.y <= 170) { //判断鼠标点击的位置
if (m.uMsg == WM_LBUTTONDOWN) { //当鼠标按下左键
gamestart(); //开始游戏
}
}
else if (m.x >= 120 && m.x <= 480 && m.y >= 220 && m.y <= 350) {
if (m.uMsg == WM_LBUTTONDOWN) { //当鼠标按下左键
seerule(); //查看规则
}
}
else if (m.x >= 100 && m.x <= 500 && m.y >= 400 && m.y <= 540) {
if (m.uMsg == WM_LBUTTONDOWN) { //当鼠标按下左键
back(); //关闭菜单退出游戏
}
}
}
}
void gamestart() {
readscore(); //读取最高分
initGame(); //游戏初始化
while (1) { //循环进行界面实时刷新,直至游戏判定失败
show(); //界面显示
WithoutInput(); //先调用与输入无关的更新
WithInput();
}
}
void seerule() {
MOUSEMSG m; //定义结构体鼠标m
readscore(); //读取历史最高分
initgraph(600, 600); //定义画布大小
setbkcolor(CYAN); //设置背景颜色
cleardevice(); //使用当前背景色清空绘图设备,类似system(cls)的刷新
setfillcolor(WHITE); //方框颜色
fillrectangle(100, 400, 500, 540); //矩形左上角xy坐标和右下角xy坐标
settextcolor(BLACK); //设置字体颜色
settextstyle(50, 0, _T("楷体")); //设置字体格式
setbkmode(TRANSPARENT); //去除文字背景
outtextxy(200, 445, "返回菜单");
settextcolor(BLACK); //设置字体颜色
settextstyle(30, 0, _T("楷体")); //设置字体格式
setbkmode(TRANSPARENT); //去除文字背景
outtextxy(45, 30, "WASD控制蛇移动,长按方向键可加速移动");
outtextxy(45, 80, "碰到墙壁游戏失败");
outtextxy(45, 130, "吃的食物越多,分数奖励越高");
outtextxy(45, 180, "历史最高分:");
sprintf(best, "%d", max);
outtextxy(220, 180, best);
while (1) {
m = GetMouseMsg(); //获取一个鼠标消息
if (m.x >= 100 && m.x <= 500 && m.y >= 400 && m.y <= 540) {
if (m.uMsg == WM_LBUTTONDOWN) { //当鼠标按下左键
drawmenu(); //返回主菜单
}
}
}
}
void back() {
closegraph(); //关闭画面
exit(0); //退出程序
}
文章来源:https://blog.csdn.net/laocooon/article/details/135200240
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!