贪吃蛇游戏 2.0
贪吃蛇游戏 2.0 在 贪吃蛇游戏 1.0 的基础上增加了图形界面。
详见GitHub:https://github.com/TaoR320/Gluttonous-Snake
资源下载:https://github.com/TaoR320/Gluttonous-Snake/archive/master.zip
游戏截图:
源码:
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_NON_CONFORMING_SWPRINTFS
#undef UNICODE
#undef _UNICODE //
#include <stdio.h>
#include <stdlib.h>
#include <graphics.h>
#include <conio.h>
#include <time.h>
#include <windows.h>
/*--------------------------------属 性---------------------------*/
#define WIDE 1170 /* 窗口大小 WIDE * HIGH像素 尺寸需为SIZE的整数倍, */
#define HIGH 640 /* 蛇的行动在A(SIZE,SIZE)到B(WIDE-FORM-SIZE,HIGH-SIZE)之间的矩形内*/
#define FORM 250 /* 设置文字边框宽度 */
#define SIZE 40 //每节蛇的尺寸
#define LEN 100 //蛇的最长长度
#define COLOUR RGB(rand()%255,rand()%255,rand()%255) //彩色
static int speed = 100; //速度(控制刷新的时间间隔)
enum Direction { right = 77, left = 75, down = 80, up = 72, Esc = 27 };//枚举方向值
/*---------------------------资源-------------------------------------*/
//蛇
typedef struct _snake
{
int x[LEN]; //坐标—X
int y[LEN]; //坐标—Y
int len; //长度
int count; //分数
int direction; //方向
}Snake;
//食物
typedef struct _food
{
int x; //坐标—X
int y; //坐标—Y
int flg; //标记食物是否已经出现
}Food;
IMAGE img[8]; //墙,蛇头,身体,蛇尾,食物
char name[8][15] = { "wall","snakeHead","body","food","snakeTailUp","snakeTailDown","snakeTailLeft","snakeTailRight" }; //标记图片变量名
IMAGE tips[4]; //其他界面图片
char tipsName[4][15] = { "snake","label","gameover","start" }; //标记图片变量名
//加载图片
void loadResources()
{
for (int i = 0; i < 8; i++)
{
char filename[20] = "";
sprintf_s(filename, "%s.bmp", name[i]);
loadimage(img + i, filename);
}
for (int i = 0; i < 4; i++)
{
char filename[20] = "";
sprintf_s(filename, "%s.bmp", tipsName[i]);
loadimage(tips + i, filename);
}
}
/************************************************************/
//变量
Snake snake; //蛇
Food food; //食物
//函数
void PrintForm(); //文字提示框
void Init_Snake(); //初始化蛇
void Init_Food(); //初始化食物
void ShowSnake(); //贴图画蛇
void ShowFood(); //贴图画食物
void MoveSnake(); //蛇的移动
void KeyDown(); //控制
void EatFood(); //吃食物
int GameOver(); //游戏结束
void DrawWall(); //画墙
//右侧展示区域
void PrintForm()
{
//setlinecolor(BLACK); //画线颜色 /*主函数中已存在*/
//setfillcolor(WHITE); //填充颜色
fillrectangle(0, 0, WIDE, HIGH - 1); //边框
LOGFONT f;
gettextstyle(&f); // 获取当前字体设置
f.lfHeight = 30; // 设置字体高度为 48
_tcscpy(f.lfFaceName, _T("幼圆")); // 设置字体为“黑体”
f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿
settextstyle(&f); // 设置字体样式
putimage(WIDE - FORM, 0, &tips[0]); //贴图——卡通贪吃蛇
//putimage(WIDE-FORM,250,&tips[1]); //提示框贴图,操作手册
setbkmode(TRANSPARENT); //文字透明方式
settextcolor(LIGHTBLUE); //文字颜色
TCHAR str[3][50];
_stprintf(str[0], _T(" 得分: %3d 分"), snake.count);
_stprintf(str[1], _T(" 速度: %3d 级"), (100 - speed) / 20);
_stprintf(str[2], _T(" 长度: %3d 节"), snake.len);
for (int i = 0; i < 3; ++i)
{
outtextxy(WIDE - FORM + 10, i * 40 + 250, str[i]); //指定位置输出字符串
}
TCHAR str2[11][50];
_stprintf(str2[0], _T(" 提示:"));
_stprintf(str2[1], _T(" 一枚果实10分,蛇最长 "));
_stprintf(str2[2], _T(" 100节,为通关。撞墙,"));
_stprintf(str2[3], _T(" 或撞到自己,游戏失败"));
_stprintf(str2[4], _T(" "));
_stprintf(str2[5], _T(" 按键:"));
_stprintf(str2[6], _T(" F1 : 加速 "));
_stprintf(str2[7], _T(" F2 : 减速"));
_stprintf(str2[8], _T(" 空格: 暂停"));
_stprintf(str2[9], _T(" Esc : 退出 "));
_stprintf(str2[10], _T(" "));
settextcolor(BLACK); //文字颜色
f.lfHeight = 20; // 设置字体高度为 20
settextstyle(&f); // 设置字体样式
for (int i = 0; i < 11; i++)
{
outtextxy(WIDE - FORM + 10, 400 + i * 20, str2[i]); //指定位置输出字符串
}
}
int main()
{
initgraph(WIDE, HIGH); //初始化窗口大小
loadResources(); //加载图片
setlinecolor(BLACK); //画线颜色
setfillcolor(WHITE); //填充颜色
fillrectangle(-1, -1, WIDE, HIGH); //边框
putimage(200, 50, &tips[3]);
while (!_kbhit()); // 没有按键就死循环 按下任意键开始
//清空缓冲区
FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
Init_Snake(); //初始化蛇
Init_Food(); //初始化食物
while (true)//强退
{
if (food.flg == 0)
{
Init_Food(); //初始化食物
}
BeginBatchDraw();
cleardevice(); //刷新窗口
PrintForm(); //打印表框
DrawWall(); //画墙
ShowFood(); //画食物
ShowSnake(); //画蛇
EndBatchDraw();
if (_kbhit())
{
KeyDown(); //玩家控制蛇移动
//清空缓冲区
FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
}
EatFood();
MoveSnake(); //蛇移动
if (GameOver() == 1) //游戏结束条件
{
//ShowSnake(); //画蛇,显示死亡状态
break;
}
Sleep(speed); //控制速度
}
_getch();
closegraph();
return 0;
}
/*--------------------------蛇的功能函数--------------------------*/
//初始化蛇
void Init_Snake()
{
//初始化前三节蛇
snake.x[0] = SIZE * 3;
snake.y[0] = SIZE;
snake.x[1] = SIZE * 2;
snake.y[1] = SIZE;
snake.x[2] = SIZE;
snake.y[2] = SIZE;
snake.len = 99;
snake.count = 0;
snake.direction = right; //默认向右
}
//初始化食物
void Init_Food()
{
srand((unsigned int)time(NULL));//随机种子
label:
int x = (WIDE - FORM - SIZE) / SIZE; //预留 FORM 像素显示信息
int y = (HIGH - SIZE) / SIZE;
food.x = rand() % x * SIZE; // *10 保证食物地址为整数,与蛇头比较
food.y = rand() % y * SIZE;
//检查食物是否在蛇身上
int i = 0;
while (i < snake.len)
{
if (food.x == snake.x[i] && food.y == snake.y[i]) //食物在蛇身上
{
goto label;
}
if (food.x == 0 || food.y == 0) //食物在上边界或左边界
{
goto label;
}
++i;
}
food.flg = 1;
}
//贴图画蛇
void ShowSnake()
{
/* 画蛇身 */
int i;
for (i = 1; i < snake.len; ++i) //若画蛇尾,条件为 i < snake.len - 1
{
putimage(snake.x[i], snake.y[i], &img[2]);
}
/* 画蛇尾 */
//注:没有好看的蛇尾,暂时不贴蛇尾,如想贴蛇尾只需把以下 注释段取消,把画蛇身的条件减一 即可
//if (snake.y[i - 1] < snake.y[i]) //倒数第一节在尾巴的上边,蛇尾向上
//{
// putimage(snake.x[i], snake.y[i], &img[4]);
//}
//else if (snake.y[i - 1] > snake.y[i]) //倒数第一节在尾巴的下边,蛇尾向下
//{
// putimage(snake.x[i], snake.y[i], &img[5]);
//}
//else if (snake.x[i - 1] < snake.x[i]) //倒数第一节在尾巴的左边,蛇尾向左
//{
// putimage(snake.x[i], snake.y[i], &img[6]);
//}
//else if (snake.x[i - 1] > snake.x[i]) //倒数第一节在尾巴的右边,蛇尾向右
//{
// putimage(snake.x[i], snake.y[i], &img[7]);
//}
/* 画蛇头 */
putimage(snake.x[0], snake.y[0], &img[1]);
}
//贴图画食物
void ShowFood()
{
putimage(food.x, food.y, &img[3]);
}
//蛇的移动
void MoveSnake()
{
//把蛇的后一节坐标移动到前一节的坐标位置
for (int i = snake.len - 1; i > 0; --i)
{
snake.x[i] = snake.x[i - 1];
snake.y[i] = snake.y[i - 1];
}
//单独移动蛇头,根据蛇的方向移动
switch (snake.direction)
{
case right:
snake.x[0] += SIZE;
break;
case left:
snake.x[0] -= SIZE;
break;
case up:
snake.y[0] -= SIZE;
break;
case down:
snake.y[0] += SIZE;
break;
default:
break;
}
}
/*getch函数从控制台读取单个字符而不回显,函数不能去读取CTRL+C,当读取一个
功能键或方向键,函数必须调用两次(这就说明可以用这个函数去监控功能键
和方向键),第一次调用返回0或0xe0,第二次返回实际的键代码*/
//控制
void KeyDown()
{
char tmp = _getch(); //接受_getch()的第一个返回值,或非功能按键
if (tmp == -32 || tmp == 0) {} //功能键,_getch()第一个返回值 -32\'?' 或0
else if (tmp == ' ') //判定空格暂停
{
TCHAR ss[] = _T("暂停,按下空格继续");
outtextxy(WIDE * 3 / 10, HIGH / 2, ss);
suspend:
while (!_kbhit()); // 没有按键就死循环 按下任意键开始
if (_getch() != ' ') goto suspend;
FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
return;
}
else if (tmp == 27) //判定Esc
{
TCHAR s[] = _T("即将退出游戏,请确认:Esc键 确认");
TCHAR s1[] = _T(" 按任意键取消");
outtextxy(WIDE * 3 / 10, HIGH / 2, s); //指定位置输出字符串
outtextxy(WIDE * 3 / 10, HIGH / 2 + 40, s1); //指定位置输出字符串
while (!_kbhit()); // 没有按键就死循环 按下任意键开始
if (_getch() == 27) exit(0);
return;
}
else return;
char key = _getch(); //接受键盘键入的值
switch (key)
{
case right:
if (snake.direction != left) //不能相反方向移动
snake.direction = right;
break;
case left:
if (snake.direction != right)
snake.direction = left;
break;
case up:
if (snake.direction != down)
snake.direction = up;
break;
case down:
if (snake.direction != up)
snake.direction = down;
break;
case 59: //F1加速
if (speed > 20)
{
speed -= 20;
}
break;
case 60: //F2减速
if (speed < 280)
{
speed += 30;
}
break;
default:
break;
}
}
//吃食物
void EatFood()
{
if (food.x == snake.x[0] && food.y == snake.y[0])
{
++snake.len;
snake.count += 10; //一个食物十分
food.flg = 0;
}
}
//死亡
int GameOver()
{
//通关
if (snake.len == LEN) //最长长度
{
TCHAR s[100];
_stprintf(s, _T("恭喜 通关 GameOver!\n您的分数为%d分"), snake.count);
//putimage(0,0,&tips[2]); //贴图,结束界面
setbkmode(TRANSPARENT); //文字透明方式
settextcolor(LIGHTBLUE); //文字颜色
outtextxy(WIDE * 2 / 10, HIGH / 2, s); //指定位置输出字符串
return 1;
}
//撞墙
else if (snake.x[0] < SIZE || snake.x[0] >= WIDE - FORM - SIZE || //横坐标出界
snake.y[0]<SIZE || snake.y[0]>HIGH - SIZE * 2) //纵坐标出界
{
TCHAR s[100];
_stprintf(s, _T("你撞墙了 GameOver!\n您的分数为%d分"), snake.count);
setbkmode(TRANSPARENT); //文字透明方式
settextcolor(LIGHTBLUE); //文字颜色
outtextxy(WIDE * 2 / 10, HIGH / 2, s); //指定位置输出字符串
return 1;
}
for (int i = 1; i < snake.len; i++)
{
//撞自己
if (snake.x[i] == snake.x[0] && snake.y[i] == snake.y[0])
{
TCHAR s[100];
_stprintf(s, _T("咬到自己了 GameOver!\n您的分数为%d分"), snake.count);
setbkmode(TRANSPARENT); //文字透明方式
settextcolor(LIGHTBLUE); //文字颜色
outtextxy(WIDE * 2 / 10, HIGH / 2, s); //指定位置输出字符串
return 1;
}
}
return 0;
}
void DrawWall()
{
for (int i = 0; i < WIDE - FORM; i += SIZE)
{
putimage(i, 0, &img[0]);
}
for (int i = 0; i < WIDE - FORM; i += SIZE)
{
putimage(i, HIGH - SIZE, &img[0]);
}
for (int j = 0; j < WIDE - FORM; j += SIZE)
{
putimage(0, j, &img[0]);
}
for (int j = 0; j < WIDE - FORM; j += SIZE)
{
putimage(WIDE - FORM - SIZE, j, &img[0]);
}
}