LoginSignup
12
14

More than 5 years have passed since last update.

Behavior Designerで超簡単なAIを作る回 (Day2)

Last updated at Posted at 2017-08-13

オリジナルのTaskを作成する

自作のActionと、ConDitionalを作成してTaskとして追加し、オリジナルのAIを組んでみます。
公式のサンプルを元に視界内にTargetが入る(Conditional)と、Targetに向かって進む(Action)Aiを作成。

自作Action

まず下準備として以下のnamespaceを追加します


using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;

そして、Actionクラスを継承する形で自作のActionを作成します。

using UnityEngine;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;

public class WithinSight : Conditional
{

}

Conditionalクラスを確認してみるとTaskクラスを継承しています。
中身を見るとわかるのですが、かなりUnityのスクリプトライフサイクルに似た形で実装されています。

TaskFlowChart.png

基本的にはOnAwake,OnStart,OnUpdate,OnEndといって仮想メソッドをoverrideして自前のTaskを作ることになります。

スクリプト解説

公式のサンプルを参考にし、最終的に以下のような実装になりました。

WithinSight.cs
using UnityEngine;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;

public class WithinSight : Conditional
{
    [SerializeField]
    private float _fieldOfViewAngle;
    [SerializeField]
    private string _targetTag;
    [SerializeField]
    private SharedGameObject _targetGameObject;

    private Transform[] possibleTargets;

    public override void OnAwake()
    {
        var targets = GameObject.FindGameObjectsWithTag(_targetTag);
        possibleTargets = new Transform[targets.Length];
        for(int i = 0; i < targets.Length; i++)
        {
            possibleTargets[i] = targets[i].transform;
        }
    }

    public override TaskStatus OnUpdate()
    {
        for(int i = 0; i < possibleTargets.Length; i++)
        {
            if(CheckWithinSight(possibleTargets[i], _fieldOfViewAngle))
            {
                _targetGameObject.Value = possibleTargets[i].gameObject;
                return TaskStatus.Success;
            }
        }
        return TaskStatus.Failure;
    }

    public bool CheckWithinSight(Transform targetTransform, float fieldOfViewAngle)
    {
        Vector3 direction = targetTransform.position - transform.position;
        return Vector3.Angle(direction, transform.forward) < fieldOfViewAngle;
    }
}

SharedGameObject変数は、Behaviour Designer独自の型で、publicな変数のような形でどのTaskからでも参照できるようにする際に使用します。
例えば、今回のようにShadredGameObject型を使うことによって、一度TargetとなるGameObjectをEditor上でInspectorに設定すればその変数を使いまわす形で各Taskのプロパティに設定することができます。

SnapCrab_NoName_2017-8-13_3-9-32_No-00.png
Name:Target,Type:GameObjectのGlobal Variableを作成

SnapCrab_NoName_2017-8-13_3-10-22_No-00.png
先ほどのGlobal Variableな変数をSharedGameObjectに設定

次に、OnUpdateをoverrideしています。
OnUpdateはTaskStatusを返すメソッドで、Inactive,Failure,Success,Runningを返すことができます。
今回は視野角内にTargetがいた時にSuccenssを返し、いなかった時はFailureを返しています。
こうすることでSequenceを使用した際に、Successの時のみ次のTaskを実行させることができます。

自作Conditional

次に、Conditionalを作っていきます。
Actionと同様にnamespaceを追加し、今度はConditionalクラスを継承します。

using UnityEngine;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;

public class MoveTowards : Action
{

}

Actionクラスと同じようにTaskクラスを継承しているので基本的なスクリプトライフサイクルは一緒です。

スクリプト解説

Actionタスクは以下のような実装になりました。

using UnityEngine;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;

public class MoveTowards : Action
{
    [SerializeField]
    private float _speed = 0;
    [SerializeField]
    private SharedGameObject _targetGameObject;

    public override TaskStatus OnUpdate()
    {
        if(Vector3.SqrMagnitude(transform.position - _targetGameObject.Value.transform.position) < 0.1f)
        {
            return TaskStatus.Success;
        }
        transform.position = Vector3.MoveTowards(transform.position, _targetGameObject.Value.transform.position, _speed * Time.deltaTime);
        transform.LookAt(_targetGameObject.Value.transform);
        return TaskStatus.Running;
    }
}

先ほどとは違い、OnUpdateでは自分自身と、Targetとの距離の2乗が0.1より小さくなった時にSuccessを返すことでそのSequenceを正常終了します。
また、0.1より大きいときはまだ追跡中と見なしてRunningを返します。
そうすることでTargetに接触するまで追いかけ続けます。

MoveTowards.cs内でもSharedGameObjectを使用していますが、これを使うことで意図せずにWithinSight.csと異なったGameObjectをTargetにすることを防ぐことができます。

動作確認

Treeは以下のようになりました。

SnapCrab_NoName_2017-8-13_3-33-18_No-00.png

実際に動かすとこんな感じ

SimpleNavMesh3.gif

WithinSight.cs内のOnUpdateFailureを返している箇所をRunningに変更することで、視野角内にTargetが入った瞬間に追跡するといった挙動に変更することができます。

SimpleNavMesh4.gif

まとめ

このようにBehavior DesignerのActionクラス,Conditionalクラスを継承することで簡単に自作のTaskを追加することができました。

12
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
14