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代碼不能成功運行代碼,想要學習的童鞋可以將其換爲自己電腦上的資源路徑或者直接刪除即可運行,遊戲相關的資源文件鏈接:資源鏈接