iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 24
2
Software Development

若沒有遊戲引擎、合作夥伴...做得出遊戲嗎? 不試試看不知道吧? [使用C語言]系列 第 24

[11屆鐵人賽Day24] 2D遊戲—生命、攻擊、防禦

勘誤:筆者發現,在movePlayer函數中開A,B,C門的部分,askUser詢問玩家的時候,若是按下F2拒絕會有問題

因此這邊麻煩大家在if(askUser())的後面稍微加上一段else

/* File: PlayGround.c */

// 以下是節錄自movePlayer函數內部,處理A,B,C門的部分

if(game_map[y][x] == 'A')
    {
        if(p.key_a_num > 0)
        {
            if(askUser("遇到A門,是否開啟?"))
            {
                game_map[p.y][p.x] = ' ';
                game_map[y][x] = p.skin;

                p.x = x;
                p.y = y;

                printf("使用鑰匙a開啟A門!\n");
                playMusic(UNLOCK, SE_MODE);
                waitForUser(500);
                p.key_a_num -= 1;
            }
            
// ------------請各位加上這部分-------------------
            else
            {
                system("cls");
            }
// ----------------------------------------------
            
        }
    }
    if(game_map[y][x] == 'B')
    {
        if(p.key_b_num > 0)
        {
            if(askUser("遇到B門,是否開啟?"))
            {
                game_map[p.y][p.x] = ' ';
                game_map[y][x] = p.skin;

                p.x = x;
                p.y = y;

                printf("使用鑰匙b開啟B門!\n");
                playMusic(UNLOCK, SE_MODE);
                waitForUser(500);
                p.key_b_num -= 1;
            }
            
// ------------請各位加上這部分-------------------
            else
            {
                system("cls");
            }
// ----------------------------------------------

        }
    }
    if(game_map[y][x] == 'C')
    {
        if(p.key_c_num > 0)
        {
            if(askUser("遇到C門,是否開啟?"))
            {
                game_map[p.y][p.x] = ' ';
                game_map[y][x] = p.skin;

                p.x = x;
                p.y = y;

                printf("使用鑰匙c開啟C門!\n");
                playMusic(UNLOCK, SE_MODE);
                waitForUser(500);
                p.key_c_num -= 1;
            }
            
// ------------請各位加上這部分-------------------
            else
            {
                system("cls");
            }
// ----------------------------------------------

        }
    }



引言

昨天我們加上了可以四處撿鑰匙開門的功能後,遊戲變得稍加有挑戰性了吧~
不過我們再來模擬得更像魔塔一點吧!

這次我們為玩家加上 生命攻擊防禦,以及金錢 等部分,
是不是越來越像RPG遊戲了呢~

我們先來說說魔塔,魔塔的攻擊怪物方式是全自動的,
只要玩家觸碰怪物,系統就會自動運算戰鬥結果,如果玩家算起來的結果是輸了
則系統會告知玩家打不過,就避免你攻擊怪物。

換句話說這遊戲玩家不會死亡,只要打不過系統就不讓你打。

而如果系統運算結果是玩家贏了,則會自動殺死怪物,玩家就會被扣除計算後被怪物打的總量。

魔塔的戰鬥系統就是這麼簡單,我們就來試著實作看看囉!


血量、攻擊、防禦,以及金錢

我們需要先在Player結構中加上玩家的新數值:hp, atk, def, money。

/* File: PlayGround.h */

typedef struct player
{
    char skin;
    int x;
    int y;
    int key_a_num;
    int key_b_num;
    int key_c_num;
    
// ------------新增部分-------------------
    int hp;
    int atk;
    int def;
    int money;
// --------------------------------------
    
}Player;

然後關於初始化,我們必須把initPlayer做個分割,
因為原本的初始化是每個PlayGround都會做一次,但如果每個關卡都初始化鑰匙生命值等等的,
會非常奇怪而且失去關卡的意義(破關就是要以現有狀態繼續闖關嘛~),

因此我們會定義一個新的函數,稱為initPlayerStatus,
裡頭會初始化hp, atk, def, 鑰匙, 金錢等等,
這個函數將只會執行一次,在遊戲剛開始時。

而原來的initPlayer將會每個關卡都執行,裡頭只會初始化玩家位置等。

因此我們會將三行鑰匙數量的初始化移到initPlayerStatus

/* File: PlayGround.h */

// 新增initPlayerStatus宣告

void initPlayerStatus();
/* File: PlayGround.c */

void initPlayer(char skin, int x, int y)
{
    isPlaying = 1;
    p.skin = skin;
    p.x = x;
    p.y = y;

// ------------把鑰匙初始化移到下面的函數-------------------
// ------------------------------------------------------

    game_map[y][x] = skin;
}

void initPlayerStatus()
{

// ------------移到這邊-------------------
    p.key_a_num = 0;
    p.key_b_num = 0;
    p.key_c_num = 0;
// --------------------------------------

// ------------新增部分-------------------
    p.hp = 1000;  // 血量初始化為1000
    p.atk = 10;  // 攻擊初始化為10
    p.def = 10;  // 防禦初始化為10
    p.money = 0;  // 一開始家徒四壁
// --------------------------------------
}

然後到main函數呼叫initPlayerStatus:

/* File: main.c */

.
..
...

// 在playGround函數呼叫前先呼叫
// ------------新增部分-------------------
initPlayerStatus();
// --------------------------------------

playGround(LEVEL0_MAP, 28, 4);

playGround(LEVEL1_MAP, 28, 27);

return 0;

最後,記得在狀態欄加上它們:

/* File: PlayGround.c */

void playerStatus()
{
    printf("[道具欄] 鑰匙A: %02d支  鑰匙B: %02d支  鑰匙C: %02d支\n", p.key_a_num, p.key_b_num, p.key_c_num);
    
    // ------------新增部分-------------------
    printf("[ 狀態 ] 生命值: %5d  攻擊力: %5d  防禦值: %5d\n", p.hp, p.atk, p.def);
    printf("[ 金錢 ] %8d\n", p.money);
    // --------------------------------------
}

藥水

有了這些狀態以後,當然也要有一些可以上升這些數值的藥水啦,
我們先在地圖內加上一些代表藥水的字元:

字元 哪種藥水
h 生命藥水,hp上升100
s 攻擊藥水,atk上升10
d 防禦藥水,def上升10

https://ithelp.ithome.com.tw/upload/images/20191008/20111429AQ8pAEFWBf.png
https://ithelp.ithome.com.tw/upload/images/20191008/20111429KMZgaHLzJ5.jpg

然後我們在程式中加入判斷:

/* File: PlayGround.c */

// 於movePlayer函數內部,加入遇到h,s,d三種字元的情況

if(game_map[y][x] == 'h')  // 若遇到h,生命+100
    {
        game_map[p.y][p.x] = ' ';
        game_map[y][x] = p.skin;

        p.x = x;
        p.y = y;

        printf("生命值+100!\n");
        playMusic(SYSTEM_OK, SE_MODE);
        waitForUser(500);
        p.hp += 100;
    }
    if(game_map[y][x] == 's')  // 若遇到s,攻擊+10
    {
        game_map[p.y][p.x] = ' ';
        game_map[y][x] = p.skin;

        p.x = x;
        p.y = y;

        printf("攻擊力上升10!\n");
        playMusic(SYSTEM_OK, SE_MODE);
        waitForUser(500);
        p.atk += 10;
    }
    if(game_map[y][x] == 'd')  // 若遇到d,防禦+10
    {
        game_map[p.y][p.x] = ' ';
        game_map[y][x] = p.skin;

        p.x = x;
        p.y = y;

        printf("防禦值上升10!\n");
        playMusic(SYSTEM_OK, SE_MODE);
        waitForUser(500);
        p.def += 10;
    }

執行

狀態欄出現新的項目:
https://ithelp.ithome.com.tw/upload/images/20191008/20111429JWjV1gysXF.png

吃到d(防禦藥水)的狀況:
https://ithelp.ithome.com.tw/upload/images/20191008/20111429tGEf1iIiYf.png


上一篇
[11屆鐵人賽Day23] 2D遊戲—撿鑰匙開門!
下一篇
[11屆鐵人賽Day25] 2D遊戲—戰鬥設定
系列文
若沒有遊戲引擎、合作夥伴...做得出遊戲嗎? 不試試看不知道吧? [使用C語言]30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言