Home English

Kuina-chan

くいなちゃんDec 15, 2017


6さいからのプログラミング」、今回はC言語でRPGを作ります!

RPG

題材にするのは、RPG(ロールプレイングゲーム)です。 とはいえテキストだけのRPGで、しかもラスボス戦のみとなります(テキストRPG)。
テキストRPG
テキストRPG
勇者「Kuina」がラスボス「Demon」に挑むストーリーです。 「0」を入力すると攻撃し、「1」で力を溜めて攻撃力アップ(戦闘が終わるまで持続します)、「2」で回復です。
なぜ今回RPGを題材にしたかと言いますと、プログラムの流れがシンプルで解りやすく改造もしやすいため、プログラミングの学習に適していると考えたからです。
少し長いですが、ソースコードはrpg.cの通りです。
#include <stdio.h>
#include <stdlib.h>
  
struct param
{
  char name[8];
  int life, attack, defense;
};
  
void act(struct param* actor, struct param* target, int command)
{
  int point;
  switch (command)
  {
    case 0:
      point = actor->attack - target->defense;
      target->life -= point;
      printf("%s attacks! %s takes %d damage points!\n", actor->name, target->name, point);
      break;
    case 1:
      point = 20;
      actor->attack += point;
      printf("%s casts Boost! %s's Attack increases by %d!\n", actor->name, actor->name, point);
      break;
    case 2:
      point = 50;
      actor->life += point;
      printf("%s casts Heal! %s's Life increases by %d!\n", actor->name, actor->name, point);
      break;
  }
}
  
int main(void)
{
  struct param you = { "Kuina", 100, 100, 100 };
  struct param enemy = { "Demon", 1000, 100, 50 };
  printf("%s appears!\n", enemy.name);
  for (; ; )
  {
    int command;
    printf("%s: Life=%d, Attack=%d, Defense=%d\n", you.name, you.life, you.attack, you.defense);
    printf("%s: Life=%d, Attack=%d, Defense=%d\n", enemy.name, enemy.life, enemy.attack, enemy.defense);
    printf("(0) Attack\n(1) Boost\n(2) Heal\n");
    printf("Command:");
    scanf("%d", &command);
    act(&you, &enemy, command);
    if (enemy.life <= 0)
    {
      printf("%s is defeated!\n", enemy.name);
      return 0;
    }
    act(&enemy, &you, rand() % 3);
    if (you.life <= 0)
    {
      printf("%s has been killed...\n", you.name);
      return 0;
    }
  }
}
rpg.c
このソースコードは大まかには、4~8行目の構造体「struct param」と、10~31行目の関数「act」と、33~59行目の関数「main」から成ります。

struct param

4~8行目の「struct param」は、味方と敵のパラメータを管理する構造体となっています。 メンバのnameはキャラクターの名前、lifeはライフ、attackは攻撃力、defenseは守備力です。
このstruct param型の変数として、関数mainの開始直後の35~36行目で、味方「you」と敵「enemy」を作成しています。 各メンバには列挙した値が順番に代入されるので、例えばenemyのメンバは、nameが"Demon"、lifeが1000、attackが100、defenseが50となります。

メインループ

その直後、38~58行目では、for文により半永久的に処理が繰り返されます。 ゲームのプログラムでは、ゲームオーバーになるまでこのように同じ処理を繰り返すことが多く、この繰り返しのことは「メインループ」と呼ばれます。
今回のプログラムでは、47~51行目で敵のライフが0以下になれば勝利とし、53~57行目で味方のライフが0以下になれば負けとし、そのどちらでもなければメインループを回り続ける流れとなっています。
メインループの中では、45行目でまずプレイヤーからの入力を受け取って46行目のact関数で味方側の行動を行い、52行目のact関数で敵側の行動を行います。

act関数

味方や敵の行動を行うact関数は、10~31行目に書かれています。 関数の引数は「actor」「target」「command」ですが、それぞれ「行動するキャラ」「行動の対象となるキャラ」「行動の種類」です。
13行目のswitch文で、行動の種類であるcommandによって処理を分けます。 0なら攻撃、1なら力溜め、2なら回復を行います。

攻撃

攻撃の処理は15~19行目です。 16行目で攻撃によって与えるダメージを計算し、変数pointに入れます。 ダメージの計算方法は、行動するキャラ「actor」の攻撃力「attack」から、行動の対象となるキャラ「target」の守備力「defense」を引いた値となっています。
17行目で対象者「target」のライフ「life」から、ダメージであるpointの値を減らし、その状況を18行目で画面に出力しています。

力溜め

力溜めは、単純に攻撃力を上げるだけの処理で、20~24行目です。 変数pointに20を入れ、その値を22行目で行動者「actor」の攻撃力「attack」に加算しています。

回復

回復は、単純にライフを増やすだけの処理で、25~29行目です。 通常のRPGのライフには最大値があり、それ以上の回復はできないようになっていますが、このプログラムでは省略しています。 つまり、いくらでも回復ができます。

act呼び出し

さて、act関数を理解したところで、再度46行目と52行目の呼び出し側を見てみましょう。
46行目ではプレイヤーが入力した行動を味方に行わせるために、行動者actorには味方「you」を、対象者targetには敵「enemy」を、行動の種類commandにはプレイヤーが入力した値を入れてact関数を呼び出しています。
対して52行目では敵を行動させるために、行動者actorには敵「enemy」を、対象者targetには味方「you」を、行動の種類commandには「rand() % 3」という値を入れてact関数を呼び出しています。 「rand() % 3」とは、「0~2の乱数を生成する」という意味です。 これにより敵は、攻撃、力溜め、回復の行動をランダムに選んで行います。
補足すると、rand関数は「0~非常に大きい数」の整数の乱数を生成します。 これを3で割った余りを求めることで、「0~2」の範囲の乱数にしています。

プログラムの改造

以上、流れとしては「プレイヤーの入力」→「味方の行動」→「敵の行動」→「プレイヤーの入力」→…を繰り返しているだけのプログラムです。 市販のゲームはこれより遙かに複雑に見えるかもしれませんが、やっていることはこのプログラムと大差ありません。
さて、このRPGを実際に実行してみると、敵があまりに強すぎてなかなか勝てないと思います。 そこで、このゲームをより面白くするために、プログラムを改造することを考えてみてください。 何をどう書き換えれば勝てるようになるでしょうか。 既存のプログラムを改造することは、プログラミングに慣れる重要な一歩となるでしょう。
1513346874ja