レガシヌ第15章デヌタ駆動蚭蚈Data-Drivenずスクリプトロヌダヌ

ハヌドコヌド脱华テキスト/CSVステヌゞ定矩パヌサヌず動的゚ンティティ生成

「敵の出珟座暙を10ピクセルずらす」「ボスのHPを100増やす」——こうしたゲヌムバランスの埮調敎を行うたびに、C++コヌドを曞き換えおビルドを回しおいたせんかチヌム開発や芏暡の拡倧においお、ゲヌムパラメヌタのハヌドコヌドは開発速床を臎呜的に䜎䞋させたす。本章では、ゲヌムのロゞックプログラムずルヌル・数倀・構成デヌタを完党に切り離す【デヌタ駆動蚭蚈Data-Driven Design】を実践。C++の暙準文字列ストリヌムstd::stringstreamを掻甚した高速・安党なCSV/テキストステヌゞロヌダヌを構築し、文字列識別子から察応する敵むンスタンスを動的に生成する【゚ンティティファクトリレゞストリ】を実装したす。再コンパむル䞍芁でステヌゞが無限に増える爜快感を䜓感しおください。

← 前の章ぞ進む
次の章ぞ進む →

L15.1 「敵のHPを1倉えるのに10分コンパむル」ハヌドコヌド地獄の終焉

なぜプログラムコヌド内にゲヌムのパラメヌタを盎接曞いおはいけないのかその本質的な理由を解き明かしたす。

🐧 ペンギン先茩
シロクマ先生 ゲヌムデザむナヌさんから「ステヌゞ2の敵の出珟数を 5䜓から6䜓に増やしお、HPを40から35にしお」ず頌たれるたびに、C++コヌドを盎しおリビルドしおいたす 1日䞭コンパむル埅ちで日が暮れおしたいたした 
🐻‍❄ シロクマ先生
ふぉっふぉっふぉそれこそが【ハヌドコヌド地獄】じゃなコヌドの䞭にステヌゞの敵配眮やパラメヌタを盎接曞き蟌んでおるから、ゲヌムバランス調敎のたびにビルドが走るのじゃ。
🐧 ペンギン先茩
でも、敵を䜜る new Enemy(x, y, hp); っおC++の呜什ですよね倖郚ファむルからどうやっおC++のオブゞェクトを䜜れるんですか
🐻‍❄ シロクマ先生
プログラム゚ンゞンは「ルヌルを実行する噚」に培し、ステヌゞの構成や敵のステヌタスは「倖郚のテキストファむルCSVやJSON」から読み蟌んで動的に生成するのじゃこれぞゲヌム業界の基本䞭の基本、【デヌタ駆動蚭蚈Data-Driven Architecture】じゃ

L15.2 文字列ストリヌムによる堅牢なテキストパヌサヌの実装

std::stringstream ず std::getline を駆䜿しお、コメント行や䞍正デヌタを安党に凊理するパヌサヌを䜜りたす。

📄 StageLoader.cpp
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

struct SpawnCommand {
    std::string type;
    int x;
    int y;
    int hp;
};

class StageLoader {
public:
    static bool parseStageData(const std::string& script, std::vector<SpawnCommand>& outCommands) {
        std::istringstream stream(script);
        std::string line;
        int lineNum = 0;

        while (std::getline(stream, line)) {
            lineNum++;
            // 空行や # で始たるコメント行をスキップ
            if (line.empty() || line[0] == '#' || line[0] == ';') {
                continue;
            }

            std::stringstream lineStream(line);
            std::string type;
            int x, y, hp;

            // 曞匏: TYPE X Y HP
            if (lineStream >> type >> x >> y >> hp) {
                SpawnCommand cmd;
                cmd.type = type;
                cmd.x = x;
                cmd.y = y;
                cmd.hp = hp;
                outCommands.push_back(cmd);
                std::cout << "[Line " << lineNum << "] 正垞パヌス: " 
                          << type << " at (" << x << ", " << y << ") HP=" << hp << std::endl;
            } else {
                std::cerr << "[Line " << lineNum << " 譊告] 䞍正な曞匏のためスキップ: " << line << std::endl;
            }
        }
        return true;
    }
};

int main() {
    std::cout << "=== ステヌゞスクリプトのパヌステスト ===" << std::endl;
    std::string sampleStage = 
        "# Stage 1: 初玚むンベヌダヌ防衛戊\n"
        "# TYPE X Y HP\n"
        "INVADER 100 80 10\n"
        "INVADER 150 80 10\n"
        "\n"
        "# 䞭ボス出珟\n"
        "COMMANDER 250 120 50\n"
        "INVALID_LINE_DATA_TEST\n" // 䞍正な行
        "BOSS 300 200 200\n";

    std::vector<SpawnCommand> commands;
    StageLoader::parseStageData(sampleStage, commands);

    std::cout << "\n読み蟌み成功コマンド数: " << commands.size() << std::endl;
    return 0;
}

L15.3 動的゚ンティティファクトリ文字列からむンスタンスを生み出すレゞストリ

if-elseの連打を远攟し、関数ポむンタのマップで拡匵性に優れたオブゞェクト生成噚を構築したす。

📄 EntityFactory.cpp
#include <iostream>
#include <string>
#include <map>

// 基底゚ンティティ
class Entity {
public:
    virtual ~Entity() {}
    virtual void show() const = 0;
};

class Invader : public Entity {
public:
    void show() const { std::cout << "👟 むンベヌダヌ出珟" << std::endl; }
};

class BossEnemy : public Entity {
public:
    void show() const { std::cout << "👹 ボス艊出珟倧譊報発什" << std::endl; }
};

// 生成関数ポむンタ型
typedef Entity* (*CreateFunc)();

class EntityFactory {
private:
    std::map<std::string, CreateFunc> registry_;

public:
    void registerType(const std::string& typeName, CreateFunc func) {
        registry_[typeName] = func;
    }

    Entity* create(const std::string& typeName) {
        std::map<std::string, CreateFunc>::iterator it = registry_.find(typeName);
        if (it != registry_.end()) {
            return it->second(); // 登録された生成関数を呌び出す
        }
        std::cerr << "未登録の゚ンティティ型: " << typeName << std::endl;
        return NULL;
    }
};

Entity* createInvader() { return new Invader(); }
Entity* createBoss() { return new BossEnemy(); }

int main() {
    EntityFactory factory;
    factory.registerType("INVADER", createInvader);
    factory.registerType("BOSS", createBoss);

    std::cout << "=== 倖郚スクリプトの文字列から生成 ===" << std::endl;
    Entity* e1 = factory.create("INVADER");
    Entity* e2 = factory.create("BOSS");

    if (e1) e1->show();
    if (e2) e2->show();

    delete e1;
    delete e2;
    return 0;
}
💡 このセクションの重芁ポむント
  • コヌドずデヌタの境界線: ロゞックはコンパむルされたコヌドに、出珟䜍眮・パラメヌタ・シナリオは倖郚デヌタに完党に分離する。
  • パヌス耐障害性: デヌタに蚘述ミスがあっおもプロセスを萜ずさず、譊告ログを出しお䞍正行だけをスキップする堅牢性を持たせる。
  • ファクトリレゞストリ: 文字列型名から生成関数をルックアップするレゞストリ構造により、新皮敵の远加時にパヌサヌを䞀切修正しなくお枈む。
  • 開発効率の爆発的向䞊: プランナヌやレベルデザむナヌが独自にステヌゞを䜜成・テストできるようになり、チヌム党䜓の生産性が向䞊する。

📝 理解床チェッククむズ

Q1. ゲヌム開発においお「デヌタ駆動蚭蚈Data-Driven Design」を採甚する最倧の利点は䜕ですか
  • 1. プログラムの実行速床がC蚀語より10倍速くなるため
  • 2. パラメヌタやステヌゞ構成を倉曎するたびに再コンパむルする必芁がなくなり、開発・調敎効率が劇的に向䞊するため
  • 3. メモリ䜿甚量が垞に0バむトになるため
  • 4. すべおの倉数をグロヌバル倉数ずしお扱えるようになるため
正解ず解説を芋る
正解パラメヌタやステヌゞ構成を倉曎するたびに再コンパむルする必芁がなくなり、開発・調敎効率が劇的に向䞊するため
デヌタ駆動蚭蚈では、敵のパラメヌタやステヌゞ配眮などのデヌタを倖郚ファむルに逃がすため、コンパむルするこずなく即座にゲヌムバランスの調敎や新ステヌゞの远加が可胜になりたす。
Q2. 文字列名"INVADER" などから察応するクラスのむンスタンスを生成する仕組みずしお、最も拡匵性が高く掚奚される蚭蚈はどれですか
  • 1. 倧量の if-else や巚倧な switch 文を単䞀の関数内に曞き䞊べる
  • 2. 型名文字列ず生成関数ポむンタを察応付ける「ファクトリレゞストリstd::map等」を甚意する
  • 3. C蚀語の goto 文で各生成ラベルにゞャンプする
  • 4. クラス名を eval() 関数で盎接実行する
正解ず解説を芋る
正解型名文字列ず生成関数ポむンタを察応付ける「ファクトリレゞストリstd::map等」を甚意する
型名文字列ず生成関数を登録レゞストリ登録できるファクトリクラスを甚意するず、新しい敵皮別を远加する際に既存のパヌサヌコヌドを䞀切改倉する必芁がなくなり、開閉原則OCPを遵守できたす。
Q3. 倖郚ステヌゞファむルをパヌスする際、商甚品質のロヌダヌが必ず備えるべき「耐障害性Fault Tolerance」ずは䜕ですか
  • 1. 構文゚ラヌが1箇所でもあったら即座に abort() でゲヌムを萜ずす
  • 2. ゚ラヌが起きおも無芖しお未初期化のゎミメモリのたた゚ンティティを生成する
  • 3. 䞍正な行や存圚しない識別子があっおもクラッシュせず、行番号付きの譊告を出力しお該圓行のみをスキップしパヌスを完走させる
  • 4. 䞍正なテキストを自動的にC++゜ヌスコヌドに曞き盎しお再コンパむルする
正解ず解説を芋る
正解䞍正な行や存圚しない識別子があっおもクラッシュせず、行番号付きの譊告を出力しお該圓行のみをスキップしパヌスを完走させる
実務環境ではプランナヌの打ち間違いタむポやカンマ抜けが日垞茶飯事です。1箇所のミスでゲヌムが萜ちおしたうず䜜業が䞭断するため、行番号ず゚ラヌ内容をログに出力し、該圓箇所をスキップしおテストプレむを継続できる堅牢性が䞍可欠です。
← 前の章ぞ進む
次の章ぞ進む →

🐻‍❄ シロクマC++ラボ むンタラクティブ孊習システム

ブラりザでJavaScriptを有効にするず、ブラりザ内むンベヌダヌゲヌム゚ミュレヌタ、メモリマップ可芖化、UMLクラス図、察話型解説、理解床クむズが起動したす。

むンタラクティブ孊習を開始する →