Skip to content
第27回 攻撃間隔とHP

1 / 2 攻撃間隔とダメージ ​

このステップの目的

待ち時間を個体で管理し、射程内の相手へ1回のダメージを確定します。

第27回の到達版を起動し、出撃した味方が敵と接触する様子を見ます。HPが一定の間隔で減ることを確認し、次の攻撃まで待つ情報をどこへ保存するか考えてください。

待ち時間を減らす ​

Battle.cppのBattle::ResolveAttacksを実装します。attackWaitSecからdtを引き、0未満にしません。対象が射程内にいて、待ち時間が0になったときだけ攻撃します。初回は待ち時間が0なので、射程へ入った更新で攻撃します。

攻撃したらattackWaitSecを定義のattackIntervalSecへ戻します。表示用にはattackDisplaySecを0.18秒へ戻し、animationSecを0にしてMotion::Attackを記録します。この表示設定でダメージを増やすことはありません。

同時の攻撃をまとめる ​

相手のHPをループ中に直接減らすと、先に列挙された側が有利になります。個体数と同じ長さのstd::vector<int> damageへ今回のダメージを加算し、列挙した後にHPへ反映してください。拠点への合計はallyDamageとenemyDamageに分けます。

この方式では同じ更新で攻撃した両者がダメージを受けます。対象のunitが-1なら相手拠点、0以上ならdamage[target.unit]を更新します。HPは0より小さくしません。

HPが0になった個体を取り除く関数は次のステップで作ります。接続を終えるまでの表示だけで、退場の完成と判断しないでください。

実装後に比較する参考コード
Source/TowerDefense/Battle.cppcpp
void Battle::ResolveAttacks(double dt)
{
    // 同じ更新内の攻撃は、退場処理より先にまとめて確定する。
    std::vector<int> damage(units_.size(), 0);
    int allyDamage = 0;
    int enemyDamage = 0;
    for (auto& unit : units_)
    {
        unit.attackWaitSec = std::max(0.0, unit.attackWaitSec - dt);
        const auto& type = data_.units[unit.definition];
        const auto target = FindTarget(unit);
        if (target.distance > type.attackRange + 1e-9 || unit.attackWaitSec > 1e-9) continue;
        if (target.unit >= 0) damage[target.unit] += type.attackDamage;
        else if (unit.team == Team::Ally) enemyDamage += type.attackDamage;
        else allyDamage += type.attackDamage;
        unit.attackWaitSec = type.attackIntervalSec;
        unit.attackDisplaySec = tuning::AttackDisplaySec;
        unit.animationSec = 0;
        unit.motion = Motion::Attack;
    }
    for (std::size_t i = 0; i < units_.size(); ++i)
    {
        units_[i].hp = std::max(0, units_[i].hp - damage[i]);
        if (damage[i] > 0) units_[i].hitDisplaySec = tuning::HitDisplaySec;
    }
    allyHp_ = std::max(0, allyHp_ - allyDamage);
    enemyHp_ = std::max(0, enemyHp_ - enemyDamage);
}
チェックリスト

今回の変更箇所を指し示し、観察した結果とその理由を説明できますか。実験後の値とファイルを確認して次へ進みます。

ゲームプログラミング実践