學完C語言後,C語言大作業或者是大項目基本是每個學校都會要求學生做一份出來的。於是各路神仙開始展現自己的功力:這個圖書管理系統,那個車票售賣管理系統,這個做個貪喫蛇,那個做個推箱子,更甚有大佬做的什麼植物大戰殭屍,節奏大師,消消樂orz......大佬終歸是大佬,那我們身爲一個有夢想的菜雞,爲了不掛科期末作業總是要交的呀!怎麼來個容易上手還能讓老師感覺有那麼一點技術的項目呢?推箱子是你的不二首選。
本人現在是一名大二本科生,這是我去年元旦這個時候交的C語言項目。但遺憾那時剛上大學懵懂無知,沒聽過還有博客這種東西,現在拿來與大家分享。
先說說我的項目能實現的功能
1.推箱子的基本功能,什麼上下左右啦,推動箱子啦,過關啦,總共有5關!
2.按R可以重置本關
3.按Q可以跳過本關
4.有音樂效果,有界面
先來幾張運行的截圖
雖然有點粗糙,但是還可以(其實是因爲做遊戲因爲能插圖片看着逼格比做系統高一點hhh)
圖片來源於百度圖片
接下來上代碼
//以下是製作遊戲用到的頭文件
#include<stdlib.h>
#include<stdio.h>
#include<conio.h>
#include<Windows.h>
#include<graphics.h>
#pragma warning(disable:4996)
#pragma comment(lib,"Winmm.lib")
const int rows = 12, cols = 12;
int level = 0, step = 0, highnum;//關卡,目前已經走的步數,
IMAGE goodbox, box, man, blank, destination, wall, sky, startpage, background, button,button2;//聲明一下要加載的圖片,和變量的聲明類似
//以下是地圖的存儲,採用三維數組,第一維代表關卡,二三維存儲地圖
/*
0代表空地
1代表牆壁
3代表目的地
4代表箱子
5代表人
7代表已經推到目的地的箱子
8代表人和目的地重疊的點,其實還是人
這個數字不是隨便定義的,是有原因的,詳情請細看函數movereason(移動原理)
*/
int map[5][12][12] =
{
{
{0,0,0,1,1,1,0,0,0,0,0,0},
{0,0,0,1,3,1,0,0,0,0,0,0},
{0,0,0,1,0,1,1,1,1,1,1,1},
{1,1,1,1,4,0,4,0,0,0,3,1},
{1,3,0,0,4,5,1,1,1,1,1,1},
{1,1,1,1,1,4,1,0,0,0,0,0},
{0,0,0,0,1,0,1,0,0,0,0,0},
{0,0,0,0,1,0,1,0,0,0,0,0},
{0,0,0,0,1,3,1,0,0,0,0,0},
{0,0,0,0,1,1,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
},
{
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,1,1,1,1,0,0,0},
{0,0,0,1,1,1,0,5,1,0,0,0},
{0,0,0,1,0,0,4,0,1,0,0,0},
{0,0,1,1,0,1,0,1,1,1,0,0},
{0,0,1,0,0,1,0,1,3,1,0,0},
{0,0,1,0,1,0,0,4,3,1,0,0},
{0,0,1,0,4,0,0,0,3,1,0,0},
{0,0,1,1,1,1,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
},
{
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,1,1,0,0,0,0,0,0},
{0,1,0,0,0,1,0,0,0,0,0,0},
{0,1,0,4,4,1,0,1,1,1,0,0},
{0,1,5,4,0,1,0,1,3,1,0,0},
{0,1,1,1,0,1,1,1,3,1,0,0},
{0,0,1,1,0,0,0,0,3,1,0,0},
{0,0,1,0,0,0,1,0,0,1,0,0},
{0,0,1,0,0,0,1,1,1,1,0,0},
{0,0,1,1,1,1,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
},
{
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,1,1,1,1,1,1,0,0},
{0,1,0,0,0,1,0,0,0,1,0,0},
{0,1,0,4,4,4,4,4,0,1,0,0},
{0,1,0,4,0,4,0,4,0,1,1,0},
{0,1,0,0,0,0,0,0,4,0,1,0},
{1,1,0,1,1,1,1,0,4,0,1,0},
{1,0,8,3,3,3,3,1,0,0,1,0},
{1,0,3,3,3,3,3,0,0,1,1,0},
{1,1,1,1,1,1,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
},
{
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,1,1,1,1,1,1,1,0,0},
{0,0,0,1,3,0,3,0,3,1,0,0},
{0,0,0,1,0,4,4,4,0,1,0,0},
{0,0,0,1,3,4,5,4,3,1,0,0},
{0,0,0,1,0,4,4,4,0,1,0,0},
{0,0,0,1,3,0,3,0,3,1,0,0},
{0,0,0,1,1,1,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
}
};
int map2[5][12][12] =//爲了實現按R重置本關卡,定義了兩個一模一樣的map,map2
{
{
{0,0,0,1,1,1,0,0,0,0,0,0},
{0,0,0,1,3,1,0,0,0,0,0,0},
{0,0,0,1,0,1,1,1,1,1,1,1},
{1,1,1,1,4,0,4,0,0,0,3,1},
{1,3,0,0,4,5,1,1,1,1,1,1},
{1,1,1,1,1,4,1,0,0,0,0,0},
{0,0,0,0,1,0,1,0,0,0,0,0},
{0,0,0,0,1,0,1,0,0,0,0,0},
{0,0,0,0,1,3,1,0,0,0,0,0},
{0,0,0,0,1,1,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
},
{
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,1,1,1,1,0,0,0},
{0,0,0,1,1,1,0,5,1,0,0,0},
{0,0,0,1,0,0,4,0,1,0,0,0},
{0,0,1,1,0,1,0,1,1,1,0,0},
{0,0,1,0,0,1,0,1,3,1,0,0},
{0,0,1,0,1,0,0,4,3,1,0,0},
{0,0,1,0,4,0,0,0,3,1,0,0},
{0,0,1,1,1,1,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
},
{
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,1,1,0,0,0,0,0,0},
{0,1,0,0,0,1,0,0,0,0,0,0},
{0,1,0,4,4,1,0,1,1,1,0,0},
{0,1,5,4,0,1,0,1,3,1,0,0},
{0,1,1,1,0,1,1,1,3,1,0,0},
{0,0,1,1,0,0,0,0,3,1,0,0},
{0,0,1,0,0,0,1,0,0,1,0,0},
{0,0,1,0,0,0,1,1,1,1,0,0},
{0,0,1,1,1,1,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
},
{
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,1,1,1,1,1,1,0,0},
{0,1,0,0,0,1,0,0,0,1,0,0},
{0,1,0,4,4,4,4,4,0,1,0,0},
{0,1,0,4,0,4,0,4,0,1,1,0},
{0,1,0,0,0,0,0,0,4,0,1,0},
{1,1,0,1,1,1,1,0,4,0,1,0},
{1,0,8,3,3,3,3,1,0,0,1,0},
{1,0,3,3,3,3,3,0,0,1,1,0},
{1,1,1,1,1,1,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
},
{
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,1,1,1,1,1,1,1,0,0},
{0,0,0,1,3,0,3,0,3,1,0,0},
{0,0,0,1,0,4,4,4,0,1,0,0},
{0,0,0,1,3,4,5,4,3,1,0,0},
{0,0,0,1,0,4,4,4,0,1,0,0},
{0,0,0,1,3,0,3,0,3,1,0,0},
{0,0,0,1,1,1,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0},
}
};
void InitGame()//顯示地圖
{
int x, y;
for (int i = 0; i < 12; i++)
{
for (int j = 0; j < 12; j++)
{
x = j * 64;
y = i * 64;
switch (map[level][i][j])
{
case 0:
{
putimage(x, y, &blank);
putimage(200, 700, &button);
putimage(500, 700, &button2);
break;
}
case 1:
{
putimage(x, y, &wall);
putimage(200, 700, &button);
putimage(500,700, &button2);
break;
}
case 3:
{
putimage(x, y, &destination);
putimage(200, 700, &button);
putimage(500, 700, &button2);
break;
}
case 4:
{
putimage(x, y, &box);
putimage(200, 700, &button);
putimage(500, 700, &button2);
break;
}
case 5:
{
putimage(x, y, &man);
putimage(200, 700, &button);
putimage(500, 700, &button2);
break;
}
case 7:
{
putimage(x, y, &goodbox);
putimage(200, 700, &button);
putimage(500, 700, &button2);
break;
}
case 8:
{
putimage(x, y, &man);
putimage(200, 700, &button);
putimage(500, 700, &button2);
break;
}
}
}
}
}
void drawstratpage()//顯示開始界面
{
initgraph(rows * 64, cols * 64);
loadimage(&startpage, "F:/kungehaoshuai/Debug./startpage.jpg", rows * 64, cols * 64);
putimage(0, 0, &startpage);
setbkmode(TRANSPARENT);
settextstyle(50, 0, _T("宋體"));
outtextxy(120, 500, _T("歡迎來到推箱子小遊戲!"));
outtextxy(120, 600, _T("按任意鍵開始遊戲!"));
system("pause");
}
void movereason()//移動的原理
{
InitGame();
int r1, r2;
for (int i = 0; i < 12; i++)
for (int j = 0; j < 12; j++)
if (map[level][i][j] == 5 || map[level][i][j] == 8)
{
r1 = i; r2 = j;
}
if (kbhit())
{
char c = getch();
switch (c)
{
case 'w':
case 72:
case 'W':
{
/*
0代表空地
1代表牆壁
3代表目的地
4代表箱子
5代表人
7代表已經推到目的地的箱子
8代表人和目的地重疊的點,其實還是人
這個數字不是隨便定義的,是有原因的,詳情請細看函數movereason(移動原理)
*/
if (map[level][r1 - 1][r2] == 0 || map[level][r1 - 1][r2] == 3)//如果要前進的格子是空格或者是目的地,那麼肯定能走吧
{
map[level][r1 - 1][r2] += 5;//+5表示人站上去了
map[level][r1][r2] -= 5;//-5表示人走了
}
else if (map[level][r1 - 1][r2] == 4 || map[level][r1 - 1][r2] == 7)//如果要前進的格子是箱子或者是已經推到目的地的箱子呢麼肯定是要實現 “推動” 的功能
{
if (map[level][r1 - 2][r2] == 3 || map[level][r1 - 2][r2] == 0)//如果箱子的下一個位置是目的地或者是空地,那就可以推動
{
map[level][r1 - 2][r2] += 4;//該格子推來了個箱子
map[level][r1 - 1][r2] += 1;//4變成了5,代表人原來在這裏的箱子變成了人
map[level][r1][r2] -= 5;//原位置-5,恢復成初始的格子
}
}
break;
}
case 's':
case 'S':
case 80:
{
{
if (map[level][r1 + 1][r2] == 0 || map[level][r1 + 1][r2] == 3)//同上,一毛一樣,就是ctrl+c/v ^_^
{
map[level][r1 + 1][r2] += 5;
map[level][r1][r2] -= 5;
}
else if (map[level][r1 + 1][r2] == 4 || map[level][r1 + 1][r2] == 7)
{
if (map[level][r1 + 2][r2] == 3 || map[level][r1 + 2][r2] == 0)
{
map[level][r1 + 2][r2] += 4;
map[level][r1 + 1][r2] += 1;
map[level][r1][r2] -= 5;
}
}
}
break;
}
case 'd':
case 'D':
case 77:
{
{
if (map[level][r1][r2 + 1] == 0 || map[level][r1][r2 + 1] == 3)
{
map[level][r1][r2 + 1] += 5;
map[level][r1][r2] -= 5;
}
else if (map[level][r1][r2 + 1] == 4 || map[level][r1][r2 + 1] == 7)
{
if (map[level][r1][r2 + 2] == 3 || map[level][r1][r2 + 2] == 0)
{
map[level][r1][r2 + 2] += 4;
map[level][r1][r2 + 1] += 1;
map[level][r1][r2] -= 5;
}
}
}
break;
}
case 'a':
case 'A':
case 75:
{
{
if (map[level][r1][r2 - 1] == 0 || map[level][r1][r2 - 1] == 3)
{
map[level][r1][r2 - 1] += 5;
map[level][r1][r2] -= 5;
}
else if (map[level][r1][r2 - 1] == 4 || map[level][r1][r2 - 1] == 7)
{
if (map[level][r1][r2 - 2] == 3 || map[level][r1][r2 - 2] == 0)
{
map[level][r1][r2 - 2] += 4;
map[level][r1][r2 - 1] += 1;
map[level][r1][r2] -= 5;
}
}
}
break;
}
case 'r':
case 'R':
{
for (int i = 0; i < 12; i++)
for (int j = 0; j < 12; j++)
map[level][i][j] = map2[level][i][j];
break;
}
case 'q':
case 'Q':
{
level++;
break;
}
case 27:
{
exit(0);
}
}
}
}
void gameintroduce()//顯示遊戲玩法介紹界面
{
initgraph(rows * 64, cols * 64);
loadimage(&startpage, "F:/kungehaoshuai/Debug./background.jpg", rows * 64, cols * 64);
putimage(0, 0, &startpage);
setbkmode(TRANSPARENT);
settextstyle(50, 0, _T("宋體"));
outtextxy(120, 200, _T("玩法介紹:"));
outtextxy(120, 300, _T("基本玩法與經典推箱子一致"));
outtextxy(120, 400, _T("按r鍵重新開始本關"));
outtextxy(120, 500, _T("你能幫助Father Christman"));
outtextxy(120, 550, _T("把禮物送到每個孩子手中嗎?"));
system("pause");
}
void boxnum()//計算還沒有被推到目的地的箱子的數量
{
int i, j, boxnum = 0;
for (i = 0; i < 10; i++)
for (j = 0; j < 12; j++)
if (map[level][i][j] == 4)
boxnum++;
if (boxnum == 0)//所有的箱子都被推到目的地了
{
loadimage(&sky, "F:/kungehaoshuai/Debug./sky.jpg", rows * 64, cols * 64);
cleardevice();
putimage(0, 0, &sky);
settextstyle(50, 0, _T("黑體"));
outtextxy(200, 200, _T("恭喜你!過關了!"));
outtextxy(220, 260, _T("你真棒!"));
outtextxy(200, 320, _T("按任意鍵進入下一關!"));
system("pause");
level++;
}
}
int flag = 0;
void finish()//顯示遊戲結束提示界面
{
if (level > 4)//滿共4關,都過了意思就是通關了
{
loadimage(&background, "F:/kungehaoshuai/Debug./background.jpg", rows * 64, cols * 64);
putimage(0, 0, &background);
settextstyle(50, 0, _T("黑體"));
outtextxy(100, 200, _T("恭喜你!通過了全部關卡!"));
outtextxy(100, 350, _T("按任意鍵退出小遊戲"));
outtextxy(100, 500, _T("HAPPY NEW YEAR!"));
flag = 1;
system("pause");
}
}
int main()
{
mciSendString("open F:/kungehaoshuai/Debug//game_music3.mp3 alias bkmusic", NULL, 0, NULL);
mciSendString("play bkmusic", NULL, 0, NULL);
drawstratpage();//顯示開始界面
gameintroduce();//顯示遊戲介紹界面
loadimage(&background, "F:/kungehaoshuai/Debug./background.jpg", rows * 64, cols * 64);
putimage(0, 0, &background);
loadimage(&box, "F:/kungehaoshuai/Debug./box.jpg", 64, 64);
loadimage(&goodbox, "F:/kungehaoshuai/Debug./goodbox.jpg", 64, 64);
loadimage(&blank, "F:/kungehaoshuai/Debug./blank.jpg", 64, 64);
loadimage(&wall, "F:/kungehaoshuai/Debug./wall.jpg", 64, 64);
loadimage(&man, "F:/kungehaoshuai/Debug./man.jpg", 64, 64);
loadimage(&destination, "F:/kungehaoshuai/Debug./destination4.jpg", 64, 64);
loadimage(&button, "F:/kungehaoshuai/Debug./button.jpg", 200, 70);
loadimage(&button2, "F:/kungehaoshuai/Debug./q_副本.jpg", 200, 70);
while (1)
{
movereason();//遊戲進行的原理
boxnum();//每次移動完了以後計算一下是不是把所有的箱子都推動到目的地了
finish();//通關提示
if (flag == 1)//flag=1標誌着通過了所有關卡
break;
}
closegraph();
return 0;
}
代碼500行左右,但用數組存地圖就200行,加上各種重複的操作ctrl+c/v和註釋,最後核心東西不到200行。emm不過畢竟要答辯嘛,細細~
這是我們當時統一買的參考書,雖然上面沒有推箱子,但是有很多關於別的遊戲如飛機大戰,貪喫蛇的開發的講解。我就是從中學到的怎麼加載音樂,怎麼在圖片上顯示字體,怎麼清空畫布等基本知識。
想弄懂這推箱子怎麼做的,就兩個:1.怎麼樣實現推箱子的這個移動過程 和 2.怎麼加載圖片
1.推箱子過程的實現
這個很簡單,用到了C語言中的數組,選擇結構if else來編寫。大家看見那兩個長長的全是數字的數組了吧,那就是地圖,數字的含義如下:
0代表空地
1代表牆壁
3代表目的地
4代表箱子
5代表人
7代表已經推到目的地的箱子
8代表人和目的地重疊的點,其實還是人
移動的原理如下,以上移爲例
char c = getch();
switch (c)
{
case 'w':
case 72:
case 'W':
{
if (map[level][r1 - 1][r2] == 0 || map[level][r1 - 1][r2] == 3)//如果要前進的格子是空格或者是目的地,那麼肯定能走吧
{
map[level][r1 - 1][r2] += 5;//+5表示人站上去了
map[level][r1][r2] -= 5;//-5表示人走了
}
else if (map[level][r1 - 1][r2] == 4 || map[level][r1 - 1][r2] == 7)//如果要前進的格子是箱子或者是已經推到目的地的箱子呢麼肯定是要實現 “推動” 的功能
{
if (map[level][r1 - 2][r2] == 3 || map[level][r1 - 2][r2] == 0)//如果箱子的下一個位置是目的地或者是空地,那就可以推動
{
map[level][r1 - 2][r2] += 4;//該格子推來了個箱子
map[level][r1 - 1][r2] += 1;//4變成了5,代表人原來在這裏的箱子變成了人
map[level][r1][r2] -= 5;//原位置-5,恢復成初始的格子
}
}
break;
}
思路都註釋在代碼中了,大家仔細每種情況走一下應該不難理解。那麼其他三個方向是一摸一樣的,就是[i-1][j],[i][j+1],[i][j-1]這些的差別。
2.怎麼加載圖片
其實這一部分是當時最讓我困擾的,因爲不用加載圖片,用黑框框運行一樣能行。但我當時聽說我們班五十個人,有五個人都做得推箱子,那總得搞出點花樣兒來啊,不然老師憑什麼給你高分。於是我沒日沒夜刻苦鑽研(上網百度)終於加上了圖片,最後那五個推箱子裏,只有我得了19分,其他人都是17分^ ^(滿分20分,雖然只高了2分,但當時別提有多開心了)。
loadimage(&background, "F:/kungehaoshuai/Debug./background.jpg", rows * 64, cols * 64);
putimage(0, 0, &background);
當時試過的各種圖片和音樂,大家注意看路徑和圖片名
這是我自己摸索出來的可以讓屏幕上覆蓋上圖片的方法,當時上網查了無數版本,但好像都有點問題。大概就是這樣的:你想顯示哪張圖片,得先loadimage它,然後使用putimage函數把他顯示出來。這是我自己通俗的理解,可能不到位或者根本不對,歡迎大家給我批評指正。
結語:這篇文章希望能夠幫到那些需要做大作業的同學,和對C語言做推箱子游戲感興趣的同學,我的理解可能還很淺顯,如果有一些大佬對我的想法存在疑問,歡迎大家給我批評指正。祝大家學習上都有個好成績,都玩的開心,寫代碼寫的有成就感~