ゴマちゃんフロンティア

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

【開発メモ】敵キャラクターの行動ロジック修正 移動編

time 2016/04/05

というわけで、新年度に入りつつもブログを放置気味なりべるんです。
やろうとしていることが難解だったり記事にしにくかったりでほとんど更新できていません。
とはいえ、「Unityゲーム開発日記」と名打っている以上、定期的に話題は出していきたいところです。

今回は敵キャラクターのロジックや行動ルーチンの見直しになります!
これまた前に適当に作って以来、手をつけていなかった部分です。

現状では、
・プレイヤーを見つけると一直線に突撃
・どの方向から近づいても瞬時にプレイヤーを発見する
・障害物があると回り込まず、完全に詰まりっぱなしになる
・プレイヤーが止まっている場合は攻撃を行わない
・ダメージモーション中も移動する
・ダメージモーションをカットして攻撃をしてくる
・押すと簡単にひっくり返る

等々、アクションゲームとしてみると欠陥品です。
行動ルーチンだけじゃどうしようもない部分もありますが、これを気に敵関連のオブジェクトやスクリプトを作り直すことにしました!

といっても全て直すのはかなり骨が折れるので、今回は移動関係のロジックを見直してみます。
ボスとか特殊な敵はひとまず考慮しないことにします。

shot2ss20160404235912272

テスト対象は↑の敵キャラクター「バブぴょん」です。
所謂スタンダードな雑魚的で、いろいろな場面に登場させる予定です。

敵キャラクター側の索敵について

基本的に雑魚敵はプレイヤーを発見した場合のみ行動を行います。
敵キャラ毎に索敵範囲を作り、それでプレイヤーが範囲内にいるかを判定します。

shot2ss20160404235946605

上はバブぴょんの場合の索敵範囲で、正面をBoxColliderでカバーしています。
ステルスアクションでも硬派なFPSでもないので、索敵に関しては割と単純です。
音とかそういう要素もありません。

敵キャラクター本体にも移動&ダメージ判定用のコライダーがあるので、索敵用オブジェクトにBoxColliderを付け、それを本体の子に設定しています。
合わせて以下のスクリプトを作成&アタッチします。

using UnityEngine;
using System.Collections;

public class EnemyActionRange : MonoBehaviour {

    public Transform lookTarget;
    public bool isDetectPlayer;

    private Enemy enemy;
    private Vector3 angle;

    void Start () {
        enemy = GetComponentInParent<Enemy>();
    }

    void Update () {

    }

    void OnTriggerEnter(Collider c){
        if (TagUtility.getParentTagName(c.gameObject) == "Player"){
            isDetectPlayer = true;
            lookTarget = c.transform;
        }
    }

    void OnTriggerExit(Collider c){
        if (TagUtility.getParentTagName(c.gameObject) == "Player") {
            isDetectPlayer = false;
            lookTarget = null;
        }
    }
}

TagUtilityに関しては以下の記事をご参照下さい。
ここでは使う意味はほとんどなかったりしますが・・・。

【Unity】タグの階層表示と判定方法について

前はこれに敵本体の移動制御まで書いており、無駄に複雑な作りになっていました。
敵の移動や攻撃はEnemyクラスで行い、シンプルに「敵が索敵範囲内にいるか」のみを設定するようにします。

敵キャラクターの移動制御

プレイヤーを発見した敵キャラクターは接近して攻撃しようとします。
攻撃部分は後々実装するので、今は「移動→攻撃→一定時間停止→移動・・・」の移動部分のみを作ってみます。

以下は敵キャラクターに付与するEnemyクラスの一部です。
(他の機能も作成中なので不要な変数や処理もありますがご了承くださいorz)

 
using UnityEngine;
using System.Collections;

[RequireComponent (typeof (Rigidbody))]

public abstract class Enemy : MonoBehaviour, ICharacter {

    public float life, attackRange, moveSpeed, rotateSpeed;
    public float defaultActionInterval;
    protected bool canAction;
    protected bool isMovement, isRotation, isAttack;

    [SerializeField]
    protected float actionInterval;

    protected Animator animator;
    protected AnimatorStateInfo stateInfo;

    protected EnemyActionRange enemyActionRange;
    protected Rigidbody rigidbody;

    protected virtual void Start () {
        actionInterval = defaultActionInterval;

        canAction = true;
        isMovement = false;
        isRotation = false;
        isAttack = false;

        animator = this.GetComponent<Animator>();
        enemyActionRange = this.GetComponentInChildren<EnemyActionRange>();
        rigidbody = this.GetComponent<Rigidbody>();
    }

    protected virtual void Update () {
        if (animator && canAction) {
            stateInfo = animator.GetCurrentAnimatorStateInfo(0);

            // アクション範囲内にプレイヤーがいる場合
            if (enemyActionRange.isDetectPlayer) {
                actionInterval += 1f;
                isMovement = true;
                isRotation = true;

                // 移動制御
                if (isMovement) {
                    Vector3 forward = new Vector3(transform.forward.x, -0.1f, transform.forward.z);
                    animator.SetBool("Move", true);
                    transform.localPosition += forward * moveSpeed;
                } else {
                    animator.SetBool("Move", false);
                }

                // 回転制御
                if (isRotation && enemyActionRange.lookTarget != null) {
                    Vector3 angle = enemyActionRange.lookTarget.position - transform.position;
                    angle.y = 0;
                    transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(angle), rotateSpeed);
                }
            } else {
                animator.SetBool("Move", false);
            }
        }
    }
}

とりあえず敵を発見したら移動、発見していないor見失った場合は止まるようにしてみました。
移動制御と回転制御を別々にしているため、後々「その場から動かないが回転だけする」といった敵を作る際にも対応できます。

実際に動かすとこんな感じ。
索敵用コライダー内にプレイヤーがいる場合のみ行動しています。

まとめ

というわけで、敵キャラクターのシンプルな移動制御について考えてみました!
次は今回の移動に攻撃ロジックを乗せていこうと思います。

スポンサーリンク

down

コメントする



ツイッター