Hi everyone,
I am fighting for the last couple of days with a seamingly simple thing. I really hope I am missing something obvious and that this is not an engine bug.
Here is the setup:
To keep my code clean (due to various wildly different states my actor gets in) I need to handle reactions and behavior differently for each state. I’ve created the following scheme - I have an interface class:
class IActorStates
{
GENERATED_IINTERFACE_BODY()
ATheForestCharacter* _forestCharacter;
public:
virtual void InitState(ATheForestCharacter* forestCharacter);
virtual void LeaveState();
virtual void ActivateState();
virtual void ExecuteUpdate();
virtual void Tick(float DeltaSeconds);
virtual void AddControllerPitchInput(float Val);
virtual void AddControllerYawInput(float Val);
virtual void MoveForward(float Value);
virtual void MoveRight(float Value);
virtual void TurnAtRate(float Rate);
virtual void LookUpAtRate(float Rate);
virtual void ActionButton();
};
For each state I’ve created a separate inherited class which handles the methods like for example this one and in associated .cpp I have handlers doing the real reactions:
UCLASS()
class UActorStateIndoorWalk : public UObject, public IActorStates
{
GENERATED_BODY()
public:
void InitState(ATheForestCharacter* forestCharacter) override;
void MoveForward(float Value) override;
void MoveRight(float Value) override;
void AddControllerPitchInput(float Val) override;
void AddControllerYawInput(float Val) override;
void ActionButton() override;
void Tick(float DeltaSeconds) override;
};
In header file of my character I have this:
IActorStates* _availableActorStates[10];
IActorStates* _currentActorState;
At the beginning I initialize it like this (I did have originally NewObject, but I thought he is a culprit, but as it turned out, he wasn’t):
_availableActorStates[0] = ConstructObject<UActorStateIndoorWalk>(UActorStateIndoorWalk::StaticClass());
_availableActorStates[1] = ConstructObject<UActorStateNonInteractive>(UActorStateNonInteractive::StaticClass());
_availableActorStates[2] = ConstructObject<UActorStateEyeTurner>(UActorStateEyeTurner::StaticClass());
for (int a = 0; a < 3; a++)
{
_availableActorStates[a]->InitState(this);
}
Eventually what my own SwitchState does is that it simply changes _currentActorState to the correct pointer.
Now this is where the fun starts! It works like charm!!! For a minute or three and the it crashes
This is the method where it crashes:
void ATheForestCharacter::MoveForward(float Value)
{
IActorStates* tmp = _currentActorState;
if (tmp)
{
tmp->MoveForward(Value); // crashes on this line
}
}
Note that I used tmp again as a result of endless debugging. Original code was:
if (_currentActorState)
{
_currentActorState->MoveForward(Value); // crashes on this line
}
This is what happens, but not right away - it’s actually after some time which makes it hard to hunt down.
Exception Code: 0xC0000005
Exception Information: The thread tried to read from or write to a virtual address for which it does not have the access
There are no other actions everything else is switched off so it’s basically just the walk and also no state switching takes place.
My only concern is that the event is bound like this:
InputComponent->BindAxis("MoveForward", this, &ATheForestCharacter::MoveForward);
Can it be that’s it’s a stack corruption caused by rapid executing of the method?
I would be really sad if I had to abandon my idea that input gets treated by actor’s state object as it would elegantly eliminate all nasty if constructions.
Thank you so much for any help!
Jan
EDIT: For clarity adding IActorStates cpp file:
#include "TheForest.h"
#include "IActorStates.h"
UActorStates::UActorStates(const class FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void IActorStates::ExecuteUpdate() {}
void IActorStates::Tick(float DeltaSeconds) {}
void IActorStates::AddControllerPitchInput(float Val) {}
void IActorStates::AddControllerYawInput(float Val) {}
void IActorStates::MoveForward(float Value) {}
void IActorStates::MoveRight(float Value) {}
void IActorStates::TurnAtRate(float Rate) {}
void IActorStates::LookUpAtRate(float Rate) {}
void IActorStates::Jump() {}
void IActorStates::StopJumping() {}
void IActorStates::ActionButton() {}
void IActorStates::RunKeyPressed() {}
void IActorStates::RunKeyReleased() {}
void IActorStates::FlashlightKeyPressed() {}
void IActorStates::MobileKeyPressed() {}
void IActorStates::InventoryKeyPressed() {}
void IActorStates::MenuKeyPressed() {}
void IActorStates::InitState(ATheForestCharacter* forestCharacter) { _forestCharacter = forestCharacter; }
void IActorStates::LeaveState() {}