W298.dev
ProjectsPostsAbout Me
youtube
Pages
ProjectsPostsAbout Me
Posts
TAGS
All
RL-Obstacle-Avoid
RL-Competitive
Robot-Escape
Assembly Definition
ML-Agent
RL-Obstacle-Avoid
RL-Competitive
Unity
RL-Obstacle-Avoid
RL-Competitive
Robot-Escape
Assembly Definition
SERIES
RL-Obstacle-Avoid
RL-Competitive
Robot-Escape

AI Behavior Tree (3)

https://velog.velcdn.com/images/lutca1320/post/f7e26b42-4973-49f0-9319-00e12f47dcab/image.png
이번에는 Seek, Patrol 부분을 구현해 보았다.

Seek

1public class Seek : Node
2{
3    private EnemyRobotBT ebt;
4
5    private float seekWaitDuration = 3;
6    private float seekWaitTimer;
7    private Vector3 seekPosition;
8
9    public Seek(BehaviorTree bt) : base(bt)
10    {
11        ebt = (EnemyRobotBT)bt;
12        seekWaitTimer = seekWaitDuration;
13    }
14
15    public override NodeState Evaluate()
16    {
17        if (!ebt.ai.seekPointReached) seekPosition = ebt.ai.lastEnemyPosition;
18
19        float remainDistance = Vector3.Distance(ebt.ai.navAgent.transform.position, seekPosition);
20        if (remainDistance <= ebt.ai.navAgent.stoppingDistance * 2)
21        {
22            seekWaitTimer -= Time.deltaTime;
23            if (seekWaitTimer < 0)
24            {
25                seekWaitTimer += seekWaitDuration;
26
27                seekPosition = CreateRandomPoint();
28                ebt.ai.seekPointReached = true;
29            }
30        }
31
32        bool success = ebt.ai.StartMove(seekPosition);
33        DebugExtension.DebugWireSphere(seekPosition, Color.green, 0.5f);
34
35        return success ? NodeState.RUNNING : NodeState.FAILURE;
36    }
37
38    private Vector3 CreateRandomPoint()
39    {
40        Vector3 randomPoint = Random.insideUnitSphere * 10 + ebt.ai.lastEnemyPosition;
41
42        NavMesh.SamplePosition(randomPoint, out NavMeshHit hit, 10, NavMesh.AllAreas);
43
44        return hit.position;
45    }
46}
47
https://velog.velcdn.com/images/lutca1320/post/8a9c2d21-2e70-4c8d-ace4-6cb6f56434be/image.webp
EnemyAI 가 플레이어를 쫒아오다가 탐지 범위에서 벗어났을 때,
  • 마지막으로 detect 된 플레이어의 위치 (위 그림에서 Cyan Sphere) 를 기준으로
  • 주변의 Random Point (위 그림에서 Green Sphere) 를 탐색한다.
  • seekLevel 이 일정 수치 아래로 내려가면 Seek 를 포기하고 Patrol 를 진행한다.

Patrol

1public class Patrol : Node
2{
3    private EnemyRobotBT ebt;
4
5    private float patrolWaitDuration = 3;
6    private float patrolWaitTimer;
7
8    private int current = 0;
9    private int reverse = -1;
10
11    public Patrol(BehaviorTree bt) : base(bt)
12    {
13        ebt = (EnemyRobotBT)bt;
14        patrolWaitTimer = patrolWaitDuration;
15    }
16
17    public override NodeState Evaluate()
18    {
19        int count = ebt.ai.patrolPoints.transform.childCount;
20        Vector3 targetPosition = ebt.ai.patrolPoints.transform.GetChild(current).transform.position;
21
22        float remainDistance = Vector3.Distance(ebt.ai.navAgent.transform.position, targetPosition);
23        if (remainDistance <= ebt.ai.navAgent.stoppingDistance)
24        {
25            patrolWaitTimer -= Time.deltaTime;
26            if (patrolWaitTimer < 0)
27            {
28                patrolWaitTimer += patrolWaitDuration;
29
30                if (current == 0 || current == count - 1) reverse *= -1;
31                current += reverse;
32            }
33        }
34
35        bool success = ebt.ai.StartMove(targetPosition);
36        return success ? NodeState.RUNNING : NodeState.FAILURE;
37    }
38}
39
https://velog.velcdn.com/images/lutca1320/post/43b3d6e7-f1ee-4fff-a8ac-cf17eac985b5/image.webp
지정된 PatrolPoint 들을 순찰한다.
  • 순서는 P1 → P2 → P3 → P2 → P1 이다.
  • Behavior Tree 에서 가장 오른쪽에 있는 노드로, 아무 이벤트가 없을 경우 발생된다.

IsSeekLevelHigh, Walk

1public class IsSeekLevelHigh : Node
2{
3    private EnemyRobotBT ebt;
4    private float threshold = 60;
5
6    public IsSeekLevelHigh(BehaviorTree bt) : base(bt)
7    {
8        ebt = (EnemyRobotBT)bt;
9    }
10
11    public override NodeState Evaluate()
12    {
13        return ebt.ai.seekLevel.currentLevel > threshold ? NodeState.SUCCESS : NodeState.FAILURE;
14    }
15}
16
1public class Walk : Node
2{
3    private EnemyRobotBT ebt;
4
5    public Walk(BehaviorTree bt) : base(bt)
6    {
7        ebt = (EnemyRobotBT)bt;
8    }
9
10    public override NodeState Evaluate()
11    {
12        ebt.ai.inputHandler.isWalk = true;
13        ebt.ai.navAgent.speed = ebt.ai.inputHandler.maxSpeed / 3;
14
15        return NodeState.SUCCESS;
16    }
17}
18