Trying to Populate a 2D Dynamic Array and Spawn Actors in it.

I’m kind of stumped here… I’ve managed to create the array structure. but I can’t seem to figure out how to actually create my node actors…

WallNode Actor Class

.H


#pragma once

#include "GameFramework/Actor.h"
#include "WallNode.generated.h"

UCLASS()
class PROCCAVE_API AWallNode : public AActor
{
	GENERATED_BODY()

// Variables
private:
	// WallStatus - 0 = No Wall, 1 = Wall, 2-255 = Could Be Anything For Now!
	uint8 WallStatus;

	FVector Location;

public:	
	// Sets default values for this actor's properties
	AWallNode();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	// Setters
	FORCEINLINE void SetLocation(const FVector & NewLocation) { SetActorLocation(NewLocation); Location = NewLocation; }
	FORCEINLINE void SetWallStatus(uint8 NewStatus) { WallStatus = NewStatus; }

	// Getters
	FORCEINLINE uint8 GetWallStatus() { return WallStatus; }
	FORCEINLINE FVector GetLocation() { return Location; }
};

.CPP


#include "ProcCave.h"
#include "WallNode.h"


// Sets default values
AWallNode::AWallNode()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = false;

	// Default Location 0,0,0
	SetActorLocation(FVector(0.0F, 0.0F, 0.0F));
	Location = FVector(0.0F, 0.0F, 0.0F);

	// Default Wall Status = 1, 1 = A Wall
	WallStatus = 1;

}

// Called when the game starts or when spawned
void AWallNode::BeginPlay()
{
	Super::BeginPlay();
	
}

And the generator class… There is some legacy code from the fixed size 2D array, that works fine, but I want to get this thing working so it can be dynamically sized.

.H


#pragma once

#include "GameFramework/Actor.h"
#include "WallNode.h"
#include "Generator.generated.h"

USTRUCT(BlueprintType)
struct FGridNodeRow
{
	GENERATED_USTRUCT_BODY()

		UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Grid Settings", meta = (ClampMin = "0", UIMin = "0"))
		TArray<AWallNode*> Width;

	void AddNewWidth() { Width.Add(NULL); }

	FGridNodeRow() {}

};

USTRUCT(BlueprintType)
struct FGridNodeArray
{
	GENERATED_USTRUCT_BODY()

		UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Grid Settings", meta = (ClampMin = "0", UIMin = "0"))
		TArray<FGridNodeRow> Height;

	void AddNewHeight() { Height.Add(FGridNodeRow()); }

	void AddUninitialized(const int32 GridHeight, const int32 GridWidth)
	{
		// Create Rows For Height
		for (int32 h = 0; h < GridHeight; h++)
		{
			AddNewHeight();
		}
		// Create Width Nodes
		for (int32 h = 0; h < GridHeight; h++)
		{
			for (int32 w = 0; w < GridWidth; w++)
			{
				Height[h].AddNewWidth();
			}
		}

	}

	void Clear()
	{
		if (Height.Num() <= 0) return;

		// Destroy All Nodes
		const int32 HeightTotal = Height.Num();
		const int32 WidthTotal = Height[0].Width.Num();

		for (int32 h = 0; h < HeightTotal; h++)
		{
			for (int32 w = 0; w < WidthTotal; w++)
			{
				if (Height[h].Width[w] && Height[h].Width[w]->IsValidLowLevel())
				{
					Height[h].Width[w]->Destroy();
				}
			}
		}

		// Empty
		for (int32 h = 0; h < Height.Num(); h++)
		{
			Height[h].Width.Empty();
		}
		Height.Empty();
	}

	FGridNodeArray() {}
};

UCLASS()
class PROCCAVE_API AGenerator : public AActor
{
	GENERATED_BODY()
// Variables
private:
	FDateTime DateAndTime;
	uint8 CaveMap[100][100] = { 0 };

	uint32 safeSeed;

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings", meta = (ClampMin = "0", ClampMax = "100", UIMin = "0", UIMax = "100"))
		uint8 RandomFillPercentage;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings", meta = (ClampMin = "0", UIMin = "0"))
		int32 Seed;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings")
		bool useRandomSeed;

	UPROPERTY()
		FGridNodeArray NodeGrid;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings", meta = (ClampMin = "0", UIMin = "0"))
		int32 MapWidth;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Settings", meta = (ClampMin = "0", UIMin = "0"))
		int32 MapHeight;


// Functions
public:	
	// Sets default values for this actor's properties
	AGenerator();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

private:
	// Generate the Map
	void GenerateMap();
	void RandomFillMap();
	void DrawDebug();

	void RandomFillNodeMap();
	
};

.CPP


#include "ProcCave.h"
#include "Generator.h"
#include "DrawDebugHelpers.h"

// Sets default values
AGenerator::AGenerator()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = false;

	// Set Defaults
	RandomFillPercentage = 50;
	useRandomSeed = false;
	Seed = 10;
	safeSeed = uint32(Seed);
	MapWidth = 100;
	MapHeight = 100;

	// Init Grid
	NodeGrid.Clear();
	NodeGrid.AddUninitialized(MapHeight, MapWidth);

}

// Called when the game starts or when spawned
void AGenerator::BeginPlay()
{
	Super::BeginPlay();

	GenerateMap();
	//DrawDebug();
	
}



void AGenerator::GenerateMap()
{
	//RandomFillMap();
	RandomFillNodeMap();
}

void AGenerator::RandomFillMap()
{
	if (useRandomSeed) {
		uint64 rawtime = DateAndTime.ToUnixTimestamp();
		safeSeed = uint32(rawtime);
	}

	FRandomStream psuRandom = FRandomStream(Seed);
	for (uint8 x = 0; x < 100; x++) {
		for (uint8 y = 0; y < 100; y++) {
			CaveMap[x][y] = (FMath::RandRange(0, 100) < RandomFillPercentage) ? 1 : 0;
			// iterate the seed
			uint32 cSeed = psuRandom.GetCurrentSeed();
			uint32 nSeed = (cSeed * 3) + 1;
			psuRandom.Initialize(nSeed);
		}
	}

}

void AGenerator::RandomFillNodeMap()
{
	if (useRandomSeed) {
		uint64 rawtime = DateAndTime.ToUnixTimestamp();
		safeSeed = uint32(rawtime);
	}

	FRandomStream psuRandom = FRandomStream(Seed);
	for (uint8 x = 0; x < MapWidth; x++) {
		for (uint8 y = 0; y < MapHeight; y++) {
			NodeGrid.Height[x].Width[y] = GetWorld()->SpawnActor(AWallNode->AWallNode());
			NodeGrid.Height[x].Width[y]->SetActorLocation(FVector(x*100, y*100, 0));
			NodeGrid.Height[x].Width[y]->SetWallStatus(((FMath::RandRange(0, 100) < RandomFillPercentage) ? 1 : 0));
			// iterate the seed
			uint32 cSeed = psuRandom.GetCurrentSeed();
			uint32 nSeed = (cSeed * 3) + 1;
			psuRandom.Initialize(nSeed);
		}
	}
}

void AGenerator::DrawDebug()
{
	if (CaveMap != NULL) {
		for (uint8 x = 0; x < 100; x++) {
			for (uint8 y = 0; y < 100; y++) {
				FVector position = FVector(x * 100.0F, y * 100.0F, 0.0F);
				if (CaveMap[x][y] == 1) { DrawDebugPoint(GetWorld(), position, 5.0F, FColor(255, 0, 0), false, 60.0F, 0); }
				else { DrawDebugPoint(GetWorld(), position, 5.0F, FColor(0, 0, 255), false, 60.0F, 0); }
			}
		}
	}
}

The main issue, as far as I can tell, seems to be in this function:


void AGenerator::RandomFillNodeMap()
{
	if (useRandomSeed) {
		uint64 rawtime = DateAndTime.ToUnixTimestamp();
		safeSeed = uint32(rawtime);
	}

	FRandomStream psuRandom = FRandomStream(Seed);
	for (uint8 x = 0; x < MapWidth; x++) {
		for (uint8 y = 0; y < MapHeight; y++) {
			NodeGrid.Height[x].Width[y] = GetWorld()->SpawnActor(AWallNode->AWallNode());
			NodeGrid.Height[x].Width[y]->SetActorLocation(FVector(x*100, y*100, 0));
			NodeGrid.Height[x].Width[y]->SetWallStatus(((FMath::RandRange(0, 100) < RandomFillPercentage) ? 1 : 0));
			// iterate the seed
			uint32 cSeed = psuRandom.GetCurrentSeed();
			uint32 nSeed = (cSeed * 3) + 1;
			psuRandom.Initialize(nSeed);
		}
	}
}

Looks like you are using SpawnActor wrong. The easiest way to call it is:


GetWorld()->SpawnActor<AWallNode>();

Also your indexes in this block is swapped:


NodeGrid.Height[x].Width[y] = 

When your arrays become non square you’ll get an error.