Hi
I’m having a problem with an access violation in my C++ in Unreal Engine 4.27 code and I’ve been stuck for about two weeks with it. I’ve added as much information as I can so apologies for the long post. I’m an experienced C++ programmer and have some experience of Unreal and Blueprint but I’m quite new to C++ in Unreal.
First I’ll describe what I’m doing for context. I’m developing a Utility AI system with both C++ and Blueprint. I’ve got an ActorComponent class in C++ which manages the Utility AI itself. This stores a TArray of pointers to an Action class which is subclassed from UObject. The action class is a Blueprintable class which in turn stores a pointer to an InsistenceSatisfaction class which is also a subclass of UObject. The InsistenceSatisfaction class has a Blueprint overridable function which returns the value of the insistence satisfaction which is then used by the ActorComponent to select the appropriate action to use.
All of this works and I am printing messages from the Blueprints showing the Blueprint overriden functions working correctly, screenshot below.
After running for around 1 minute, and this time period is quite consistent an Access Violation exception occurs. After some debugging I’ve established that the reason appears to be that the InsistenceSatisfaction class is being scheduled for destruction by the garbage collector. But I don’t know why. I’ve attached the debugging output below.
- Logging output at time of exception
[2023.11.06-13.11.08:749][669]LogTemp: Display: the action being considered is Action1
[2023.11.06-13.11.08:750][669]LogTemp: Display: satisfies insistence Jane with base value 0.000000 and calculated value 0.000000
[2023.11.06-13.11.08:750][669]LogTemp: Display: base value 15.000000
[2023.11.06-13.11.08:750][669]LogTemp: Display: calculated value 15.000000
[2023.11.06-13.11.08:750][669]LogTemp: Display: the action being considered is Action2
[2023.11.06-13.11.08:750][669]LogTemp: Display: satisfies insistence Joe with base value 0.000000 and calculated value 0.000000
[2023.11.06-13.11.08:750][669]LogTemp: Display: base value 30.000000
[2023.11.06-13.11.08:750][669]LogBlueprintUserMessages: [InsistenceSatisfactionJoe_C_0] 30.0
[2023.11.06-13.11.08:750][669]LogBlueprintUserMessages: [InsistenceSatisfactionJoe_C_0] 60.0
[2023.11.06-13.11.08:750][669]LogTemp: Display: calculated value 60.000000
[2023.11.06-13.11.08:750][669]LogBlueprintUserMessages: [InsistenceSatisfactionJoe_C_0] 30.0
[2023.11.06-13.11.08:751][669]LogBlueprintUserMessages: [InsistenceSatisfactionJoe_C_0] 60.0
[2023.11.06-13.11.08:751][669]LogBlueprintUserMessages: [InsistenceSatisfactionJoe_C_0] 30.0
[2023.11.06-13.11.08:751][669]LogBlueprintUserMessages: [InsistenceSatisfactionJoe_C_0] 60.0
[2023.11.06-13.11.08:751][669]LogBlueprintUserMessages: [TestAction2_C_0] Action2
[2023.11.06-13.11.08:762][670]LogBlueprintUserMessages: [UtilityAIController_C_0] Mary : 7.109609
[2023.11.06-13.11.08:762][670]LogBlueprintUserMessages: [UtilityAIController_C_0] Fred : 31.547951
[2023.11.06-13.11.08:763][670]LogBlueprintUserMessages: [UtilityAIController_C_0] Joe : 62.095924
[2023.11.06-13.11.08:763][670]LogBlueprintUserMessages: [UtilityAIController_C_0] Jane : 31.547951
[2023.11.06-13.11.08:763][670]LogTemp: Display: the action being considered is Action1
[2023.11.06-13.11.08:763][670]LogTemp: Display: satisfies insistence Jane with base value 0.000000 and calculated value 0.000000
[2023.11.06-13.11.08:763][670]LogTemp: Display: base value 15.000000
[2023.11.06-13.11.08:763][670]LogTemp: Display: calculated value 15.000000
[2023.11.06-13.11.08:763][670]LogTemp: Display: the action being considered is Action2
[2023.11.06-13.11.08:763][670]LogTemp: Display: satisfies insistence Joe with base value 0.000000 and calculated value 0.000000
[2023.11.06-13.11.08:764][670]LogTemp: Display: base value 30.000000
[2023.11.06-13.11.08:764][670]LogBlueprintUserMessages: [InsistenceSatisfactionJoe_C_0] 30.0
[2023.11.06-13.11.08:764][670]LogBlueprintUserMessages: [InsistenceSatisfactionJoe_C_0] 60.0
[2023.11.06-13.11.08:764][670]LogTemp: Display: calculated value 60.000000
[2023.11.06-13.11.08:764][670]LogBlueprintUserMessages: [InsistenceSatisfactionJoe_C_0] 30.0
[2023.11.06-13.11.08:764][670]LogBlueprintUserMessages: [InsistenceSatisfactionJoe_C_0] 60.0
[2023.11.06-13.11.08:764][670]LogBlueprintUserMessages: [InsistenceSatisfactionJoe_C_0] 30.0
[2023.11.06-13.11.08:764][670]LogBlueprintUserMessages: [InsistenceSatisfactionJoe_C_0] 60.0
[2023.11.06-13.11.08:764][670]LogBlueprintUserMessages: [TestAction2_C_0] Action2
[2023.11.06-13.11.08:782][671]LogBlueprintUserMessages: [UtilityAIController_C_0] Mary : 7.110914
[2023.11.06-13.11.08:782][671]LogBlueprintUserMessages: [UtilityAIController_C_0] Fred : 31.55448
[2023.11.06-13.11.08:782][671]LogBlueprintUserMessages: [UtilityAIController_C_0] Joe : 62.108982
[2023.11.06-13.11.08:782][671]LogBlueprintUserMessages: [UtilityAIController_C_0] Jane : 31.55448
[2023.11.06-13.11.08:782][671]LogTemp: Display: the action being considered is Action1
[2023.11.06-13.11.08:783][671]LogTemp: Display: satisfies insistence Jane with base value 0.000000 and calculated value 0.000000
[2023.11.06-13.11.08:783][671]LogTemp: Display: base value 15.000000
Exception thrown: read access violation.
this->**** was 0xFFFFFFFFFFFFFD1F.
The program '[18832] UE4Editor-Win64-DebugGame.exe' has exited with code 0 (0x0).
- Exception point without logging
- Line causing exception in my code (using call stack)
- Exception with logging (my code)
- Unreal check function catching problem
- Check showing UObject flags sheduled for destruction
- Check showing action class in valid state at same point in execution
- Breakpoint of BeginPlay showing no UObject flags for Insistence satisfaction class
- Breakpoint on thousandth iteration showing no UObject flags for insistence satisfaction class
My code follows, the header files for the Action class and the InsistenceSatisfation classes, and the code in the ActorComponent class where the problem occurs.
UInsistenceSatisfaction.h
#include "CoreMinimal.h"
#include "UInsistenceSatisfaction.generated.h"
/**
*
*/
UCLASS(Blueprintable)
class MYUTILITYAIPROJECT_API UInsistenceSatisfaction : public UObject
{
GENERATED_BODY()
public:
UInsistenceSatisfaction();
~UInsistenceSatisfaction();
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = EditorCategory)
FName InsistenceName = "";
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = EditorCategory)
float BaseSatisfactionValue = 0;
// Need this so we can access the UE4 gameplay framework in the action Blueprints
virtual class UWorld* GetWorld() const override;
void BeginPlay();
UFUNCTION(BlueprintNativeEvent, Category = EditorCategory)
float GetSatisfationValue();
virtual float GetSatisfationValue_Implementation();
};
UUtilityActionBase.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "UInsistenceSatisfaction.h"
#include "UtilityActionBase.generated.h"
/**
*
*/
UCLASS(Blueprintable)
class MYUTILITYAIPROJECT_API UUtilityActionBase : public UObject
{
GENERATED_BODY()
public:
UUtilityActionBase();
void BeginPlay();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = EditorCategory)
FName ActionName;
UPROPERTY(EditAnywhere, Category = EditorCategory)
TSubclassOf<UInsistenceSatisfaction> InsistenceSatisfactionClass;
UInsistenceSatisfaction* InsistenceSatisfaction;
// Blueprint event for the user to implement thier action logic
UFUNCTION(BlueprintImplementableEvent, Category = EditorCategory)
void OnTick(float DeltaTime);
void Tick(float DeltaTime);
// Need this so we can access the UE4 gameplay framework in the action Blueprints
virtual class UWorld* GetWorld() const override;
};
UMyUtilityAIComponent.cpp - UpdateBestAction() called in TickComponent()
void UMyUtilityAIComponent::UpdateBestAction()
{
// get the highest insistence
for (auto& currentInsistence : Insistences)
{
//UE_LOG(LogTemp, Display, TEXT("currentInsistence name %s, value %f, curve value %f"), *currentInsistence.Name.ToString(), currentInsistence.Value, currentInsistence.InsistenceCurve->GetFloatValue(currentInsistence.Value));
// Use the curve value to test for max insistence value
if (currentInsistence.InsistenceCurve->GetFloatValue(currentInsistence.Value) > MaxInsistence.InsistenceCurve->GetFloatValue(MaxInsistence.Value))
{
MaxInsistence = currentInsistence;
}
}
//UE_LOG(LogTemp, Display, TEXT("***** MaxInsistence %s with value %f, curve value %f"), *MaxInsistence.Name.ToString(), MaxInsistence.Value, MaxInsistence.InsistenceCurve->GetFloatValue(MaxInsistence.Value));
// Get the action that satisfies the highest insistence
for (auto& action : ActionInstances)
{
//check(action->IsValidLowLevel());
//check(action->InsistenceSatisfaction->IsValidLowLevel());
//UE_LOG(LogTemp, Display, TEXT("the insistence being satisfied is %s has the final (curve) value %f"), *MaxInsistence.Name.ToString(), MaxInsistence.InsistenceCurve->GetFloatValue(MaxInsistence.Value));
UE_LOG(LogTemp, Display, TEXT("the action being considered is %s"), *action->ActionName.ToString());
UE_LOG(LogTemp, Display, TEXT("satisfies insistence %s with base value %f and calculated value %f"), *action->InsistenceSatisfaction->InsistenceName.ToString());
UE_LOG(LogTemp, Display, TEXT("base value %f"), action->InsistenceSatisfaction->BaseSatisfactionValue);
UE_LOG(LogTemp, Display, TEXT("calculated value %f"), action->InsistenceSatisfaction->GetSatisfationValue());
//UE_LOG(LogTemp, Display, TEXT("calculated value %f"), action->InsistenceSatisfaction->GetFlags());
// This is an ugly hack to get round the fact that
if (action->InsistenceSatisfaction->InsistenceName == MaxInsistence.Name)
{
CurrentAction = action;
if (action->InsistenceSatisfaction->GetSatisfationValue() > CurrentAction->InsistenceSatisfaction->GetSatisfationValue())
{
CurrentAction = action;
}
//UE_LOG(LogTemp, Display, TEXT("the best action is %s and satisfies insistence %s by %f"), *CurrentAction->ActionName.ToString(), *CurrentAction->InsistenceSatisfaction->InsistenceName.ToString(), CurrentAction->InsistenceSatisfaction->BaseSatisfactionValue);
}
}
//UE_LOG(LogTemp, Display, TEXT("*********** The --final-- CurrentAction name = %s, insistence name = %s, base insistence value = %f, calculated insistence value %f"), *CurrentAction->ActionName.ToString(), *CurrentAction->InsistenceSatisfaction->InsistenceName.ToString(), CurrentAction->InsistenceSatisfaction->BaseSatisfactionValue, CurrentAction->InsistenceSatisfaction->GetSatisfationValue());
}
If any more information is required I’ll happily provide it. Many thanks.