ゴマちゃんフロンティア

気まぐれと勢いで作るUnityゲーム開発日記です。

【開発メモ】攻撃判定制御のリファクタリング

time 2015/11/17

2016/11/14
いろいろと問題があったため、本記事のような実装は使用していません。
「前にこんなことやっていた」という感じで、参考程度にご参照下さい。

というわけで、もう11月も半ばな今日この頃です。
モンハンクロスが近づいてくる一方で、秋が名残惜しい気もします。

今回は攻撃判定の生成・制御に関するお話です!
機能自体は前からありますが、今回はその部分のリファクタをしようと考えております。

思えば、前もソースコード構成のリファクタを行ったばかりです。
いかに初期の実装の基盤がへっぽこだったかと感じる今日この頃です。

概要

今まで攻撃判定は全てプレイヤーに付いているPlayerクラスから直接Instantiateしていました。
Playerクラスに攻撃判定と生成位置の情報を持たせ、各攻撃用の関数を使って行います。

ここで気になってくるのは、Playerクラスの役割が大きくなりすぎてしまうこと。
攻撃判定と言っても、近接攻撃から遠距離攻撃、攻撃回数や速度など、多くの要素があります。
これらの判定生成処理を全てPlayerで実装しているので、かなり長くて読みにくいソースになってしまっています。

また攻撃判定毎にエフェクトを再生しているので、「多段ヒットする極太ビーム」などを今の仕様で作ると、攻撃判定分エフェクトが出現してしまいます。
最近はEffekseerで派手なものを作ることも多くなってきたので、こちらも何とかしたいところでした。

そこでプレイヤーから直接生成するのではなく、攻撃判定を管理するオブジェクトを生成するようにします。
管理用オブジェクトから改めて攻撃判定を生成させます。

処理的には回りくどくなりますが、
・Playerクラスの攻撃判定生成を管理オブジェクトに委譲できる
・管理オブジェクトにエフェクトを付けることで、攻撃判定1つ1つにエフェクトを持つ必要がなくなる
 →上で挙げた「多段ヒットする極太ビーム」の問題が解消できます。
・判定生成時に子オブジェクトとして持つことで、攻撃判定を一括して削除できる

などなど、プレイヤーと攻撃判定の仲介役として諸問題を解決できそうです。
「管理オブジェクト分のプレハブが必要になる」「どうしても処理が遠回しになる」等の難点はありそうですが、とりあえずやってみます!

具体的な実装

例によって全て載せると長くなるので、重要な部分のみを記載します!
基本的にパラメータの宣言や初期化は載せていないので、そのあたりはStart()あたりで行うか、インスペクターから設定すれば大丈夫です。
分かりにくいようなら全ソース載せようと思いますので、コメント等でご連絡頂ければと思います!

まずはPlayerクラスで管理オブジェクトをInstantiateします。
今までは「攻撃判定オブジェクト」と「判定生成位置」を指定していましたが、前者を管理用オブジェクトを指定するように変更します。

protected void createHitManager(GameObject hitManager, GameObject hitOffset) {
    GameObject manager = Instantiate(hitManager, hitOffset.transform.position, transform.rotation) as GameObject;
    manager.transform.parent = hitOffset.transform;
    manager.GetComponent<HitManager>().setPlayer(this);
}

生成位置はキャラクターのboneに空のオブジェクトを付与し、インスペクターから設定しておきます。
自分の場合は「hitOffset」という配列にまとめて格納しました。
キャラクターのAmatureに対して生成位置を設定しないと、動きに追従してくれないので注意が必要です。
遠距離攻撃の場合は追従させる必要がないので、parentをnullにしてしまいます。

Instantiateの返り値から管理オブジェクトのコンポーネントを取得し、Playerの参照を渡しておきます。
アクションゲームで「ヒット時にプレイヤー側で何か処理をする」といった処理は多いので、この書き方は結構使います。

次に攻撃判定を制御するオブジェクト用のスクリプトを作成します。
このゲームでの攻撃判定は大きく分けて「近距離型」と「遠距離型」があります。
「近距離型」は剣や槍などの攻撃判定で、武器に動きに合わせて攻撃判定を追従させる必要のあるものです。
「遠距離型」は飛び道具のようなもので、一度発射したらキャラクターや武器の動きとは関係せずに移動します。

そんなわけで、ベースとなるクラス「HitManager」を作成し、それを継承する形で遠距離型と近距離型のクラスを作成します。
各パラメータとして攻撃判定生成に必要な情報を持たせます。
Start()内で各コルーチンを実行し、攻撃判定を生成します。

【AttackHitController】

protected IEnumerator attackInstantiate() {
    // 攻撃までの遅延時間
    yield return new WaitForSeconds(delayTime);

    GameObject hit;

// 攻撃回数分だけ攻撃判定を生成
    for (int i = 0; i < hitCount; i++) {
        hit = Instantiate(hitObject, transform.position, transform.rotation) as GameObject;
        hit.transform.parent = this.transform;
        hit.GetComponent<PlayerHit>().setPlayer(this.player);

        yield return new WaitForSeconds(GameConstants.ATTACK_HIT_INTERVAL);
    }
}

【BulletHitController】

protected IEnumerator bulletInstantiate() {
    // 発射までの遅延時間
    yield return new WaitForSeconds(delayTime);

    GameObject hit;

    // 発射数分だけ生成
    for (int i = 0; i < hitCount; i++) {
        hit = Instantiate(hitObject, transform.position, player.transform.rotation) as GameObject;
        hit.transform.parent = this.transform;
        hit.GetComponent<PlayerHit>().setPlayer(this.player).setHitManager(this);

        yield return new WaitForSeconds(hitInterval);
    }
}

今までのソースでは、例えば遠距離攻撃であれば「単発型」「連射型」「同時発射型」というように、弾の挙動によって関数を変えていましたが、ソースコード的に冗長になってしまうので1つにまとめました。
パラメータを変えることで連射数を変えられたり、発射間隔を指定できたりするイメージです。

エフェクトを再生する必要のある場合はエフェクト用のオブジェクトを付与します。
管理オブジェクトはboneに追従する以外は動かないので、エフェクト自体が飛んでいったりするような場合でも問題ありません。
ただ攻撃判定と無関係に再生されてしまうので、生成タイミングや生存時間を上手く調節して合わせる必要があります。

最後は攻撃判定用のスクリプトです。
攻撃力や弾速度などをパラメータとして持たせ、Triggerで攻撃がヒットしたかを判定します。
攻撃判定が個別にエフェクトを出す場合はその再生も行います。

以下はPlayerHitクラスのOnTriggerEnterの処理です。

protected virtual void OnTriggerEnter(Collider c){
    if(c.gameObject.tag == "Enemy"){
        c.GetComponent<Enemy>().damage(power);
        Instantiate(damageEffect, transform.position, Quaternion.identity);
    }
}

内容は今までとあまり変わりませんが、いくつかのパラメータは管理オブジェクトに持たせるようにしています。
攻撃判定個別でエフェクトを再生する場合、こちらにエフェクトを付与します。

テスト

前回Effekseerを使って衝撃波を作ったので、マリンパから発射するようにしてみます。
「剣を横→縦に振って衝撃波を出し、前方に飛ばす」というものです。

20151117_1

剣の攻撃判定は近距離用の管理オブジェクトから生成されています。
衝撃波は遠距離用の管理オブジェクトから生成し、衝撃波のエフェクトを再生します。
攻撃判定は縦振り時までコルーチンを使って遅延させ、エフェクトの発射に合わせたタイミングで前方に飛ばします。

ゲーム的な性能としては、攻撃力と判定の大きさに優れています。
振った剣にも攻撃判定があるので、近距離で打てば追加でダメージを与えます。
ちなみにこれがマリンパ唯一の遠距離攻撃になりそうです。

まとめ

ということで、攻撃判定の制御に関するリファクタリングを行ってみました!
何よりPlayerクラスがスッキリしたのが一番大きいです。
エフェクトと攻撃判定が別になったので、「ド派手なエフェクト+攻撃判定を連続で生成」といったことも可能になりました。
総じて前のロジックより良い結果になったと思われます!

まだまだ粗い部分もありますので、これからも改修していきたいと思います!
様々な攻撃判定を制御できるようにしたいところです。

今日のイラスト

ペンタブでマリンパを描いてみました。
前に描いたシルリスと比べるとやや微妙な感じです。
今までGIMPで描いていたデジタルイラストとは違った味はあります。

marinpa19

スポンサーリンク

down

コメントする



ツイッター