650 lines
33 KiB
C++
650 lines
33 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "StateTreeTest.h"
|
|
#include "StateTreeTestBase.h"
|
|
#include "StateTreeTestTypes.h"
|
|
|
|
#include "StateTreeCompilerLog.h"
|
|
#include "StateTreeEditorData.h"
|
|
#include "StateTreeCompiler.h"
|
|
#include "Conditions/StateTreeCommonConditions.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "AITestSuite_StateTreeTest"
|
|
|
|
UE_DISABLE_OPTIMIZATION_SHIP
|
|
|
|
namespace UE::StateTree::Tests
|
|
{
|
|
struct FStateTreeTest_Delegate_ConcurrentListeners : FStateTreeTestBase
|
|
{
|
|
virtual bool InstantTest() override
|
|
{
|
|
UStateTree& StateTree = NewStateTree();
|
|
UStateTreeEditorData& EditorData = *Cast<UStateTreeEditorData>(StateTree.EditorData);
|
|
UStateTreeState& Root = EditorData.AddSubTree(FName("Root"));
|
|
|
|
TStateTreeEditorNode<FTestTask_BroadcastDelegate>& DispatcherTask = Root.AddTask<FTestTask_BroadcastDelegate>(FName("DispatcherTask"));
|
|
TStateTreeEditorNode<FTestTask_ListenDelegate>& ListenerTaskA = Root.AddTask<FTestTask_ListenDelegate>(FName("ListenerTaskA"));
|
|
TStateTreeEditorNode<FTestTask_ListenDelegate>& ListenerTaskB = Root.AddTask<FTestTask_ListenDelegate>(FName("ListenerTaskB"));
|
|
|
|
EditorData.AddPropertyBinding(DispatcherTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_BroadcastDelegate_InstanceData, OnTickDelegate), ListenerTaskB, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_ListenDelegate_InstanceData, Listener));
|
|
EditorData.AddPropertyBinding(DispatcherTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_BroadcastDelegate_InstanceData, OnTickDelegate), ListenerTaskA, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_ListenDelegate_InstanceData, Listener));
|
|
|
|
FStateTreeCompilerLog Log;
|
|
FStateTreeCompiler Compiler(Log);
|
|
const bool bResult = Compiler.Compile(StateTree);
|
|
AITEST_TRUE("StateTree should get compiled", bResult);
|
|
|
|
FStateTreeInstanceData InstanceData;
|
|
FTestStateTreeExecutionContext Exec(StateTree, StateTree, InstanceData);
|
|
const bool bInitSucceeded = Exec.IsValid();
|
|
AITEST_TRUE("StateTree should init", bInitSucceeded);
|
|
|
|
Exec.Start();
|
|
AITEST_FALSE(TEXT("StateTree ListenerTaskA should not trigger."), Exec.Expect(ListenerTaskA.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 1)));
|
|
AITEST_FALSE(TEXT("StateTree ListenerTaskB should not trigger."), Exec.Expect(ListenerTaskB.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 1)));
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE(TEXT("StateTree ListenerTaskA should be triggered once."), Exec.Expect(ListenerTaskA.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 1)));
|
|
AITEST_TRUE(TEXT("StateTree ListenerTaskB should be triggered once."), Exec.Expect(ListenerTaskB.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 1)));
|
|
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE(TEXT("StateTree ListenerTaskA should be triggered twice."), Exec.Expect(ListenerTaskA.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 2)));
|
|
AITEST_TRUE(TEXT("StateTree ListenerTaskB should be triggered twice."), Exec.Expect(ListenerTaskB.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 2)));
|
|
Exec.LogClear();
|
|
|
|
Exec.Stop();
|
|
|
|
return true;
|
|
}
|
|
};
|
|
IMPLEMENT_STATE_TREE_INSTANT_TEST(FStateTreeTest_Delegate_ConcurrentListeners, "System.StateTree.Delegate.ConcurrentListeners");
|
|
|
|
struct FStateTreeTest_Delegate_MutuallyExclusiveListeners : FStateTreeTestBase
|
|
{
|
|
virtual bool InstantTest() override
|
|
{
|
|
UStateTree& StateTree = NewStateTree();
|
|
UStateTreeEditorData& EditorData = *Cast<UStateTreeEditorData>(StateTree.EditorData);
|
|
UStateTreeState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
|
UStateTreeState& StateA = Root.AddChildState("A");
|
|
UStateTreeState& StateB = Root.AddChildState("B");
|
|
|
|
StateA.AddTransition(EStateTreeTransitionTrigger::OnTick, EStateTreeTransitionType::GotoState, &StateB);
|
|
StateB.AddTransition(EStateTreeTransitionTrigger::OnTick, EStateTreeTransitionType::GotoState, &StateA);
|
|
|
|
TStateTreeEditorNode<FTestTask_BroadcastDelegate>& DispatcherTask = Root.AddTask<FTestTask_BroadcastDelegate>(FName(TEXT("DispatcherTask")));
|
|
TStateTreeEditorNode<FTestTask_ListenDelegate>& ListenerTaskA0 = StateA.AddTask<FTestTask_ListenDelegate>(FName(TEXT("ListenerTaskA0")));
|
|
TStateTreeEditorNode<FTestTask_ListenDelegate>& ListenerTaskA1 = StateA.AddTask<FTestTask_ListenDelegate>(FName(TEXT("ListenerTaskA1")));
|
|
TStateTreeEditorNode<FTestTask_ListenDelegate>& ListenerTaskB = StateB.AddTask<FTestTask_ListenDelegate>(FName(TEXT("ListenerTaskB")));
|
|
|
|
EditorData.AddPropertyBinding(DispatcherTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_BroadcastDelegate_InstanceData, OnTickDelegate), ListenerTaskA0, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_ListenDelegate_InstanceData, Listener));
|
|
EditorData.AddPropertyBinding(DispatcherTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_BroadcastDelegate_InstanceData, OnTickDelegate), ListenerTaskA1, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_ListenDelegate_InstanceData, Listener));
|
|
EditorData.AddPropertyBinding(DispatcherTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_BroadcastDelegate_InstanceData, OnTickDelegate), ListenerTaskB, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_ListenDelegate_InstanceData, Listener));
|
|
|
|
FStateTreeCompilerLog Log;
|
|
FStateTreeCompiler Compiler(Log);
|
|
const bool bResult = Compiler.Compile(StateTree);
|
|
AITEST_TRUE("StateTree should get compiled", bResult);
|
|
|
|
FStateTreeInstanceData InstanceData;
|
|
FTestStateTreeExecutionContext Exec(StateTree, StateTree, InstanceData);
|
|
const bool bInitSucceeded = Exec.IsValid();
|
|
AITEST_TRUE("StateTree should init", bInitSucceeded);
|
|
|
|
Exec.Start();
|
|
AITEST_TRUE(TEXT("StateTree Active States should be in Root/A"), Exec.ExpectInActiveStates(Root.Name, StateA.Name));
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE(TEXT("StateTree ListenerTaskA0 should be triggered once"), Exec.Expect(ListenerTaskA0.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 1)));
|
|
AITEST_TRUE(TEXT("StateTree ListenerTaskA1 should be triggered once"), Exec.Expect(ListenerTaskA1.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 1)));
|
|
AITEST_TRUE(TEXT("StateTree ListenerTaskB shouldn't be triggered."), !Exec.Expect(ListenerTaskB.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 1)));
|
|
AITEST_TRUE("StateTree Active States should be in Root/B", Exec.ExpectInActiveStates(Root.Name, StateB.Name));
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE(TEXT("StateTree ListenerTaskB should be triggered once"), Exec.Expect(ListenerTaskB.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 1)));
|
|
AITEST_TRUE(TEXT("StateTree ListenerTaskA0 shouldn't be triggered."), !Exec.Expect(ListenerTaskA0.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 1)));
|
|
AITEST_TRUE(TEXT("StateTree ListenerTaskA1 shouldn't be triggered."), !Exec.Expect(ListenerTaskA1.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 1)));
|
|
AITEST_TRUE(TEXT("StateTree Active States should be in Root/A"), Exec.ExpectInActiveStates(Root.Name, StateA.Name));
|
|
Exec.LogClear();
|
|
|
|
Exec.Stop();
|
|
|
|
return true;
|
|
}
|
|
};
|
|
IMPLEMENT_STATE_TREE_INSTANT_TEST(FStateTreeTest_Delegate_MutuallyExclusiveListeners, "System.StateTree.Delegate.MutuallyExclusiveListeners");
|
|
|
|
struct FStateTreeTest_Delegate_Transitions : FStateTreeTestBase
|
|
{
|
|
virtual bool InstantTest() override
|
|
{
|
|
UStateTree& StateTree = NewStateTree();
|
|
UStateTreeEditorData& EditorData = *Cast<UStateTreeEditorData>(StateTree.EditorData);
|
|
UStateTreeState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
|
UStateTreeState& StateA = Root.AddChildState("A");
|
|
UStateTreeState& StateB = Root.AddChildState("B");
|
|
|
|
FStateTreeTransition& TransitionAToB = StateA.AddTransition(EStateTreeTransitionTrigger::OnDelegate, EStateTreeTransitionType::GotoState, &StateB);
|
|
FStateTreeTransition& TransitionBToA = StateB.AddTransition(EStateTreeTransitionTrigger::OnDelegate, EStateTreeTransitionType::GotoState, &StateA);
|
|
|
|
TStateTreeEditorNode<FTestTask_BroadcastDelegate>& DispatcherTask0 = Root.AddTask<FTestTask_BroadcastDelegate>(FName(TEXT("DispatcherTask0")));
|
|
TStateTreeEditorNode<FTestTask_BroadcastDelegate>& DispatcherTask1 = Root.AddTask<FTestTask_BroadcastDelegate>(FName(TEXT("DispatcherTask1")));
|
|
|
|
EditorData.AddPropertyBinding(FPropertyBindingPath(DispatcherTask0.ID, GET_MEMBER_NAME_CHECKED(FTestTask_BroadcastDelegate_InstanceData, OnTickDelegate)), FPropertyBindingPath(TransitionAToB.ID, GET_MEMBER_NAME_CHECKED(FStateTreeTransition, DelegateListener)));
|
|
EditorData.AddPropertyBinding(FPropertyBindingPath(DispatcherTask1.ID, GET_MEMBER_NAME_CHECKED(FTestTask_BroadcastDelegate_InstanceData, OnTickDelegate)), FPropertyBindingPath(TransitionBToA.ID, GET_MEMBER_NAME_CHECKED(FStateTreeTransition, DelegateListener)));
|
|
|
|
FStateTreeCompilerLog Log;
|
|
FStateTreeCompiler Compiler(Log);
|
|
const bool bResult = Compiler.Compile(StateTree);
|
|
AITEST_TRUE("StateTree should get compiled", bResult);
|
|
|
|
FStateTreeInstanceData InstanceData;
|
|
FTestStateTreeExecutionContext Exec(StateTree, StateTree, InstanceData);
|
|
const bool bInitSucceeded = Exec.IsValid();
|
|
AITEST_TRUE("StateTree should init", bInitSucceeded);
|
|
|
|
Exec.Start();
|
|
AITEST_TRUE(TEXT("StateTree Active States should be in Root/A"), Exec.ExpectInActiveStates(Root.Name, StateA.Name));
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE(TEXT("StateTree Active States should be in Root/B"), Exec.ExpectInActiveStates(Root.Name, StateB.Name));
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE(TEXT("StateTree Active States should be in Root/A"), Exec.ExpectInActiveStates(Root.Name, StateA.Name));
|
|
Exec.LogClear();
|
|
|
|
Exec.Stop();
|
|
|
|
return true;
|
|
}
|
|
};
|
|
IMPLEMENT_STATE_TREE_INSTANT_TEST(FStateTreeTest_Delegate_Transitions, "System.StateTree.Delegate.Transitions");
|
|
|
|
struct FStateTreeTest_Delegate_Rebroadcasting : FStateTreeTestBase
|
|
{
|
|
virtual bool InstantTest() override
|
|
{
|
|
UStateTree& StateTree = NewStateTree();
|
|
UStateTreeEditorData& EditorData = *Cast<UStateTreeEditorData>(StateTree.EditorData);
|
|
UStateTreeState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
|
TStateTreeEditorNode<FTestTask_BroadcastDelegate>& DispatcherTask = Root.AddTask<FTestTask_BroadcastDelegate>(FName(TEXT("DispatcherTask")));
|
|
TStateTreeEditorNode<FTestTask_RebroadcastDelegate>& RedispatcherTask = Root.AddTask<FTestTask_RebroadcastDelegate>(FName(TEXT("RedispatcherTask")));
|
|
TStateTreeEditorNode<FTestTask_ListenDelegate>& ListenerTask = Root.AddTask<FTestTask_ListenDelegate>(FName(TEXT("ListenerTask")));
|
|
|
|
EditorData.AddPropertyBinding(DispatcherTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_BroadcastDelegate_InstanceData, OnTickDelegate), RedispatcherTask, TEXT("Listener"));
|
|
EditorData.AddPropertyBinding(RedispatcherTask, TEXT("Dispatcher"), ListenerTask, TEXT("Listener"));
|
|
|
|
FStateTreeCompilerLog Log;
|
|
FStateTreeCompiler Compiler(Log);
|
|
const bool bResult = Compiler.Compile(StateTree);
|
|
AITEST_TRUE(TEXT("StateTree should get compiled"), bResult);
|
|
|
|
FStateTreeInstanceData InstanceData;
|
|
FTestStateTreeExecutionContext Exec(StateTree, StateTree, InstanceData);
|
|
const bool bInitSucceeded = Exec.IsValid();
|
|
AITEST_TRUE(TEXT("StateTree should init"), bInitSucceeded);
|
|
|
|
Exec.Start();
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE(TEXT("StateTree ListenerTask should be triggered once."), Exec.Expect(ListenerTask.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 1)));
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE(TEXT("StateTree ListenerTask should be triggered twice."), Exec.Expect(ListenerTask.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 2)));
|
|
Exec.LogClear();
|
|
|
|
Exec.Stop();
|
|
|
|
return true;
|
|
}
|
|
};
|
|
IMPLEMENT_STATE_TREE_INSTANT_TEST(FStateTreeTest_Delegate_Rebroadcasting, "System.StateTree.Delegate.Rebroadcasting");
|
|
|
|
struct FStateTreeTest_Delegate_SelfRemoval : FStateTreeTestBase
|
|
{
|
|
virtual bool InstantTest() override
|
|
{
|
|
UStateTree& StateTree = NewStateTree();
|
|
UStateTreeEditorData& EditorData = *Cast<UStateTreeEditorData>(StateTree.EditorData);
|
|
UStateTreeState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
|
TStateTreeEditorNode<FTestTask_BroadcastDelegate>& DispatcherTask = Root.AddTask<FTestTask_BroadcastDelegate>(FName(TEXT("DispatcherTask")));
|
|
TStateTreeEditorNode<FTestTask_CustomFuncOnDelegate>& CustomFuncTask = Root.AddTask<FTestTask_CustomFuncOnDelegate>(FName(TEXT("CustomFuncTask")));
|
|
|
|
uint32 TriggersCounter = 0;
|
|
|
|
EditorData.AddPropertyBinding(DispatcherTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_BroadcastDelegate_InstanceData, OnTickDelegate), CustomFuncTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_CustomFuncOnDelegate_InstanceData, Listener));
|
|
CustomFuncTask.GetNode().CustomFunc = [&TriggersCounter](const FStateTreeWeakExecutionContext& WeakContext, FStateTreeDelegateListener Listener)
|
|
{
|
|
++TriggersCounter;
|
|
WeakContext.UnbindDelegate(Listener);
|
|
};
|
|
|
|
FStateTreeCompilerLog Log;
|
|
FStateTreeCompiler Compiler(Log);
|
|
const bool bResult = Compiler.Compile(StateTree);
|
|
AITEST_TRUE(TEXT("StateTree should get compiled"), bResult);
|
|
|
|
FStateTreeInstanceData InstanceData;
|
|
FTestStateTreeExecutionContext Exec(StateTree, StateTree, InstanceData);
|
|
const bool bInitSucceeded = Exec.IsValid();
|
|
AITEST_TRUE(TEXT("StateTree should init"), bInitSucceeded);
|
|
|
|
Exec.Start();
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_EQUAL(TEXT("StateTree Delegate should be triggered once"), TriggersCounter, 1);
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_EQUAL(TEXT("StateTree Delegate should be triggered once"), TriggersCounter, 1);
|
|
Exec.LogClear();
|
|
|
|
Exec.Stop();
|
|
|
|
return true;
|
|
}
|
|
};
|
|
IMPLEMENT_STATE_TREE_INSTANT_TEST(FStateTreeTest_Delegate_SelfRemoval, "System.StateTree.Delegate.SelfRemoval");
|
|
|
|
struct FStateTreeTest_Delegate_WithoutRemoval : FStateTreeTestBase
|
|
{
|
|
virtual bool InstantTest() override
|
|
{
|
|
UStateTree& StateTree = NewStateTree();
|
|
UStateTreeEditorData& EditorData = *Cast<UStateTreeEditorData>(StateTree.EditorData);
|
|
UStateTreeState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
|
UStateTreeState& StateA = Root.AddChildState("A");
|
|
UStateTreeState& StateB = Root.AddChildState("B");
|
|
|
|
FStateTreeTransition& TransitionAToB = StateA.AddTransition(EStateTreeTransitionTrigger::OnTick, EStateTreeTransitionType::GotoState, &StateB);
|
|
|
|
TStateTreeEditorNode<FTestTask_BroadcastDelegate>& DispatcherTask = Root.AddTask<FTestTask_BroadcastDelegate>(FName(TEXT("DispatcherTask")));
|
|
TStateTreeEditorNode<FTestTask_ListenDelegate>& ListenerTask = StateA.AddTask<FTestTask_ListenDelegate>(FName(TEXT("ListenerTask")));
|
|
EditorData.AddPropertyBinding(DispatcherTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_BroadcastDelegate_InstanceData, OnTickDelegate), ListenerTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_ListenDelegate_InstanceData, Listener));
|
|
ListenerTask.GetNode().bRemoveOnExit = false;
|
|
|
|
FStateTreeCompilerLog Log;
|
|
FStateTreeCompiler Compiler(Log);
|
|
const bool bResult = Compiler.Compile(StateTree);
|
|
AITEST_TRUE(TEXT("StateTree should get compiled"), bResult);
|
|
|
|
FStateTreeInstanceData InstanceData;
|
|
FTestStateTreeExecutionContext Exec(StateTree, StateTree, InstanceData);
|
|
const bool bInitSucceeded = Exec.IsValid();
|
|
AITEST_TRUE(TEXT("StateTree should init"), bInitSucceeded);
|
|
|
|
Exec.Start();
|
|
Exec.LogClear();
|
|
AITEST_TRUE(TEXT("StateTree Active States should be in Root/A"), Exec.ExpectInActiveStates(Root.Name, StateA.Name));
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE(TEXT("StateTree Delegate should be triggered once."), Exec.Expect(ListenerTask.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 1)));
|
|
AITEST_TRUE(TEXT("StateTree Active States should be in Root/B"), Exec.ExpectInActiveStates(Root.Name, StateB.Name));
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_FALSE(TEXT("StateTree Delegate shouldn't be triggered again."), Exec.Expect(ListenerTask.GetName()));
|
|
AITEST_TRUE(TEXT("StateTree Active States should be in Root/B"), Exec.ExpectInActiveStates(Root.Name, StateB.Name));
|
|
Exec.LogClear();
|
|
|
|
Exec.Stop();
|
|
|
|
return true;
|
|
}
|
|
};
|
|
IMPLEMENT_STATE_TREE_INSTANT_TEST(FStateTreeTest_Delegate_WithoutRemoval, "System.StateTree.Delegate.WithoutRemoval");
|
|
|
|
struct FStateTreeTest_Delegate_GlobalDispatcherAndListener : FStateTreeTestBase
|
|
{
|
|
virtual bool InstantTest() override
|
|
{
|
|
UStateTree& StateTree = NewStateTree();
|
|
UStateTreeEditorData& EditorData = *Cast<UStateTreeEditorData>(StateTree.EditorData);
|
|
UStateTreeState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
TStateTreeEditorNode<FTestTask_Stand>& RootTask = Root.AddTask<FTestTask_Stand>();
|
|
RootTask.GetNode().TicksToCompletion = 100;
|
|
|
|
TStateTreeEditorNode<FTestTask_BroadcastDelegate>& DispatcherTask = EditorData.AddGlobalTask<FTestTask_BroadcastDelegate>(FName(TEXT("DispatcherTask")));
|
|
TStateTreeEditorNode<FTestTask_ListenDelegate>& ListenerTask = EditorData.AddGlobalTask<FTestTask_ListenDelegate>(FName(TEXT("ListenerTask")));
|
|
ListenerTask.GetNode().bRemoveOnExit = false;
|
|
|
|
EditorData.AddPropertyBinding(DispatcherTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_BroadcastDelegate_InstanceData, OnTickDelegate), ListenerTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_CustomFuncOnDelegate_InstanceData, Listener));
|
|
|
|
FStateTreeCompilerLog Log;
|
|
FStateTreeCompiler Compiler(Log);
|
|
const bool bResult = Compiler.Compile(StateTree);
|
|
AITEST_TRUE(TEXT("StateTree should get compiled"), bResult);
|
|
|
|
FStateTreeInstanceData InstanceData;
|
|
FTestStateTreeExecutionContext Exec(StateTree, StateTree, InstanceData);
|
|
const bool bInitSucceeded = Exec.IsValid();
|
|
AITEST_TRUE(TEXT("StateTree should init"), bInitSucceeded);
|
|
|
|
Exec.Start();
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE(TEXT("StateTree Delegate should be triggered once."), Exec.Expect(ListenerTask.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 1)));
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE(TEXT("StateTree Delegate should be triggered twice."), Exec.Expect(ListenerTask.GetName(), *FString::Printf(TEXT("OnDelegate%d"), 2)));
|
|
Exec.LogClear();
|
|
|
|
Exec.Stop();
|
|
|
|
return true;
|
|
}
|
|
};
|
|
IMPLEMENT_STATE_TREE_INSTANT_TEST(FStateTreeTest_Delegate_GlobalDispatcherAndListener, "System.StateTree.Delegate.GlobalDispatcherAndListener");
|
|
|
|
struct FStateTreeTest_Delegate_ListeningToDelegateOnExit : FStateTreeTestBase
|
|
{
|
|
virtual bool InstantTest() override
|
|
{
|
|
UStateTree& StateTree = NewStateTree();
|
|
UStateTreeEditorData& EditorData = *Cast<UStateTreeEditorData>(StateTree.EditorData);
|
|
UStateTreeState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
|
TStateTreeEditorNode<FTestTask_BroadcastDelegate>& DispatcherTask = Root.AddTask<FTestTask_BroadcastDelegate>(FName(TEXT("DispatcherTask")));
|
|
TStateTreeEditorNode<FTestTask_ListenDelegate>& ListenerTask = Root.AddTask<FTestTask_ListenDelegate>(FName(TEXT("ListenerTask")));
|
|
ListenerTask.GetNode().bRemoveOnExit = false;
|
|
|
|
EditorData.AddPropertyBinding(DispatcherTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_BroadcastDelegate_InstanceData, OnExitDelegate), ListenerTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_CustomFuncOnDelegate_InstanceData, Listener));
|
|
|
|
FStateTreeCompilerLog Log;
|
|
FStateTreeCompiler Compiler(Log);
|
|
const bool bResult = Compiler.Compile(StateTree);
|
|
AITEST_TRUE(TEXT("StateTree should get compiled"), bResult);
|
|
|
|
FStateTreeInstanceData InstanceData;
|
|
FTestStateTreeExecutionContext Exec(StateTree, StateTree, InstanceData);
|
|
const bool bInitSucceeded = Exec.IsValid();
|
|
AITEST_TRUE(TEXT("StateTree should init"), bInitSucceeded);
|
|
|
|
Exec.Start();
|
|
AITEST_TRUE("Expected Root to be active.", Exec.ExpectInActiveStates(Root.Name));
|
|
Exec.LogClear();
|
|
|
|
Exec.Stop();
|
|
AITEST_FALSE(TEXT("StateTree Delegate shouldn't be triggered"), Exec.Expect(ListenerTask.GetName()));
|
|
Exec.LogClear();
|
|
|
|
return true;
|
|
}
|
|
};
|
|
IMPLEMENT_STATE_TREE_INSTANT_TEST(FStateTreeTest_Delegate_ListeningToDelegateOnExit, "System.StateTree.Delegate.ListeningToDelegateOnExit");
|
|
|
|
/** Test same state and the state index < number of instance data. */
|
|
struct FStateTreeTest_Delegate_ListeningToDelegateOnExit2 : FStateTreeTestBase
|
|
{
|
|
virtual bool InstantTest() override
|
|
{
|
|
UStateTree& StateTree = NewStateTree();
|
|
UStateTreeEditorData& EditorData = *Cast<UStateTreeEditorData>(StateTree.EditorData);
|
|
UStateTreeState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
UStateTreeState& StateB = Root.AddChildState("StateA")
|
|
.AddChildState("StateB");
|
|
UStateTreeState& StateC= Root.AddChildState("StateC");
|
|
{
|
|
EditorData.AddGlobalTask<FTestTask_Stand>().GetNode().TicksToCompletion = 100;
|
|
EditorData.AddGlobalTask<FTestTask_Stand>().GetNode().TicksToCompletion = 100;
|
|
EditorData.AddGlobalTask<FTestTask_Stand>().GetNode().TicksToCompletion = 100;
|
|
EditorData.AddGlobalTask<FTestTask_Stand>().GetNode().TicksToCompletion = 100;
|
|
EditorData.AddGlobalTask<FTestTask_Stand>().GetNode().TicksToCompletion = 100;
|
|
EditorData.AddGlobalTask<FTestTask_Stand>().GetNode().TicksToCompletion = 100;
|
|
EditorData.AddGlobalTask<FTestTask_Stand>().GetNode().TicksToCompletion = 100;
|
|
EditorData.AddGlobalTask<FTestTask_Stand>().GetNode().TicksToCompletion = 100;
|
|
EditorData.AddGlobalTask<FTestTask_Stand>().GetNode().TicksToCompletion = 100;
|
|
EditorData.AddGlobalTask<FTestTask_Stand>().GetNode().TicksToCompletion = 100;
|
|
}
|
|
{
|
|
TStateTreeEditorNode<FTestTask_BroadcastDelegate>& DispatcherTask = StateB.AddTask<FTestTask_BroadcastDelegate>(FName(TEXT("DispatcherTask")));
|
|
TStateTreeEditorNode<FTestTask_ListenDelegate>& ListenerTask = StateB.AddTask<FTestTask_ListenDelegate>(FName(TEXT("ListenerTask")));
|
|
ListenerTask.GetNode().bRemoveOnExit = false;
|
|
|
|
EditorData.AddPropertyBinding(DispatcherTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_BroadcastDelegate_InstanceData, OnExitDelegate), ListenerTask, GET_MEMBER_NAME_STRING_CHECKED(FTestTask_CustomFuncOnDelegate_InstanceData, Listener));
|
|
|
|
FStateTreeTransition& Transition = StateB.AddTransition(EStateTreeTransitionTrigger::OnTick, EStateTreeTransitionType::GotoState, &StateC);
|
|
Transition.bDelayTransition = true;
|
|
Transition.DelayDuration = 0.1f;
|
|
}
|
|
{
|
|
FStateTreeCompilerLog Log;
|
|
FStateTreeCompiler Compiler(Log);
|
|
const bool bResult = Compiler.Compile(StateTree);
|
|
AITEST_TRUE("StateTree should get compiled", bResult);
|
|
}
|
|
|
|
FStateTreeInstanceData InstanceData;
|
|
FTestStateTreeExecutionContext Exec(StateTree, StateTree, InstanceData);
|
|
const bool bInitSucceeded = Exec.IsValid();
|
|
AITEST_TRUE("StateTree should init", bInitSucceeded);
|
|
|
|
Exec.Start();
|
|
AITEST_TRUE("Expected Root to be active.", Exec.ExpectInActiveStates("Root", "StateA", "StateB"));
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(1.0f);
|
|
AITEST_TRUE("Expected Root to be active.", Exec.ExpectInActiveStates("Root", "StateA", "StateB"));
|
|
AITEST_FALSE(TEXT("StateTree Delegate shouldn't be triggered"), Exec.Expect("ListenerTask"));
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(1.0f);
|
|
AITEST_TRUE("Expected Root to be active.", Exec.ExpectInActiveStates("Root", "StateC"));
|
|
AITEST_FALSE(TEXT("StateTree Delegate shouldn't be triggered"), Exec.Expect("ListenerTask"));
|
|
Exec.LogClear();
|
|
|
|
Exec.Stop();
|
|
AITEST_FALSE(TEXT("StateTree Delegate shouldn't be triggered"), Exec.Expect("ListenerTask"));
|
|
Exec.LogClear();
|
|
|
|
return true;
|
|
}
|
|
};
|
|
IMPLEMENT_AI_INSTANT_TEST(FStateTreeTest_Delegate_ListeningToDelegateOnExit2, "System.StateTree.Delegate.ListeningToDelegateOnExit2");
|
|
|
|
struct FStateTreeTest_Delegate_ListenerDispatcherOnNode : FStateTreeTestBase
|
|
{
|
|
virtual bool InstantTest() override
|
|
{
|
|
// Root(Dispatcher, ListenerOnNode1 -> DispatcherOnNode, ListenerOnNode2 -> DispatcherOnInstance, ListenerOnInstance -> DispatcherOnNode)
|
|
|
|
UStateTree& StateTree = NewStateTree();
|
|
UStateTreeEditorData& EditorData = *Cast<UStateTreeEditorData>(StateTree.EditorData);
|
|
UStateTreeState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
|
|
bool bListenerOnNode1Broadcasted = false;
|
|
bool bListenerOnNode2Broadcasted = false;
|
|
bool bListenerOnInstanceBroadcasted = false;
|
|
|
|
{
|
|
TStateTreeEditorNode<FTestTask_DispatcherOnNodeAndInstance>& DispatcherTaskNode = Root.AddTask<FTestTask_DispatcherOnNodeAndInstance>(FName(TEXT("Dispatcher")));
|
|
|
|
TStateTreeEditorNode<FTestTask_ListenerOnNode>& ListenerOnNode1TaskNode = Root.AddTask<FTestTask_ListenerOnNode>(FName(TEXT("ListenerOnNode1")));
|
|
ListenerOnNode1TaskNode.GetNode().CustomFunc = [&bListenerOnNode1Broadcasted](const FStateTreeWeakExecutionContext& WeakExecContext)
|
|
{
|
|
bListenerOnNode1Broadcasted = true;
|
|
};
|
|
|
|
TStateTreeEditorNode<FTestTask_ListenerOnNode>& ListenerOnNode2TaskNode = Root.AddTask<FTestTask_ListenerOnNode>(FName(TEXT("ListenerOnNode2")));
|
|
ListenerOnNode2TaskNode.GetNode().CustomFunc = [&bListenerOnNode2Broadcasted](const FStateTreeWeakExecutionContext& WeakExecContext)
|
|
{
|
|
bListenerOnNode2Broadcasted = true;
|
|
};
|
|
|
|
TStateTreeEditorNode<FTestTask_ListenerOnInstance>& ListenerOnInstanceTaskNode = Root.AddTask<FTestTask_ListenerOnInstance>(FName(TEXT("ListenerOnInstance")));
|
|
ListenerOnInstanceTaskNode.GetNode().CustomFunc = [&bListenerOnInstanceBroadcasted](const FStateTreeWeakExecutionContext& WeakExecContext)
|
|
{
|
|
bListenerOnInstanceBroadcasted = true;
|
|
};
|
|
|
|
EditorData.AddPropertyBinding(
|
|
FPropertyBindingPath(DispatcherTaskNode.GetNodeID(), GET_MEMBER_NAME_CHECKED(FTestTask_DispatcherOnNodeAndInstance, DispatcherOnNode)),
|
|
FPropertyBindingPath(ListenerOnNode1TaskNode.GetNodeID(), GET_MEMBER_NAME_CHECKED(FTestTask_ListenerOnNode, ListenerOnNode)));
|
|
|
|
EditorData.AddPropertyBinding(
|
|
FPropertyBindingPath(DispatcherTaskNode.ID, GET_MEMBER_NAME_CHECKED(FTestTask_DispatcherOnNodeAndInstance_InstanceData, DispatcherOnInstance)),
|
|
FPropertyBindingPath(ListenerOnNode2TaskNode.GetNodeID(), GET_MEMBER_NAME_CHECKED(FTestTask_ListenerOnNode, ListenerOnNode)));
|
|
|
|
EditorData.AddPropertyBinding(
|
|
FPropertyBindingPath(DispatcherTaskNode.GetNodeID(), GET_MEMBER_NAME_CHECKED(FTestTask_DispatcherOnNodeAndInstance, DispatcherOnNode)),
|
|
FPropertyBindingPath(ListenerOnInstanceTaskNode.ID, GET_MEMBER_NAME_CHECKED(FTestTask_ListenerOnInstance_InstanceData, ListenerOnInstance)));
|
|
}
|
|
|
|
{
|
|
FStateTreeCompilerLog Log;
|
|
FStateTreeCompiler Compiler(Log);
|
|
const bool bResult = Compiler.Compile(StateTree);
|
|
AITEST_TRUE("StateTree should get compiled", bResult);
|
|
|
|
const FCompactStateTreeState& RootState = StateTree.GetStates()[0];
|
|
int32 TaskNodeIndex = RootState.TasksBegin;
|
|
|
|
FConstStructView DispatcherView = StateTree.GetNode(TaskNodeIndex++);
|
|
FConstStructView DispatcherInstanceDataView = StateTree.GetDefaultInstanceData().GetStruct(DispatcherView.Get<const FStateTreeTaskBase>().InstanceTemplateIndex.Get());
|
|
|
|
FConstStructView ListenerOnNode1View = StateTree.GetNode(TaskNodeIndex++);
|
|
|
|
FConstStructView ListenerOnNode2View = StateTree.GetNode(TaskNodeIndex++);
|
|
|
|
FConstStructView ListenerOnInstanceView = StateTree.GetNode(TaskNodeIndex++);
|
|
FConstStructView ListenerOnInstanceInstanceDataView = StateTree.GetDefaultInstanceData().GetStruct(ListenerOnInstanceView.Get<const FStateTreeTaskBase>().InstanceTemplateIndex.Get());
|
|
|
|
const FStateTreeDelegateDispatcher DispatcherOnNode = DispatcherView.Get<const FTestTask_DispatcherOnNodeAndInstance>().DispatcherOnNode;
|
|
const FStateTreeDelegateDispatcher DispatcherOnInstance = DispatcherInstanceDataView.Get<const FTestTask_DispatcherOnNodeAndInstance_InstanceData>().DispatcherOnInstance;
|
|
const FStateTreeDelegateListener ListenerOnNode1 = ListenerOnNode1View.Get<const FTestTask_ListenerOnNode>().ListenerOnNode;
|
|
const FStateTreeDelegateListener ListenerOnNode2 = ListenerOnNode2View.Get<const FTestTask_ListenerOnNode>().ListenerOnNode;
|
|
const FStateTreeDelegateListener ListenerOnInstance = ListenerOnInstanceInstanceDataView.Get<const FTestTask_ListenerOnInstance_InstanceData>().ListenerOnInstance;
|
|
|
|
AITEST_TRUE("Dispatcher on Node should init", DispatcherOnNode.IsValid());
|
|
AITEST_TRUE("Dispatcher on Instance should init", DispatcherOnInstance.IsValid());
|
|
AITEST_TRUE("Listener on Node should init", ListenerOnNode1.IsValid() && ListenerOnNode2.IsValid());
|
|
AITEST_TRUE("Listener on Instance should init", ListenerOnInstance.IsValid());
|
|
|
|
AITEST_EQUAL("ListenerOnNode1 should be bound to Dispatcher on Node", DispatcherOnNode, ListenerOnNode1.GetDispatcher());
|
|
AITEST_EQUAL("ListenerOnNode2 should be bound to Dispatcher on Instance", DispatcherOnInstance, ListenerOnNode2.GetDispatcher());
|
|
AITEST_EQUAL("ListenerOnInstance should be bound to Dispatcher on Node", DispatcherOnNode, ListenerOnInstance.GetDispatcher());
|
|
}
|
|
|
|
{
|
|
FStateTreeInstanceData InstanceData;
|
|
FTestStateTreeExecutionContext Exec(StateTree, StateTree, InstanceData);
|
|
const bool bInitSucceeded = Exec.IsValid();
|
|
AITEST_TRUE("StateTree should init", bInitSucceeded);
|
|
|
|
Exec.Start();
|
|
AITEST_TRUE("Expected Root to be active.", Exec.ExpectInActiveStates("Root"));
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE("Expected Root to be active.", Exec.ExpectInActiveStates("Root"));
|
|
AITEST_TRUE("ListenerOnNode1 should be broadcasted", bListenerOnNode1Broadcasted);
|
|
AITEST_TRUE("ListenerOnNode2 should be broadcasted", bListenerOnNode2Broadcasted);
|
|
AITEST_TRUE("ListenerOnInstance should be broadcasted", bListenerOnInstanceBroadcasted);
|
|
|
|
Exec.Stop();
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
};
|
|
IMPLEMENT_AI_INSTANT_TEST(FStateTreeTest_Delegate_ListenerDispatcherOnNode, "System.StateTree.Delegate.ListenerDispatcherOnNode");
|
|
|
|
struct FStateTreeTest_Delegate_DispatcherOnNodeListenerOnTransition : FStateTreeTestBase
|
|
{
|
|
virtual bool InstantTest() override
|
|
{
|
|
// Root(Dispatcher)
|
|
// State1(Transition on Delegate -> DispatcherOnNode) -> State2
|
|
// State2(Transition on Delegate -> DispatcherOnInstance) -> Tree Succeeded
|
|
|
|
UStateTree& StateTree = NewStateTree();
|
|
UStateTreeEditorData& EditorData = *Cast<UStateTreeEditorData>(StateTree.EditorData);
|
|
|
|
UStateTreeState& Root = EditorData.AddSubTree(FName(TEXT("Root")));
|
|
TStateTreeEditorNode<FTestTask_DispatcherOnNodeAndInstance> DispatcherTaskNode = Root.AddTask<FTestTask_DispatcherOnNodeAndInstance>(TEXT("Dispatcher"));
|
|
UStateTreeState& State1 = Root.AddChildState(FName(TEXT("State1")));
|
|
FStateTreeTransition& DelegateOnNodeTransition = State1.AddTransition(EStateTreeTransitionTrigger::OnDelegate, EStateTreeTransitionType::NextState);
|
|
|
|
UStateTreeState& State2 = Root.AddChildState(FName(TEXT("State2")));
|
|
FStateTreeTransition& DelegateOnInstanceTransition = State2.AddTransition(EStateTreeTransitionTrigger::OnDelegate, EStateTreeTransitionType::Succeeded);
|
|
|
|
EditorData.AddPropertyBinding(
|
|
FPropertyBindingPath(DispatcherTaskNode.GetNodeID(), GET_MEMBER_NAME_CHECKED(FTestTask_DispatcherOnNodeAndInstance, DispatcherOnNode)),
|
|
FPropertyBindingPath(DelegateOnNodeTransition.ID, GET_MEMBER_NAME_CHECKED(FStateTreeTransition, DelegateListener)));
|
|
|
|
EditorData.AddPropertyBinding(
|
|
FPropertyBindingPath(DispatcherTaskNode.ID, GET_MEMBER_NAME_CHECKED(FTestTask_DispatcherOnNodeAndInstance_InstanceData, DispatcherOnInstance)),
|
|
FPropertyBindingPath(DelegateOnInstanceTransition.ID, GET_MEMBER_NAME_CHECKED(FStateTreeTransition, DelegateListener)));
|
|
|
|
{
|
|
FStateTreeCompilerLog Log;
|
|
FStateTreeCompiler Compiler(Log);
|
|
const bool bResult = Compiler.Compile(StateTree);
|
|
AITEST_TRUE("StateTree should get compiled", bResult);
|
|
|
|
const FCompactStateTreeState& RootState = StateTree.GetStates()[0];
|
|
const FCompactStateTreeState& State1State = StateTree.GetStates()[1];
|
|
const FCompactStateTreeState& State2State = StateTree.GetStates()[2];
|
|
|
|
const FTestTask_DispatcherOnNodeAndInstance& DispatcherTask = StateTree.GetNode(RootState.TasksBegin).Get<const FTestTask_DispatcherOnNodeAndInstance>();
|
|
const FStateTreeDelegateDispatcher DispatcherOnNode = DispatcherTask.DispatcherOnNode;
|
|
const FStateTreeDelegateDispatcher DispatcherOnInstance = StateTree.GetDefaultInstanceData().GetStruct(DispatcherTask.InstanceTemplateIndex.Get()).Get<const FTestTask_DispatcherOnNodeAndInstance_InstanceData>().DispatcherOnInstance;
|
|
|
|
AITEST_TRUE("Dispatcher on node should be valid", DispatcherOnNode.IsValid());
|
|
AITEST_TRUE("Dispatcher on instance should be valid", DispatcherOnInstance.IsValid());
|
|
|
|
const FCompactStateTransition* CompactDelegateOnNodeTransition = StateTree.GetTransitionFromIndex(FStateTreeIndex16(State1State.TransitionsBegin));
|
|
const FCompactStateTransition* CompactDelegateOnInstanceTransition = StateTree.GetTransitionFromIndex(FStateTreeIndex16(State2State.TransitionsBegin));
|
|
|
|
AITEST_EQUAL("State1 Transition Delegate Listener should be bound to Dispatcher on Node", CompactDelegateOnNodeTransition->RequiredDelegateDispatcher, DispatcherOnNode);
|
|
AITEST_EQUAL("State2 Transition Delegate Listener should be bound to Dispatcher on Instance", CompactDelegateOnInstanceTransition->RequiredDelegateDispatcher, DispatcherOnInstance);
|
|
}
|
|
|
|
{
|
|
FStateTreeInstanceData InstanceData;
|
|
FTestStateTreeExecutionContext Exec(StateTree, StateTree, InstanceData);
|
|
const bool bInitSucceeded = Exec.IsValid();
|
|
AITEST_TRUE("StateTree should init", bInitSucceeded);
|
|
|
|
Exec.Start();
|
|
AITEST_TRUE("Expected [Root, State1] to be active.", Exec.ExpectInActiveStates("Root", "State1"));
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE("Expected [Root, State2] to be active.", Exec.ExpectInActiveStates("Root", "State2"));
|
|
Exec.LogClear();
|
|
|
|
Exec.Tick(0.1f);
|
|
AITEST_TRUE("Expected Tree to be succeeded.", Exec.GetStateTreeRunStatus() == EStateTreeRunStatus::Succeeded);
|
|
Exec.LogClear();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
IMPLEMENT_AI_INSTANT_TEST(FStateTreeTest_Delegate_DispatcherOnNodeListenerOnTransition, "System.StateTree.Delegate.DispatcherOnNodeListenerOnTransition");
|
|
} // namespace UE::StateTree::Tests
|
|
|
|
UE_ENABLE_OPTIMIZATION_SHIP
|
|
|
|
#undef LOCTEXT_NAMESPACE
|