My TArray of one structure works when declared inside a function, but not when declared on header file

I get an exception error when trying to add one element in an TArray declared on header, declaring the TArray inside de function on the cpp file and adding a new element works, but I get an exception when declared in the header.

I think that declaring the TArray on the header file somehow don’t initialize it, so when trying to find the array to add the element it goes null ptr because it’s not initialized, my guess, but i’m really lost here.

Here is the code:

WORKS:
.cpp:

void APj_013_VerticalCppGameModeBase::AddProgressEvent(AProgressEvent* ProgressEvent, float EventLocation)
{
	TArray<FStr_ProgressEvent> ProgressEventList;
	FStr_ProgressEvent NewStr;
	NewStr.EventRef = ProgressEvent;
	NewStr.XLoc = EventLocation;
	ProgressEventList.Add(NewStr);
}

DON’T WORK:

.h

//(...)
UCLASS()
class PJ_013_VERTICALCPP_API APj_013_VerticalCppGameModeBase : public AGameModeBase
{
	GENERATED_BODY()

public:

	TArray<FStr_ProgressEvent> ProgressEventList;

	APj_013_VerticalCppGameModeBase();
//(...)
};

.cpp

void APj_013_VerticalCppGameModeBase::AddProgressEvent(AProgressEvent* ProgressEvent, float EventLocation)
{
	FStr_ProgressEvent NewStr;
	NewStr.EventRef = ProgressEvent;
	NewStr.XLoc = EventLocation;
	ProgressEventList.Add(NewStr);
}

EDIT1:
Some screenshots of the errors:

EDIT2:
Declaration of the structure:

class AProgressEvent;

USTRUCT(BlueprintType)
struct FStr_ProgressEvent

{
	GENERATED_BODY()

		UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressEvent")
		AProgressEvent* EventRef;

		UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressEvent")
		float XLoc;

	FStr_ProgressEvent()
	{
		EventRef = nullptr;
		XLoc = 0;
	}
	FStr_ProgressEvent(AProgressEvent* ProgressEvent, float EventLocation)
	{
		EventRef = ProgressEvent;
		XLoc = EventLocation;
	}
};

EDIT3:

Changed the structure from AProgressEvent*, float to AActor*, float. Same results.

Sorry not to good at english, hope I made myself clear, thanks for the help.

Paste error message.

Understood. I’ll edit the post.

Did a test project and it works ok

.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "ProgressEvent.h"
#include "APj_013_VerticalCppGameModeBase.generated.h"


USTRUCT(BlueprintType)
struct FStr_ProgressEvent {
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	AProgressEvent* EventRef;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float XLoc;
};


//class AProgressEvent;
/**
 * 
 */
UCLASS()
class DROID_API AAPj_013_VerticalCppGameModeBase : public AGameModeBase
{
	GENERATED_BODY()

	public:
		UFUNCTION(BlueprintCallable)
		void AddProgressEvent(AProgressEvent* ProgressEvent, float EventLocation);

		UPROPERTY(BlueprintReadWrite, EditAnywhere)
		TArray<FStr_ProgressEvent> ProgressEventList;
};

.cpp

#include "APj_013_VerticalCppGameModeBase.h"

void AAPj_013_VerticalCppGameModeBase::AddProgressEvent(AProgressEvent* ProgressEvent, float EventLocation)
{
	FStr_ProgressEvent NewStr;
	NewStr.EventRef = ProgressEvent;
	NewStr.XLoc = EventLocation;
	ProgressEventList.Add(NewStr);
}

Could you be resetting the TArray somewhere in your code or making it nullptr?

1 Like

The only 2 references to that TArray is the declaration on the header, and the Add function, so the array is not being nulled or reseted.

I replicate that function in Blueprint only and it works fine.

The “AddProgressEvent” is not marked as UFUNCTION, the array was tested as UPROPERTY, same result.

The USTRUCT is declared in other header file and include in the header of the game mode.

My USTRUCT has a constructor, setting the default values of to nullptr and 0 (I’ll add that to the question). But even so, I should be able to use one Structure nullptr and 0 as a member of the array, and the array has no member, as adding one causes the exception and crash.

I’ll keep trying to make it work. thanks for the help, with one example working I’ll get there. Many thanks.

Irrational crashes usually stem from problems elsewhere in the project. Maybe try a reference search to see if nothing is messing with the values along the way?

Best of luck with the bug hunt

1 Like

Calling the function from blueprints works just fine, calling the function using c++ results in the crash, tried using the function from other actor and it crashs anyway.

Did a test in pure c++ and no crashes

in .h

virtual void InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage) override;

in .cpp

void AAPj_013_VerticalCppGameModeBase::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
	Super::InitGame(MapName, Options, ErrorMessage);

	UWorld* w = GetWorld();
	FActorSpawnParameters sp;
	sp.bNoFail = true;
	sp.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;

	AProgressEvent* pe1 = w->SpawnActor< AProgressEvent>(sp);
	AddProgressEvent(pe1, 1.3f);

	AProgressEvent* pe2 = w->SpawnActor< AProgressEvent>(sp);
	AddProgressEvent(pe2, 2.4f);

	AProgressEvent* pe3 = w->SpawnActor< AProgressEvent>(sp);
	AddProgressEvent(pe3, 2.3f);


	for (int i = 0; i < ProgressEventList.Num(); i++) {
		FString m = ProgressEventList[i].EventRef->GetName()  +"["+FString::SanitizeFloat(ProgressEventList[i].XLoc) + "]";
		GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Orange, m);
	}

}

In pure c++ this is what i get when I run the game

Maybe you are calling the function before anything is passed the initialized phase?

1 Like

I’m calling the function on each actor BeginPlay,

The actors are already on the level, on begin play each actor individually uses a Function Library to get a ref to GameMode and from that they call the AddProgressEvent, passing “this” and GetActorLocation().X.

I tried setting a timer on the begin play, so that the functions is called only 3seconds after, but it crashs anyway, same result.

Is the method in the Function Library static? (just want to get as close to the error source)

1 Like

Function library declarations (.h)

static APj_013_VerticalCppGameModeBase* GetGModeRef();

static void SetGModeRef(APj_013_VerticalCppGameModeBase* GModePtr);

Function definition on the Library (.cpp)

void UFnLib_Main::SetGModeRef(APj_013_VerticalCppGameModeBase* GModePtr)
{
	GModeRef = GModePtr;
}

APj_013_VerticalCppGameModeBase* UFnLib_Main::GetGModeRef()
{
	return GModeRef;
}

At the Game Mode begin play I call the function to set the reference

void APj_013_VerticalCppGameModeBase::BeginPlay()
{
	Super::BeginPlay();
	
	UFnLib_Main::SetGModeRef(this);
}

At the begin play of each actor I use the GetGModeRef and call the function.

I think the GameMode beginplay is called before the other AActors, not?

I alter the code to check if the gmode ref is valid before calling the AddProgressEvent, and the function don’t get called. So is that it? the GMode ref is the problem?

void AProgressEvent::BeginPlay()
{
	Super::BeginPlay();

	SendEventToGMode();
}
void AProgressEvent::SendEventToGMode()
{
	if (UFnLib_Main::GetGModeRef())
	{
		UFnLib_Main::GetGModeRef()->AddProgressEvent(this, GetActorLocation().X);
	}	
}

I already tried on the Gamo Mode Begin Play get all actors of class, and using a loop to add each actor to the array, so that everything happens on the Game Mode, but even so it results in the exception. I’ll try do it again.

the class AGameModeBase that you derive you game mode from does not have a BeginPlay to override. I’m guessing it’s not being called and your passed on reference is not being set.
Put a breakpoint there and run the project and see if it fires.

I’m guessing you might need to replace that functionality with the InitGame method override

1 Like

AGameModeBase does have a “void BeginPlay() override”, and when debugging it does call the SetGModeRef, and change its value.

But i set two break points, one at the SetGModeRef, and one at the actor call to AddProgressEvent, and the AddProgressEvent is hitting first.

void UFnLib_Main::SetGModeRef(APj_013_VerticalCppGameModeBase* GModePtr)
{
	GModeRef = GModePtr;
}

First break point to hit.

Second break point, line 16

Third break point, line 17

Ok. You are calling beginplay on the derived class AActor that AGameModeBase inherits from. I’m not sure of the order in which it is called in that case. (maybe it’s random)

I would strongly suggest to use the InitGame function in place of beginplay

Information from within the gamemodebase class:

/**
 * Initialize the game.
 * The GameMode's InitGame() event is called before any other functions (including PreInitializeComponents() )
 * and is used by the GameMode to initialize parameters and spawn its helper classes.
 * @warning: this is called before actors' PreInitializeComponents.
 */

You will be sure it is called before any of your characters

1 Like

Man I can’t thank you enough, I’m too tired now to try it, sorry, thanks for your time, I’m stuck trying to solve this since 6pm now it’s 8am, I’ll get some rest,.

Didn’t know about the InitGame function, and I bet it will solve my problems, and spare me from many others. I’ll bring the results ASAP. and again thank you.

1 Like

That was it! It solved the problem, the function was being called before the game mode reference was set, so there’s no valid game mode resulting in the engine to crash.

Thank you 3dRaven to take the time and step through the problem with me, even replicating my code to test. I really can’t thank you enough.

1 Like

No problem. Best of luck on your project.
It’s best to get over these types of obstacles as fast as possible and get back to the fun part of developing :slight_smile:

1 Like

A last update: Instead of cheking every frame, something I try my best to avoid, I came up with another solution, setting a timer for every event, with the initial delay being the event location/speed, as the speed is constant setting a speed on the level start allows me to use it to calc the initial delay.

I needed to change the way I was moving the actors, instead of a value/frame, I needed to make it frame independent, so I set a value in UnrealUnits/Second (300), and multiplied it by the tick DeltaS.

Here’s the code:

On the component responsible for the movement:

void UVerticalMove::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)

{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
	
	LocOffset.X = ScrollVeloc * DeltaTime;
	GetOwner()->AActor::K2_AddActorWorldOffset(LocOffset, false, SweepHitResult, false);
}

On the GameMode:

void APj_013_VerticalCppGameModeBase::AddProgressEvent(AProgressEvent* ProgressEvent, float EventLocation)
{
	//New logic, set timers for all events;
	FTimerHandle NewHandle;
	TimerHandleArray.Add(NewHandle);
	GetWorldTimerManager().SetTimer(NewHandle, ProgressEvent, &AProgressEvent::PlayEvent, EventLocation / ScrollVeloc, false);
}

Once again thank you for the help, hope that aditional info be helpful to others.

1 Like