C 實現 Atari 經典街機遊戲《飛天蜈蚣》

最後一次更新於2019/07/08git

效果演示圖

game gif

這是一個用C語言實現的《飛天蜈蚣》的「復刻」,因爲Linux操做系統下的圖形庫不夠繪製絢麗的畫面,所以在本程序中僅用簡單的符號來表明不一樣的遊戲角色。
訪問本油管連接能夠阿達雅游戲的視頻: https://www.youtube.com/watch...github

基本介紹

注意: 本程序須要引入 ncurses 庫, 這個庫只存在於 Unix 的操做系統. 在開始運行以前,建議您經過如下 Debian/Ubuntu Linux 的命令行安裝須要的庫:數組

sudo apt−get install libncurses5−dev libncursesw5−dev
//libncurses5−dev: Developer’s libraries for ncurses
//libncursesw5−dev: Developer’s libraries for ncursesw

問題重申

基於原始的雅達利遊戲, 我重寫了部分規則. 爲了使個人程序畫面更加穩定和流程,我作了如下必要的前提條件.緩存

遊戲規則

蠍子是遊戲裏另外一個獨斷獨行的角色. 相比於蜘蛛, 它就徹底是個壞蛋, 它雖然只能向左或向右走可是一旦它碰到玩家玩家就會失去一條生命.
多線程

蜈蚣

蜈蚣是玩家最主要要消滅的敵人. 若是沒有任何障礙物, 蜈蚣只會一行接着一行地往下走.每次玩家擊中蜈蚣的部位都會變成一個蘑菇. 若是擊中的部位既不在頭也不在尾, 蜈蚣就會本身撕裂成兩部分, 得到新的頭部並向上或向下接近玩家. 除此以外, 若是蜈蚣遇到蘑菇或者牆壁時, 它會拐彎下移. 儘可能讓本身遠離蜈蚣,若是被它碰到的話也會失去生命哦!
app

爲了讓上述規則更易懂, 我建立了以下關係表格:框架

撞擊關係 玩家 子彈 蘑菇 蜈蚣
蜘蛛 玩家死亡, 失誤一次生命 蜘蛛死亡, 得到600分 蘑菇消失, 蘑菇總數 - 1 不考慮
蠍子 玩家死亡, 失誤一次生命 蠍子死亡, 得到600分 不考慮 不考慮
蜈蚣 玩家死亡, 失誤一次生命 擊中頭部給100分,其他狀況給10分 蜈蚣掉頭 其中一隻蜈蚣掉頭
蘑菇 蘑菇消失, 蘑菇總數 - 1 4 次成功射擊後得到1分 不考慮 不考慮

除此之外, 我在遊戲中設置了不一樣關卡. 每一個關卡初始的蜈蚣身節是相等的. 但隨着蘑菇數量的增多, 玩家很難能擊中蜈蚣. 更不用說蠍子和蜘蛛了.less

基本猜測

我在運行程序的時候遇到許多非程序性問題. 所以, 我羅列出必要的假設:ide

  • 在每一個關卡最開始的時候, 若是蘑菇所在位置和蜈蚣初始位置重合, 玩家能夠認爲該蘑菇會被蜈蚣吃掉.
  • 蜘蛛只會出如今遊戲窗口的下端. 相反的, 蠍子只會出如今遊戲窗口的上方.
  • 玩家的操控範圍不能超過遊戲窗口的邊界. 除此以外, 玩家不能穿越蘑菇但子彈能夠.
  • 蜈蚣被射中的身節會變成蘑菇, 這些新生成的蘑菇會隨着失敗重玩或闖關成功而存在除非被吃掉或擊中.

遊戲流程圖

flow

下圖詳細地介紹整個遊戲的流程, 包括碰撞結果, 分數更新和角色移動等. 在這些過程當中, 每次計算機從 I/O 得到運行順序並決定是知足碰撞條件, 而後, 再創新畫布並更新分數.
若是您仍然不熟悉遊戲流程,可訪問下方的油管連接觀看遊戲通關全過程:
https://www.youtube.com/watch...
或者查看原始的流程圖:
https://www.processon.com/vie...函數

函數實現

開始菜單

首先, 我須要建立一個主菜單來簡單介紹遊戲規則. 這個滾動菜單須要給用戶提供兩種選擇: 開始遊戲或退出遊戲.

/* 這些選項將在介紹菜單裏顯示*/
char *startMenu[] = {
        "Start", "Exit",
};

/* 介紹菜單裏的文字內容*/
char *readme[] = {
        "*Centipede Game*",
        " ",
        "This game is written by Hephaest, Lancaster University.",
        "Before the game, you'd better recognize these following characters.",
        " ",
        "*Character*",
        " ",
        "_^_",
        "|H|  Master",
        "---",
        " ",
        "This role is for you! you can move it by pressing keyboard:",
        "KEY_LEFT, KEY_RIGHT, KEY_UP and KEY_DOWN.",
        "Besides, you can fire bullets by pressing the blank space key.",
        " ",
        "*********O Centipede",
        " ",
        "This is the major character you need to shoot.",
        "Once it hits you, you lose your life.",
        "You only have 4 lives, be careful!",
        " ",
        "& Mushroom",
        " ",
        "This is a barrier, you cannot walk through it unless you destroy it.",
        "Once you destroy one of them, you will lose 4 bullets.",
        "Don't worry, you have tons of bullets.",
        " ",
        "_$ Sea monster",
        " ",
        "This character walks line by line. You can shoot it and get a bonus.",
        "However, once it hits you, you lose your life.",
        " ",
        "^W^ Spider",
        " ",
        "This character crawls all the time.",
        "Spider could either help you by eating mushrooms or hitting you.",
        " ",
        "*Tips*",
        " ",
        "Press \'P\' or \'p\' to pause.",
        "Press \'C\' or \'c\' to continue.",
        "Press \'Q\' or \'q\' to quit or replay.",
        " ",
        "All in all, be careful! Hope you enjoy the game!",
        " ",
};

/* 下列選項將出如今退出菜單 */
char *quitMenu[] = {
        "No",
        "Yes",
        "Replay",
};

在此以後, 我須要建立一個窗口來顯示這個菜單. 由於我沒有找到滾動菜單的UI框架, 只要再每行文字裏追加滾動菜單屬性.

/* 此功能是簡介菜單裏用戶指針使用 */
void start()
{
    ITEM **start_items;
    int c;
    MENU *menu;
    WINDOW *start_menu;
    int n_startMenu, item, n_readme;

    /* Curses initialization */
    initscr();
    start_color();
    cbreak();
    noecho();
    init_pair(1, COLOR_WHITE, COLOR_BLACK);
    init_pair(2, COLOR_RED, COLOR_YELLOW);

    /* 建立介紹菜單選項.
     * 如下操做主要旨在實現如下要求:
     * 1. Readme 只是用來顯示文字的.
     * 2. 高亮全部在開始菜單裏的選項.
     */
    n_startMenu = ARRAY_SIZE(startMenu);
    n_readme = ARRAY_SIZE(readme);
    start_items = (ITEM **)calloc(n_startMenu+n_readme+1, sizeof(ITEM *));
    for(item = 0; item < n_readme; item++)
    {
        start_items[item] = new_item(readme[item], readme[item]);
        item_opts_off(start_items[item], O_SELECTABLE);
        set_item_userptr(start_items[item], Click);
    }
    int cho = 0;
    for(;item < n_readme + n_startMenu; item++)
    {
        start_items[item] = new_item(startMenu[cho], startMenu[cho]);
        /* Set the user pointer, even in readme*/
        set_item_userptr(start_items[item], Click);
        cho++;
    }
    /* 設置最後一個爲 NULL */
    start_items[n_readme + n_startMenu] = (ITEM *)NULL;
    /* 建立菜單 */
    menu = new_menu((ITEM **)start_items);

    /* 這些顏色設置將應用於開始選項*/
    set_menu_fore(menu, COLOR_PAIR(2));
    set_menu_back(menu, COLOR_PAIR(1));
    set_menu_grey(menu, COLOR_PAIR(1));
    menu_opts_off(menu, O_SHOWDESC);

    /* 建立窗戶並讓他位於屏幕中央 */
    int max_y, max_x;
    getmaxyx(stdscr, max_y, max_x);
    int x_position = (max_x-box_length)/2;
    int y_position = (max_y-box_width)/2;
    start_menu = newwin(box_width, box_length, y_position, x_position);
    keypad(start_menu, TRUE);//To listen keyboard

    /* 設置主窗口和子窗口 */
    set_menu_win(menu, start_menu);
    set_menu_sub(menu, derwin(start_menu, box_width-1, box_length-2, 1, 1));
    set_menu_format(menu, 18, 1);// Set 18 lines inside the box and one choices each line

    /* 建立邊界框 */
    box(start_menu, 0, 0);
    refresh();

    /* 發佈菜單 */
    post_menu(menu);
    wrefresh(start_menu);

    while((c = wgetch(start_menu)) != KEY_END)
    {
        switch(c)
        {    case KEY_DOWN:
                menu_driver(menu, REQ_DOWN_ITEM);
                break;
            case KEY_UP:
                menu_driver(menu, REQ_UP_ITEM);
                break;
            case KEY_NPAGE:
                menu_driver(menu, REQ_SCR_DPAGE);
                break;
            case KEY_PPAGE:
                menu_driver(menu, REQ_SCR_UPAGE);
                break;
            case 10: /* This is Enter key*/
            {
                /*To point to current choice*/
                ITEM *cur;
                void (*p)(char *);
                cur = current_item(menu);
                p = item_userptr(cur);
                p((char *)item_name(cur));
                pos_menu_cursor(menu);
                break;
            }
        }

        wrefresh(start_menu);
    }

    /* 清除菜單並清除緩存 */
    unpost_menu(menu);
    for(int i = 0; i < n_startMenu + n_readme; ++i)
        free_item(start_items[i]);
    free_menu(menu);
    /* 退出當前窗口並等待下一個窗口*/
    endwin();
}

主菜單被分爲2部分: 第一部分是文字(只是用來顯示, 就算點擊了也無任何響應). 在第二部分, 設置用戶指針容許玩家作出選擇. 爲了提醒玩家肯定本身的選擇, 我高亮每一個選項的背景色.

/* 過濾選擇 */
void Click(char *choice)
{
    if(choice == "Exit")
    {
        endwin();
        exit(0);
    }
    if(choice == "Start") {
        clear();
        refresh();
        Initialization();
        GameInterface();
    }
}

以上代碼的運行結果以下圖所示.

圖片描述

角色扮演

蘑菇 被隨機地放置在整個遊戲窗口, 可使用循環語句查找位置重疊的蘑菇並給它們從新設置新的位置.

/*
 * 建立蘑菇
 * 如下操做主要旨在實現如下要求:
 * 1. 蘑菇的位置應隨機生成.
 * 2. 第一行和後三行被蜈蚣和玩家預先佔有.
 */
void MushroomProduce(int y, int x)
{
    int i = 0;
    mushLength = 20;
    srand(time(NULL));
    do {
        mushroom[i].x = rand() % (x - 3) + 1;
        mushroom[i].y =rand() % (y - 4) + 1;
        for (int j = 0; j < mushLength; j++) {
            if (mushroom[i].x == mushroom[j].x && mushroom[i].y == mushroom[j].y && i != j)
            {
                mushroom[i].x = rand() % (x - 2);
                mushroom[i].y =rand() % (y - 4) + 1;
            }
        }
        i++;
    } while (i < mushLength); /* 直到沒有重疊 */
    for(int i = 0; i < mushLength; i++) mushroom[i].mush_record=4;
}

玩家 經過使用 getmaxyx() 函數來放置在遊戲窗口下方中央的位置.

/*
 * 玩家建立
 * 如下操做主要旨在實現如下要求:
 * 1. 確保玩家在遊戲窗口底端的中央位置.
 */
void MasterProduce(WINDOW *win)
{
    int win_y;
    int win_x;
    getmaxyx(win, win_y, win_x);
    master_1_y = win_y - 3;
    master_1_x = (win_x - master_length) / 2;
    master_2_y = win_y - 2;
    master_2_x = (win_x - master_length) / 2;
    master_3_y = win_y - 1;
    master_3_x = (win_x - master_length) / 2;
    curr_bullet_y = master_1_y;
    curr_bullet_x = master_1_x + 1;
    /* 繪製窗口 */
    mvwprintw(win, master_1_y, master_1_x, "_");
    mvwprintw(win, master_1_y, master_1_x + 1, "^");
    mvwprintw(win, master_1_y, master_1_x + 2, "_");
    mvwprintw(win, master_2_y, master_2_x, "|") ;
    mvwprintw(win, master_2_y, master_2_x + 1, "H");
    mvwprintw(win, master_2_y, master_2_x + 2, "|");
    mvwprintw(win, master_3_y, master_3_x, "-");
    mvwprintw(win, master_3_y, master_3_x + 1, "-");
    mvwprintw(win, master_3_y, master_3_x + 2, "-");
}

蜈蚣 經過 getmaxyx() 方法放置在遊戲窗口上方的中央位置並使用 positions(x,y) 從尾到頭賦值.

/*
 * 建立蜈蚣
 * 從尾部開始
 */
void CentipedeProduce(int x)
{
    int i;
    Length = 10;
    int start_x = (x - Length) / 2;
    for (i = Length - 1; i >= 0; i--) {
        Centipede[i].x = start_x;
        Centipede[i].y = 0;
        start_x++;
    }
    for(int i = 0; i < Length; i++)
    {
        for(int j = 0; j < mushLength; j++)
        {
            if(Centipede[i].x == mushroom[j].x && Centipede[i].y == mushroom[j].y)
            {
                j++;
                while (j < mushLength) {
                    mushroom[j - 1].x = mushroom[j].x;
                    mushroom[j - 1].y = mushroom[j].y;
                    mushroom[j - 1].mush_record = mushroom[j].mush_record;
                    j++;
                }
                mushLength -= 1;
            }
        }
        Centipede[i].head = -1;
        Centipede[i].Clear = -1;
        Centipede[i].x_direction = 1;
        Centipede[i].y_direction = 1;
    }
    Centipede[0].head = 0;
}

對於蜘蛛和蠍子而言, 然而, 它們並不老是出如今固定位置, 須要使用變量 num 來決定從遊戲窗口左側仍是右側出現. 除此以外, 可使用另外一個隨機變量控制它們的出現與否 (詳見下方源碼).

/*
 * 建立蠍子
 * 如下操做主要旨在實現如下要求:
 * 確保蠍子隨機出現.
 */
void Sea_MonsterProduce(int y, int x)
{
    srand(time(NULL));
    int start_y = rand() % y/2+1;
    int num = rand() % 2;
    if(num == 0)
    {
        Sea_Monster[0].x = 1;
        Sea_Monster[0].y = start_y;
        Sea_Monster[1].x = 0;
        Sea_Monster[1].y = start_y;
        Sea_Monster[0].x_direction = 1;
    }
    if(num == 1)
    {
        Sea_Monster[0].x = x - 2;
        Sea_Monster[0].y = start_y;
        Sea_Monster[1].x = x - 1;
        Sea_Monster[1].y = start_y;
        Sea_Monster[0].x_direction = -1;
    }
}

/*
 * 建立蜘蛛
 * 如下操做主要旨在實現如下要求:
 * 確保蜘蛛隨機出現.
 */
void SpiderProduce(int y, int x)
{
    int start_y = y - (rand() % y / 2 + 1);
    if(num == 1)
    {
        int start_x = 0;
        for(int i = 0; i < 3; i++)
        {
            Spider[i].x = start_x;
            Spider[i].y = start_y;
            start_x++;
            Spider[i].x_direction = 1;
            Spider[i].y_direction = 1;
        }
    }
    if(num == -1)
    {
        int start_x = x - 2;
        for(int i = 0; i < 3; i++)
        {
            Spider[i].x = start_x;
            Spider[i].y = start_y;
            start_x--;
            Spider[i].x_direction = -1;
            Spider[i].y_direction = 1;
        }
    }
}

隨機函數:

/* 當蜈蚣在移動使,蠍子和蜘蛛也有隨機出現的機會 */
    srand(time(NULL));
    int ran_sea = rand() % 10;
    if(ran_sea == 1) sea_appear=1;
    int ran_spider = rand() % 5;
    if(ran_spider == 1) spider_appear = 1;

移動的敵人

這些敵人包括蜘蛛, 蠍子和蜈蚣. 前二者比後者在處理上簡單不少.

monster

如上圖所示, 蠍子是平行移動的, 並會撞上游戲窗口的邊界, 計算機會給它一個初始位置並讓它消失在遊戲窗口. 而蜘蛛的爬行是很隨意的, 無規律可言. 然而, 我使用了比 飛天蜈蚣 種更復雜的方法實現蜘蛛的爬行: 在 x 和 y 方向上的二維隨機數. 但這也可能會形成蜘蛛在同一條軌跡上來回爬行 (詳見下圖) 但因爲隨機數 x 和 y 都是非負數所以蜘蛛最終會總窗口一端爬向窗口另外一端.

trace

除此之外, 我設置了蜘蛛 x 和 y 方向的邊界條件以防它撞上本身的同伴蠍子. 另外, 我設置了布爾二值來延緩蜘蛛爬行的速度並讓他依次從遊戲窗口的左邊或右邊出現.

/* 移動的蠍子.
 * 此功能爲了實現蠍子的水平移動.
 */
void sea_monsterMove(int x)
{
        Sea_Monster[1].x = Sea_Monster[0].x;
        Sea_Monster[0].next_x = Sea_Monster[0].x + Sea_Monster[0].x_direction;
        /* 若是蠍子即將走到窗口邊界, 使其消失 */
        if (Sea_Monster[0].next_x > x || Sea_Monster[0].next_x < 0)
        {
            Sea_Monster[0].x = -1;
            Sea_Monster[1].x = -1;
            sea_appear = 0;
        }
        else
        {
            Sea_Monster[0].x += Sea_Monster[0].x_direction;
        }
}

/* 移動的蜘蛛.
 * 此功能爲了實現蜘蛛在 x 和 y 方向的移動.
 */
void SpiderMove(int x,int y)
{
    srand(time(NULL));
    int ran_y = y-(rand() % y / 2 + 3);
    int ran_x = rand() % 2;
    for(int j = 0; j < 3; j++)
    {
        Spider[j].next_x = Spider[j].x + ran_x*Spider[j].x_direction;
        Spider[j].next_y = Spider[j].y + Spider[j].y_direction;
        /* 若是蜘蛛即將走到窗口邊界, 重設初始位置並使其消失 */
        if (Spider[j].next_x > x || Spider[j].next_x < 0)
        {
            Spider[0].x = -1;
            Spider[1].x = -1;
            Spider[2].x = -1;
            spider_appear = 0;
            num *= -1;
            break;
        }
        /* 若是蜘蛛將觸碰遊戲窗口的下邊界, 更改 y 方向*/
        if(Spider[j].next_y > y - 1 || Spider[j].next_y < ran_y)
        {
            Spider[j].y_direction *= -1;
        }
            Spider[j].x += ran_x*Spider[j].x_direction;
            Spider[j].y += Spider[j].y_direction;
    }
}

就蠍子而言, 它是本遊戲中最複雜的狀況. 我將我能想到的全部狀況都在下方列出. 我主要使用方法是結構體和數組. 由於碰撞每次都須要考慮每一個身節的變化狀況, 所以數組更容易幫我定位到指定身節. 每一次移動, 只須要處理蜈蚣頭部運動狀況, 並讓它的身節跟着頭部運動便可. 最後, 重刷畫布. 具體的操做已在下方源碼中列出.

hitMush

hitWall

/*
 * 此功能用於肯定蜈蚣的每一個身節應該到達的位置.
 * 一旦蜈蚣開始移動, 須要對它的運動狀況進行分開討論.
 */
void CentipedeMove(int x, int y)
{
    for(int i = 0; i < Length; i++)
    {
        int skip=0;//This is used to mark
        /* 僅移動還沒有射中的蜈蚣的身節 */
        if(Centipede[i].head >=0 && Centipede[i].Clear < 0)
        {
            int j = i;
            int k = i + 1;
            /* 對於多個蜈蚣,識別他們的頭部,並使他們本身的身體跟隨他們 */
            while(k <= Length - 1 && Centipede[k].head < 0 && Centipede[k].Clear < 0)
            {
                j++;
                k++;
            }
            while(j > i)
            {
                Centipede[j].x = Centipede[j - 1].x;
                Centipede[j].y = Centipede[j - 1].y;
                Centipede[j].x_direction = Centipede[j-1].x_direction;
                j--;
            }
            Centipede[i].next_x = Centipede[i].x + Centipede[i].x_direction;
            Centipede[i].next_y = Centipede[i].y + Centipede[i].y_direction;
            /* 若是蜈蚣即將撞上游戲窗口左右邊界, 它須要下移並掉頭. 然而:
             * 1. 若是有另外一條蜈蚣正在它下方, 此蜈蚣只能原路返回.
             * 2. 若是蜈蚣下移時會撞上蘑菇. 那麼, 蜈蚣須要躲避蘑菇.
             * 3. 若是蜈蚣下移後的下一秒會撞上蘑菇, 它就沒法掉頭只能再繼續下移一格.
             * 4. 若是下移後蜈蚣即將撞上游戲窗口的下邊界, 蜈蚣須要上移掉頭.
             */
            if (Centipede[i].next_x > x || Centipede[i].next_x < 0)
            {
                /* 針對狀況1 */
                for(int k = 0; k < Length; k++)
                {
                    if(Centipede[k].Clear < 0 && (k < i || k > j))
                    {
                        if(Centipede[i].next_y == Centipede[k].y && Centipede[i].x == Centipede[k].x)
                        {
                            Centipede[i].x_direction *= -1;
                            skip = 1;
                        }
                    }
                }

                for(int j = 0; j < mushLength; j++)
                {
                    /* 針對狀況2 */
                    if(Centipede[i].next_y == mushroom[j].y && Centipede[i].x == mushroom[j].x)
                    {
                        Centipede[i].x_direction *= -1;
                        Centipede[i].y += 1;
                        skip=1;
                        Centipede[i].x += Centipede[i].x_direction;
                    }
                    /* 針對狀況3 */
                    if(Centipede[i].next_y == mushroom[j].y && Centipede[i].x - 1 == mushroom[j].x || Centipede[i].next_y == mushroom[j].y && Centipede[i].x + 1 == mushroom[j].x)
                    {
                        Centipede[i].y += 1;
                        skip = 1;
                    }
                }
                /* 針對狀況4 */
                if (Centipede[i].next_y > y)
                {
                    Centipede[i].y -= 3;
                    Centipede[i].x_direction *= -1;
                    skip = 1;
                }
                /* 默認狀況, 只須要下移掉頭 */
                if(!skip)
                {
                    Centipede[i].x_direction *= -1;
                    Centipede[i].y += 1;
                }
            }
            else
            {
                /* 若是蜈蚣的頭部即將撞上蘑菇, 它將會下移掉頭. 然而:
                 * 5. 若是它正下方是另外一條蜈蚣, 該蜈蚣只能原路返回.
                 * 6. 若是下移後會撞上蘑菇. 它須要躲避蘑菇.
                 */
                for(int j = 0; j < mushLength; j++)
                {
                    if(Centipede[i].next_x == mushroom[j].x && Centipede[i].y == mushroom[j].y)
                    {
                        /* 針對狀況5 */
                        for(int k = 0; k < Length; k++)
                        {
                            if(Centipede[k].Clear < 0 && (k < i || k > j))
                            {
                                if(Centipede[i].next_y == Centipede[k].y && Centipede[i].x == Centipede[k].x)
                                {
                                    Centipede[i].x_direction *= -1;
                                    skip = 1;
                                }
                            }
                        }
                        /* 針對狀況6 */
                        for(int k = 0; k < mushLength; k++)
                        {
                            if(Centipede[i].next_y == mushroom[k].y && Centipede[i].x == mushroom[k].x && j!=k)
                            {
                                Centipede[i].x_direction *= -1;
                                Centipede[i].y += 1;
                                skip = 1;
                                Centipede[i].x += Centipede[i].x_direction;
                            }
                        }
                        /* 默認狀況, 只須要下移和頭 */
                        if(!skip)
                        {
                            Centipede[i].x_direction *= -1;
                            Centipede[i].y += 1;
                            skip = 1;
                        }
                    }
                }
                /* 針對狀況 7
                 * 若是該蜈蚣和另外一條蜈蚣相向而行, 那麼:
                 * 該蜈蚣接受妥協, 向下移動並掉頭.
                 */
                for(int k = 0; k < Length; k++)
                {
                    if(Centipede[k].Clear < 0 && (k < i || k > j))
                    {
                        if(Centipede[i].next_x == Centipede[k].x && Centipede[i].y == Centipede[k].y)
                        {
                                Centipede[i].x_direction *= -1;
                                Centipede[i].y += 1;
                                skip = 1;
                        }
                     }
                }
                /* 若是都不屬於上述狀況, 只須要讓蜈蚣繼續沿 x 方向移動 */
                if(skip==0) Centipede[i].x += Centipede[i].x_direction;
            }
        }
    }
}

移動的玩家

我使用 getch() 函數來監聽鍵盤輸入. 然而, 在同步 I/O 中監聽意味着計算機沒法調度程序的其餘部分運行. 所以必須使用多線程來監聽鍵盤輸入. 這部分沒有什麼須要探討的實現原理, 只須要記住玩家是不能穿越遊戲窗口的邊界和蘑菇的. 如下是源碼部分:

/*
 * 此功能用於始終從鍵盤接收玩家輸入!
 */
void * waitForKey() {
    while (1) {
        usleep(10);//以防宏塊
        ch = getch();
    }
}

/*
 * 鍵盤監聽器
 * 如下操做主要實現如下要求:
 * 1. 玩家能夠上下左右移動.
 * 2. 玩家按下空格鍵便可射擊.
 * 3. 若是玩家觸碰蘑菇, 玩家暫停移動.
 * 4. 若是玩家觸碰遊戲窗口邊界, 玩家暫停移動.
 * 5. 當子彈被觸發時, 子彈應從玩家當前位置射出.
 */
void getOrder(WINDOW *win, int x, int y)
{
    int skip = 0;
    switch(ch)
    {
        case KEY_LEFT:
            for(int i = 0; i < mushLength; i++)
            {
                if(master_1_x == 0 || (master_1_y == mushroom[i].y && master_1_x == mushroom[i].x + 1) || (master_2_y == mushroom[i].y && master_2_x == mushroom[i].x + 1) || (master_3_y == mushroom[i].y && master_3_x == mushroom[i].x + 1))//on the  left hand of master
                {
                    // 略過
                    skip = 1;
                }
            }
            if(!skip)
            {
                master_1_x -= 1;
                master_2_x -= 1;
                master_3_x -= 1;
                curr_bullet_y = master_1_y;
                curr_bullet_x = master_1_x + 1;
            }
            break;
        case KEY_RIGHT:
            for(int i = 0; i < mushLength; i++)
            {
                if ((master_1_x + 2) == x - 1 || (master_1_y == mushroom[i].y && master_1_x + 2 == mushroom[i].x - 1) ||
                    (master_2_y == mushroom[i].y && master_2_x + 2 == mushroom[i].x - 1) ||
                    (master_3_y == mushroom[i].y && master_3_x + 2 == mushroom[i].x - 1))
                {
                    // 略過
                    skip=1;
                }
            }
            if(!skip)
            {
                master_1_x += 1;
                master_2_x += 1;
                master_3_x += 1;
                curr_bullet_y = master_1_y;
                curr_bullet_x = master_1_x + 1;
            }
            break;
        case KEY_UP:
            for(int i = 0; i < mushLength; i++)
            {
                if (master_1_y == 0 || (master_1_y == mushroom[i].y + 1 && master_1_x == mushroom[i].x) || (master_1_y == mushroom[i].y + 1 && master_1_x + 1 == mushroom[i].x) || (master_1_y == mushroom[i].y + 1 && master_1_x + 2 == mushroom[i].x))
                {
                    // 略過
                    skip=1;
                }
            }
            if(!skip)
            {
                master_1_y -= 1;
                master_2_y -= 1;
                master_3_y -= 1;
                curr_bullet_y = master_1_y;
                curr_bullet_x = master_1_x + 1;
            }
            break;
        case KEY_DOWN:
            for(int i = 0; i < mushLength; i++)
            {
                if (master_3_y == y - 1 || (master_3_y == mushroom[i].y - 1 && master_3_x == mushroom[i].x) || (master_3_y == mushroom[i].y - 1 && master_3_x + 1 == mushroom[i].x) || (master_3_y == mushroom[i].y - 1 && master_3_x + 2 == mushroom[i].x))
                {
                    // 略過
                    skip = 1;
                }
            }
            if(!skip)
            {
                master_1_y += 1;
                master_2_y += 1;
                master_3_y += 1;
                curr_bullet_y = master_1_y;
                curr_bullet_x = master_1_x + 1;
            }
            break;
        case ' ':
            bullet_x = curr_bullet_x;
            bullet_y = curr_bullet_y;
            Fire = 1;
            break;
    }
    ch = 0;
    mvwprintw(win, master_1_y, master_1_x, "_");
    mvwprintw(win, master_1_y, master_1_x + 1, "^");
    mvwprintw(win, master_1_y, master_1_x + 2, "_");
    mvwprintw(win, master_2_y, master_2_x, "|");
    mvwprintw(win, master_2_y, master_2_x + 1, "H");
    mvwprintw(win, master_2_y, master_2_x + 2, "|");
    mvwprintw(win, master_3_y, master_3_x , "-");
    mvwprintw(win, master_3_y, master_3_x + 1, "-");
    mvwprintw(win, master_3_y, master_3_x + 2, "-");
}

菜單操做

我在遊戲界面建立了三個選項. 若是玩家按下 Pp, 該按鈕將消失. 全部的操做都會被暫停而且 Continue 按鈕出現. 相反地, 若是玩家按下 Cc, 該按鈕將消失. 全部被中斷的操做繼續而且 Pause 按鈕出現. 除此以外, 玩家能夠隨時退出遊戲窗口, 只須要按下 Q 或者 q 並選擇 Yes.

screens

quit

/* 這個結構體爲了實現 "Pause" 和 "Continue" 按鈕的隱藏 */
typedef struct PANEL_HIDE {
    int hide;
}Panel;

/*
 * 事件監聽器
 * 如下操做主要實現如下要求:
 * 1. 若是玩家按下 "Continue", 該按鈕消失並顯示 "Pause" 按鈕.
 * 2. 若是玩家按下 "Pause", 該按鈕消失並顯示t "Continue" 按鈕.
 */
void getInt(PANEL *Con, PANEL *Pau, int x, int y, WINDOW* win)
{
    Panel *temp;
    switch(ch)
    {
        case 'c':
        case 'C':
            temp = (Panel *)panel_userptr(Con);
            hide_panel(Con);
            temp->hide = TRUE;
            if(temp->hide == TRUE)
            {
                temp = (Panel *) panel_userptr(Pau);
                show_panel(Pau);
                temp->hide = FALSE;
            }
            stop = 0;
            interrupt_end = time(NULL);
            break;
        case 'p':
        case 'P':
            temp = (Panel *)panel_userptr(Pau);
            hide_panel(Pau);
            temp->hide = TRUE;
            temp = (Panel *)panel_userptr(Con);
            if(temp->hide == TRUE)
            {    show_panel(Con);
                temp->hide = FALSE;
            }
            stop = 1;
            if(interrupt_begin == 0) interrupt_begin = time(NULL);
            break;
        case 'Q':
        case 'q':
            QuitMenu(Pau,x,y);
            break;
        case KEY_END:
            endwin();
            exit(0);
    }
}

/*
 * 退出菜單
 * 如下操做主要實現如下要求:
 * 1. 建立一個新窗口並覆蓋原來的窗口.
 * 2. 然而, 若是玩家點擊 "No", 當前窗口消失並使原來的窗口浮現.
 * 3. 若是玩家點擊 "Yes", 關閉全部的窗口並結束遊戲進程.
 * 4. 不然, 玩家仍能夠繼續玩遊戲.
 */
void QuitMenu(PANEL *Pau, int x, int y)
{
    ITEM **my_items;
    MENU *my_menu;
    WINDOW *my_menu_win;
    Panel *temp;
    int n_quitMenu, i;

    /* Curses 初始化 */
    initscr();
    start_color();
    cbreak();
    noecho();
    keypad(stdscr, TRUE);
    init_pair(1, COLOR_WHITE, COLOR_BLACK);
    init_pair(2, COLOR_RED, COLOR_YELLOW);

    /* 建立菜單和選項 */
    n_quitMenu = ARRAY_SIZE(quitMenu);
    my_items = (ITEM **)calloc(n_quitMenu + 1, sizeof(ITEM *));
    for(i = 0; i < n_quitMenu; i++)
        my_items[i] = new_item(quitMenu[i], quitMenu[i]);
    my_items[n_quitMenu] = (ITEM *)NULL;
    my_menu = new_menu(my_items);

    /* 使窗口位於正中央 */
    menu_opts_off(my_menu, O_SHOWDESC);
    int lent=(x-quit_length) / 2;
    int widt=(y-quit_width) / 2;
    my_menu_win = newwin(8, 35, widt, lent);
    keypad(my_menu_win, TRUE);

    /* 設置主窗口和子窗口 */
    set_menu_win(my_menu, my_menu_win);
    set_menu_sub(my_menu, derwin(my_menu_win,2, 26, 4, 6));
    set_menu_format(my_menu, 1, 3);
    set_menu_mark(my_menu, ">");
    box(my_menu_win, 0, 0);
    wattron(my_menu_win,COLOR_PAIR(2));
    mvwprintw(my_menu_win, 1, 1,"    Are you really want to quit? ");
    wattroff(my_menu_win,COLOR_PAIR(2));
    post_menu(my_menu);
    wrefresh(my_menu_win);
    int check = 0;
    while(check != 1)
    {
        switch(ch)
        {
            /* 每一次 ch = getch()後, 須要給 ch 一個初始值. 不然, 報告錯誤. */
            case KEY_LEFT:
                menu_driver(my_menu, REQ_PREV_ITEM);
                ch = 0;
                break;
            case KEY_RIGHT:
                menu_driver(my_menu, REQ_NEXT_ITEM);
                ch = 0;
                break;
            case 10:    /* 這是 Enter 鍵 */
            {
                if(item_name(current_item(my_menu)) == "Yes")
                {
                    ch = 0;
                    wclear(my_menu_win);
                    endwin();
                    delwin(subwin(my_menu_win, 2, 26, 4, 6));
                    delwin(my_menu_win);
                    endwin();
                    exit(0);
                }
                /* 清空退出菜單並返回遊戲 */
                if(item_name(current_item(my_menu)) == "No")
                {
                    wclear(my_menu_win);
                    endwin();
                    delwin(subwin(my_menu_win, 2, 26, 4, 6));
                    delwin(my_menu_win);
                    check = 1;
                    ch = 0;
                    break;
                }
                /* 清空退出菜單並從新開始遊戲 */
                if(item_name(current_item(my_menu)) == "Replay")
                {
                    check = 1;
                    temp = (Panel *)panel_userptr(Pau);
                    hide_panel(Pau);
                    temp->hide = TRUE;
                    endwin();
                    clear();
                    Initialization();
                    GameInterface();
                    ch = 0;
                    break;
                }
            }
        }
        wrefresh(my_menu_win);
    }
    unpost_menu(my_menu);
    free_menu(my_menu);
    for(i = 0; i < n_quitMenu; ++i)
        free_item(my_items[i]);
}

碰撞問題

我把碰撞問題分紅2部分. 第一部分是子彈射中敵人: 對於蜘蛛和蠍子只須要一個子彈就夠了, 蜈蚣的單個身節也只須要一個但蘑菇須要4枚子彈才能徹底消除. 然而, 射中蜈蚣不一樣位置得到的獎勵分數也不一樣. 另外, 蜈蚣被擊中的部位可能會長出新的頭部, 我已把全部我能想到的狀況羅列在下方. 若是玩家把蜈蚣所有身節都射中了, 玩家就能成功進入下一關.

coli

第二部分是敵人成功擊中玩家, 玩家會失去一次生命當沒有多餘的生命以後宣告玩家遊戲失敗. 在被擊中後, 若是玩家還有多餘的生命, 玩家能夠選擇從新挑戰, 然而, 每個蘑菇須要4枚子彈才能消除. 全部的敵人將從初始位置從新出現.

您可能會好奇如何實現蜈蚣一分爲二的. 其實, 只須要在結構體裏定義一個整型 Clear 就能夠追蹤蜈蚣的每一個身節. 好比, 玩家射中第 ith 個身節, 只須要將此位置 Centipede[i] 記錄它的狀態. 清除, 全部身節的初始清除狀態值都是 -1. 在這以後, 遍歷全部清除值 -1 的身節並使頭部的清除值大於或等於 0 (這意味着頭部沒有被射中而且它們是蜈蚣的第一個身節). 我不須要關係其餘身節清除值的變化, 在移動中只須要讓它們跟從頭節點變化便可.

clear

/*
 * 碰撞檢驗
 * 如下操做主要旨在實現如下要求:
 * 1. 一旦子彈射中目標, 程序須要計算分數並確認是否使其消失.
 * 2. 一旦敵人攻擊到玩家, 程序斷定玩家當此遊戲失敗並確認玩家是否還有多餘的生命.
 */
void CollisionCheck(WINDOW *win,WINDOW *win0,int boundary,PANEL *Pau)
{
    /* 子彈射中蘑菇, 蘑菇只有經受4次子彈攻擊纔會被銷燬 */
    for(int i = 0; i < mushLength; i++) {
        if (bullet_x == mushroom[i].x && bullet_y == mushroom[i].y)
        {
            mushroom[i].mush_record -=1;
            score += 0;// 略過
            mvwprintw(win, 1, 1, "Score: %d", score);
            if (mushroom[i].mush_record == 0) {
                i++;
                while (i < mushLength) {
                    mushroom[i - 1].x = mushroom[i].x;
                    mushroom[i - 1].y = mushroom[i].y;
                    mushroom[i - 1].mush_record = mushroom[i].mush_record;
                    i++;
                }
                mushLength -= 1;
                score += 1;// 若是蘑菇被銷燬,得到1分獎勵
                mvwprintw(win, 1, 1, "Score: %d", score);
            }
            Fire = 0;
        }
    }
    /* 當蜘蛛碰到蘑菇, 蘑菇被蜘蛛吃掉 */
    for(int i = 0; i < mushLength; i++) {
        if (Spider[1].x == mushroom[i].x && Spider[1].y == mushroom[i].y)
        {
            mushroom[i].mush_record = 0;
            i++;
            while (i < mushLength)
            {
                mushroom[i - 1].x = mushroom[i].x;
                mushroom[i - 1].y = mushroom[i].y;
                mushroom[i - 1].mush_record = mushroom[i].mush_record;
                i++;
            }
            mushLength -= 1;
        }
    }
    /* 一旦蜘蛛或蠍子碰到玩家, 玩家死亡 */
    for(int i = 0; i < 3; i++)
    {
        if((Spider[i].x == master_1_x && Spider[i].y == master_1_y) || (Spider[i].x == master_1_x + 1 && Spider[i].y == master_1_y) || (Spider[i].x==master_1_x+2 && Spider[i].y == master_1_y) || 
           (Spider[i].x == master_2_x && Spider[i].y == master_2_y) || (Spider[i].x == master_2_x + 2 && Spider[i].y == master_2_y) || 
           (Spider[i].x == master_3_x &&  Spider[i].y == master_3_y) || (Spider[i].x == master_3_x + 1 && Spider[i].y == master_3_y) || (Spider[i].x==master_3_x+2 && Spider[i].y == master_3_y))
        {
            end(win0,boundary,Pau);
        }
    }

    for(int i=0;i<2;i++)
    {
        if((Sea_Monster[i].x == master_1_x && Sea_Monster[i].y == master_1_y) || (Sea_Monster[i].x == master_1_x + 1 && Sea_Monster[i].y == master_1_y) || (Sea_Monster[i].x == master_1_x + 2 && Sea_Monster[i].y == master_1_y) || 
           (Sea_Monster[i].x == master_2_x && Sea_Monster[i].y == master_2_y) || (Sea_Monster[i].x == master_2_x + 2 && Sea_Monster[i].y == master_2_y) || 
           (Sea_Mo nster[i].x == master_3_x && Sea_Monster[i].y == master_3_y) || (Sea_Monster[i].x == master_3_x + 1 && Sea_Monster[i].y == master_3_y) || (Sea_Monster[i].x == master_3_x + 2 && Sea_Monster[i].y == master_3_y))
        {
            end(win0,boundary,Pau);
        }
    }


    /* 一旦子彈射中蜘蛛或蠍子, 它們被當即消滅*/
    for(int i = 0; i < 2; i++) {
        if (bullet_x == Sea_Monster[i].x && bullet_y == Sea_Monster[i].y)
        {
            score += 600;// 當此射擊得到600分獎勵
            mvwprintw(win, 1, 1, "Score: %d", score);
            Sea_Monster[0].x = -1;
            Sea_Monster[1].x = -1;
            sea_appear = 0;
            Fire = 0;
        }
    }

    for(int i = 0; i < 3; i++) {
        if (bullet_x == Spider[i].x && bullet_y == Spider[i].y)
        {
            score += 600;// 當此射擊得到600分獎勵
            mvwprintw(win, 1, 1, "Score: %d", score);
            Spider[0].x = -1;
            Spider[1].x = -1;
            Spider[2].x = -1;
            spider_appear = 0;
            Fire = 0;
        }
    }
    /* 一旦子彈打中蜈蚣, 程序應對打中狀況分類討論 */
    for(int i = 0; i < Length; i++)
    {
        if(bullet_x == Centipede[i].x && bullet_y == Centipede[i].y)
        {
            /* 若是打中的部位沒有任何標記 */
            if(Centipede[i].Clear < 0)
            {
                /* 全部下述狀況都有一個共同結果: 打中身節變成蘑菇 */
                mushLength += 1;
                mushroom[mushLength - 1].x = Centipede[i].x;
                mushroom[mushLength - 1].y = Centipede[i].y;
                mushroom[mushLength - 1].mush_record = 4;
                Centipede[i].Clear = i;
                /* 若是打中的身節是尾身節 */
                if(Centipede[i + 1].Clear >= 0 && i + 1 < Length)
                {
                    /* 若是打中的是頭部 */
                    if (i == Centipede[i].head)
                    {
                        score += 100;// 當此射擊得到100分獎勵
                        mvwprintw(win, 1, 1, "Score: %d",score);
                    }
                    else
                    {
                        score += 10;// 當此射擊得到10分獎勵
                        mvwprintw(win, 1, 1, "Score: %d",score);
                    }
                    Fire = 0;
                    break;
                }
                /* 若是打中的身節不是尾身節 */
                if(Centipede[i + 1].Clear < 0 && i + 1 < Length)
                {
                    // 蜈蚣分裂成兩個, 並長出新的頭部
                    Centipede[i + 1].head = i + 1;
                    /* 若是打中的是頭部 */
                    if (i == Centipede[i].head)
                    {
                        score += 100;// 當此射擊得到100分獎勵
                        mvwprintw(win, 1, 1, "Score: %d",score);
                    }
                    else
                    {
                        score += 10;// 當此射擊得到10分獎勵
                        mvwprintw(win, 1, 1, "Score: %d",score);
                    }
                    Fire=0;
                    break;
                }
            }
        }
        /* 一旦蜈蚣擊中玩家, 程序應對打中狀況分類討論 */
        if(Centipede[i].Clear<0)
        {
            if((Centipede[i].x == master_1_x && Centipede[i].y == master_1_y) || (Centipede[i].x == master_1_x + 1 && Centipede[i].y == master_1_y) || (Centipede[i].x == master_1_x + 2 && Centipede[i].y == master_1_y) || 
               (Centipede[i].x == master_2_x && Centipede[i].y == master_2_y) || (Centipede[i].x == master_2_x + 2 && Centipede[i].y == master_2_y) || (Centipede[i].x==master_3_x && Centipede[i].y == master_3_y)||(Centipede[i].x == master_3_x + 1 && Centipede[i].y == master_3_y) || (Centipede[i].x == master_3_x + 2 && Centipede[i].y == master_3_y))
            {
                end(win0, boundary, Pau);
                break;
            }
        }
    }
}

/*
 * 此函數將玩家的死亡分類成兩種狀況.
 * 如下操做主要旨在實現如下要求:
 * 1. 一旦玩家失去生命, 他們有權選擇是否重試一遍.
 * 2. 然而, 一旦玩家失去全部生命, 宣告玩家挑戰失敗.
 */
void end(WINDOW* win, int boundary, PANEL *Pau) {
    int x;
    int y;
    int start_x;
    int start_y;
    int start1_x;
    int start2_x;
    life--;
    if(life > 0)
    {
        getmaxyx(win, y, x);
        start_x = (x - fake_lost) / 2; 
        start1_x = (x - fake_lost_w) / 2;
        start2_x = (x - chance_left) / 2;
        start_y = (y - 3) / 2;
        int Times = 3;
        interrupt_begin = time(NULL);
        while(Times > 0)
        {
            wclear(win);
            mvwprintw(win,start_y, start_x, "You lost!");
            mvwprintw(win,start_y + 1, start1_x, "%d second later you will try again!", Times);
            mvwprintw(win,start_y + 2, start2_x, "Chance left: %d", life);
            wrefresh(win);
            usleep(SECOND);
            Times--;
        }
        interrupt_end = time(NULL);
        wclear(win);
        Fire = 0;
        /* 重啓全部角色, 包括新生成的蘑菇 */
        MasterProduce(win);
        Reset_Sea();
        Reset_Spider();
        CentipedeProduce(boundary);
        Reset_Mushroom(win);
    }
    else
    {
        getmaxyx(win, y, x);
        start_x=(x - lost) / 2;
        start_y=(y - 3) / 2;
        wclear(win);
        mvwprintw(win,start_y, start_x, "Game Over! you lost!");
        mvwprintw(win,start_y + 1, start_x, "Your score: %d", score);
        mvwprintw(win,start_y + 2, start_x, "Playtime: %d m %d s", min, sec);
        wrefresh(win);
        while(ch != 'Q'||ch != 'q')
        {
            switch(ch)
            {
                case 'Q':
                case 'q':
                    QuitMenu(Pau,x,y);
                    ch = 0;
                    break;
            }
            mvwprintw(win, start_y, start_x, "Game Over! you lost!");
            mvwprintw(win, start_y + 1, start_x, "Your score: %d", score);
            mvwprintw(win, start_y + 2, start_x, "Playtime: %d m %d s", min, sec);
            wrefresh(win); 
        }
    }
}

/*
 * 該函數用於檢驗玩家是否消滅了蜈蚣全部的身節.
 * 如下操做主要旨在實現如下要求:
 * 1. 每當玩家成功擊中蜈蚣, 程序記錄擊中的次數.
 * 2. 若是擊中次數等於蜈蚣總長, 恭喜玩家進入下一關. 不然, 不發生任何變更.
 */
int success(WINDOW* win,int boundary,PANEL *Pau)
{
    int count=0;
    int x;
    int y;
    int start_x;
    int start_y;
    int start1_x;

    for(int i=0;i<Length;i++)
    {
        if(Centipede[i].Clear>=0) count++;
    }
    if(count == Length)
    {
        Level += 1;
        /* 總共只有5個關卡. */
        if(Level == 6)
        {
            getmaxyx(win, y, x);
            start_x=(x - WIN) / 2;
            start_y=(y - 2) / 2;
            wclear(win);
            mvwprintw(win, start_y, start_x, "You win!");
            mvwprintw(win, start_y + 1, start_x, "Your score: %d", score);
            mvwprintw(win, start_y + 2, start_x, "Playtime: %d m %d s", min, sec);
            wrefresh(win);
            while(ch != 'Q'||ch != 'q')
            {
                switch(ch)
                {
                    case 'Q':
                    case 'q':
                        QuitMenu(Pau,x,y);
                        break;
                }
            }
        }
        else
        {
            getmaxyx(win, y, x);
            start_x = (x - Congrat) / 2;
            start1_x = (x - Congrat_w) / 2;
            start_y = (y - 2) / 2;
            int Times = 3;
            interrupt_begin = time(NULL);
            while(Times > 0)
            {
                wclear(win);
                mvwprintw(win,start_y, start_x,"Congratulations!");
                mvwprintw(win,start_y + 1, start1_x,"%d second later you will go to level %d",Times,Level);
                wrefresh(win);
                usleep(SECOND);
                Times--;
            }
            interrupt_end = time(NULL);
            /*
             * 若是玩家挑戰成功, 更改全部角色的顏色並增加蜈蚣長度.
             * 從新設置全部角色的初始位置.
             */
            wclear(win);
            Fire = 0;
            changeColor();
            MasterProduce(win);
            Reset_Sea();
            Reset_Spider();
            CentipedeProduce(boundary);
            wrefresh(win);
        }
    }
    /* 若是隻剩下最後一個身節未被擊中, 加速蜈蚣運行的速度 */
    else if(count == Length - 1) usleep(SHORT_DELAY);
    else usleep(DELAY);
}

畫布刷新

通過屢次調試後我終於找到比較理想的畫布刷新順序 (詳見下方源碼). 經過控制變量 delay_spider 來控制蜘蛛爬行的速度. 下方的代碼主要是爲了刷新遊戲窗口的畫布. 沒有特別有技術困難的地方, 流程詳情詳見上方提到的流程圖.

game flow

/* 此功能用於建立遊戲界面 */
void GameInterface()
{

    WINDOW *my_wins[8];
    PANEL  *my_panels[6];
    Panel panel_datas[6];
    int i = 0, max_y = 0, max_x = 0, win_6_x, win_6_y;
    score = 0; min = 0; sec = 0;
    interrupt = 0; interrupt_begin = 0; interrupt_end = 0;
    begin = 0; over = 0;
    
    /* Curses 初始化 */
    initscr();
    noecho();//don't display input character
    start_color();
    init_pair(8, COLOR_MAGENTA, COLOR_BLACK);
    init_pair(7, COLOR_CYAN, COLOR_BLACK);
    init_pair(6, COLOR_BLUE, COLOR_BLACK);
    init_pair(5, COLOR_YELLOW, COLOR_BLACK);
    init_pair(4, COLOR_GREEN, COLOR_BLACK);
    init_pair(3, COLOR_RED, COLOR_BLACK);
    curs_set(FALSE);

    int listener;
    listener = pthread_create(& work, NULL, waitForKey, NULL);//Create thread for master
    if (listener != 0) {
        exit(1);
    }

    /* 如下操做主要旨在實現如下要求:
     * 1. 在大窗口裏新建7個小窗口.
     * 2. 除此以外, 其餘窗口用來展現分數, 用時以及可選按鈕.
     * 3. 除了大窗口, 每一個小窗口都有屬於本身的畫板.
     * 4. 只有「continue」的畫板須要在遊戲的開始時被隱藏.
     */
    getmaxyx(stdscr, max_y, max_x);
    /* 爲窗口新建畫板 */
    my_wins[0] = newwin(max_y-score_line, max_x, 0, 0);//big window
    my_wins[1] = newwin(score_line, max_x-quit-level-Pause-Continue-Time, max_y-score_line, 0);//score
    mvwprintw(my_wins[1], 1, 1, "Score: %d", score);
    my_wins[7] = newwin(score_line,Time,max_y-score_line,max_x-quit-level-Pause-Continue-Time);//time
    mvwprintw(my_wins[7], 1, 1, "Time: ");
    my_wins[2] = newwin(score_line, level, max_y-score_line, max_x-quit-level-Pause-Continue);//level
    my_wins[3] = newwin(score_line, Pause, max_y-score_line,max_x-quit-Pause-Continue);//Pause
    mvwprintw(my_wins[3], 1, 1, "\"P\"ause");//Pause label
    my_wins[4] = newwin(score_line, Continue, max_y-score_line, max_x-quit-Continue);//Replay
    mvwprintw(my_wins[4], 1, 1, "\"C\"ontinue"); //Continue label
    my_wins[5] = newwin(score_line, quit, max_y-score_line, max_x-quit);//quit
    mvwprintw(my_wins[5], 1, 1, "\"Q\"uit");//quit label
    my_wins[6] = newwin(max_y-score_line-2, max_x-2, 1, 1);// ball
    getmaxyx(my_wins[6], win_6_y, win_6_x);
    keypad(stdscr, TRUE);

    for(i = 0; i < 6; i++) box(my_wins[i], 0, 0);
    for(i = 0; i < 6; i++) my_panels[i] = new_panel(my_wins[i]);
    my_panels[6] = new_panel(my_wins[7]);
    for(i = 0; i < 6; i++)
    {
        panel_datas[i].hide = FALSE;
        set_panel_userptr(my_panels[i], &panel_datas[i]);
    }
    panel_datas[6].hide = FALSE;
    set_panel_userptr(my_panels[6], &panel_datas[7]);
    panel_datas[4].hide = TRUE;
    hide_panel(my_panels[4]);

    /* 將畫板顯示在屏幕上 */
    doupdate();
    /* 在遊戲的剛開始新建全部角色並初始化 */
    MushroomProduce(win_6_y,win_6_x-1);
    MasterProduce(my_wins[6]);
    CentipedeProduce(win_6_x);
    /* 若是發現用戶沒有多餘的生命, 則宣告遊戲失敗 */
    begin = time(NULL);
    while(life > 0)
    {
        wclear(my_wins[6]);
        for(i = 0; i < mushLength; i++)
        {
            wattron(my_wins[6],COLOR_PAIR(mushroom_color));
            mvwprintw(my_wins[6],mushroom[i].y,mushroom[i].x,"&");
            wattroff(my_wins[6],COLOR_PAIR(mushroom_color));
        }
        wattron(my_wins[6],COLOR_PAIR(centipede_color));
        for(i = 0; i < Length; i++)
        {
            if(Centipede[i].Clear<0)
            {

                if(i == Centipede[i].head) mvwprintw(my_wins[6],Centipede[i].y,Centipede[i].x,"O");
                else mvwprintw(my_wins[6],Centipede[i].y,Centipede[i].x,"*");
            }
        }
        wattroff(my_wins[6],COLOR_PAIR(centipede_color));
        if(sea_appear)
        {
            wattron(my_wins[6],COLOR_PAIR(sea_color));
            mvwprintw(my_wins[6],Sea_Monster[0].y, Sea_Monster[0].x, "$");
            mvwprintw(my_wins[6],Sea_Monster[1].y, Sea_Monster[1].x, "_");
            wattroff(my_wins[6],COLOR_PAIR(sea_color));
        }
        if(Spider[1].x != -1 & spider_appear)
        {
                wattron(my_wins[6],COLOR_PAIR(spider_color));
                mvwprintw(my_wins[6],Spider[0].y, Spider[0].x, "^");
                mvwprintw(my_wins[6],Spider[1].y, Spider[1].x, "W");
                mvwprintw(my_wins[6],Spider[2].y, Spider[2].x, "^");
                wattroff(my_wins[6],COLOR_PAIR(spider_color));
        }
        else SpiderProduce(win_6_y,win_6_x);

        mvwprintw(my_wins[2], 1, 1, "Level: %d",Level);
        getInt(my_panels[4],my_panels[3],win_6_x,win_6_y,my_wins[6]);
        if(stop<1)
        {
            wattron(my_wins[6],COLOR_PAIR(master_color));
            getOrder(my_wins[6],win_6_x,win_6_y);
            wattroff(my_wins[6],COLOR_PAIR(master_color));
            wattron(my_wins[6],COLOR_PAIR(bullet_color));
            if(Fire)
            {
                bullet_y -= 1;
                mvwprintw(my_wins[6],bullet_y,bullet_x,"|");
            }
            else
            {
                bullet_y = master_1_y;
                bullet_x = master_1_x+1;
            }
            wattroff(my_wins[6],COLOR_PAIR(bullet_color));
            wrefresh(my_wins[2]);
            wrefresh(my_wins[1]);
            wrefresh(my_wins[6]);
            CollisionCheck(my_wins[1],my_wins[6],win_6_x,my_panels[3]);
            success(my_wins[6],win_6_x,my_panels[3]);
            if(sea_appear)
            {
                if(Sea_Monster[0].x != -1) sea_monsterMove(win_6_x);
                else Sea_MonsterProduce(win_6_y,win_6_x);
            }

            if(spider_appear && delay_spider == 1) SpiderMove(win_6_x,win_6_y);

            CentipedeMove(win_6_x - 1, win_6_y - 1);
            delay_spider *= -1;
            //timer
            over = time(NULL);
            double seconds = difftime(over,begin);
            if(difftime(interrupt_end,interrupt_begin)>0)
            {
                interrupt += difftime(interrupt_end,interrupt_begin);
                interrupt_begin = 0;
                interrupt_end = 0;
            }
            seconds -= interrupt;
            if(seconds >= 60)
            {
                min = ((int)seconds) / 60;
                sec = ((int)seconds) - min*60;
            }
            else sec=(int)seconds;
            wclear(my_wins[7]);
            box(my_wins[7], 0, 0);
            mvwprintw(my_wins[7], 1, 1, "Time: %d m %d s",min,sec);
            wrefresh(my_wins[7]);
        }
        update_panels();
        doupdate();
    };
    endwin();
}

源碼地址

若是個人文章能夠幫到您,勞煩您點進源碼點個 ★ Star 哦!
https://github.com/Hephaest/A...

相關文章
相關標籤/搜索