#13 Robot! Escape! - AI Behavior Tree (3)

w298·2022년 11월 8일
0

DEVLOG - Robot! Escape!

목록 보기
13/21

이번에는 Seek, Patrol 부분을 구현해 보았다.

Seek

public class Seek : Node
{
    private EnemyRobotBT ebt;
    
    private float seekWaitDuration = 3;
    private float seekWaitTimer;
    private Vector3 seekPosition;

    public Seek(BehaviorTree bt) : base(bt)
    {
        ebt = (EnemyRobotBT)bt;
        seekWaitTimer = seekWaitDuration;
    }

    public override NodeState Evaluate()
    {
        if (!ebt.ai.seekPointReached) seekPosition = ebt.ai.lastEnemyPosition;

        float remainDistance = Vector3.Distance(ebt.ai.navAgent.transform.position, seekPosition);
        if (remainDistance <= ebt.ai.navAgent.stoppingDistance * 2)
        {
            seekWaitTimer -= Time.deltaTime;
            if (seekWaitTimer < 0)
            {
                seekWaitTimer += seekWaitDuration;

                seekPosition = CreateRandomPoint();
                ebt.ai.seekPointReached = true;
            }
        }

        bool success = ebt.ai.StartMove(seekPosition);
        DebugExtension.DebugWireSphere(seekPosition, Color.green, 0.5f);

        return success ? NodeState.RUNNING : NodeState.FAILURE;
    }

    private Vector3 CreateRandomPoint()
    {
        Vector3 randomPoint = Random.insideUnitSphere * 10 + ebt.ai.lastEnemyPosition;

        NavMesh.SamplePosition(randomPoint, out NavMeshHit hit, 10, NavMesh.AllAreas);

        return hit.position;
    }
}

EnemyAI 가 플레이어를 쫒아오다가 탐지 범위에서 벗어났을 때,

  • 마지막으로 detect 된 플레이어의 위치 (위 그림에서 Cyan Sphere) 를 기준으로
  • 주변의 Random Point (위 그림에서 Green Sphere) 를 탐색한다.
  • seekLevel 이 일정 수치 아래로 내려가면 Seek 를 포기하고 Patrol 를 진행한다.

Patrol

public class Patrol : Node
{
    private EnemyRobotBT ebt;

    private float patrolWaitDuration = 3;
    private float patrolWaitTimer;

    private int current = 0;
    private int reverse = -1;

    public Patrol(BehaviorTree bt) : base(bt)
    {
        ebt = (EnemyRobotBT)bt;
        patrolWaitTimer = patrolWaitDuration;
    }

    public override NodeState Evaluate()
    {
        int count = ebt.ai.patrolPoints.transform.childCount;
        Vector3 targetPosition = ebt.ai.patrolPoints.transform.GetChild(current).transform.position;

        float remainDistance = Vector3.Distance(ebt.ai.navAgent.transform.position, targetPosition);
        if (remainDistance <= ebt.ai.navAgent.stoppingDistance)
        {
            patrolWaitTimer -= Time.deltaTime;
            if (patrolWaitTimer < 0)
            {
                patrolWaitTimer += patrolWaitDuration;

                if (current == 0 || current == count - 1) reverse *= -1;
                current += reverse;
            }
        }

        bool success = ebt.ai.StartMove(targetPosition);
        return success ? NodeState.RUNNING : NodeState.FAILURE;
    }
}

지정된 PatrolPoint 들을 순찰한다.

  • 순서는 P1 → P2 → P3 → P2 → P1 이다.
  • Behavior Tree 에서 가장 오른쪽에 있는 노드로, 아무 이벤트가 없을 경우 발생된다.

IsSeekLevelHigh, Walk

public class IsSeekLevelHigh : Node
{
    private EnemyRobotBT ebt;
    private float threshold = 60;

    public IsSeekLevelHigh(BehaviorTree bt) : base(bt)
    {
        ebt = (EnemyRobotBT)bt;
    }

    public override NodeState Evaluate()
    {
        return ebt.ai.seekLevel.currentLevel > threshold ? NodeState.SUCCESS : NodeState.FAILURE;
    }
}
public class Walk : Node
{
    private EnemyRobotBT ebt;

    public Walk(BehaviorTree bt) : base(bt)
    {
        ebt = (EnemyRobotBT)bt;
    }

    public override NodeState Evaluate()
    {
        ebt.ai.inputHandler.isWalk = true;
        ebt.ai.navAgent.speed = ebt.ai.inputHandler.maxSpeed / 3;

        return NodeState.SUCCESS;
    }
}
profile
Game Developer & Web Developer

0개의 댓글