Background
So I’m trying to make a higher level actor that manages a turn based game that I’ve dubbed my “GameManager”. From here I’ve created two sub actors which are the “GridManager” and “UnitManager” respectively.
I’ve been able to use the GameManager to spawn a GridManager Actor and call it’s GenerateTileMap function. Using this same principle I was hoping to move on to spawning a Unit via my UnitManager.
Problem
When I attempt to spawn a Unit I need to place that unit on a Tile, so I created a FindTile function within my GridManager to return the Tile that I randomly selected. Then I made my GameManager Spawn a UnitManager which then, during spawning, calls my GridManager to fetch that Tile form the TileMap. This causes my crash.
Upon debugging, it seems like when I attempt to spawn a unit, I call FindTile to designate where to spawn it. Then
I navigate my TileMap(TArray) with a ForEach loop, I get an exception that resembles the following…
Read Access Violation
I can see that my TileMap exists during GridManager construction, however after making a call to my UnitManager and from there calling my GridManager, I must have lost a reference to my TileMap somewhere.
Question
So my question is what is considered a good/strong practice for accessing member variables and functions from a higher level (conceptually speaking) class (actor in this case)? What is the fix to the problem described above?
I’ve considered some things like how to properly initialize an instance of these classes and call their functions but that understanding may be incorrect, I could really use some insights since I feel like I’m just not remembering a c++ concept and I’m trying to relearn things that feel so much easier to do in other languages or other tools like Unity.
I’m posting sections that I thought were good critical sections of code for brevity, I apologize for formatting as I did my best here.
###GameManager
void AGameManager::BeginPlay()
{
Super::BeginPlay();
UWorld* world = GetWorld();
if (world)
{
GridManager = world->SpawnActor<AGridManager>();
UE_LOG(LogTemp, Warning, TEXT("Spawned a GridManager"));
// Moved to BeginPlay when I spawn GridManager
//GridManager->GenerateTileMap();
}
if (world)
{
UnitManager = world->SpawnActor<AUnitManager>();
UE_LOG(LogTemp, Warning, TEXT("Spawned a UnitManager"));
UnitManager->SpawnTest();
}
}
###GridManager
// Called when the game starts or when spawned
void AGridManager::BeginPlay()
{
Super::BeginPlay();
GenerateTileMap();
// FindTile tests
FindTile(FVector2D(2, 2));
FindTile(FVector2D(-2, -2));
}
ATile* AGridManager::FindTile(FVector2D TargetTileCoordinates)
{
for (ATile* TempTile : TileMap)
{
if (TempTile->GetTileCoordinates() == TargetTileCoordinates)
{
UE_LOG(LogTemp, Warning, TEXT("Tile found at: %s"),
*TargetTileCoordinates.ToString());
return TempTile;
}
}
UE_LOG(LogTemp, Warning, TEXT("No tile at specified coordinates"));
return NULL;
}
###UnitManager
void AUnitManager::SpawnTest()
{
// first two rows on the bottom left
int randomX = FMath::RandRange(0, 7);
int randomY = FMath::RandRange(0, 1);
UE_LOG(LogTemp, Warning, TEXT("Random Coords: (%d, %d)"), randomX, randomY);
// Search our tilemap for their respective coords and return their respective Tile
ATile* SpawnTile;
SpawnTile = GridManager->FindTile(FVector2D(randomX, randomY));
FVector Location = FVector(
GetActorLocation().X,
GetActorLocation().Y,
0.0f);
AUnit* TempUnit = GetWorld()->SpawnActor<AUnit>(
Location,
GetActorRotation());
}