
C言語入門|STEP15:敵キャラクターとAIの実装-敵テンプレートと行動選択アルゴリズム
はじめに:敵が動くからRPGは面白い
STEP14では、味方側の魔法とスキルの実装を解説しました。
STEP15では、いよいよ 敵キャラクターとAI(行動ロジック) に注目します。
敵がただ殴ってくるだけでは、
RPGの戦闘はすぐに単調になってしまいます。
このRPGでは、
- 敵ごとの個性
- 魔法を使う判断
- HP状況による行動変化
を シンプルなAIロジック で表現しています。
敵キャラクターを表す Enemy 構造体
まずは、敵を表す構造体を確認しましょう。
typedef struct {
char name[24];
int hp, max_hp;
int mp, max_mp;
int atk;
int magic;
int exp;
int is_boss;
int defending;
EnemySpell spells[SPELLS_PER_ENEMY];
} Enemy;Enemy 構造体の役割
| 項目 | 内容 |
|---|---|
| name | 敵の名前 |
| hp / mp | HP・MP管理 |
| atk / magic | 攻撃力・魔力 |
| exp | 倒した時の獲得経験値 |
| is_boss | ボス判定 |
| defending | 防御状態 |
| spells | 使用可能な敵魔法 |
味方キャラと似た構成にしているため、
戦闘処理を共通化しやすくなっています。
敵専用魔法 EnemySpell の設計
敵の魔法は、味方とは別の構造体で管理されています。
typedef enum {
EN_SPELL_DMG = 0,
EN_SPELL_HEAL = 1,
EN_SPELL_ATKUP = 2,
EN_SPELL_DEFUP = 3
} EnemySpellKind;
typedef struct {
char name[24];
EnemySpellKind kind;
int mp_cost;
int power;
} EnemySpell;なぜ味方と分けているのか
- 敵は回復や強化を自動判断で使う。
- 効果の意味が少し違う。
- 実装をシンプルに保てる。
味方用と敵用を分ける設計 は、
RPGではよく使われる考え方です。
敵テンプレートという考え方
敵のデータは、
EnemyTemplate 配列として定義されています。
typedef struct {
char name[24];
int hp, mp, atk, magic, exp;
int can_cast;
EnemySpell spells[SPELLS_PER_ENEMY];
} EnemyTemplate;テンプレートを使う理由
| 理由 | 内容 |
|---|---|
| 敵追加が簡単 | データを1つ増やすだけ。 |
| バランス調整 | 数値を一覧で確認できる。 |
| ロジック共通化 | 生成処理を統一できる。 |
RPGでは、
敵データと処理を分離すること がとても重要です。
ランダムな敵を生成する処理
通常戦闘では、
ランダムに敵が選ばれます。
static Enemy make_random_enemy(void) {
int n = sizeof(g_enemy_templates) / sizeof(g_enemy_templates[0]);
const EnemyTemplate* t = &g_enemy_templates[rand() % n];この処理のポイント
- 敵の種類数を自動計算
- rand でランダム選択
- テンプレートから Enemy を生成
これにより、
戦闘ごとに違う敵と遭遇 する仕組みになります。
敵AIの入口:enemy_try_cast_spell
敵の行動判断は、
enemy_try_cast_spell 関数で行われます。
static int enemy_try_cast_spell(Enemy* e, Character party[])この関数は、
- 魔法を使うかどうか判断
- 使う場合はどの魔法か選択
- 成功したら1を返す
という役割を持っています。
魔法を使うかどうかの確率判定
最初に、魔法を使うかどうかを
確率で判定しています。
if ((rand() % 100) >= 50) return 0;この判定の意味
- 約50%の確率で魔法を使う。
- 毎回同じ行動にならない。
これだけで、
敵が考えて行動しているように見える ようになります。
使用可能な魔法の絞り込み
次に、MPが足りる魔法だけを抽出します。
if (e->mp >= e->spells[i].mp_cost)なぜ事前に絞り込むのか
- MP不足の魔法を選ばない。
- 無駄な失敗行動を防ぐ。
AIにとっても、
基本的な安全チェックは必須 です。
HP状況による行動選択
敵のHPが少ない場合、
回復魔法を優先するロジックがあります。
if (hp_rate <= 40) {
if (sp.kind == EN_SPELL_HEAL) chosen = i;
}この処理が生む効果
- 瀕死で回復する賢い敵
- 戦闘が一方的にならない。
- プレイヤーに緊張感を与える。
簡単な条件分岐だけで、
AIらしさが一気に増します。
敵の通常攻撃との切り替え
もし魔法を使わなかった場合は、
通常攻撃が行われます。
int dmg = enemy.atk + (rand() % 4);ランダム要素を入れる理由
- ダメージが毎回同じにならない。
- 戦闘に揺らぎが生まれる。
完全な固定ダメージより、
少しの乱数がある方がRPGらしい です。
防御フラグの扱い方
敵も味方と同様に、
defending フラグを使っています。
e->defending = 1;フラグ設計のメリット
- 次の攻撃だけ半減
- 状態管理がシンプル
- 味方側と処理を共通化しやすい。
敵と味方で
同じ考え方を使っている点 が重要です。
STEP15で押さえておきたいポイントまとめ
STEP15で理解しておきたいポイントはこちらです。
- 敵は Enemy 構造体で管理する。
- 敵データはテンプレート化する。
- ランダム要素でAIらしさを出す。
- HP状況による行動分岐が重要
- 防御や強化もフラグで管理する。
