AIに対する考察

AIに対する考察


考察するソース

ステートマシンの要件定義


  • 状態遷移(busy、sleeping)
  • トリガー範囲内で攻撃
  • ターゲットに攻撃
  • ダメージを与える
  • ダメージを受ける
  • 死ぬ
  • アニメーション制御

眠っていなくてターゲットを見つけていればターゲットに接近する
移動には割り当てられたキャラクターコントローラーを利用
アニメーションは割り当てられたアニメーションコンポーネントを利用
距離を比較する際は2乗の値を利用して比較している(平方根を使わない事でパフォーマンスアップ)
敵キャラクタが空中に配置されてもゲームスタート時は設置するように設計されている


"いびき"処理はタイマーを持っている
プレイヤー以外も攻撃して敵同士攻撃できるような設計になっている
SendMessageとコルーチンが積極的に利用されている

このほかにムード(機嫌)クラスを持っていて攻撃時に機嫌が悪いと攻撃してくるようになっている


部分理解


  public static IEnumerator WaitForAnimation (AnimationState state, float ratio)
   {
       state.wrapMode = WrapMode.ClampForever;
       state.enabled = true;
       state.speed = (state.speed == 0) ? 1 : state.speed;
       
       while (state.normalizedTime < ratio - float.Epsilon) {
           yield return null;
       }
   }

この関数はfloat.Epsilonとstate.normalizedTimeの意味を理解しないと何をしているかわからないと思う

float.Epsilon(イプシロン)はunityで扱える0に近い最小の数字を返す。資料:Mathf.Epsilon
つまり print (float.Epsilon); とすると 1.401298E-45 になる。これは0.000000000000000000000000000000000000000000001401298464324817 である

state.normalizedTimeは0~1の正規化されたアニメーションの進行度をあらわす。資料:AnimationState.normalizedTime
つまり0や1を返す状況がある。このことからコードを見ればratioが1だった場合は1までしっかり再生するし0なら即終了する事がわかる

state.speedがなんらかの要因で0になっていた場合は1になるし0.5等の値が入っていた場合はその値を保持する。これによりアニメーションは必ず進行再生される




  public static IEnumerator PlayAnimation (AnimationState state)
   {
       state.time = 0;
       state.weight = 1;
       state.speed = 1;
       state.enabled = true;
       var wait = WaitForAnimation (state, 1f);
       while (wait.MoveNext()) {
           yield return null;
       }
       state.weight = 0;
   }

while (wait.MoveNext()){}の部分がわかりにくい。まずIEnumerator.MoveNextがどんな関数であるか理解する必要がある。
列挙子が次の要素に正常に進んだ場合は true。列挙子がコレクションの末尾を越えた場合は false。

つまり、ここではwaitはWaitForAnimationの列挙コレクションでありyield return null;されている間はコレクション末尾ではないということでtrueが返されている
WaitForAnimation列挙コレクション関数が終了の際は末尾となりfalseとなる



<簡単な理解用のテストコード>

using UnityEngine;
using System.Collections;

public class QuaternionTest3 : MonoBehaviour
{
  

  void Start ()
   {
       StartCoroutine (Test2 ());
       print ("MainEnd");
   }
   
   public static IEnumerator TimerCoroutine (float x)
   {
       float timer = 0;
       
       while (timer < x) {
           timer += Time.deltaTime;
           yield return null;
       }
       print ("TestEnd");
       
   }
   
   public static IEnumerator Test2 ()
   {
       var wait = TimerCoroutine (3);
       while (wait.MoveNext()) {
           yield return null;
       }
       print ("Test2End");
   }
   
}

部分理解その2


分離されたオブジェクト

<要件定義>
  • オブジェクトが移動していればwalkアニメーションを再生する

<仕組み>
前回update時の移動終了位置と今回の更新された位置を引き算してベクトルを得る
このベクトルの長さがminimumDistanceより大きければ「移動した」と判断して
walkのアニメーションを再生する


using UnityEngine;
using System.Collections;

public class WalkingAnimationTest : MonoBehaviour {
  
   Transform _transform;
   Vector3 _lastPosition;
   AnimationState _walk;
   
   public float minimumDistance = 0.01f;
   
   void Start () {
       _transform = transform;
       _lastPosition = _transform.position;
       _walk = animation["walk"];
       _walk.layer =2;
   }
   
   void Update () {
       float moved= (_transform.position - _lastPosition).magnitude;
       _lastPosition = _transform.position;
       
       if(moved < minimumDistance){
           _walk.weight = 0;
       }else{
           _walk.speed =1;
           _walk.weight = moved * 100;
           _walk.enabled = true;
       }
   }
}

<メモ>
今回のチュートリアルから得られた経験として「ひとつのスクリプト内にいくつもの機能を無理に収める必要は無い」という事がある

このWalkingAnimationTestオブジェクトの様に汎用的に他のオブジェクトに流用できる事が想定でき
またコードの可読性を高めるような分離は、何らかのオーバーヘッドが想定されるものの有用であるように感じる

また個々のオブジェクトにstrat()、update()が利用できるのでステートマシンの状態管理も簡潔に書けるのではないだろうか?
これはunity特有のテクニックになるだろうが今後少し意識して検証してみる必要がありそうに感じる


  • 最終更新:2013-11-12 13:30:52

このWIKIを編集するにはパスワード入力が必要です

認証パスワード