Nodachisoft Nodachi Sword Icon
  
@あまじ✎ 2021年3月8日に更新

第2章54 関数ポインタと構造体を合わせて使う

イチからゲーム作りで覚えるC言語
第2章53 関数ポインタを扱う : PREV
NEXT : 第2章55 バッファの利用 :

概要

C言語で関数ポインタを使って、キャラクタごとの行動と関数を紐づけしておき、 順番に各キャラクタが行動をすることで、自動的に紐づいた関数が実行されます。

この仕組みを使って、簡単なターン制バトルの動きを確認してみます。

プログラム例

まずはキャラクタの構造体を定義し、 キャラクタがとれる動作を、関数で定義しておきます。

次に、キャラクタごとに、各ターンでどの行動(関数)をとるのかの紐づけを定義しておくことで、 簡単にターン制のバトルを実現できます。

 
funcp_action.c
#include <stdio.h>
struct Character {
    char name[64];
    int hp;
    void (*action)(struct Character* src, struct Character* dest, int val);
};
typedef struct Character Ch;
void punch(Ch* src, Ch* dest, int val) {
    printf("%s のパンチ! %s に %d のダメージ!\n", src->name, dest->name, val);
    dest->hp -= val;
}
void magic(Ch* src, Ch* dest, int val) {
    int damage = (dest->hp * 4) / 10; // 3割ダメージ
    printf("%s の火炎魔法! %s は %d のダメージ!\n", src->name, dest->name, damage);
    dest->hp -= damage;
}
int main() {
    Ch planc = { "ぷらんく", 17, punch };
    Ch firerat = { "炎のネズミ", 13, magic };
    for (int i = 0; i < 20; i++) {
        printf("=== %d ターン目 ===\n", i);
        planc.action( &planc, &firerat, 5);
        firerat.action( &firerat, &planc, 3);
        printf("%s の体力は %d\n", planc.name, planc.hp);
        printf("%s の体力は %d\n", firerat.name, firerat.hp);
        if (planc.hp <= 0 || firerat.hp <= 0) break; // END
    }
}

このコードを実行するとしたのような結果になります。

実行結果
=== 1 ターン目 ===
ぷらんく のパンチ! 炎のネズミ に 5 のダメージ!
炎のネズミ の火炎魔法! ぷらんく は 6 のダメージ!
ぷらんく の体力は 11
炎のネズミ の体力は 8
=== 2 ターン目 ===
ぷらんく のパンチ! 炎のネズミ に 5 のダメージ!
炎のネズミ の火炎魔法! ぷらんく は 4 のダメージ!
ぷらんく の体力は 7
炎のネズミ の体力は 3
=== 3 ターン目 ===
ぷらんく のパンチ! 炎のネズミ に 5 のダメージ!
炎のネズミ の火炎魔法! ぷらんく は 2 のダメージ!
ぷらんく の体力は 5
炎のネズミ の体力は -2
勝負ありです!

では、コードの中身を確認していきます。 2~7行目でキャラクタの構造体を定義しており、そのメンバとして action という関数ポインタを持たせています。

 
funcp_action.c
struct Character {
    char name[64];
    int hp;
    void (*action)(struct Character* src, struct Character* dest, int val);
};
typedef struct Character Ch;

この action には、キャラクタごとのデータを定義するときに、あわせて行動(関数)を設定しておきます。

続いて、8 ~ 16 行目ではキャラクタが実行できる行動を関数で定義しています。

 
funcp_action.c
void punch(Ch* src, Ch* dest, int val) {
    printf("%s のパンチ! %s に %d のダメージ!\n", src->name, dest->name, val);
    dest->hp -= val;
}
void magic(Ch* src, Ch* dest, int val) {
    int damage = (dest->hp * 4) / 10; // 3割ダメージ
    printf("%s の火炎魔法! %s は %d のダメージ!\n", src->name, dest->name, damage);
    dest->hp -= damage;
}

今回は punch 関数と、magic 関数の2つをキャラクタが選択できる行動(関数)として定義しています。 なお、punch は固定ダメージ、magic は指定した相手の HP を 4 割削るダメージです。

続いて、main 関数です。 18行目、19行目では キャラクタの名前、体力、ターンごとの行動 を定義しています。

 
funcp_action.c
int main() {
    Ch planc = { "ぷらんく", 17, punch };
    Ch firerat = { "炎のネズミ", 13, magic };

今回は main 関数の中で、キャラクタ「ぷらんく」は punch 関数を実行するように定義し、 キャラクタ「炎のネズミ」は magic 関数を実行するように定義しました。

このようにキャラクタの行動する内容を事前に定義してしまえば、 後は構造体の中の action メンバを呼び出せば最初に定義しておいた、それぞれの行動(関数)が実行される仕組みです。

今回のプログラムでは、「ぷらんく」か「炎のネズミ」のどちらかが、 体力(hp)が 0 以下となったとき、for ループから抜けて勝負終了となります。

オブジェクト指向プログラミング

今回のプログラムのように、ある特定のデータの集まりと、そのデータの振る舞い をまとめるようなプログラムは、「オブジェクト指向プログラミング」にある考え方の一つです。

キャラクタというデータがどんな風に行動をするか、というオブジェクトという単位で 定義していくことで、非常にプログラムが設計しやすくなります。

オブジェクト指向プログラミングは非常に奥が深く、とても少しでは書き表せないため、 今回は ”そういったプログラミングの考え方があるよ” というご紹介までに留めます。

補足:インタプリタ言語

今回はキャラクタの行動に着目して、関数ポインタを利用しましたが、 もっと幅広く、様々な定型的な処理を関数にわけておき、 ゲームのシナリオなどの順番で関数を読み込ませてから、順番に実行していくことで、 簡易的な紙芝居型の ADV のスクリプト処理などを自作できます。

関数を実行する順番を別のテキストファイルなどで定義しておき、 プログラムからそのテキストファイルを読込んで、該当する関数を実行していくような仕組みを インタプリタと呼び、ゲームシナリオをインタプリタの仕組みを使って、シナリオエディターなどで 作成し、プログラム本体とは分けて作っていく方法は良くあります。

参考

  • なし
イチからゲーム作りで覚えるC言語
第2章53 関数ポインタを扱う : PREV
NEXT : 第2章55 バッファの利用 :
 
 
送信しました!

コメント、ありがとうございます。

なんかエラーでした

ごめんなさい。エラーでうまく送信できませんでした。ご迷惑をおかけします。しばらくおいてから再度送信を試していただくか、以下から DM などでご連絡頂ければと思います。

Twitter:@NodachiSoft_jp
お名前:
 
連絡先:
 
メッセージ:
 
戻る
内容の確認!

以下の内容でコメントを送信します。よろしければ、「送信」を押してください。修正する場合は「戻る」を押してください

お名前:
 
連絡先:
 
メッセージ:
 
Roboto からの操作ではないという確認のため確認キーを入れてください。
確認キー=95
戻る
 / 
送信確認へ
コメント欄
コメント送信確認へ

関連ありそうな記事(5件)です!

第1章01 Visual Studio Community 2019 のインストール手順

#C11仕様#C言語#ゲームプログラミング✎ 2021-08-08
C言語でプログラミングをするために、無料で使える Visual Studio Community を使った開発環境を揃えていく手順や注意点をお話しています。
目次
第2章54 関数ポインタと構造体を合わせて使う
第2章54 関数ポインタと構造体を合わせて使う
概要
概要
プログラム例
プログラム例
補足:インタプリタ言語
補足:インタプリタ言語
参考
参考
Nodachisoft © 2021