Easyx图形库+C++做一个贪吃蛇小游戏 数据结构课程设计
程序员文章站
2024-03-18 14:02:04
...
Easyx图形库+C++做一个贪吃蛇小游戏 数据结构课程设计
- 程序界面
① 游戏开始界面(如下图):
显示游戏标题,提供“开始游戏”、“游戏模式”和“游戏关于”(用以查看游戏的相关信息)选项
② 游戏界面(如下图):
显示“蛇”和“食物”的位置及“等级框”、“分数框”、“提示框”等。
并在蛇移动时,更新游戏信息
③ 游戏结束界面(如下图):
显示游戏结束的提示,并提供“restart”和“quit”按钮
附上代码
#include<iostream>
#include<graphics.h>
#include<time.h>
#include<conio.h>
#include<Mmsystem.h>
#pragma comment(lib,"winmm.lib")
#include"resource.h"
using namespace std;
#define initSize 4 //蛇的初始
#define bgc BLACK //背景色
#define gbgc RGB(43,215,225) //游戏界面的背景色
#define shc1 YELLOW //RGB(138,232,23) //蛇头边框色
#define shc2 RED //蛇头色
#define sbc1 BLUE //蛇身边框色
#define sbc2 RGB(138,232,23) //蛇身色
#define fc1 RGB(255,128,64)
#define fc2 YELLOW
#define boxc RGB(70,226,205)
//定义结构体点 这个点为游戏地图上的点,并非像素点
typedef struct point {
int x; //点的横坐标
int y; //点的纵坐标
point() {};
point(int _x, int _y) {
x = _x;
y = _y;
}
}point;
//蛇身
typedef struct sBody{
point _point; //位置
sBody *next; //下一个
sBody *pre;//上一个
}sBody;
//方向
enum direction {up,down,left,right };
typedef struct snake {
sBody *head;//蛇头
sBody *body;//蛇身
sBody *tail;//蛇尾
direction drc;
snake() {
head = new sBody;
body = new sBody;
tail = new sBody;
}
}snake;
typedef struct food {
point _point;
bool flash; //闪烁的标志
}food;
snake *S = new snake; //主角
food *Food = new food;
int score; //分数
int level; //级别
int goal;
int mode; //模式选择
int pointWidth;
int _end; //是否结束游戏的标志
//---------------------------------------------------------------point部分
void drawPoint(point p,int color=GREEN) {
setfillcolor(color);
int sx, sy, ex, ey;
sx = 100 + pointWidth * p.x+1;
sy = 100 + pointWidth * p.y+1;
ex = sx + pointWidth-1;
ey = sy + pointWidth-1;
fillrectangle(sx,sy,ex,ey);
}
//高级版画点
void drawPointPlus(point p,int color1 ,int color2){
int x1 = 100 + pointWidth * p.x+1;
int y1 = 100 + pointWidth * p.y+1;
int x2 = x1 + pointWidth-1;
int y2 = y1 + pointWidth-1;
setfillcolor(color2);
fillrectangle(x1, y1, x2, y2);
setlinecolor(color1);
rectangle(x1, y1, x2, y2);
}
void clearPoint(point p) {
setlinecolor(gbgc);
drawPoint(p,gbgc);
}
//---------------------------------------------------------------snake
void initSnake(snake *S) {
S->drc = up;
S->head->_point.x = 10; //蛇头的初始位置
S->head->_point.y = 10;
S->body->_point.x = S->head->_point.x;
S->body->_point.y = S->head->_point.y + 1;
S->head->next = S->body; //指针域
S->body->pre = S->head;
sBody *q = S->head->next;
for (int i = 1; i < initSize; i++) {
sBody *p = new sBody;
p->_point.x = q->_point.x;
p->_point.y = q->_point.y + 1;
//指针域
q->next = p;
p->pre = q;
S->tail = p;
q = p;
}
q->next = NULL;
}
//画蛇头
void drawHead(snake *S) {
setfillcolor(fc1);
fillrectangle((S->head->_point.x)*pointWidth +100+1, (S->head->_point.y)*pointWidth +100+1, (S->head->_point.x)*pointWidth +100 + pointWidth-1, (S->head->_point.y)*pointWidth +100-1 + pointWidth);
drawPointPlus(S->head->_point, shc1,shc2);
}
//画蛇身体
void drawBody(sBody * a) {
setfillcolor(sbc2);
setlinecolor(YELLOW);
fillrectangle((a->_point.x)*pointWidth +100+1, (a->_point.y)*pointWidth +100+1, (a->_point.x)*pointWidth +100-1 + pointWidth, (a->_point.y)*pointWidth +100-1 + pointWidth);
drawPointPlus(a->_point, sbc1,sbc2);
}
//画蛇
void drawSnake(snake *S) {
drawHead(S);
sBody *p = S->head->next;
while (p!=S->tail->next) {
drawBody(p);
p = p->next;
}
}
判断蛇是否撞到自己
bool hitItself(snake *S) {
int X, Y;
X = S->head->_point.x;
Y = S->head->_point.y;
sBody *p = S->head->next;
while (p!=S->tail->next) {
if (p->_point.x == X && p->_point.y == Y) return true;
p = p->next;
}
return false;
}
//判断蛇是否撞到边框
bool hitEdge(snake *S) {
int X = S->head->_point.x;
int Y = S->head->_point.y;
if (X < 0 || X>(480/ pointWidth-1) || Y < 0 || Y>(600/ pointWidth-1))
return true;
return false;
}
//判断蛇是否吃到食物
bool getFood(snake *S, food *Food) {
int X = S->head->_point.x;
int Y = S->head->_point.y;
int fx = Food->_point.x;
int fy = Food->_point.y;
if (X == fx && Y == fy) {
return true; }
return false;
}
//普通的移动
void move(snake *S) {
sBody *temp = new sBody;
switch (S->drc)
{
case direction::up:
temp->_point.x = S->head->_point.x;
temp->_point.y = S->head->_point.y - 1;
break;
case direction::down:
temp->_point.x = S->head->_point.x;
temp->_point.y = S->head->_point.y + 1;
break;
case direction::left:
temp->_point.x = S->head->_point.x-1;
temp->_point.y = S->head->_point.y;
break;
case direction::right:
temp->_point.x = S->head->_point.x+1;
temp->_point.y = S->head->_point.y;
break;
default:
break;
}
temp->next =S->head;
S->head->pre = temp;
S->head = temp;
drawHead(S); //重画蛇头
drawBody(S->head->next);
clearPoint(S->tail->_point);
sBody *t = S->tail;
S->tail = S->tail->pre;
t = NULL;
}
//吃到食物的移动
void growAndMove(snake * S) {
sBody *temp = new sBody;
switch (S->drc)
{
case direction::up:
temp->_point.x = S->head->_point.x;
temp->_point.y = S->head->_point.y - 1;
break;
case direction::down:
temp->_point.x = S->head->_point.x;
temp->_point.y = S->head->_point.y + 1;
break;
case direction::left:
temp->_point.x = S->head->_point.x - 1;
temp->_point.y = S->head->_point.y;
break;
case direction::right:
temp->_point.x = S->head->_point.x + 1;
temp->_point.y = S->head->_point.y;
break;
default:
break;
}
temp->next = S->head;
S->head->pre = temp;
S->head = temp;
drawHead(S);
clearPoint(S->head->next->_point);
drawBody(S->head->next);
}
//---------------------------------------------------------------------food
//判断该位置是否被蛇的身体占据
bool isOccupied(snake *S, int fx, int fy) {
sBody *p = S->head;
while (p != S->tail->next) {
if (p->_point.x == fx && p->_point.y == fy) {
return true;
}
p = p->next;
}
return false;
}
//画一个新的食物
void drawANewFood(food *Food) {
int MAX_X = 480 / pointWidth;
int MAX_Y = 600 / pointWidth;
int fx = rand() % MAX_X;
int fy = rand() % MAX_Y;
while (isOccupied(S, fx, fy)) {
fx = rand() % MAX_X;
fy = rand() % MAX_Y;
}
Food->_point.x = fx;
Food->_point.y = fy;
Food->flash = 0;
drawPointPlus(Food->_point, fc1, fc2);
}
//控制食物闪烁
void flashFood(food *Food) {
if (Food->flash) {
clearPoint(Food->_point);
Food->flash = 0;
}
else {
drawPointPlus(Food->_point, fc1, fc2);
Food->flash = 1;
}
}
//---------------------------------------------------------------------game
void initGame(int mode) { //初始化蛇,画出蛇,画第一个食物,初始化级别和分数
if (mode == 0) pointWidth = 20;
else pointWidth = 10;
initSnake(S);
drawSnake(S);
srand(unsigned(time(NULL))); //食物随机位置的种子
level = 1;
goal = level * 100;
score = 0;
_end = 0;
}
//画出模式切换箭头
void drawLeftArrow(int x, int y) {
POINT pts[] = { {x,(y + 10)}, {x + 20, y + 20}, {x + 20, y} };
fillpolygon(pts, 3);
}
//画出模式切换箭头
void drawRightArrow(int x, int y) {
POINT pts[] = { {x + 20,(y + 10)}, {x, y}, {x , y + 20} };
fillpolygon(pts, 3);
}
//绘制成绩框
void drawScoreBox() {
setlinecolor(BLACK);
setfillcolor(boxc);
fillroundrect(630,320,950,420,20,20);
fillroundrect(630, 550, 950, 650, 20, 20);
}
//更新成绩显示
void updateScore() {
drawScoreBox();
_TCHAR a[12],b[12];
_stprintf_s(a,L"%d",score);
settextcolor(BLACK);
settextstyle(60,0,_T("Rosewood Std Regular"));
outtextxy(760,345,a);
_stprintf_s(b, L"%d", goal);
outtextxy(760, 575, b);
}
//绘制等级框
void drawRinkBox() {
setlinecolor(BLACK);
setfillcolor(boxc);
fillroundrect(820, 100, 950, 200, 20, 20);
}
//更新等级显示
void updateRank() {
settextcolor(BLACK);
drawRinkBox();
_TCHAR a[4];
_stprintf_s(a, L"%d", level);
settextstyle(60, 0, _T("Rosewood Std Regular"));
outtextxy(860, 125, a);
}
//游戏开始界面,开始播放背景音乐
void startInterface(int &cmd,int &mode) {
mciSendString(L"open C:\\Users\\TTODS\\source\\repos\\贪吃蛇\\贪吃蛇\\背景音乐.mp3 alias bgm", NULL, 0, NULL);
mciSendString(L"play bgm repeat", NULL, 0, NULL);
initgraph(1000, 800);
setbkcolor(bgc);
cleardevice();
setlinecolor(RED);
initgraph(1000, 800);
setbkcolor(YELLOW);
setfillcolor(RED);
fillrectangle(0, 0, 1000, 800);
setfillcolor(YELLOW);
setbkmode(TRANSPARENT);
IMAGE img;
img.Resize(960, 760);
loadimage(&img, _T("IMAGE"), MAKEINTRESOURCE(IDR_IMAGE1));
putimage(20, 20, &img);
//fillrectangle(120, 120 , 930, 730);
settextcolor(RED);
settextstyle(64, 0, _T("Rosewood Std Regular"));
outtextxy(270, 200, _T("GLUTTONOUS SNAKE"));
settextstyle(45, 0, _T("Viner Hand ITC"));
outtextxy(490, 450, _T("S T A R T"));
outtextxy(470, 500, _T("STANDARD"));
outtextxy(490, 550, _T("ABOUT"));
setfillcolor(GREEN);
drawLeftArrow(435, 510);
drawRightArrow(650, 510);
setfillcolor(RGB(158, 255, 142));
setlinecolor(RGB(158, 255, 142));
while (true) {
MOUSEMSG m = GetMouseMsg();
if (m.mkLButton&&m.x > 455 && m.x < 640 && m.y>450 && (m.y) < 490) { cmd = 1; break; }
else if (m.mkLButton&&m.x > 455 && m.x < 640 && m.y>500 && (m.y) < 540) { cmd = 2; break; }
else if (m.mkLButton&&m.x > 455 && m.x < 640 && m.y>550 && (m.y) < 590) { cmd = 3; break; }
else if ((m.mkLButton&&m.x > 435 && m.x < 455 && m.y>510 && m.y < 530) || (m.mkLButton&&m.x > 650 && m.x < 670 && m.y>510 && m.y < 530)) {
mode++;
mode %= 2;
if (mode == 0) { fillrectangle(470, 500, 640, 540); outtextxy(470, 500, _T("STANDARD")); }
else {
fillrectangle(470, 500, 640, 540);
outtextxy(470, 500, _T("ENTERTAIN"));
}
}
}
}
//游戏界面描绘
void gameInterface() {
setbkcolor(bgc);
cleardevice();
IMAGE img;
img.Resize(1000, 800);
loadimage(&img, _T("IMAGE"), MAKEINTRESOURCE(IDR_IMAGE3));
putimage(0, 0, &img);
int width = 6; //空心边界线的宽度
setlinecolor(BLACK);
//游戏界面
setfillcolor(YELLOW);
fillroundrect(99 - width, 99 - width, 581 + width, 701 + width, 20, 20);
setfillcolor(gbgc);
fillrectangle(99, 99, 581, 701);
setlinecolor(BLACK);
settextcolor(BLACK);
settextstyle(80, 0, _T("Rosewood Std Regular"));
outtextxy(600, 220, _T("Score:"));
outtextxy(600, 450, _T("Goal:"));
outtextxy(600, 100, _T("Level:"));
drawScoreBox(); //分数框
updateScore();
drawRinkBox(); //等级框
updateRank();
drawANewFood(Food); //第一个食物
//提示框
setfillcolor(YELLOW);
fillroundrect(95, 40, 586, 85, 5, 5);
setfillcolor(gbgc);
fillroundrect(101, 46, 580, 79, 5, 5);
settextcolor(RED);
settextstyle(20, 20, L"Adobe 仿宋 Std R");
outtextxy(100, 48, L" Be careful not to hit yourself or the border");
}
//
void gameOver(int &_end) {
mciSendString(L"close bgm",NULL,0,NULL);
PlaySound(L"C:\\Users\\TTOD\\source\\repos\\贪吃蛇\\贪吃蛇\\死亡.wav", NULL, SND_FILENAME | SND_ASYNC);
setbkmode(TRANSPARENT);
setfillcolor(YELLOW);
fillroundrect(150, 300, 530, 500, 10, 10);
setlinecolor(BLACK);
fillroundrect(153, 303, 527, 497, 10, 10);
settextcolor(BLACK);
settextstyle(50, 0, _T("Rosewood Std Regular"));
fillroundrect(220, 370, 470, 440, 10, 10);
outtextxy(230, 380, _T("GAME OVER!"));
settextstyle(30, 0, 0);
fillroundrect(155, 465, 270, 495, 5, 5);
fillroundrect(455, 465, 520, 495, 5, 5);
outtextxy(160, 470, _T("Restart"));
outtextxy(460, 470, _T("Quit"));
MOUSEMSG m;
while (true) {
m = GetMouseMsg();
if (m.mkLButton && m.x >= 155 && m.x <= 270 && m.y >= 465 && m.y <= 495) { _end = 0; break; }
else if (m.mkLButton && m.x >= 455 && m.x <= 520 && m.y >= 465 && m.y <= 495) { _end = 1; break; }
}
}
void game();
//游戏关于界面,用以显示游戏的相关信息
void drawAboutBox() {
IMAGE img;
img.Resize(1000,800);
loadimage(&img,_T("IMAGE"),MAKEINTRESOURCE(IDR_IMAGE3));
putimage(0, 0, &img);
fillrectangle(100, 100, 100 + 20 * 20,100+25*20);
point p(0, 0);
for (int i = 0; i <= 20; i++) {
p.y = 0;
p.x = i;
drawPointPlus(p,YELLOW,RED);
p.y = 25;
drawPointPlus(p, YELLOW, RED);
}
for (int i = 0; i <= 25; i++) {
p.x = 0;
p.y = i;
drawPointPlus(p, YELLOW, RED);
p.x =20 ;
drawPointPlus(p, YELLOW, RED);
}
setlinecolor(RED);
for (int i = 160; i < 600; i+=40) {
line(130, i, 480, i);
}
setbkmode(TRANSPARENT);
settextcolor(RED);
settextstyle(20,20,L"Adobe 仿宋 Std R");
outtextxy(130,130,L"ABOUT");
outtextxy(130, 170, L" Gluttonous Snake is a fun game ,have");
outtextxy(130, 210, L" a good time~~");
outtextxy(130, 250, L"( click anywhere to back )");
outtextxy(130, 490, L" The last updated time :");
outtextxy(130, 530, L" 2019.12.8");
//cleardevice();
MOUSEMSG m;
while (1) {
m = GetMouseMsg();
if (m.mkLButton) break;
}
game();
}
//游戏过程
void playGame() {
while (true) {
Sleep(120 - 5 * level);
if (getFood(S, Food)) {
growAndMove(S);
drawANewFood(Food);
score += 10*level;
setlinecolor(BLACK);
setfillcolor(YELLOW);
fillroundrect(95, 40, 586, 85,5,5);
setfillcolor(gbgc);
fillroundrect(101, 46, 580, 79, 5, 5);
updateScore();
int temp = score%100;
if (score == goal) {
PlaySound(L"C:\\Users\\TTODS\\source\\repos\\贪吃蛇\\贪吃蛇\\关卡升级.wav", NULL, SND_FILENAME | SND_ASYNC);
settextstyle(40, 0, _T("Rosewood Std Regular"));
settextcolor(RED);
outtextxy(240, 42, L"level up!!");
level++;
updateRank();
goal += 100 * level;
}
else {
settextcolor(RED);
settextstyle(20, 20, L"Adobe 仿宋 Std R");
PlaySound(L"C:\\Users\\TTODS\\source\\repos\\贪吃蛇\\贪吃蛇\\吃到食物.wav", NULL, SND_FILENAME | SND_ASYNC);
switch (temp)
{
case 0:
outtextxy(100, 48, L" Be careful not to hit yourself or the border");
break;
case 10:
case 40:
case 70:
outtextxy(100, 48, L" Press the ASDW keys to change the direction");
break;
case 20:
case 50:
case 80:
outtextxy(100, 48, L" Speed increases with level");
break;
case 30:
case 60:
case 90:
outtextxy(100, 48, L" It will grow long when you eat food");
default:
break;
}
}
}
else
move(S);
flashFood(Food);
if (hitEdge(S) || hitItself(S)) {
gameOver(_end);
return;
} //游戏结束标志
//监听按键,w s a d 分别为上、下、左、右。空格为暂停(按下任意键继续)
if (_kbhit()) {
char ch = _getch();
switch (ch)
{
case 'A':
case 'a':
if (S->drc != direction::right) {
S->drc = direction::left;
break;
}
case 'W':
case 'w':
if (S->drc != direction::down) {
S->drc = direction::up;
break;
}
case 's':
case 'S':
if (S->drc != direction::up) {
S->drc = direction::down;
break;
}
case 'd':
case 'D':
if (S->drc != direction::left) {
S->drc = direction::right;
break;
}
case ' ':
_getch();
break;
default:
break;
}
}
}
}
void restart() {
mciSendString(L"open C:\\Users\\TTODS\\source\\repos\\贪吃蛇\\贪吃蛇\\背景音乐.mp3 alias bgm", NULL, 0, NULL);
mciSendString(L"play bgm repeat", NULL, 0, NULL);
initGame(mode);
gameInterface();
playGame();
}
void game() {
int cmd = 0; mode = 0;
startInterface(cmd,mode);
initGame(mode);
//cleardevice();
switch (cmd)
{
case 1:
gameInterface();
playGame();
while (_end == 0) {
restart();
}
return;
break;
case 2:
break;
case 3:
drawAboutBox();
break;
default:
break;
}
};
int main() {
game();
return 0;
}
符加说明:本程序使用了简单好用的easyx图形库:可以Easyx官网中下载安装,且Easyx官网提供的文档详细的介绍了各种函数的用法,很容易上手。
代码中可能会有一些本人电脑上的资源路径,导致直接copy代码不能成功运行代码,想要学习的童鞋可以将其换为自己电脑上的资源路径或者直接删除即可运行,游戏相关的资源文件链接:资源链接