Still not working. Is this software suppose to be used as a toy or is it to run a business on?
I am also facing the same issue.
great engine
Fix? Nah who cares?
Almost coming up on a year now. Fantastic work Epic team.
Finally,it can work in 5.6,but needed struct still can’t assign in blueprint.
really?? Finally.
What do you mean about the structs?
That func “RequestTransition” need pass a StateTreeStateLink struct,but that struct not a BlueprintType,so need pass/wrap that struct in cpp.
I just expose it to select the requested task in the state tree - works fine.
Just tested in 5.6 and it does work finally
Saw this post and tried using it on 5.6 and it is working like a charm.
It seems like the State Tree request transition might be broken in 5.4. Has anyone else experienced unexpected behavior or failures during transitions?
it doesn’t work in 5.4 or 5.5 but is fixed in 5.6 so if you want to use it, you’ll have to update engine.
I found a way to fix this for 5.5 and lower. It will involve some C++, if you’re using Unreal source build, just add this into FStateTreeTransitionRequest Request; in RequestTransition function
Request.SourceStateTree = CachedFrameStateTree.Get();
Request.SourceRootState = CachedFrameRootState;
Or if not, you can make a base class for the task and use my custom transition function
// © 2025 Team Bacon. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/StateTreeTaskBlueprintBase.h"
#include "BrawlStateTreeTaskBlueprintBase.generated.h"
/**
* Base class for Blueprint based State Tree Task
* This class fixes an issue in UE5.5 and earlier where RequestTransition called from Blueprint tasks
*/
UCLASS()
class PROJECTBRAWL_API UBrawlStateTreeTaskBlueprintBase : public UStateTreeTaskBlueprintBase
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "StateTree", meta = (HideSelfPin = "true", DisplayName = "Brawl StateTree Request Transition"))
void BrawlRequestTransition(const FStateTreeStateLink& TargetState, const EStateTreeTransitionPriority Priority = EStateTreeTransitionPriority::Normal);
#if (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION <= 5)
public:
virtual EStateTreeRunStatus EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) override;
virtual void ExitState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) override;
protected:
void BrawlSetCachedInstanceDataFromContext(const FStateTreeExecutionContext& Context) const;
void BrawlClearCachedInstanceData() const;
private:
/** Cached instance data while the node is active. */
mutable TWeakPtr<FStateTreeInstanceStorage> WeakInstanceStorage;
/** Cached owner while the node is active. */
// It's TObjectPtr in base class so we only need TWeakObjectPtr here, the base class will keep it alive
mutable TWeakObjectPtr<UObject> CachedOwner = nullptr;
/** Cached State Tree of owning execution frame. */
// It's TObjectPtr in base class so we only need TWeakObjectPtr here, the base class will keep it alive
mutable TWeakObjectPtr<const UStateTree> CachedFrameStateTree = nullptr;
/** Cached root state of owning execution frame. */
mutable FStateTreeStateHandle CachedFrameRootState;
/** Cached State where the node is processed on. */
mutable FStateTreeStateHandle CachedState;
#endif
};
// © 2025 Team Bacon. All Rights Reserved.
#include "AI/ST/BrawlStateTreeTaskBlueprintBase.h"
#include "StateTreeExecutionContext.h"
void UBrawlStateTreeTaskBlueprintBase::BrawlRequestTransition(const FStateTreeStateLink& TargetState,
const EStateTreeTransitionPriority Priority)
{
#if (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION <= 5)
TSharedPtr<FStateTreeInstanceStorage> InstanceStorage = WeakInstanceStorage.Pin();
if (InstanceStorage == nullptr || CachedOwner == nullptr)
{
UE_VLOG_UELOG(this, LogStateTree, Error, TEXT("Trying to call SendEvent() while node is not active. Use SendEvent() on UStateTreeComponent instead for sending signals externally."));
return;
}
FStateTreeTransitionRequest Request;
Request.SourceState = CachedState;
Request.TargetState = TargetState.StateHandle;
Request.Priority = Priority;
// The fix
Request.SourceStateTree = CachedFrameStateTree.Get();
Request.SourceRootState = CachedFrameRootState;
InstanceStorage->AddTransitionRequest(CachedOwner.Get(), Request);
#else
// Fixed in 5.6, fallback to base implementation.
RequestTransition(TargetState, Priority);
#endif
}
#if (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION <= 5)
EStateTreeRunStatus UBrawlStateTreeTaskBlueprintBase::EnterState(FStateTreeExecutionContext& Context,
const FStateTreeTransitionResult& Transition)
{
BrawlSetCachedInstanceDataFromContext(Context);
return Super::EnterState(Context, Transition);
}
void UBrawlStateTreeTaskBlueprintBase::ExitState(FStateTreeExecutionContext& Context,
const FStateTreeTransitionResult& Transition)
{
BrawlClearCachedInstanceData();
Super::ExitState(Context, Transition);
}
void UBrawlStateTreeTaskBlueprintBase::BrawlSetCachedInstanceDataFromContext(const FStateTreeExecutionContext& Context) const
{
if (FStateTreeInstanceData* InstanceData = Context.GetMutableInstanceData())
{
WeakInstanceStorage = InstanceData->GetWeakMutableStorage();
}
CachedState = Context.GetCurrentlyProcessedState();
CachedOwner = Context.GetOwner();
const FStateTreeExecutionFrame* CurrentlyProcessedFrame = Context.GetCurrentlyProcessedFrame();
check(CurrentlyProcessedFrame);
CachedFrameStateTree = CurrentlyProcessedFrame->StateTree;
CachedFrameRootState = CurrentlyProcessedFrame->RootState;
}
void UBrawlStateTreeTaskBlueprintBase::BrawlClearCachedInstanceData() const
{
WeakInstanceStorage = nullptr;
CachedState = FStateTreeStateHandle::Invalid;
CachedOwner = nullptr;
CachedFrameStateTree = nullptr;
CachedFrameRootState = FStateTreeStateHandle::Invalid;
}
#endif