Last active
March 5, 2024 10:32
-
-
Save CoffeeVampir3/52ffb0d299d50094273882e79ae5a87e to your computer and use it in GitHub Desktop.
cpp_sm ue
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Fill out your copyright notice in the Description page of Project Settings. | |
#include "Ai/Behaviours/NecroAiBasicCombatant.h" | |
#include "NavigationSystem.h" | |
#include "NecroGameplayTags.h" | |
#include "AbilitySystem/NecroAbilitySystem.h" | |
#include "ActorInterfaces/CombatInterface.h" | |
#include "GameFramework/Character.h" | |
NecroAiState UNecroAiBasicCombatant::BasicCombatant() | |
{ | |
std::function<NecroAiState()> RandomWander, AcquiredTarget, Attacking; | |
RandomWander = | |
[&]()->NecroAiState | |
{ | |
StateMachine->AddTransition(MakeLazy(AiComponent->ScanForEnemies, AcquisitionRange), AcquiredTarget); | |
while(true) { | |
FVector PointResult; | |
UNavigationSystemV1::K2_GetRandomReachablePointInRadius(GetWorld(), | |
ControlledCharacter->GetActorLocation(), PointResult, 1000.0f); | |
co_await StateMachine->WaitForTask(AiComponent->MoveToPoint(PointResult, 50.0f)); | |
} | |
}; | |
AcquiredTarget = [&]()->NecroAiState | |
{ | |
const auto ActorCombatInterface = Cast<ICombatInterface>(ControlledCharacter); | |
auto Target = ActorCombatInterface->GetCombatTargetActor(); | |
check(ActorCombatInterface); | |
StateMachine->AddTransition([&]() | |
{ | |
return Target != ActorCombatInterface->GetCombatTargetActor(); | |
}, RandomWander) | |
.ContinueWith(Attacking); | |
co_await StateMachine->WaitForTask(AiComponent->MoveToActor(Target, AttackRange-100.0f)); | |
}; | |
Attacking = [&]()->NecroAiState | |
{ | |
const auto ActorCombatInterface = Cast<ICombatInterface>(ControlledCharacter); | |
const auto Target = ActorCombatInterface->GetCombatTargetActor(); | |
ActorCombatInterface->SetMotionWarpingCombatTargetActor(Target); | |
StateMachine->AddTransition([&]() | |
{ | |
const auto EnemiesInRange = AiComponent->ScanForEnemies(AcquisitionRange); | |
return !EnemiesInRange || Target != ActorCombatInterface->GetCombatTargetActor(); | |
}, RandomWander) | |
.ContinueWith(RandomWander) | |
.OnExit([&ActorCombatInterface]() | |
{ | |
ActorCombatInterface->ClearMotionWarpingCombatTarget(); | |
}); | |
AbilitySystem->TryActivateAbilitiesByTag(TAGS::ABILITY::SLOT::Primary.GetTag().GetSingleTagContainer()); | |
co_await StateMachine->WaitForTargetedSignal(TAGS::ABILITY::Ability); | |
}; | |
StateMachine->ChangeToState(RandomWander()); | |
co_await std::suspend_always{}; | |
} | |
NecroAiState UNecroAiBasicCombatant::ConstructBehaviour() | |
{ | |
return BasicCombatant(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Ai/NecroAiStateMachine.h" | |
#include "Subsystem/NecroAiSubsystem.h" | |
void NecroAiStateMachine::Destroy() | |
{ | |
if(AiSubsystem && OwningObject) | |
{ | |
AiSubsystem->ClearAllSignals(OwningObject); | |
} | |
Reset(); | |
Sleeping = true; | |
} | |
void NecroAiStateMachine::ChangeToState(const NecroAiState& NewState) | |
{ | |
if(CurrentState && OnExitFunc) | |
{ | |
OnExitFunc(); | |
} | |
Reset(); | |
CurrentTask = NewState.Handle; | |
CurrentState = NewState; | |
} | |
void NecroAiStateMachine::Reset() | |
{ | |
FreeEntireCoroutineStack(); | |
CurrentStateTransitions.clear(); | |
CurrentStatelessTasks.clear(); | |
NextState = nullptr; | |
OnExitFunc = nullptr; | |
CurrentTask = nullptr; | |
Sleeping = false; | |
} | |
void NecroAiStateMachine::FreeEntireCoroutineStack() | |
{ | |
while(!CoroutineStack.empty()) | |
{ | |
if(auto Top = CoroutineStack.top()) | |
{ | |
Top.destroy(); | |
} | |
CoroutineStack.pop(); | |
} | |
} | |
void NecroAiStateMachine::ClearSignalBuffers() const | |
{ | |
AiSubsystem->ClearSignalBuffers(OwningObject); | |
} | |
void NecroAiStateMachine::AwaitPush(const coroutine_handle<> NewHandle) | |
{ | |
if(CurrentTask) | |
CoroutineStack.push(CurrentTask); | |
CurrentTask = NewHandle; | |
} | |
bool NecroAiStateMachine::AwaitSignal(const FGameplayTag Tag) | |
{ | |
const auto Result= AiSubsystem->AwaitTargetedSignal(OwningObject, Tag, [this]() | |
{ | |
if(!this) return; | |
this->Sleeping = false; | |
}); | |
//The signal was buffered, resume immediately. | |
if(Result) return false; | |
//Not ready, go to sleep. | |
Sleeping = true; | |
return true; | |
} | |
std::function<NecroAiState()> NecroAiStateMachine::CheckNextTransition() | |
{ | |
if(CurrentStateTransitions.empty()) return nullptr; | |
auto &[TransFunc, StateFunc] = CurrentStateTransitions.front(); | |
if(TransFunc()) return StateFunc; | |
//Rotate our deque so the transition we just evaluated is now the back. | |
CurrentStateTransitions.push_back(CurrentStateTransitions.front()); | |
CurrentStateTransitions.pop_front(); | |
return nullptr; | |
} | |
bool NecroAiStateMachine::Run() | |
{ | |
check(AiSubsystem) | |
check(OwningObject) | |
if(Sleeping) return true; | |
if(!CurrentState) | |
{ | |
if(NextState) | |
{ | |
//We have a valid next state, so we move to that. | |
ChangeToState(std::move(NextState())); | |
return true; | |
} | |
return false; | |
} | |
for(const auto& StatelessTask : CurrentStatelessTasks) | |
{ | |
StatelessTask(); | |
} | |
//Check trasition, if we should transition run any exit code and switch states. | |
if(const auto CheckResult = CheckNextTransition()) | |
{ | |
ChangeToState(CheckResult()); | |
} | |
//If we have no valid task or the current one is done, pop the stack until we have a valid task or return false. | |
while(!CurrentTask || CurrentTask.done()) { | |
if(CoroutineStack.empty()) | |
{ | |
if(NextState) | |
{ | |
//We have a valid next state, so we move to that. | |
ChangeToState(std::move(NextState())); | |
return true; | |
} | |
//CurrentState.ExitAndDestroy(); | |
return false; | |
} | |
if(CurrentTask) | |
{ | |
CurrentTask.destroy(); | |
} | |
CurrentTask = CoroutineStack.top(); | |
CoroutineStack.pop(); | |
} | |
CurrentTask.resume(); | |
return true; | |
} | |
//Transition on true. | |
NecroAiStateMachine& NecroAiStateMachine::AddTransition(const std::function<bool()>& TransitionFunc, | |
const std::function<NecroAiState()>& StateConstructor) | |
{ | |
CurrentStateTransitions.push_back(TransitionBundle{TransitionFunc, StateConstructor}); | |
return *this; | |
} | |
NecroAiStateMachine& NecroAiStateMachine::ContinueWith(const std::function<NecroAiState()>& StateConstructor) | |
{ | |
NextState = StateConstructor; | |
return *this; | |
} | |
NecroAiStateMachine& NecroAiStateMachine::OnExit(const std::function<void()>& Finalizer) | |
{ | |
OnExitFunc = Finalizer; | |
return *this; | |
} | |
TaskAwaiter NecroAiStateMachine::WaitForTask(NecroAiTask&& TaskToAwait) | |
{ | |
return TaskAwaiter{*this, std::forward<NecroAiTask>(TaskToAwait)}; | |
} | |
SignalAwaiter NecroAiStateMachine::WaitForTargetedSignal(const FGameplayTag TagToAwait) | |
{ | |
return SignalAwaiter{*this, TagToAwait}; | |
} | |
NecroAiStateMachine& NecroAiStateMachine::AddStatelessTask(const std::function<void()>& Task) | |
{ | |
CurrentStatelessTasks.push_back(std::move(Task)); | |
return *this; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Fill out your copyright notice in the Description page of Project Settings. | |
#include "Ai/Behaviours/NecroAiWanderBehaviour.h" | |
#include "NavigationSystem.h" | |
#include "GameFramework/Character.h" | |
NecroAiState UNecroAiWanderBehaviour::RandomWander() const | |
{ | |
while(true) | |
{ | |
FVector PointResult; | |
UNavigationSystemV1::K2_GetRandomReachablePointInRadius(GetWorld(), | |
ControlledCharacter->GetActorLocation(), PointResult, WanderRadius); | |
co_await StateMachine->WaitForTask(AiComponent->MoveToPoint(PointResult, AcceptanceRadius)); | |
} | |
} | |
NecroAiState UNecroAiWanderBehaviour::ConstructBehaviour() | |
{ | |
return RandomWander(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment