C語言實現(xiàn)推箱子游戲

很早就想過做點小游戲了,但是一直沒有機(jī)會動手雀费。今天閑來無事干奢,動起手來。過程還是蠻順利的盏袄,代碼也不是非常難律胀。今天給大家分享一下~

一、介紹

開發(fā)語言:C語言
開發(fā)工具:Dev-C++ 5.11
日期:2019年9月28日
作者:ZackSock

也不說太多多余的話了貌矿,先看一下效果圖:


效果圖

游戲中的人物、箱子罪佳、墻壁逛漫、球都是字符構(gòu)成的。通過wasd鍵移動赘艳,規(guī)則的話就是推箱子的規(guī)則酌毡,也就不多說了。

二蕾管、代碼實現(xiàn)

關(guān)于代碼方面枷踏,我盡可能講的細(xì)致。希望大家可以理解~

(1)方法列表

//主函數(shù)
void main();

//初始化一些數(shù)據(jù)
initData();

//在控制臺上打印地圖
drawMap();

//向上移動
moveUp();

//向左移動
moveLeft()

//向下移動
moveDown()

//向右移動
moveRight();

這幾個方法都顧名思義掰曾,而且用意也非常明確旭蠕,就initData可能不知道具體用處,但是沒有什么大問題旷坦。唯一的問題就是掏熬,上左下右的順序可能會逼死幾個強(qiáng)迫癥患者,哈哈秒梅。

(2)參數(shù)列表

為了方便旗芬,我把include和宏定義也放到參數(shù)列表當(dāng)中

//導(dǎo)入函數(shù)庫
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

//宏定義
#define WIDTH 8
#define HEIGHT 8

//定義地圖數(shù)組,二維數(shù)組有兩個維度捆蜀,而地圖也是二維的矩形
int map[HEIGHT][WIDTH] = {
    {0, 0, 1, 1, 1, 0, 0, 0},
    {0, 0, 1, 4, 1, 0, 0, 0},
    {0, 0, 1, 0, 1, 1, 1, 1},
    {1, 1, 1, 3, 0, 3, 4, 1},
    {1, 4, 0, 3, 2, 1, 1, 1},
    {1, 1, 1, 1, 3, 1, 0, 0},
    {0, 0, 0, 1, 4, 1, 0, 0},
    {0, 0, 0, 1, 1, 1, 0, 0} 
};

//人的位置疮丛,在二維地圖中,我們可以用坐標(biāo)表示一個人的位置辆它,就好比經(jīng)緯度
int x, y;

//箱子的個數(shù)誊薄,推箱子肯定要有箱子嘛。
int boxs;

這里參數(shù)不多娩井,其中橫為x暇屋,縱為y,另外這里再規(guī)定一下map的一些東西:

/**
*   0   表示空
*   1   表示墻
*   2   表示人
*   3   表示箱子
*   4   表示目的地(球)
*   5   表示已完成的箱子
*/

(3)函數(shù)具體分析

接下來我們一個一個函數(shù)來分析洞辣。

1咐刨、main函數(shù)
int main(int argc, char *argv[]) {
    char direction;     //存儲鍵盤按的方向 
    initData();         //初始化一些數(shù)據(jù)
    
    //開始游戲的循環(huán)昙衅,這里是個死循環(huán),每按一次按鈕循環(huán)一次
    while(1){
        //每次循環(huán)的開始清除屏幕
        system("cls");
        //繪畫地圖
        drawMap();

        //判斷定鸟,當(dāng)boxs的數(shù)量0時而涉,!0為真,然后走break跳出循環(huán)(結(jié)束游戲) 
        if(!boxs){
            break;
        }
        
        //鍵盤輸入方向联予,這里使用getch啼县,因為getch讀取字符不會顯示在屏幕上
        direction = getch();
        
        //用switch判斷用戶輸入的方向
        switch(direction){
            case 'w':
                //按w時,調(diào)用向上移動函數(shù)
                moveUp();
                break;
            case 'a':
                //按a時沸久,調(diào)用向左移動函數(shù)
                moveLeft(); 
                break;
            case 's':
                moveDown();
                break;
            case 'd':
                moveRight();
                break; 
        }
    }  
    //當(dāng)跳出循環(huán)時季眷,運行該語句,游戲結(jié)束
    printf("恭喜你完成游戲卷胯!※");
    return 0;
}

我大概說一下流程子刮,循環(huán)外面沒有什么特別的。initData()只是一些簡單數(shù)據(jù)的初始化窑睁,不需要太在意挺峡。循環(huán)中大致流程如下:

  • 清除屏幕
  • 繪制地圖
  • 判斷游戲是否結(jié)束
  • 對用戶按下的按鈕進(jìn)行反饋

進(jìn)入循環(huán)體,先清除屏幕担钮,再繪制地圖橱赠,然后再判斷游戲是否結(jié)束◇锝颍可能大家對這個順序不是很理解狭姨,這里我們先不考慮判斷游戲結(jié)束的問題。我們把清屏和繪制地圖合在一起鲤嫡,簡稱“重繪地圖”送挑,而游戲結(jié)束的判斷先不考慮,那么流程就簡化為“重繪地圖 + 響應(yīng)用戶的操作”暖眼。簡單來說就是惕耕,用戶按一下按鈕,我改變一下地圖诫肠。

2司澎、initData()

void initData(){
    int i, j;
    
    //加載數(shù)據(jù)時讓用戶等待,一般情況加載數(shù)據(jù)比較快
    printf("游戲加載中栋豫,請稍后........."); 
    
    //遍歷地圖中的數(shù)據(jù)
    for(i = 0; i < HEIGHT; i++){
        for(j = 0; j < WIDTH; j++){
            //遍歷到2(人)時挤安,記錄人的坐標(biāo)。x丧鸯, y是前面定義的全局變量
            if(map[i][j] == 2){
                x = j;
                y = i;
            } 
            //遍歷到3時蛤铜,箱子的數(shù)目增加。boxs是前面定義的全局變量 
            if(map[i][j] == 3){
                boxs++;
            }
        }
    } 
}

這個方法很簡單,就是遍歷地圖围肥,然后初始化人的位置和箱子的個數(shù)剿干。這里有一點要注意一下,就是到底內(nèi)層循環(huán)是WIDTH還是外層循環(huán)是WIDTH穆刻。
地圖數(shù)組

如圖置尔,在遍歷過程中。外層循環(huán)控制行數(shù)氢伟,即HEIGHT榜轿。那么內(nèi)層循環(huán)應(yīng)該是WIDTH。

3朵锣、drawMap()

void drawMap(){
    int i, j;
    for(i = 0; i < WIDTH; i++){
        for(j = 0; j < HEIGHT; j++){
            switch(map[i][j]){
                case 0:
                    printf("  ");
                    break;
                case 1:
                    printf("■");
                    break;
                case 2:
                    printf("♀");
                    break;
                case 3:
                    printf("◆");
                    break;
                case 4:
                    printf("●");
                    break;
                case 5:
                    printf("★");
                    break; 
            }
        }
        printf("\n");
    }
}

這里也非常簡單谬盐,變量map中的元素,然后通過switch判斷應(yīng)該輸出的內(nèi)容诚些。然后內(nèi)層循環(huán)每走完一次就換行设褐。

4、moveUp()

這個函數(shù)內(nèi)容有點多泣刹,想講一下大概思路:

向上移有兩種情況
1、前面為空白
    這種情況有兩個步驟
    (1)將人當(dāng)前的位置設(shè)置為空白(0)犀被,
    (2)再講人前面的位置設(shè)置為人(2)
2椅您、前面為箱子
    當(dāng)前面為箱子時有三種情況
    1、箱子前面為空白
        移動人和箱子寡键,這個操作有三個步驟
        (1)將人當(dāng)前位置設(shè)置為空(0)
        (2)將箱子位置設(shè)置為人(2)
        (3)將箱子前面設(shè)置為箱子(3)
    2掀泳、箱子前面為墻
        這種情況不需要做任何操作
    3、箱子前面為終點
        這種情況有四個個步驟
        (1)將人的位置設(shè)置為空(0)
        (2)將箱子的位置設(shè)置為人(2)
        (3)將終點位置設(shè)置為★(5)
        (4)箱子boxs的數(shù)量減一
3西轩、前面為墻
    這種情況最簡單员舵,不需要做任何操作
4、前面為終點
    我這里沒有考慮太多藕畔,這種情況不做操作马僻。(如果更換地圖的話可能需要修改代碼)

具體代碼如下,解析我全寫在注釋里面:

void moveUp(){
    //定義變量存放人物上方的坐標(biāo)
    int ux, uy; 
    
    //當(dāng)上方?jīng)]有元素時注服,直接return (其實人不可能在邊緣)
    if(y == 0){
        return;
    }
    
    //記錄上方坐標(biāo)韭邓,x為橫,y為縱溶弟,所有ux = x, uy = y - 1;
    ux = x;
    uy = y - 1; 
    
    //上方為已完成的箱子
    if(map[uy][ux] == 5){
        return;
    } 
    //假設(shè)上方為墻女淑,直接return,這個和上面的判斷可以合在一起辜御,這里為了看清楚分開寫 
    if(map[uy][ux] == 1){
        return;
    }
    
    //假設(shè)上方為箱子
    if(map[uy][ux] == 3){
        //判斷箱子上方是否為墻 
        if(map[uy - 1][ux] == 1){
            return;
        }
        
        //判斷箱子上方是否為終點
        if(map[uy - 1][ux] == 4){
            //將箱子上面內(nèi)容賦值為5★ 
            map[uy - 1][ux] = 5;
            map[uy][ux] = 0;
                    
            //箱子的數(shù)目減1   
            boxs--; 
        }else{
            //移動箱子
            map[uy - 1][ux] = 3;
        }
    }
    //當(dāng)上面幾種return的情況都沒遇到鸭你,人肯定會移動,移動操作如下
    map[y][x] = 0;
    map[uy][ux] = 2;
    //更新人的坐標(biāo)
    y = uy; 
} 

這是一個方向的,其它方向要考慮的問題也和前面一樣袱巨,我也就不贅述了阁谆。

6、moveLeft()

這里大致都和上面一樣瓣窄,就是在記錄左邊坐標(biāo)時笛厦,應(yīng)該應(yīng)該是lx = x - 1。

void moveLeft(){
    //定義變量存放人物左邊的坐標(biāo)
    int lx, ly; 
    
    //當(dāng)左邊沒有元素時俺夕,直接return 
    if(x == 0){
        return;
    }
    
    //記錄左邊坐標(biāo)
    lx = x - 1;
    ly = y; 
    
    //左邊為已完成方塊
    if(map[ly][lx] == 5){
        return;
    } 
    
    //假設(shè)左邊為墻裳凸,直接return 
    if(map[ly][lx] == 1){
        return;
    }
    
    //假設(shè)左邊為箱子
    if(map[ly][lx] == 3){
        //判斷箱子左邊是否為墻 
        if(map[ly][lx - 1] == 1){
            return;
        }
        
        //判斷箱子左邊是否為球
        if(map[ly][lx - 1] == 4){
            //將箱子左邊內(nèi)容賦值為5★ 
            map[ly][lx - 1] = 5;
            map[ly][lx] = 0;
        
            //箱子的數(shù)目減1 
            boxs--; 
        }else{
            //移動箱子 
            map[ly][lx - 1] = 3; 
        }
    }
    map[y][x] = 0;
    map[ly][lx] = 2;
    x = lx; 
}

7、moveDown()

這里在判斷邊界時劝贸,判斷的是 y == HEIGHT - 1姨谷。

void moveDown(){
    //定義變量存放人物下方的坐標(biāo)
    int dx, dy; 
    
    //當(dāng)下方?jīng)]有元素時,直接return 
    if(y == HEIGHT - 1){
        return;
    }
    
    //記錄下方坐標(biāo)
    dx = x;
    dy = y + 1; 
    
    //下方為已完成方塊
    if(map[dy][dx] == 5){
        return;
    } 
    
    //假設(shè)下方為墻映九,直接return 
    if(map[dy][dx] == 1){
        return;
    }
    
    //假設(shè)下方為箱子
    if(map[dy][dx] == 3){
        //判斷箱子下方是否為墻 
        if(map[dy + 1][dx] == 1){
            return;
        }
        
        //判斷箱子下方是否為球
        if(map[dy + 1][dx] == 4){
            //將箱子下面內(nèi)容賦值為5★ 
            map[dy + 1][dx] = 5;
            map[dy][dx] = 0;
            
            //箱子的數(shù)目減1 
            boxs--; 
        }else{
            //移動箱子
            map[dy + 1][dx] = 3; 
        }
    }
    map[y][x] = 0;
    map[dy][dx] = 2;
    y = dy; 
}

8梦湘、moveRight()

這里也沒什么特別說的:

void moveRight(){
    //定義變量存放人物右邊的坐標(biāo)
    int rx, ry; 
    
    //當(dāng)右邊沒有元素時,直接return 
    if(x == WIDTH - 1){
        return;
    }
    
    //記錄右邊坐標(biāo)
    rx = x + 1;
    ry = y; 
    
    //右邊為已完成方塊
    if(map[ry][rx] == 5){
        return;
    } 
    
    //假設(shè)右邊為墻件甥,直接return 
    if(map[ry][rx] == 1){
        return;
    }
    
    //假設(shè)右邊為箱子
    if(map[ry][rx] == 3){
        //判斷箱子右邊是否為墻 
        if(map[ry][rx + 1] == 1){
            return;
        }
        
        //判斷箱子左邊是否為球
        if(map[ry][rx + 1] == 4){
            //將箱子右邊內(nèi)容賦值為5★ 
            map[ry][rx + 1] = 5;
            map[ry][rx] = 0;
            
            //箱子的數(shù)目減1 
            boxs--; 
        }else{
            //移動箱子 
            map[ry][rx + 1] = 3; 
        }
    }
    map[y][x] = 0;
    map[ry][rx] = 2;
    x = rx; 
}

三捌议、總結(jié)

現(xiàn)在再回顧開始的運行步驟

  • 清除屏幕
  • 繪制地圖
  • 判斷游戲是否結(jié)束
  • 對用戶按下的按鈕進(jìn)行反饋

這里把判斷游戲是否結(jié)束放到了重繪圖像后面,因為在對用戶進(jìn)行反饋的時候只是改變了map中的數(shù)據(jù)引有,實際上最后一個箱子推到終點的圖像還沒有顯示出來瓣颅,所以要在重繪之后再判斷是否結(jié)束游戲。

代碼有很多冗余的地方譬正,一方面是想大家更好的理解宫补,還有一方面出于懶。哈哈曾我,代碼運行起來沒有問題粉怕,源碼和源程序我會上傳,有興趣的可以下下來抒巢,或者直接復(fù)制代碼運行也是沒問題的贫贝。
百度云連接如下:
鏈接:https://pan.baidu.com/s/1pwEKt3VTKmssDgU6dLx0pg 提取碼:4o9v

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蛉谜,隨后出現(xiàn)的幾起案子平酿,更是在濱河造成了極大的恐慌,老刑警劉巖悦陋,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜈彼,死亡現(xiàn)場離奇詭異,居然都是意外死亡俺驶,警方通過查閱死者的電腦和手機(jī)幸逆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門棍辕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人还绘,你說我怎么就攤上這事楚昭。” “怎么了拍顷?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵抚太,是天一觀的道長。 經(jīng)常有香客問我昔案,道長尿贫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任踏揣,我火速辦了婚禮庆亡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捞稿。我一直安慰自己又谋,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布娱局。 她就那樣靜靜地躺著彰亥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪衰齐。 梳的紋絲不亂的頭發(fā)上剩愧,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音娇斩,去河邊找鬼。 笑死穴翩,一個胖子當(dāng)著我的面吹牛犬第,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播芒帕,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼歉嗓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了背蟆?” 一聲冷哼從身側(cè)響起鉴分,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎带膀,沒想到半個月后志珍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡垛叨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年伦糯,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡敛纲,死狀恐怖喂击,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情淤翔,我是刑警寧澤翰绊,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站旁壮,受9級特大地震影響监嗜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寡具,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一秤茅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧童叠,春花似錦框喳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至杜秸,卻和暖如春放仗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背撬碟。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工诞挨, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人呢蛤。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓惶傻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親其障。 傳聞我的和親對象是個殘疾皇子银室,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容