普通版
知识点
结构体,循环,函数,easyx图形库,数组
玩法
1.通过控制w,s,a,d来实现蛇的移动,或者上下左右。
2.蛇撞到自己或者边界都会死亡。
3.蛇吃到食物会变长,分数增加。
4.分数大于最高分时会保存为最高分。
要求
要使用easyx图形库,需要同学们自己去官网搜索下载哦
创作历程
1.用结构体创建蛇和食物
struct Snake
{
int size; //节数
int dir;
int speed;
int highestScore;
POINT coor[SNAKE_NUM]; //坐标
}snake;
struct Food
{
int x;
int y;
int r;
int eatgrade;
bool index;
DWORD color;
}food;
2. 初始化数据
//数据的初始化
void GameInit()
{
//播放背景音乐
//mciSendString("open ./res/snake_bgm.mp3 alias BGM", 0, 0, 0);
//mciSendString("play BGM repeat", 0, 0, 0);
mciSendString("play res/snake_bgm.mp3 repeat", 0, 0, 0);
//初始化游戏窗口
initgraph(640, 480);
//设置随机数种子
srand(time(nullptr));
//初始化蛇
snake.size = 3;
snake.speed = 10;
snake.dir = RIGHT;
for (int i = 0; i < snake.size; i++)
{
snake.coor[i].x = 20 + 10 * (snake.size - i - 1);
snake.coor[i].y = 10;
}
//初始化食物 rand()随机生成一个整数,如果没有设置随机数种子,则每次产生的都是一个固定的整数
//设置种子需要头文件stdlib.h
food.x = rand() % 540;
food.y = rand() % 400;
food.color = RGB(rand() % 255, rand() % 255, rand() % 255);
food.r = rand() % 5 + 5;
food.eatgrade = 0;
food.index = true;
//初始化最高分
ifstream file(RECORDER_FILE);
if (!file.is_open())
{
cout << RECORDER_FILE << "打开失败" << endl;
snake.highestScore = 0;
}
else
{
file >> snake.highestScore;
}
file.close(); //关闭文件
}
3.绘制游戏界面
void GameDraw()
{
//双缓冲绘图 防止闪屏
BeginBatchDraw();
//设置背景颜色 需要两步
//setbkcolor(RGB(28, 115, 119));
//cleardevice(); //一定要有这个,没有的话不会将蛇走过的部分清掉,如果插图得注释这句话
IMAGE img;
loadimage(&img, _T("D:\\MyCPPWork\\贪吃蛇\\res\\snake_background.jpg"), 0, 0);
putimage(0, 0, &img);
//绘制蛇
for (int i = 0; i < snake.size; i++)
{
//setfillcolor(RGB(rand() % 255, rand() % 255, rand() % 255));
setfillcolor(GREEN);
solidcircle(snake.coor[i].x, snake.coor[i].y, 5);
}
//绘制食物
if (food.index)
{
setfillcolor(food.color);
solidcircle(food.x, food.y, food.r);
}
EndBatchDraw();
}
4.实现蛇的移动
//移动蛇
void SnakeMove()
{
//让身体跟着3头移动
for (int i = snake.size - 1; i > 0; i--)
{
snake.coor[i] = snake.coor[i - 1];
}
//玩法1 可穿墙
switch (snake.dir)
{
case UP:
snake.coor[0].y -= snake.speed;
//if (snake.coor[0] .y <= 0)
//{
// snake.coor[0].y = 480;
//}
break;
case DOWN:
snake.coor[0].y += snake.speed;
//if (snake.coor[0].y >= 480)
//{
// snake.coor[0].y = 0;
//}
break;
case LEFT:
snake.coor[0].x -= snake.speed;
//if (snake.coor[0].x <= 0)
//{
// snake.coor[0].x = 640;
//}
break;
case RIGHT:
snake.coor[0].x += snake.speed;
//if (snake.coor[0].x >= 640)
//{
// snake.coor[0].x = 0;
//}
break;
}
}
5.通过按键控制贪吃蛇移动以及暂停
//通过按键改变蛇的移动方向
void KeyControl()
{
//判断有没有按键
if (_kbhit())
{
//72 80 75 77 上下左右键值
switch (_getch())
{
case 'w':
case 'W':
case 72:
//改变方向
if (snake.dir != DOWN)
{
snake.dir = UP;
}
break;
case 's':
case 'S':
case 80:
if (snake.dir != UP)
{
snake.dir = DOWN;
}
break;
case 'a':
case 'A':
case 75:
if (snake.dir != RIGHT)
{
snake.dir = LEFT;
}
break;
case 'd':
case 'D':
case 77:
if (snake.dir != LEFT)
{
snake.dir = RIGHT;
}
break;
case ' '://游戏暂停逻辑
while (1)
{
if (_getch() == ' ')
return;
}
break;
}
}
}
6. 吃食物加分和食物重新生成
void EatFood()
{
if (food.index && snake.coor[0].x >= food.x - food.r && snake.coor[0].x <= food.x + food.r &&
snake.coor[0].y >= food.y - food.r && snake.coor[0].y <= food.y + food.r)
{
food.index = false;
snake.size++;
food.eatgrade += food.r;
}
//如果食物消失,则重新生成一个食物
if (!food.index)
{
food.x = rand() % 540;
food.y = rand() % 400;
food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
food.r = rand() % 6 + 5; //rand()%6取的是0 1 2 3 4 5
food.index = true;
}
}
7.绘制分数和最高分
//得分
void ShowGrade()
{
BeginBatchDraw();
char grade[100] = "";
sprintf(grade, "%d", food.eatgrade); //用sprintf要加#define _CRT_SECURE_NO_WARNINGS
setbkmode(TRANSPARENT);
settextcolor(RED);
outtextxy(530, 10, "分数:");
outtextxy(530 + 50, 10, grade);
//绘制最高分
sprintf_s(grade, sizeof(grade), "%d", snake.highestScore); //可以直接用sprintf_s,不用加#define _CRT_SECURE_NO_WARNINGS
outtextxy(530, 30, "最高分数:");
outtextxy(530 + 80, 30, grade);
EndBatchDraw();
}
8.保存最高分
void saveScore()
{
if (food.eatgrade > snake.highestScore)
{
snake.highestScore = food.eatgrade;
ofstream file(RECORDER_FILE);
file << snake.highestScore;
file.close();
}
}
9.蛇的死亡判定
//蛇死亡
int SnakeDie()
{
//玩法2,撞墙死
if (snake.coor[0].x > 640 || snake.coor[0].x < 0 || snake.coor[0].y>480 || snake.coor[0].y < 0)
{
mciSendString("stop res/snake_bgm.mp3", 0, 0, 0);
outtextxy(200, 200, "你撞墙啦!");
MessageBox(hwnd, "Game Over!", "撞墙警告", MB_OK);
return 1;
}
//通用代码:自己吃自己
for (int i = 1; i < snake.size; i++)
{
if (snake.coor[0].x == snake.coor[i].x &&
snake.coor[0].y == snake.coor[i].y)
{
mciSendString("stop res/snake_bgm.mp3", 0, 0, 0);
outtextxy(200, 200, "你撞到自己啦!");
MessageBox(hwnd, "Game Over!", "自杀警告", MB_OK);
return 1;
}
}
return 0;
}
10.主函数调用上述函数
int main()
{
GameInit();
while (1)
{
GameDraw();
if (SnakeDie())
{
break;
}
SnakeMove();
KeyControl();
EatFood();
ShowGrade();
saveScore();
Sleep(70);
}
return 0;
}
最终代码
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<conio.h> //读取按键的头文件 _kbhit() _getch()
#include<stdlib.h> //设置随机数种子的头文件
#include<graphics.h> //图形库的头文件
#include<fstream> //读取文件的头文件
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib") //添加音乐的两步
#define SNAKE_NUM 500 //蛇的最大节数
#define RECORDER_FILE "recorder.txt"
using namespace std;
enum DIR
{
UP,
DOWN,
LEFT,
RIGHT
};
struct Snake
{
int size; //节数
int dir;
int speed;
int highestScore;
POINT coor[SNAKE_NUM]; //坐标
}snake;
struct Food
{
int x;
int y;
int r;
int eatgrade;
bool index;
DWORD color;
}food;
//死亡提示框
HWND hwnd = NULL;
//数据的初始化
void GameInit()
{
//播放背景音乐
//mciSendString("open ./res/snake_bgm.mp3 alias BGM", 0, 0, 0);
//mciSendString("play BGM repeat", 0, 0, 0);
mciSendString("play res/snake_bgm.mp3 repeat", 0, 0, 0);
//初始化游戏窗口
initgraph(640, 480);
//设置随机数种子
srand(time(nullptr));
//初始化蛇
snake.size = 3;
snake.speed = 10;
snake.dir = RIGHT;
for (int i = 0; i < snake.size; i++)
{
snake.coor[i].x = 20 + 10 * (snake.size - i - 1);
snake.coor[i].y = 10;
}
//初始化食物 rand()随机生成一个整数,如果没有设置随机数种子,则每次产生的都是一个固定的整数
//设置种子需要头文件stdlib.h
food.x = rand() % 540;
food.y = rand() % 400;
food.color = RGB(rand() % 255, rand() % 255, rand() % 255);
food.r = rand() % 5 + 5;
food.eatgrade = 0;
food.index = true;
//初始化最高分
ifstream file(RECORDER_FILE);
if (!file.is_open())
{
cout << RECORDER_FILE << "打开失败" << endl;
snake.highestScore = 0;
}
else
{
file >> snake.highestScore;
}
file.close(); //关闭文件
}
void GameDraw()
{
//双缓冲绘图 防止闪屏
BeginBatchDraw();
//设置背景颜色 需要两步
//setbkcolor(RGB(28, 115, 119));
//cleardevice(); //一定要有这个,没有的话不会将蛇走过的部分清掉,如果插图得注释这句话
IMAGE img;
loadimage(&img, _T("D:\\MyCPPWork\\贪吃蛇\\res\\snake_background.jpg"), 0, 0);
putimage(0, 0, &img);
//绘制蛇
for (int i = 0; i < snake.size; i++)
{
//setfillcolor(RGB(rand() % 255, rand() % 255, rand() % 255));
setfillcolor(GREEN);
solidcircle(snake.coor[i].x, snake.coor[i].y, 5);
}
//绘制食物
if (food.index)
{
setfillcolor(food.color);
solidcircle(food.x, food.y, food.r);
}
EndBatchDraw();
}
//移动蛇
void SnakeMove()
{
//让身体跟着3头移动
for (int i = snake.size - 1; i > 0; i--)
{
snake.coor[i] = snake.coor[i - 1];
}
//玩法1 可穿墙
switch (snake.dir)
{
case UP:
snake.coor[0].y -= snake.speed;
//if (snake.coor[0] .y <= 0)
//{
// snake.coor[0].y = 480;
//}
break;
case DOWN:
snake.coor[0].y += snake.speed;
//if (snake.coor[0].y >= 480)
//{
// snake.coor[0].y = 0;
//}
break;
case LEFT:
snake.coor[0].x -= snake.speed;
//if (snake.coor[0].x <= 0)
//{
// snake.coor[0].x = 640;
//}
break;
case RIGHT:
snake.coor[0].x += snake.speed;
//if (snake.coor[0].x >= 640)
//{
// snake.coor[0].x = 0;
//}
break;
}
}
//通过按键改变蛇的移动方向
void KeyControl()
{
//判断有没有按键
if (_kbhit())
{
//72 80 75 77 上下左右键值
switch (_getch())
{
case 'w':
case 'W':
case 72:
//改变方向
if (snake.dir != DOWN)
{
snake.dir = UP;
}
break;
case 's':
case 'S':
case 80:
if (snake.dir != UP)
{
snake.dir = DOWN;
}
break;
case 'a':
case 'A':
case 75:
if (snake.dir != RIGHT)
{
snake.dir = LEFT;
}
break;
case 'd':
case 'D':
case 77:
if (snake.dir != LEFT)
{
snake.dir = RIGHT;
}
break;
case ' '://游戏暂停逻辑
while (1)
{
if (_getch() == ' ')
return;
}
break;
}
}
}
void EatFood()
{
if (food.index && snake.coor[0].x >= food.x - food.r && snake.coor[0].x <= food.x + food.r &&
snake.coor[0].y >= food.y - food.r && snake.coor[0].y <= food.y + food.r)
{
food.index = false;
snake.size++;
food.eatgrade += food.r;
}
//如果食物消失,则重新生成一个食物
if (!food.index)
{
food.x = rand() % 540;
food.y = rand() % 400;
food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
food.r = rand() % 6 + 5; //rand()%6取的是0 1 2 3 4 5
food.index = true;
}
}
//得分
void ShowGrade()
{
BeginBatchDraw();
char grade[100] = "";
sprintf(grade, "%d", food.eatgrade); //用sprintf要加#define _CRT_SECURE_NO_WARNINGS
setbkmode(TRANSPARENT);
settextcolor(RED);
outtextxy(530, 10, "分数:");
outtextxy(530 + 50, 10, grade);
//绘制最高分
sprintf_s(grade, sizeof(grade), "%d", snake.highestScore); //可以直接用sprintf_s,不用加#define _CRT_SECURE_NO_WARNINGS
outtextxy(530, 30, "最高分数:");
outtextxy(530 + 80, 30, grade);
EndBatchDraw();
}
void saveScore()
{
if (food.eatgrade > snake.highestScore)
{
snake.highestScore = food.eatgrade;
ofstream file(RECORDER_FILE);
file << snake.highestScore;
file.close();
}
}
//蛇死亡
int SnakeDie()
{
//玩法2,撞墙死
if (snake.coor[0].x > 640 || snake.coor[0].x < 0 || snake.coor[0].y>480 || snake.coor[0].y < 0)
{
mciSendString("stop res/snake_bgm.mp3", 0, 0, 0);
outtextxy(200, 200, "你撞墙啦!");
MessageBox(hwnd, "Game Over!", "撞墙警告", MB_OK);
return 1;
}
//通用代码:自己吃自己
for (int i = 1; i < snake.size; i++)
{
if (snake.coor[0].x == snake.coor[i].x &&
snake.coor[0].y == snake.coor[i].y)
{
mciSendString("stop res/snake_bgm.mp3", 0, 0, 0);
outtextxy(200, 200, "你撞到自己啦!");
MessageBox(hwnd, "Game Over!", "自杀警告", MB_OK);
return 1;
}
}
return 0;
}
int main()
{
GameInit();
while (1)
{
GameDraw();
if (SnakeDie())
{
break;
}
SnakeMove();
KeyControl();
EatFood();
ShowGrade();
saveScore();
Sleep(70);
}
return 0;
}
变态版(无敌版)
玩法
1.通过控制w,s,a,d来实现蛇的移动,或者上下左右。
2.蛇除了撞到自己会死亡,其他情况不会死亡。
3.蛇吃到食物会变长,分数增加。
4.分数大于最高分时会保存为最高分。
最终代码
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<conio.h> //读取按键的头文件 _kbhit() _getch()
#include<stdlib.h> //设置随机数种子的头文件
#include<graphics.h> //图形库的头文件
#include<fstream> //读取文件的头文件
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib") //添加音乐的两步
#define SNAKE_NUM 500 //蛇的最大节数
#define RECORDER_FILE "recorder.txt"
using namespace std;
enum DIR
{
UP,
DOWN,
LEFT,
RIGHT
};
struct Snake
{
int size; //节数
int dir;
int speed;
int highestScore;
POINT coor[SNAKE_NUM]; //坐标
}snake;
struct Food
{
int x;
int y;
int r;
int eatgrade;
bool index;
DWORD color;
}food;
//死亡提示框
HWND hwnd = NULL;
//数据的初始化
void GameInit()
{
//播放背景音乐
//mciSendString("open ./res/snake_bgm.mp3 alias BGM", 0, 0, 0);
//mciSendString("play BGM repeat", 0, 0, 0);
mciSendString("play res/snake_bgm.mp3 repeat", 0, 0, 0);
//初始化游戏窗口
initgraph(640, 480);
//设置随机数种子
srand(time(nullptr));
//初始化蛇
snake.size = 3;
snake.speed = 10;
snake.dir = RIGHT;
for (int i = 0; i < snake.size; i++)
{
snake.coor[i].x = 20 + 10 * (snake.size - i - 1);
snake.coor[i].y = 10;
}
//初始化食物 rand()随机生成一个整数,如果没有设置随机数种子,则每次产生的都是一个固定的整数
//设置种子需要头文件stdlib.h
food.x = rand() % 540;
food.y = rand() % 400;
food.color = RGB(rand() % 255, rand() % 255, rand() % 255);
food.r = rand() % 5 + 5;
food.eatgrade = 0;
food.index = true;
//初始化最高分
ifstream file(RECORDER_FILE);
if (!file.is_open())
{
cout << RECORDER_FILE << "打开失败" << endl;
snake.highestScore = 0;
}
else
{
file >> snake.highestScore;
}
file.close(); //关闭文件
}
void GameDraw()
{
//双缓冲绘图 防止闪屏
BeginBatchDraw();
//设置背景颜色 需要两步
//setbkcolor(RGB(28, 115, 119));
//cleardevice(); //一定要有这个,没有的话不会将蛇走过的部分清掉,如果插图得注释这句话
IMAGE img;
loadimage(&img, _T("D:\\MyCPPWork\\贪吃蛇\\res\\snake_background.jpg"), 0, 0);
putimage(0, 0, &img);
//绘制蛇
for (int i = 0; i < snake.size; i++)
{
//setfillcolor(RGB(rand() % 255, rand() % 255, rand() % 255));
setfillcolor(GREEN);
solidcircle(snake.coor[i].x, snake.coor[i].y, 5);
}
//绘制食物
if (food.index)
{
setfillcolor(food.color);
solidcircle(food.x, food.y, food.r);
}
EndBatchDraw();
}
//移动蛇
void SnakeMove()
{
//让身体跟着3头移动
for (int i = snake.size - 1; i > 0; i--)
{
snake.coor[i] = snake.coor[i - 1];
}
//玩法1 可穿墙
switch (snake.dir)
{
case UP:
snake.coor[0].y -= snake.speed;
if (snake.coor[0] .y <= 0)
{
snake.coor[0].y = 480;
}
break;
case DOWN:
snake.coor[0].y += snake.speed;
if (snake.coor[0].y >= 480)
{
snake.coor[0].y = 0;
}
break;
case LEFT:
snake.coor[0].x -= snake.speed;
if (snake.coor[0].x <= 0)
{
snake.coor[0].x = 640;
}
break;
case RIGHT:
snake.coor[0].x += snake.speed;
if (snake.coor[0].x >= 640)
{
snake.coor[0].x = 0;
}
break;
}
}
//通过按键改变蛇的移动方向
void KeyControl()
{
//判断有没有按键
if (_kbhit())
{
//72 80 75 77 上下左右键值
switch (_getch())
{
case 'w':
case 'W':
case 72:
//改变方向
if (snake.dir != DOWN)
{
snake.dir = UP;
}
break;
case 's':
case 'S':
case 80:
if (snake.dir != UP)
{
snake.dir = DOWN;
}
break;
case 'a':
case 'A':
case 75:
if (snake.dir != RIGHT)
{
snake.dir = LEFT;
}
break;
case 'd':
case 'D':
case 77:
if (snake.dir != LEFT)
{
snake.dir = RIGHT;
}
break;
case ' '://游戏暂停逻辑
while (1)
{
if (_getch() == ' ')
return;
}
break;
}
}
}
void EatFood()
{
if (food.index && snake.coor[0].x >= food.x - food.r && snake.coor[0].x <= food.x + food.r &&
snake.coor[0].y >= food.y - food.r && snake.coor[0].y <= food.y + food.r)
{
food.index = false;
snake.size++;
food.eatgrade += food.r;
}
//如果食物消失,则重新生成一个食物
if (!food.index)
{
food.x = rand() % 540;
food.y = rand() % 400;
food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
food.r = rand() % 6 + 5; //rand()%6取的是0 1 2 3 4 5
food.index = true;
}
}
//得分
void ShowGrade()
{
BeginBatchDraw();
char grade[100] = "";
sprintf(grade, "%d", food.eatgrade); //用sprintf要加#define _CRT_SECURE_NO_WARNINGS
setbkmode(TRANSPARENT);
settextcolor(RED);
outtextxy(530, 10, "分数:");
outtextxy(530 + 50, 10, grade);
//绘制最高分
sprintf_s(grade, sizeof(grade), "%d", snake.highestScore); //可以直接用sprintf_s,不用加#define _CRT_SECURE_NO_WARNINGS
outtextxy(530, 30, "最高分数:");
outtextxy(530 + 80, 30, grade);
EndBatchDraw();
}
void saveScore()
{
if (food.eatgrade > snake.highestScore)
{
snake.highestScore = food.eatgrade;
ofstream file(RECORDER_FILE);
file << snake.highestScore;
file.close();
}
}
//蛇死亡
int SnakeDie()
{
//玩法2,撞墙死
//if (snake.coor[0].x > 640 || snake.coor[0].x < 0 || snake.coor[0].y > 480 || snake.coor[0].y < 0)
//{
// mciSendString("stop res/snake_bgm.mp3", 0, 0, 0);
// outtextxy(200, 200, "你撞墙啦!");
// MessageBox(hwnd, "Game Over!", "撞墙警告", MB_OK);
// return 1;
//}
//通用代码:自己吃自己
for (int i = 1; i < snake.size; i++)
{
if (snake.coor[0].x == snake.coor[i].x &&
snake.coor[0].y == snake.coor[i].y)
{
mciSendString("stop res/snake_bgm.mp3", 0, 0, 0);
outtextxy(200, 200, "你撞到自己啦!");
MessageBox(hwnd, "Game Over!", "自杀警告", MB_OK);
return 1;
}
}
return 0;
}
int main()
{
GameInit();
while (1)
{
GameDraw();
if (SnakeDie())
{
break;
}
SnakeMove();
KeyControl();
EatFood();
ShowGrade();
saveScore();
Sleep(70);
}
return 0;
}
资源
图片:
音乐:
在顶部文章代码包资源内,如没有去各大音乐平台搜索下载,改下文件名,在游戏初始化函数内播放。
注意事项
1.要在终端设置中将下图设置为Windows控制台主机
作用:防止乱码
2.在vs中修改项目属性
项目->属性->高级->字符集
将使用Unicode字符集改为使用多字节字符集,否则将无法播放音乐。