Unity 최종 프로젝트 - 41

이준호·2024년 3월 7일
0
post-custom-banner

📌 Unity 최종 프로젝트



📌 브로셔 요약 전 정리

➔ 🌳 Behavior Tree / Editor View






❓ Why Behavior Tree?

사진 출저

  • 유한 상태 기계(Finite State Machine)가 비교적 간단한 행동표현들을 하기에 매우 직관적이고 관리 유용한것은 분명합니다.
  • 하지만 Enemy의 경우는 플레이어가 직접 키를 누르며 조종하며 상호작용하는 것이 아닌, Enemy가 알아서 그 상황에 맞는 적절한 행동이 나오도록 하게 해야합니다.
  • 그러기 위한 상황에 따른 조건의 분기들이 규모가 커지면 커질수록 오히려 스파게티처럼 꼬이고 복잡해져서 유지보수성이 떨어지고 가독성이 나빠집니다. 계층적 유한 상태기계(Hierarchical Finite State Machine)을 사용하면 나아지긴 하지만 Enemy의 입장으로는 근본적인 문제에 대한 해결책은 되지 못합니다.
  • 하지만, BT를 사용하여 트리 구조를 계측적으로 잘 짜두어 이미지화를 시켜 그에 맞게 클래스들을 나눠 행동 로직들의 추가나 제거가 편리해져 유지보수에 용이합니다.
  • 또한 한번 잘 짜둔 트리 구조를 기반으로 파생형을 만들거나, 다른 행동들을 편하게 확장하는 것이 가능하여 BT를 선정하였습니다.





🌳 Behavior Tree 구현

Originally Behavior Tree

  • Core

    • Behavior Tree Runner
      • Tree의 최상위 위치한 root노드가 있다.
      • root노드를 실행시켜주는 Operate 메소드.
      • 단지 처음 BT를 시작해주는 역할
    • DataContext
      • BT를 이용한 AI를 만들 때, 필요한 Component들의 저장소.
      • 추후 만들때 더 필요한 데이터는 추가해서 사용.
    • INode
      • 노드의 통일성을 위한 interface
      • 노드의 상태를 반환하는 Evaluate() 메소드
  • Compositie

    • Sequence Node
      • 자식 노드의 상태가 Success라면 다음 자식노드 진행. (AND)
    • Selector Node
      • 자식 노드의 상태가 Failure라면 다음 자식노드 진행. (OR)
    • RandomSelector Node
      • 하위 노드중 하나를 랜덤으로 진행
  • Decorator

    • Inverter Node
      • 자식 노드의 상태를 반전시킨다.
      • 성공이면 실패를, 실패면 성공을
    • Repeat Node
      • 자식 노드의 상태에 상관없이 지정된 횟수만큼 Running을 반복하고 횟수가 완료되면 Success를 반환한다.
      • 추후 적용하며 리팩토링 예정.
    • Until Fail Node
      • 자식 노드가 실패를 반환할 때까지 계속해서 재검사를 진행한다.
      • 실패를 반환하면 성공을 반환한다.
    • Succeed Node
      • 자식의 상태에 상관없이 항상 성공을 반환.
      • 고장이 예상되는 분기의 테스트 처리
      • 반대는 Inverter를 이용
  • Action
    - Func 델리게이트를 사용하여 실제 행동을 정의.






Renewal Behavior Tree

  • Editor View를 구현하여 사용하기 위해 구조를 전체적으로 바꿔주었습니다.

  • Behavior Tree 에 FSM 요소를 결합

  • 각 노드들은 추상클래스 Node를 상속받고 추상메서드 OnStart, OnUpdate, OnStop을 구현해야 합니다.
  • 노드의 반환이 Running일 경우, 다음 순회때 OnStart를 실행하지 않습니다.
  • Running이 아닌 Success, Failure를 반환해야 다음 순회에 다시 OnStart가 실행됩니다.
  • OnUpdate는 반환값에 상관없이 매 순회마다 실행합니다.










🎨 Behavior Tree Editor View 구현

Editor View를 구현하게 된 이유

  • Action을 각 클래스로 나누지 않고 생성자와 Func를 통해서 한 스크립트에서 구조를 짜고 행동을 구현하다 보니 점점 규모가 커지며 코드가 매우 길어지고 각 액션들의 확인이 매우 불편해졌습니다.
  • 그리하여 Action을 각각의 클래스로 나눠 분할을 해봤지는데 코드의 가독성이 훨씬 좋아졌습니다. 하지만 그래도 행동트리의 구조가 어떤식으로 작동해서 돌고있는지 내부적인 확인은 아직 어려움이 있었습니다.
  • 최상위 Node를 Scriptable Object로 만들어 중복 데이터를 막고, 하위 노드들이 Node를 상속받아 SO로 만들어 인스펙터 창에서 해당 트리가 어떤 노드들을 가지고있는지 확인할 수 있게 하였는데 노드의 구조상 일열로 배치된 것만 보이니 어떤 노드가 들어갔는지는 알아도 어떻게 작동되는지는 확인이 어려운 점이 남아있었습니다.
  • 그리하여 UI Builder를 이용한 GraphView Editor를 만들어 내부적인 코드 구조를 몰라도 각 마우스와 버튼의 상호작용을 통해 쉽게 BT를 구성할 수 있고, 시각적으로도 한눈에 어떻게 구성되고 어떻게 흘러가는지 확인이 가능하여 유지보수성과 가독성이 매우 향상되었습니다.
  • 또한 SO작업을 하며 최상위 Node 클래스에서 FSM의 구조와 비슷한 Start, Update, Stop을 만들어 Start -> Update -> Stop 순으로 작동하고, 노드가 최초에 Start를 한번 실행하고 Running을 반환하면 다음 순회 때 Start는 실행되지 않게 하였습니다. 이렇게 바꾸게된 이유는 랜덤 좌표를 찍어서 이동하면 그 좌표에 도달할 때 까지는 다시 랜덤좌표가 찍히면 안되는데 이 방식을 쓰기 전에는 Bool값을 사용하여 여러곳에서 처리를 막아줘야 했습니다. 하지만 Start를 활용하여 유지보수성이 향상시키기 위해 변경하였습니다.





Editor View 구현 일지












➔ 👀 Field Of View / Event Sound Detect

좀비가 플레이어를 감지하는 방법은 단순히 범위 내에 들어오는 것을 넘어, 실제 현실에서와 같이 시야각과 소리를 통해 플레이어를 인지합니다. 이를 통해 보다 현실적이고 몰입감 있는 경험을 제공합니다.






📐 내적을 이용한 시야각 확인

  • 두 벡터의 방향이 같으면 내적 값은 1, 수직이면 0, 반대 방향이면 -1입니다.
  • 좀비의 전방 방향과 플레이어를 바라보는 방향 간의 내적이 설정된 기준(enemyDot, 예: 0.9)보다 작다면, 거리(distanceToTarget)를 측정합니다.
  • 좀비 위치에서 플레이어 방향으로 distanceToTarget만큼 Ray를 발사해 장애물이 없는지 확인합니다.
  • 모든 조건이 충족되면, 좀비는 플레이어를 추적 및 공격 대상으로 설정합니다.





🔊 이벤트를 이용한 소리 감지

  • 좀비는 생성 시 소리 감지 이벤트를 구독하고, 사망 시 해당 이벤트 구독을 해제합니다.

  • 플레이어의 움직임이나 공격 소리는 좀비의 플레이어 감지 로직에 영향을 주는 이벤트를 발동시킵니다.

  • 이를 통해 좀비는 시야각 내에 플레이어가 없어도 소리를 통해 플레이어의 존재를 인지할 수 있습니다.
profile
No Easy Day
post-custom-banner

0개의 댓글