2013年2月3日日曜日

Unityで初めてのAIキャラクター(5.さあ近づいてこい(範囲チェック))

さて、前回記事でデリゲートを勉強した後、敵キャラがプレイヤーとの距離にあわせて行動できるようにコーディングを進めていくところをみていく。

関連記事


では例のごとく、Unity Gemsからの翻訳をどうぞ!



November 22, 2012


さあ近づいてこい(範囲チェック)


さあ、NPCキャラクターには動作パターンが設定出来てAからB、BからCへの簡単な動作は出来た(もう十分だよね?)。だけどすぐ近くまで移動しても完全に無視される。まあ、まだ近づいたら何をするか指令してないので当たり前だ。

範囲チェック、すなわちプレイヤーとNPCキャラクターの距離を見ていく必要がある。これには二つのアプローチがある:
  • Trigger Colliderを使用する方法
  • Vector3.Distanceを使用する方法

Trigger Colliderを使用する方法:


長所:

  • 使用するのが簡単
  • エンジンに任せるだけ
  • コライダオブジェクトへの参照を取得出来る

短所:

  • エンジン以上にやらせたい場合に対応しづらい

Trigger Colliderを使用するのは簡単で球体コライダをアタッチしてIsTriggerをセットしてそのあとにコリジョンをチェックする。何かが範囲に入ると誰が入ったかを作成された関数参照で確認を行い、それに対するアクションを適用する。エンジンで行われるので毎フレーム全てのコライダを使って全ての三角形とコライダの距離を測って

Vector3.Distanceを使用する方法:


長所:

  • 使用するのが簡単で実装しやすい

欠点:

  • 関数の選択によっては高価な場合がある
  • オブジェクトへの参照が必要

Vector3.Distance を使用することも出来るが平方根演算は本当に遅いので、sqrMagnitudeを使用して平方根演算を避けることにする。

では、2つの例を示してそれぞれが良い場合を見ていく。はじめに、敵キャラがいて周囲の全て(敵、プレイヤー、NPCキャラクター)と衝突判定をする。それぞれの全てについて参照を保持する、ことは出来ず、それに加えて、定期的に新規のオブジェクトまたは破棄されたオブジェクトを定期的にコールする必要がある。このケースではコライダが役に立つ:

void OnTriggerEnter(Collider other){}

何かが衝突するとコライダへの参照を自動的に取得出来る。これにより配列や参照のリストなどは必要がない。もうひとつは参照のことであり、衝突の後は参照は破棄される。欠点としては、コライダ オブジェクトが丸ごと作成、破棄されるのでコンストラクタ、ガーベージコレクションが実施され、NPCキャラクターと衝突するのは誰か自分でチェックする必要がある。


もし敵キャラをある特定のオブジェクト、例えばプレイヤーと相互作用させたい場合で、さらに何らかの理由により定常的に発生する場合はプレイヤーへの参照Start()時に取得し、継続的に使用するかもしれない。
Transform _player;

void Start(){
   _player = GameObject.Find("Player").GetComponent();
}
次にプレイヤーが範囲内にいるかチェックしよう。すでに述べた通り、Vector3.Distanceはひとつの案だが平方根演算があり、最悪とまでは言わないが避けることができる。

距離の二乗(すでにこのチュートリアルでも用いた)をここでは使用して平方根演算を避ける。squareRangeが100だったら実際の距離は10と覚えれば良い。
Transform _player;
Transform _transform;
float squareFarRange = 100f;

void Start(){
   _transform = GetComponent();
   _player = GameObject.Find("Player").GetComponent();
}

function Update(){
    if(AIFunction())//Player in
}

bool AIFunction(){
    if((_transform.position - _player.position).sqrMagnitude < squareFarRange)return true;
    else return false;
}
よっしゃ、これで関数を修正して範囲の中に入ったらNPCキャラクターに追跡され、範囲から出ると通常のじょうたに戻るように出来る。
bool AIFunction(){
    if((_transform.position - player.position).sqrMagnitude < attackRange){
       _delFunc = this.Attack;
      return true;
   }else{
        _delFunc = this.Walk;
        return false;
    }
}
Updateも合わせて修正が必要だ:
if(AIFunction()&&isCorouting){
    StopAllCoroutines();
    del = true;
}
AIFunctionをコールすると戻り値はtrueとなり、このタイミングではもし範囲内にいればすでにプレイヤーはターゲットに設定されていることに注目すべきだ。isCorouting はチェックされていてNPCキャラクターがコルーチンを実行中の場合は、アニメーション実行中であればそれを停止して、プレイヤーの追跡が始まる。delはtrueにセットされて歩行の関数がコールされる。

_delFunc は Walk のみを使用するため変更されることがない。もし修正するのであれば _delFunc に 含まれる関数が Walk であることを確認する必要がある。

これで敵キャラは巡回ルーチンを備えつつ、近づきすぎると攻撃してきて、プレイヤーが離れると再度、巡回に戻る。ところで、まだ攻撃のコードは書いてなかったのでここでそれを追加しよう。

Attack 関数が必要だ:
void Attack(){
   if((_transform.position - player.position).sqrMagnitude >range){
      Move(player);
      animation.CrossFade("walk");
  }else{
      print ("バシッ!!");
   }
}
そしてようやくAI関数を修正する:
bool AIFunction(){
   if((_transform.position - player.position).sqrMagnitude < attackRange){
      _delFunc = this.Attack;
      return true;
   }else{
      _delFunc = this.Walk;
      return false;
   }
}
距離に応じて異なる関数を割り当てる。

そして完成スクリプトは次のとおりだ。

範囲チェックの短いコード(クリックして展開)


これで十分機能するが、前方から行ってもこうほうから行っても結果が同じであることに気付いたかもしれない。さらに、もし木の後ろや家の後ろにいる場合はどうだろう。範囲チェックのロジックだけだと範囲内にさえいれば木の後ろだろうがNPCキャラクターが見えないはずなのに場所を知られてしまう。次のセクションでそれは修正していこうと思う。
---

次回に続くぜ!

関連記事


0 件のコメント:

コメントを投稿

ブックマークに追加

このエントリーをはてなブックマークに追加

自己紹介

自分の写真
Unity3D公式マニュアル翻訳やってる人がスマホ(iPhone, Android)のゲーム開発しています。気軽に面白く初心者が遊べる内容がモットー。Blogでは開発情報をひたすら、Twitterではゲーム作成の過程で参考にしている情報を中心につぶやきます

ページビューの合計

過去7日間の人気投稿