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 (2)

https://velog.velcdn.com/images/lutca1320/post/a81dac09-2b44-44d9-a040-a951a0fb03cf/image.png
이번에는 다음 부분을 구현할 것이다.

IsDetectEnemy

이 노드는 하는 일이 많다. 추후에 수정할 것이다.

Success

  1. redZone 에 Enemy 가 존재하는지 확인한다. 있으면 enemyObject 로 지정 및 SUCCESS -> 발포
  2. redZone 에 Enemy 가 없으면, yellowZone 에서 확인한다. 있으면 seekLevel 을 올린다. 80이 넘을 시 enemyObject 로 지정해 yellowZone 에 있을 때에도 SUCCESS -> 발포

Failure

  1. yellowZone 에 있고, seekLevel 이 80 미만일 시 FAILURE
  2. yellowZone 에도 없고, redZone 에도 없으면 FAILURE
1public class IsDetectEnemy : Node
2{
3    private EnemyRobotBT ebt;
4
5    private Timer detectLevelStayTimer;
6
7    public IsDetectEnemy(BehaviorTree bt) : base(bt)
8    {
9        ebt = (EnemyRobotBT)bt;
10
11        detectLevelStayTimer = new Timer(2f, () =>
12        {
13            ebt.ai.detectLevel.decTimer.active = true;
14
15            detectLevelStayTimer.active = false;
16            detectLevelStayTimer.Reset();
17        });
18        detectLevelStayTimer.active = false;
19    }
20
21    public override NodeState Evaluate()
22    {
23        if (!ebt.ai.isHit)
24        {
25            var redZoneEnemy = ebt.ai.visonSensor.redZoneObjectList.Find(o => o != null && !o.GetComponent<RobotStatusController>().isDeath && o.name == "Player");
26
27            ebt.ai.enemyObject = redZoneEnemy;
28
29            if (!redZoneEnemy)
30            {
31                var yellowZoneEnemy = ebt.ai.visonSensor.yellowZoneObjectList.Find(o =>
32                    o != null && !o.GetComponent<RobotStatusController>().isDeath && o.name == "Player");
33
34                if (yellowZoneEnemy)
35                {
36                    ebt.ai.detectLevel.incTimer.active = true;
37                    ebt.ai.detectLevel.decTimer.active = false;
38
39                    if (ebt.ai.detectLevel.currentLevel >= 80)
40                    {
41                        ebt.ai.enemyObject = yellowZoneEnemy;
42                    }
43                }
44                else
45                {
46                    if (ebt.ai.detectLevel.incTimer.active)
47                    {
48                        detectLevelStayTimer.active = true;
49                    }
50
51                    ebt.ai.detectLevel.incTimer.active = false;
52                }
53            }
54            else
55            {
56                ebt.ai.detectLevel.currentLevel = 100;
57            }
58        }
59
60        if (ebt.ai.enemyObject)
61        {
62            ebt.ai.seekLevel.currentLevel = 100;
63            ebt.ai.seekLevel.decTimer.active = false;
64            ebt.ai.lastEnemyPosition = ebt.ai.enemyObject.transform.position;
65            ebt.ai.seekPointReached = false;
66            ebt.ai.closestCoverPoint = null;
67        }
68        else
69        {
70            ebt.ai.seekLevel.decTimer.active = true;
71        }
72
73        detectLevelStayTimer.Update();
74
75        DebugExtension.DebugWireSphere(ebt.ai.lastEnemyPosition, Color.cyan, 0.5f);
76
77        return ebt.ai.enemyObject ? NodeState.SUCCESS : NodeState.FAILURE;
78    }
79}
80
https://velog.velcdn.com/images/lutca1320/post/309438f5-ec92-42a7-8ce5-4aa584081945/image.gif
바깥이 YelloZone, 안쪽이 RedZone 이다. YelloZone 에 오래 머물거나 RedZone 에 진입하면 발포한다.

TakeDistance, Aim

AI가 AI와 enemyObject 간의 거리를 일정 거리를 유지하려고 하게 만드는 코드이다.
1public class TakeDistance : Node
2{
3    private EnemyRobotBT ebt;
4
5    public TakeDistance(BehaviorTree bt) : base(bt)
6    {
7        ebt = (EnemyRobotBT)bt;
8    }
9
10    public override NodeState Evaluate()
11    {
12        Vector3 direction = ebt.ai.enemyObject.transform.position - ebt.ai.transform.position;
13        float distance = direction.magnitude;
14
15        direction.Normalize();
16
17        switch (distance)
18        {
19            case < 7.5f:
20                ebt.ai.StartMove(ebt.ai.transform.position - direction * 5);
21                break;
22            case > 10:
23                ebt.ai.StartMove(ebt.ai.transform.position + direction * 5);
24                break;
25            default:
26                ebt.ai.StopMove();
27                break;
28        }
29
30        return NodeState.SUCCESS;
31    }
32}
33
1public class Aim : Node
2{
3    private EnemyRobotBT ebt;
4
5    public Aim(BehaviorTree bt) : base(bt)
6    {
7        ebt = (EnemyRobotBT)bt;
8    }
9
10    public override NodeState Evaluate()
11    {
12        ebt.ai.inputHandler.isAim = true;
13        ebt.ai.navAgent.speed = ebt.ai.inputHandler.maxSpeed / 2;
14
15        return NodeState.SUCCESS;
16    }
17}
18
https://velog.velcdn.com/images/lutca1320/post/92fc5d00-27e8-45b8-8b20-32945145d706/image.gif
앞으로 돌격하면 뒤로 물러나고, 도망가면 쫒아온다.

NeedReload

gunController 에 직접 접근해서 남은 탄약을 확인하고, 재장전이 필요하면 SUCCESS, 필요하지 않으면 FAILURE 를 리턴한다.
1public class NeedReload : Node
2{
3    private EnemyRobotBT ebt;
4
5    public NeedReload(BehaviorTree bt) : base(bt)
6    {
7        ebt = (EnemyRobotBT)bt;
8    }
9
10    public override NodeState Evaluate()
11    {
12        bool needReload = ebt.ai.gunController.ammoSystem.magAmmo <= 0;
13        return needReload ? NodeState.SUCCESS : NodeState.FAILURE;
14    }
15}
16

Reload

실제로 재장전한다.
1public class Reload : Node
2{
3    private EnemyRobotBT ebt;
4
5    public Reload(BehaviorTree bt) : base(bt)
6    {
7        ebt = (EnemyRobotBT)bt;
8    }
9
10    public override NodeState Evaluate()
11    {
12        if (!ebt.ai.GetComponent<Animator>().GetBool("isReload"))
13        {
14            ebt.ai.inputHandler.Reload();
15        }
16
17        return NodeState.RUNNING;
18    }
19}
20

Fire

발포한다.
1public class Fire : Node
2{
3    private EnemyRobotBT ebt;
4
5    public Fire(BehaviorTree bt) : base(bt)
6    {
7        ebt = (EnemyRobotBT)bt;
8    }
9
10    public override NodeState Evaluate()
11    {
12        ebt.ai.inputHandler.isFire = true;
13
14        return NodeState.RUNNING;
15    }
16}
17