개요
StateTree 는 비헤이비어 트리의 셀렉터(Selectors) 와 스테이트 머신의 스테이트(States) 및 트랜지션(Transitions) 을 결합한 일반적인 용도의 계층 스테이트 머신입니다. 이를 통해 안정적이고 체계화된 고성능 로직을 만들 수 있습니다.
스테이트 트리에는 트리 구조로 정렬된 스테이트가 있습니다. 스테이트 선택은 트리의 어느 위치에서든 트리거할 수 있지만, 처음에는 루트에서 시작합니다. 선택 프로세스 동안 각 스테이트의 진입 조건(Enter Conditions) 이 평가됩니다. 통과하면, 선택이 스테이트의 사용할 수 있는 자손 스테이트로 넘어갑니다. 사용할 수 있는 자손 스테이트가 없으면 현재 스테이트가 활성화됩니다.
스테이트 하나를 선택하면 루트부터 리프 스테이트까지 모든 스테이트가 활성화됩니다. 각 스테이트는 태스크(Task) 와 트랜지션(Transition) 으로 구성됩니다.
스테이트가 선택되면, 선택된 스테이트 및 모든 부모 스테이트가 활성화됩니다. 모든 태스크는 모든 활성화된 스테이트에 대해 실행되며, 루트에서 시작하여 선택된 스테이트로 내려갑니다.
각 태스크는 StateTree에 출력을 제공합니다. 일반적인 출력 예시에는 타깃 선택, 애니메이션 재생, 오브젝트 살펴보기 등이 있습니다. 각 스테이트는 여러 개의 태스크를 가질 수 있으며, 스테이트의 모든 태스크는 스테이트가 활성화 상태를 유지하는 한 동시에 실행됩니다. 실행이 완료된 첫 번째 태스크는 새로운 스테이트 선택으로 이어지는 트랜지션을 트리거합니다.
간단한 예시로, StateTree를 사용하여 시간(time of day) 시스템을 생성할 수 있습니다. 하루의 각 시간에 대한 스테이트를 생성하고, 각 스테이트 아래에 개별 태스크를 생성하면 됩니다. 각 태스크는 포그 밀도(fog density), 스카이 스피어 컬러(sky sphere color) 등 서로 다른 엘리먼트를 다룹니다.
또 다른 예시는 AI 에이전트가 레벨에서 이곳저곳 돌아다니고 살펴보면서 지속적으로 히트를 확인하도록 하는 것입니다. 걸어다니고 살펴보는 용도의 스테이트를 하나 생성하고, 히트에 반응하는 용도의 다른 스테이트를 하나 생성하면 됩니다.
StateTree의 트랜지션은 트리의 어떤 다른 스테이트라도 가리킬 수 있습니다. 트랜지션에는 트랜지션이 스테이트 선택 프로세스를 트리거하기 전에 충족해야 할 트리거 조건 이 있습니다.
스테이트 선택에 성공하면 새로운 스테이트가 선택됩니다. 일부 트랜지션은 지속적으로 모니터링되는 반면, 스테이트가 완료되는 즉시 실행되기만 하는 트랜지션도 있습니다. 트랜지션 평가는 리프 스테이트에서부터 시작되어 루트를 향해 위쪽 방향으로 진행됩니다. 이 프로세스 동안에는, 성공하여 스테이트 선택으로 이어지는 첫 번째 트랜지션이 선택됩니다. 이러한 계층구조를 통해 일반 트랜지션을 그룹화할 수 있습니다.
StateTree의 다양한 엘리먼트는 다음과 같습니다.
범례 | StateTree 엘리먼트 | 설명 |
---|---|---|
1 | 루트(Root) | StateTree 실행 시작 시 선택되는 첫 번째 스테이트입니다. |
2 | 셀렉터 스테이트(Selector State) | 자손 스테이트가 있는 스테이트를 뜻합니다. 이 스테이트는 직접 선택되지 않으며, 선택되면 자손 스테이트 중 하나에게로 선택이 이어집니다. |
3 | 스테이트 진입 조건(State Enter Condition) | 스테이트 선택 가능 여부를 결정하는 조건 목록을 말합니다. |
4 | 태스크(Task) | 스테이트에 속하면서 스테이트 활성화 시 실행되는 액션 세트를 뜻합니다. |
5 | 트랜지션(Transition) | 스테이트 선택 프로세스를 트리거하는 조건을 정의합니다. 트랜지션은 태스크가 완료, 성공 또는 실패하거나 모니터링되는 조건이 성공하면 트리거됩니다. |
선택 흐름
새로운 스테이트 선택하기
StateTree는 비헤이비어 트리와 유사한 방식으로 활성 스테이트를 선택합니다. 스테이트 선택은 첫 번째 틱 발생 시 루트에서부터 시작하며, 각 스테이트의 진입 조건을 평가하면서 트리를 따라 계속 내려갑니다.
- 진입 조건이 통과하지 못할 경우, 선택은 다음 형제 스테이트로 넘어갑니다.
- 진입 조건이 통과하고 스테이트가 리프 스테이트일 경우, 해당 스테이트가 새로운 스테이트로 선택됩니다.
- 스테이트에 자손 스테이트가 있을 경우, 리프 스테이트를 찾을 때까지 동일한 프로세스가 첫 번째 자손 스테이트로 계속됩니다.
스테이트에 자손 스테이트가 있지만 그 자손 스테이트가 선택될 수 없는 경우(진입 조건 실패), 해당 스테이트는 모든 진입 조건이 테스트를 통과했더라도 선택되지 않습니다.
비헤이비어 트리와 스테이트 머신의 주요 차이점은 스테이트 머신은 주로 실행이 트리를 따라 내려감에 따라 스테이트를 선택하지만, 비헤이비어 트리는 적합한 리프 노드를 찾으려고 시도한다는 점입니다.
쉽게 설명하자면, 비헤이비어 트리는 스테이트가 선택되었을 때도 스테이트 선택 로직 실행을 유지합니다. 이는 스테이트 간의 트랜지션을 위한 유일한 메서드입니다.
StateTree는 트랜지션에 기반하여 온디맨드 방식의 스테이트 선택 프로세스를 실행합니다. 첫 번째 틱에서 루트 스테이트에 대한 묵시적인 트랜지션이 일어나고, 이에 따라 첫 번째로 실행할 스테이트를 선택합니다. 해당 스테이트가 선택되면 트랜지션은 선택 로직을 실행할 시점 및 위치를 나타냅니다.
스테이트 태스크 실행하기
스테이트가 선택되면 그 스테이트의 모든 태스크가 동시 실행되기 시작합니다. 태스크는 트랜지션이 선택 프로세스를 트리거하여 스테이트가 선택될 때까지 계속 실행됩니다. 선택된 스테이트는 현재 스테이트(스테이트가 실행을 계속함) 또는 새로운 스테이트일 수 있습니다.
가장 일반적인 트랜지션 트리거는 완료(completion) 로, 활성 스테이트의 첫 번째 태스크가 완료되면 실행됩니다. 다른 트랜지션은 틱(Tick) 으로 표시되며 각 틱마다 테스트됩니다. 조건식 트랜지션이 테스트를 통과하면 스테이트 선택 로직이 실행되어 타깃 스테이트에서 선택 프로세스가 시작됩니다. 타깃 스테이트에 자손 스테이트가 있을 경우 선택 프로세스는 자손 스테이트를 선택 로직의 일부로 간주합니다.
데이터 흐름
스테이트 트리는 데이터 바인딩을 사용하여 트리 내에 데이터를 전달합니다. 데이터 바인딩을 사용하여 조건을 생성하거나 실행할 태스크를 구성할 수 있습니다. 데이터 바인딩은 스테이트 트리, 또는 특정 방식으로 노드 사이에 전달된 데이터에 액세스할 수 있습니다.
스테이트 트리의 모든 노드에서 사용 가능한 일반 데이터 타입은 다음과 같습니다.
데이터 타입 | 설명 |
---|---|
스테이트 트리 파라미터(State Tree Parameters) | 사용자는 트리가 실행 중인 동안 참조될 수 있는 스테이트 트리에 입력 파라미터를 추가할 수 있습니다. 이러한 파라미터를 통해 사용에 따라 트리의 용도를 커스터마이징할 수 있습니다. 예를 들어 사용자는 게임플레이 도중 사용할 수 있도록 트리에 외부로 전달될 수 있는 애니메이션 에셋 파라미터를 정의할 수 있습니다. |
컨텍스트 데이터(Context Data) | 컨텍스트 데이터는 선택한 스테이트 트리 스키마를 기반으로 스테이트 트리에 제공되는 사전 정의된 데이터를 의미합니다. 이는 스테이트 트리가 사용되는 위치에 따라 달라집니다. 예를 들어 액터가 사용하는 스테이트 트리는 컨텍스트로 해당 액터를 보유하게 됩니다. 하지만 동일한 스테이트 트리가 스마트 오브젝트 비헤이비어에서 사용되는 경우, 컨텍스트는 사용된 스마트 오브젝트와 이를 사용하는 액터를 의미합니다. |
이벨류에이터(Evaluators) | 이벨류에이터는 파라미터 또는 컨텍스트 데이터로는 노출할 수 없는 데이터를 스테이트 트리에 노출하는 방법을 제공합니다. 이벨류에이터는 런타임 도중 스테이트 트리에서 실행 가능한 별도의 클래스입니다. 이벨류에이터에는 변수가 포함되고 트리 시작 및 중지 시, 그리고 각 틱마다 커스텀 코드를 실행할 수 있습니다. 이벨류에이터의 프로퍼티는 파라미터나 컨텍스트 데이터, 또는 이벨류에이터 목록에 제공되는 다른 이벨류에이터에 바인딩될 수 있습니다. |
글로벌 태스크(Global Tasks) | 글로벌 태스크는 트리 시작 및 중지 이벤트 간에 활성 상태인 StateTree 태스크를 실행하는 방법을 제공합니다. 글로벌 태스크는 스테이트 선택에 사용 가능한 영구 데이터가 필요할 때 사용할 수 있습니다. 예를 들어, 시간 시스템이 현재 시간을 결정하는 글로벌 태스크를 생성할 수 있습니다. 글로벌 태스크는 트리의 루트 스테이트보다 앞서 시작되기 때문에, 이 정보는 트리가 시작되는 시점과 첫 번째 스테이트 선택 중에 사용 가능합니다. |
StateTree 노드는 서로 데이터를 공유할 수 있습니다. StateTree의 다양한 엘리먼트는 다음 방식으로 데이터에 바인딩 가능합니다.
StateTree 엘리먼트 | 엘리먼트가 바인딩 가능한 데이터 |
---|---|
진입 조건(Enter Conditions) | 공통 데이터, 그리고 모든 부모 스테이트의 태스크에 바인딩할 수 있습니다. |
트랜지션 조건(Transition Conditions) | 공통 데이터, 그리고 현재 스테이트 및 모든 부모 스테이트의 태스크에 바인딩할 수 있습니다. |
태스크(Task) | 공통 데이터, 그리고 현재 스테이트와 모든 부모 스테이트의 이전 태스크에 바인딩할 수 있습니다. |
블루프린트 통합
StateTree는 블루프린트 스크립팅을 통해 확장되도록 설계되었습니다. 다음 블루프린트 클래스를 확장함으로써 커스텀 태스크, 이밸류에이터, 조건을 생성할 수 있습니다.
베이스 클래스 | 설명 |
---|---|
UStateTreeTaskBlueprintBase | StateTree 태스크의 베이스 클래스입니다. |
UStateTreeEvaluatorBlueprintBase | StateTree 이밸류에이터의 베이스 클래스입니다. |
UStateTreeConditionBlueprintBase | StateTree 조건의 베이스 클래스입니다. |
이전에 소개한 예시의 후속으로, StateTree와 블루프린트를 활용한 시간 시스템을 만들어 볼 수 있습니다. 이 시스템은 시간에 따라 레벨의 포그 밀도를 바꿉니다. 또한 이 시스템은 폭풍우 스펠 시전 여부를 확인하여 시간에 관계없이 라이팅 조건을 변경할 수도 있습니다.
이 예시의 경우, 다음과 같은 블루프린트 클래스를 생성할 수 있습니다.
블루프린트 클래스 | 기능 |
---|---|
태스크(Task) | 시간에 따라 포그 밀도가 점차적으로 바뀌도록 합니다. 이 태스크는 특정한 시간을 나타내는 모든 스테이트에 추가할 수 있습니다. |
조건(Condition) | 폭풍우 스펠 시전 여부를 확인합니다. 이 조건은 루트 스테이트에서 평가할 수 있습니다. 참일 경우 특수한 폭풍우 스테이트로 실행을 이동합니다. |
이밸류에이터(Evaluator) | 트랜지션 및 진입 조건에 시간을 노출합니다. |
일반 패턴
유사한 태스크 그룹화하기

태스크는 일반 스테이트 아래에 그룹화될 수 있습니다. 위의 예시에는 월드의 스마트 오브젝트를 처리하는 스테이트가 있습니다. 이 스테이트에는 스마트 오브젝트 도달 및 스마트 오브젝트 사용을 처리하는 자손 스테이트가 포함되어 있습니다.
각 자손 스테이트에는 해당 스테이트 선택 시 실행되는 태스크가 포함됩니다. 도달 자손 스테이트에는 스마트 오브젝트를 찾고, AI 에이전트를 스마트 오브젝트 방향으로 움직이는 태스크가 있습니다.
이러한 그룹화 전략을 활용하면 모든 태스크가 스테이트를 통해 동일한 트랜지션을 공유하게 됩니다.
시퀀스

StateTree에는 스테이트 시퀀스 생성 및 배치를 간소화해 주는 다음(Next) 트랜지션이 동반됩니다.
위의 예시에서 도달(Reach) 스테이트 선택 시 스마트 오브젝트 타깃 찾기(Find SO Target) , 스마트 오브젝트로 이동(Move to SO) , 보기(Look) 태스크가 실행됩니다. 일단 태스크가 실행되면 다음 트랜지션이 아래의 사용(Use) 스테이트로 실행을 이동하며, 여기에서 태스크 실행이 시작됩니다.
실패 처리하기

StateTree는 계층구조 방식으로 태스크 완료 실패를 처리하며, 활성 태스크에서부터 시작하여 트리를 따라 올라갑니다.
위의 예시에서 도달 슬롯(Reach Slot) 스테이트는 성공 시 실행을 다음 스테이트로 이동하거나(대기), 실패 시 실행을 부모 스테이트로 이동합니다(교차점에서 대기). 교차점에서 대기(Wait at Intersection) 스테이트는 자손 스테이트가 실패할 경우 유휴(Idling) 스테이트로의 트랜지션을 트리거합니다.
대기(Wait) 스테이트는 성공 또는 실패 시 부모 스테이트가 다른 스테이트를 선택할 때까지 자기 자신으로 무한정 실행을 이동합니다.
계층 데이터

태스크는 서로 데이터를 공유할 수 있습니다. 태스크에 노출된 데이터는 활성 스테이트에 속한 다른 태스크 어디에서나 사용 가능합니다. 이를 통해 StateTree 내의 리소스를 더욱 효율적으로 처리할 수 있습니다.
위의 예시에서 크라우드 독점 대기 슬롯(Crowd Claim Wait Slot) 태스크는 AI 에이전트에 대한 스마트 오브젝트 슬롯을 독점하려고 시도하며, 성공할 경우 실행을 대기 슬롯으로 이동(Move To Wait Slot) 태스크로 넘깁니다. 이 태스크는 부모 태스크에서 받은 슬롯 위치를 사용합니다. 성공할 경우 슬롯에서 대기(Wait At Slot) 태스크로 실행을 넘기며, 그러면 마찬가지로 부모 태스크에서 받은 슬롯 위치를 사용하게 됩니다.
비헤이비어 세분화하기

StateTree에서 제공하는 태스크 정리 방법을 사용하면 컨텍스트 비헤이비어를 손쉽게 달성할 수 있습니다.
위의 예시에서 대기(Wait) 스테이트는 AI 에이전트가 이곳저곳을 둘러보면서 히트 시 반응하는 동작인 스탠딩 로코모션을 처리합니다. 기본적으로 보기 대기(Wait Look) 스테이트가 실행됩니다. 스테이트가 성공하면 실행을 부모에게 반환합니다. 그러나 실패하면 실행을 히트 대기(Wait Hit) 스테이트로 이동합니다.
히트 대기(Wait Hit) 스테이트는 매스 룩앳(Mass LookAt) 및 매스 컨텍스트 애니메이션(Mass Contextual Anim) 태스크를 실행합니다. 그러면 이러한 태스크들이 히트에 적합한 애니메이션을 재생합니다.