★ゲームを作ろう2(模擬戦闘)★


続いて、昔のRPGを題材に戦闘プログラムの初歩を作ってみましょう。

イメージするのはドラゴンクエスト1のような戦闘プログラムです。

ドラゴンクエスト1では、主人公が1人でパーティという概念がありませんでした。

敵もグループで出現せず1匹で出現し、主人公と敵が1対1で戦うという方法を採用しています。

1986年の作品ですから、プレイしたことの無い方も多いかもしれません。

最近のRPGはパーティプレイが主流ですから、1対1の戦闘が面白いと感じるかどうかも分かりません。

1986年といえば、すでにウィザードリィやウルティマといった海外のRPGが存在していましたが、日本のファミコンユーザはRPG未体験者がほとんどでした。

また、ウィザードリィやウルティマは、すでにパーティプレイが導入されており、職業という概念も存在していました。

日本のファミコンユーザにもRPGを浸透させたいと考えたスタッフは、いきなりパーティプレイや職業といった概念を盛り込んだRPGを発表しても、日本のユーザが受け入れられないのではないかと考え、段階的に導入する手法を考えたのです。

1でRPGの基本的な遊び方を伝え、2でパーティプレイの面白さを伝え、3で職業を導入したのです。

話は随分逸れましたが、日本のRPG発展の礎を築いたゲームの戦闘場面の「初歩」を作ってみます。


このプログラムは、ドラゴンクエスト1の戦闘を完全に模写するということが目的ではありません。

C言語を理解する上での題材として、基本的な考え方を利用するだけです。

ですから、以下のように作ってみます。

<ゲーム概要>

1.プレイヤー(自分)、エネミー(敵)の1対1で戦う。

2.パラメータはヒットポイント(HP)のみとする。

3.HPは乱数で決める。(50〜150)

4.戦闘はターン制(交互に攻撃を行う)とする。

5.ターンは常にプレイヤー、エネミーの順とする。

6.ターンの開始時にお互いのHPを表示する。

7.攻撃力はその都度、乱数で決定する。(20〜30)

8.攻撃力が決定したら、相手のHPから攻撃力分の数値を減算する。

9.減算した結果、相手のHPが0以下になった場合、勝利者を表示し終了する。

10.どちらかのHPが0以下になるまで戦闘を続ける。

少し長めのプログラムになりますので、前もって流れを図にしておきます。

この流れに沿って段階的にプログラムを作ってみましょう。

この図さえあれば作れそうだという方は先に全体を作ってから読み進めてください。

まずは乱数の種を設定し、プレイヤーとエネミーのHPを計算するところまで作りましょう。


プレイヤーのHPを格納する変数として「player_hp」を用意します。

エネミーのHPを格納する変数として「enemy_hp」を用意します。

それぞれ初期値として、50〜150までの数値を乱数を使って設定します。









































解答例です。


<sample program 049-01>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    int player_hp;

    int enemy_hp;

    srand((unsigned int)time(NULL));

    player_hp = rand() % 101 + 50;

    enemy_hp = rand() % 101 + 50;

    return 0;
}

乱数の設定が難しかったかもしれません。

rand()で発生した数値を101で割った余りを使います。

101で割った余りは、0〜100までですから、これに50を加算することで、50〜150の値が設定できます。


次に、お互いのHPを表示します。

<実行結果>

現在のステータス
player HP 132
enemy HP 128
続行するには何かキーを押してください・・・

このように表示してみましょう。









































解答例です。


<sample program 049-02>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    int player_hp;

    int enemy_hp;

    srand((unsigned int)time(NULL));

    player_hp = rand() % 101 + 50;

    enemy_hp = rand() % 101 + 50;

    printf("現在のステータス\n");

    printf("player HP %d\n", player_hp);

    printf("enemy  HP %d\n", enemy_hp);

    return 0;
}

<実行結果>

現在のステータス
player HP 73
enemy HP 103
続行するには何かキーを押してください・・・

実行するたびに、お互いのHPが変化することを確認してください。


次は、攻撃部分を作ります。

まず、プレイヤーの攻撃を考えてみましょう。

上に書いたように、

  7.攻撃力はその都度、乱数で決定する。(20〜30)

とあります。

そこで、

プレイヤーの攻撃力を格納する変数「player_atk」を追加します。

乱数を発生させ、player_atkに20〜30の値を設定します。

player_atkの値をenemy_hpから引きます。

エネミーを倒したかどうかの判定は後で作りますので、ここまでプログラムしてみましょう。

分かりやすくするために、下記のようにメッセージを表示するよう考えてみてください。

<実行結果>

現在のステータス
player HP 73
enemy HP 103
playerの攻撃!
24のダメージを与えた!
続行するには何かキーを押してください・・・









































解答例です。


<sample program 049-03>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    int player_hp;
    int player_atk;

    int enemy_hp;

    srand((unsigned int)time(NULL));

    player_hp = rand() % 101 + 50;

    enemy_hp = rand() % 101 + 50;

    printf("現在のステータス\n");

    printf("player HP %d\n", player_hp);

    printf("enemy  HP %d\n", enemy_hp);

    printf("playerの攻撃!\n");

    player_atk = rand() % 11 + 20;

    printf("%dのダメージを与えた!\n", player_atk);

    enemy_hp -= player_atk;

    return 0;
}

<実行結果>

現在のステータス
player HP 85
enemy HP 68
playerの攻撃!
28のダメージを与えた!
続行するには何かキーを押してください・・・

攻撃力は、rand()の結果を11で割った余りに20を加算しています。

11で割った余りは0〜10ですから、20を加算することで、20〜30の値が設定できます。

最終的には、enemy_hpからplayer_atkを引いた後に、enemy_hpが0以下になったかどうかを追加します。

※これは後で追加しましょう。


では、続いてエネミーの攻撃を作ります。

エネミーの攻撃力を格納する変数「enemy_atk」を追加します。

乱数を発生させ、enemy_atkに20〜30の値を設定します。

enemy_atkの値をplayer_hpから引きます。

プレイヤーを倒したかどうかの判定は後で作りますので、ここまでプログラムしてみましょう。

プレイヤーと同じように、下記のメッセージを表示するよう考えてみてください。

<実行結果>

現在のステータス
player HP 102
enemy HP 113
playerの攻撃!
20のダメージを与えた!
enemyの攻撃!
28のダメージを受けた!
続行するには何かキーを押してください・・・









































解答例です。


<sample program 049-04>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    int player_hp;
    int player_atk;

    int enemy_hp;
    int enemy_atk;

    srand((unsigned int)time(NULL));

    player_hp = rand() % 101 + 50;

    enemy_hp = rand() % 101 + 50;

    printf("現在のステータス\n");

    printf("player HP %d\n", player_hp);

    printf("enemy  HP %d\n", enemy_hp);

    printf("playerの攻撃!\n");

    player_atk = rand() % 11 + 20;

    printf("%dのダメージを与えた!\n", player_atk);

    enemy_hp -= player_atk;

    printf("enemyの攻撃!\n");

    enemy_atk = rand() % 11 + 20;

    printf("%dのダメージを受けた!\n", enemy_atk);

    player_hp -= enemy_atk;

    return 0;
}

<実行結果>

現在のステータス
player HP 115
enemy HP 68
playerの攻撃!
24のダメージを与えた!
enemyの攻撃!
20のダメージを受けた!
続行するには何かキーを押してください・・・

ここまでで、プレイヤーとエネミーの攻撃に関するプログラムが完成しました。

次は、このプログラムを繰り返し行えるようにしなければなりません。

毎回、お互いのHPと攻撃力が変化しますので、何回繰り返すかは全く分かりません。

そこで、無限ループを使って繰り返しの設定を行います。

説明文と図をよく見て、どこからどこまで繰り返せば良いか考えてみてください。









































解答例です。


<sample program 049-05>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    int player_hp;
    int player_atk;

    int enemy_hp;
    int enemy_atk;

    srand((unsigned int)time(NULL));

    player_hp = rand() % 101 + 50;

    enemy_hp = rand() % 101 + 50;

    while (1) {

        printf("現在のステータス\n");

        printf("player HP %d\n", player_hp);

        printf("enemy  HP %d\n", enemy_hp);

        printf("playerの攻撃!\n");

        player_atk = rand() % 11 + 20;

        printf("%dのダメージを与えた!\n", player_atk);

        enemy_hp -= player_atk;

        printf("enemyの攻撃!\n");

        enemy_atk = rand() % 11 + 20;

        printf("%dのダメージを受けた!\n", enemy_atk);

        player_hp -= enemy_atk;
    }

    return 0;
}

<実行結果>

永遠に繰り返し・・・

ターンの開始時にお互いのHPを表示する、という説明がありますので、ステータスを表示する箇所からエネミーの攻撃が終了する箇所までが繰り返しの対象となります。

実行結果が書いていないのは、無限ループから抜け出す仕組みを作っていませんので、永遠にプログラムが止まらないからです。

では、繰り返しを止める条件を考えてみましょう。


戦闘が終了するのは、プレイヤーかエネミーのHPが0以下になった時です。

プレイヤーかエネミーのHPが0以下になるかどうかは、それぞれのHPからお互いの攻撃力を引いたときに分かります。

では、どこにプログラムを追加するかをよく考えて、どちらかのHPが0以下になったら繰り返しから抜けるというプログラムを追加してみてください。

なお、playerが勝った場合は「playerの勝利!」、enemyが勝った場合は「enemyの勝利!」というメッセージを表示するようにしてみましょう。









































解答例です。


<sample program 049-06>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    int player_hp;
    int player_atk;

    int enemy_hp;
    int enemy_atk;

    srand((unsigned int)time(NULL));

    player_hp = rand() % 101 + 50;

    enemy_hp = rand() % 101 + 50;

    while(1) {

        printf("現在のステータス\n");

        printf("player HP %d\n", player_hp);

        printf("enemy  HP %d\n", enemy_hp);

        printf("playerの攻撃!\n");

        player_atk = rand() % 11 + 20;

        printf("%dのダメージを与えた!\n", player_atk);

        enemy_hp -= player_atk;

        if (enemy_hp <= 0) {
            printf("playerの勝利!\n");
            break;
        }

        printf("enemyの攻撃!\n");

        enemy_atk = rand() % 11 + 20;

        printf("%dのダメージを受けた!\n", enemy_atk);

        player_hp -= enemy_atk;

        if (player_hp <= 0) {
            printf("enemyの勝利!\n");
            break;
        }
    }

    return 0;
}

<実行結果>

現在のステータス
player HP 106
enemy HP 68
palyerの攻撃!
24のダメージを与えた!
enemyの攻撃!
22のダメージを受けた!
現在のステータス
player HP 84
enemy HP 44
palyerの攻撃!
23のダメージを与えた!
enemyの攻撃!
29のダメージを受けた!
現在のステータス
player HP 55
enemy HP 21
palyerの攻撃!
22のダメージを与えた!
playerの勝利!
続行するには何かキーを押してください・・・

完成です。

が、プログラムを実行すると一気に最終結果が分かってしまいます。

実際はどちらが勝つのか、一つ一つ確認しながらプレイしますよね。

その仕組みを取り入れましょう。

前にゲームを作った時に使った「getchar」という命令(関数)を使ってみましょう。


getcharをどこに挿入すると、臨場感が増すのか考えながら作ってみてください。









































解答例です。


<sample program 049-07>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    int player_hp;
    int player_atk;

    int enemy_hp;
    int enemy_atk;

    srand((unsigned int)time(NULL));

    player_hp = rand() % 101 + 50;

    enemy_hp = rand() % 101 + 50;

    while(1) {

        printf("現在のステータス\n");

        printf("player HP %d\n", player_hp);

        printf("enemy  HP %d\n", enemy_hp);

        getchar();

        printf("playerの攻撃!\n");

        getchar();

        player_atk = rand() % 11 + 20;

        printf("%dのダメージを与えた!\n", player_atk);

        getchar();

        enemy_hp -= player_atk;

        if (enemy_hp <= 0) {
            printf("playerの勝利!\n");
            break;
        }
                
        printf("enemyの攻撃!\n");

        getchar();

        enemy_atk = rand() % 11 + 20;

        printf("%dのダメージを与えた!\n", enemy_atk);

        getchar();

        player_hp -= enemy_atk;

        if (player_hp <= 0) {
            printf("enemyの勝利!\n");
            break;
        }
    }

    return 0;
}

<実行結果>

現在のステータス
player HP 106
enemy HP 68

palyerの攻撃!

24のダメージを与えた!

enemyの攻撃!

22のダメージを受けた!

現在のステータス
player HP 84
enemy HP 44

palyerの攻撃!

23のダメージを与えた!

enemyの攻撃!

29のダメージを受けた!

現在のステータス
player HP 55
enemy HP 21

palyerの攻撃!

22のダメージを与えた!

playerの勝利!
続行するには何かキーを押してください・・・

エンターキーを押すごとにメッセージが表示されます。

メッセージは下方向に流れていきますので、最後の調整をします。

「現在表示されているメッセージをクリアする」という命令を追加します。

while (1) {

の直後に、

system("cls");

という文を追加してみてください。

ステータス表示の前に画面がクリアされるはずです。


このsystemという命令(関数)は「stdlib.h」をincludeすることで使えるようになります。

元々は画面をクリアするという命令ではないのですが、ここでは画面クリアに使っています。


さて、いかがでしたでしょうか。

本物とはかけ離れていますが、「初歩」はつかめたでしょうか。

本物では、魔法があったり、攻撃を避けたり、「会心のいちげき」があったりと、偶然性に左右される要素を色々と追加することで戦闘に対する緊張感が増します。

皆さんも色々考えて、どんどん改良してみてください。


次へ

戻る

目次へ