Hot reload not working, possibly a compile issue?

I have been having issues with compiling and hot reloading. would someone be able to review my code to see if there is a reason it wont compile properly.

My best guess at this point is that despite my code compiling successfully, it doesn’t actually compile properly for editor use. I think it might have something to do with using a class called Room as a C++ object, but I honestly have no idea. This code worked perfectly fine as-is 2 days ago but since yesterday I have been unable to compile unless I remove my Room class and any code requiring it in MapLayout (keeping in mind that this code DID work 2 days ago with no changes made since and then just stopped all of a sudden with no changes when I reopened my project yesterday).

here is another post I have made trying other fixes for similar-ish issues.

code and resources can be found here, but I will also include the code below

MapLayout.h

#pragma once
#include "CoreMinimal.h"
#include "Engine.h"
#include "GameFramework/Actor.h"
#include "Components/InstancedStaticMeshComponent.h"
#include <vector>
#include "Room.h"
#include "MapLayout.generated.h"

UCLASS()
class MAPGEN_API AMapLayout : public AActor {
GENERATED_BODY()

public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map")
	int MAIN_CHAIN_MIN = 5;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Generation")
	int MAIN_CHAIN_RAND = 4;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Generation")
	int EXT_LENGTH = 3;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Generation")
	int ROOM_SIZE = 200;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Generation")
	int TILE_SIZE = 10;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Generation")
	float DOOR_WIDTH = 1.5f;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Generation")
	UStaticMesh* FloorMesh;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Generation")
	UStaticMesh* DoorMesh;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Map Generation")
	UStaticMesh* WallMesh;
UPROPERTY()
	UInstancedStaticMeshComponent* floor_ISMC;
UPROPERTY()
	UInstancedStaticMeshComponent* door_ISMC;
UPROPERTY()
	UInstancedStaticMeshComponent* wall_ISMC;

AMapLayout();

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

MapLayout.cpp

#include "MapLayout.h"

std::vector<Room*> rooms;							// stores a pointer to each room in the map

/**
 * Checks if a there is a room that already exists at a given coordinate
 */
bool validLoc(int x, int y) {
	for (int i = 0; i < rooms.size(); i++)
		if ((*rooms[i]).posEquals(x, y))
			return false;
	return true;
}

/**
* Generates a new room in the main chain of rooms
*/
Room* genNextRoom() {
	Room *last = rooms[rooms.size() - 1];		// get a pointer to the most recently created room in the chain
	int dir = FMath::RandRange(0, 2);		// choose a random direction to place the next room in, (dont go DOWN because its easy to get locked in and no extra rooms can be added)
	int x = last->x;						// get the x position of the last room
	int y = last->y;						// get the y position of the last room
	switch (dir) {							// calculate the coordinate of the new room going in the random direction
	case 0: x = last->x + 1; break;			// if the new room is to the left, x = last.x + 1
	case 1: y = last->y + 1; break;			// if the new room is above, y = last.y + 1
	case 2:	x = last->x - 1; break;			// if the new room is to the right, x = last.x - 1
	}
	if (!validLoc(x, y))					// check if the location of the new room is not already occupied by another room
		return genNextRoom();				// if the room wasnt valid then try again (random has a good chance not to try the same direction again) [can RARELY cause a stack overflow]
	Room* next = new Room(x, y, dir, last);	// get a pointer to the new room
	next->addDoor();						// create a doorway between this room and the last room
	return next;							// return a pointer to the new room
}

/**
* Generates any rooms that extend off of the main chain.
* @param current A pointer to the Room that extensions should be added on to
* @param extend The number of recursive extensions should be made on this tile (dont make this big, like 2..3 is sufficent)
*/
void genExtRooms(Room* current, int extend) {
	std::vector<Room*> validPos;											// stores a pointer to each RoomPosition that is in a valid location

	int x = current->x + 1;
	int y = current->y;
	if (validLoc(x, y)) validPos.push_back(new Room(x, y, 0, current));		// Create a temporary room to the left if there is no room already there
	x = current->x;
	y = current->y + 1;
	if (validLoc(x, y)) validPos.push_back(new Room(x, y, 1, current));		// Create a temporary room above if there is no room already there
	x = current->x - 1;
	y = current->y;
	if (validLoc(x, y)) validPos.push_back(new Room(x, y, 2, current));		// Create a temporary room to the right if there is no room already there
	x = current->x;
	y = current->y - 1;
	if (validLoc(x, y)) validPos.push_back(new Room(x, y, 3, current));		// Create a temporary room below if there is no room already there

	int numRooms = FMath::RandRange(0, 2);							// pick a random number of rooms to add onto the current room (0..2)
	while (numRooms-- > 0 && validPos.size() > 0) {					// while we should still add another room and there is another valid location for an adjacent room
		int index = FMath::RandRange(0, validPos.size() - 1);		// randomly pick one of the available Rooms
		Room *nextRoom = validPos[index];							// get the chosen room from the list of valid rooms
		nextRoom->addDoor();										// create a door between the new room and the current room
		rooms.push_back(nextRoom);									// add the new Room's pointer to the list of rooms
		if (extend > 0) genExtRooms(nextRoom, extend - 1);			// recursively call self so that each extension room on the main chain has a chance to be extended farther
		validPos[index] = validPos.back();							// overwrite the used Room in the list of availble rooms to remove it from the list
		validPos.pop_back();										// remove the duplicate created by the overwrite
	}
}

AMapLayout::AMapLayout() {
	USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));	// create a sphere to use as the root of the map generator
	RootComponent = SphereComponent;																		// set the sphere as the root component

	floor_ISMC = CreateDefaultSubobject<UInstancedStaticMeshComponent>(TEXT("Floor Instances"));			// create the instanced mesh component for placing floors down for each room
	floor_ISMC->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepWorldTransform);			// make it a child of the root component

	door_ISMC = CreateDefaultSubobject<UInstancedStaticMeshComponent>(TEXT("Door Instances"));				// create the instanced mesh component for placing doors between each room
	door_ISMC->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepWorldTransform);				// make it a child of the root component

	wall_ISMC = CreateDefaultSubobject<UInstancedStaticMeshComponent>(TEXT("Wall Instances"));				// create the instanced mesh component for placing walls
	wall_ISMC->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepWorldTransform);				// make it a child of the root component
}

// Called when the game starts
void AMapLayout::BeginPlay() {
	/* Setup variables and clearing old information */
	Super::BeginPlay();
	this->RegisterAllComponents();						// Make sure all components are registered

	floor_ISMC->SetStaticMesh(FloorMesh);				// Set the mesh to use for the floor
	floor_ISMC->ClearInstances();						// Clear any instances already on the map
	door_ISMC->SetStaticMesh(DoorMesh);					// Set the mesh to use for the doors
	door_ISMC->ClearInstances();						// Clear any instances already on the map
	wall_ISMC->SetStaticMesh(WallMesh);					// Set the mesh to use for the walls in each room
	wall_ISMC->ClearInstances();						// Clear any instances already on the map

	Room::ROOM_SIZE = ROOM_SIZE;						// Set the size of rooms for the Room class
	Room::TILE_SIZE = TILE_SIZE;						// Set the size of tiles for the Room class
	Room::DOOR_WIDTH = DOOR_WIDTH * TILE_SIZE;			// Set the size of doorways for the Room class

	rooms.clear();										// Clear any rooms stored from the last run
	rooms.push_back(new Room());						// Create the first room in the map

	int roomCount = FMath::RandRange(MAIN_CHAIN_MIN, MAIN_CHAIN_RAND + MAIN_CHAIN_MIN);		// Calculate how many rooms should be in the main chain of rooms

	/* Generate the main chain of rooms */
	for (int i = 0; i < roomCount; i++)								// For each room that should be created
		rooms.push_back(genNextRoom());								// Generate a new room in a valid location adjacent to the previous room

	/* Generate extension rooms branching off the main chain of rooms */
	for (int i = 0; i < roomCount - 1; i++)							// For all rooms in the main chain besides the last room (the exit)
		genExtRooms(rooms[i], EXT_LENGTH);							// Generate rooms that branch off in different directions

	/* Start placing the objects in the world */
	for (int i = 0; i < rooms.size(); i++) {						// For every room that was generated
		Room *cur = rooms[i];										// Store a pointer to the current room

		/* Place the floor for the room in the world */
		floor_ISMC->AddInstance(cur->getWorldPosition());			// Create an instance of the floor mesh in the world location

		/* Place doors in the world */
		std::vector<FTransform> doorPos = cur->getDoorPositions();	// Get the location of all the doors in the room
		for (int j = 0; j < doorPos.size(); j++)					// For each door
			door_ISMC->AddInstance(doorPos[j]);						// Create an instance of it in the world

		/* Place walls around each room */
		std::vector<FTransform> wallPos = cur->getWallPositions();	// Get the location of all the walls in the room
		for (int j = 0; j < wallPos.size(); j++)					// For each wall
			wall_ISMC->AddInstance(wallPos[j]);						// Create an instance of it in the world
	}
}

Room.h

#pragma once
#include "CoreMinimal.h"
#include <vector>


/**
* An object that represents an individual room in the level
*/
class MAPGEN_API Room {
private:
	struct Coord {						// stores the real world coordinates of components in the room, ex: walls, doors
		float x;						// the x position of the object
		float y;						// the y position of the object
	};

	int worldX;							// coodinate location of this room in world units
	int worldY;							// coodinate location of this room in world units

	static int EDGE_OFFSET;				// constant value for the offset from the center of a room to the edge
	static int DOOR_OFF_RNG;			// constant value for how far a door can be offset from the center of a wall

	Room* last;							// stores a pointer to the previous room in the chain

	std::vector<Coord> doors;			// stores the location of each door in the room

	float left;							// stores the position of the door on the left wall if there is one
	float above;						// stores the position of the door on the upper wall if there is one
	float right;						// stores the position of the door on the right wall if there is one
	float below;						// stores the position of the door on the lower wall if there is one

	/**
	 * Gets the side of a room a door is on
	 */
	int getDoorSide(Coord door);

public:
	int x;								// coordinate location of this room in relation to other rooms
	int y;								// coordinate location of this room in relation to other rooms
	int dir;							// stores the direction to the previous room

	static int ROOM_SIZE;				// constant value for the size in world units of each room
	static int TILE_SIZE;				// constant value for the size of each tile in world units
	static float DOOR_WIDTH;

	Room(int x = 0, int y = 0, int dir = -1, Room* last = NULL);

	/**
	* Add a door between this room and the previous room in the chain
	*/
	void addDoor();

	/**
	* Add a door between this room and the previous room in the chain (only called from other Room objects)
	* @param doorPos The coordinates of the door on the new room, to be reveresed on this room so the doors line up
	*/
	void addDoorEntrance(Coord doorPos);

	/**
	* Checks if this room already exists at the given coordinates
	*/
	bool posEquals(int x, int y);

	/**
	* Gets the FTransform of where this room should be located in the world space
	*/
	FTransform getWorldPosition();

	/**
	* Gets the FTansform for every door belonging to this Room
	*/
	std::vector<FTransform> getDoorPositions();

	/**
	* Gets the FTansform for every wall belonging to this Room
	*/
	std::vector<FTransform> getWallPositions();
};

Room.cpp

#include "Room.h"

/* Init static constants */
int Room::ROOM_SIZE;
int Room::TILE_SIZE;
float Room::DOOR_WIDTH;

int Room::EDGE_OFFSET = (ROOM_SIZE / 2) - (TILE_SIZE / 2);
int Room::DOOR_OFF_RNG = (ROOM_SIZE / TILE_SIZE) / 2 - 4;	// doors are 4 tiles wide + keeps doors atleast 2 tiles from a corner

Room::Room(int x, int y, int dir, Room* last) {
	this->x = x;
	this->y = y;
	this->dir = dir;
	this->last = last;

	this->worldX = x * ROOM_SIZE;				// calculate this rooms position in world space
	this->worldY = y * ROOM_SIZE;				// calculate this rooms position in world space

	left = ROOM_SIZE + worldY + DOOR_WIDTH;		// simulate no door (distance is always larger than distance to a door, which would prevent a wall from spawning there)
	above = ROOM_SIZE + worldX + DOOR_WIDTH;	// simulate no door (distance is always larger than distance to a door, which would prevent a wall from spawning there)
	right = ROOM_SIZE + worldY + DOOR_WIDTH;	// simulate no door (distance is always larger than distance to a door, which would prevent a wall from spawning there)
	below = ROOM_SIZE + worldX + DOOR_WIDTH;	// simulate no door (distance is always larger than distance to a door, which would prevent a wall from spawning there)
}

void Room::addDoor() {
	Coord doorPos;								// stores the location of a door in world space
	doorPos.x = 0;								// coordinates allways start at 0
	doorPos.y = 0;								// coordinates allways start at 0
	int door_offset = FMath::RandRange(-DOOR_OFF_RNG, DOOR_OFF_RNG) * TILE_SIZE;	// pick a random tile to shift the door by (so doors are not always centered on each wall)
	switch (this->dir) {						// determine what wall the door is on
	case 0:										// if the door is on the left wall
		doorPos.x -= EDGE_OFFSET;				// set its x position to be on the left wall
		doorPos.y += door_offset;				// set its y position to be a random spot along the left wall
		left = doorPos.y + worldY;				// cache the offset location of this door in world cordinates to be used for wall placement
		break;
	case 1:										// if the door is on the above wall	
		doorPos.y -= EDGE_OFFSET;				// set its y position to be on the above wall
		doorPos.x += door_offset;				// set its x position to be a random spot along the above wall
		above = doorPos.x + worldX;				// cache the offset location of this door in world cordinates to be used for wall placement
		break;
	case 2:										// if the door is on the right wall
		doorPos.x += EDGE_OFFSET;				// set its x position to be on the right wall
		doorPos.y += door_offset;				// set its y position to be a random spot along the right wall
		right = doorPos.y + worldY;				// cache the offset location of this door in world cordinates to be used for wall placement
		break;
	case 3:										// if the door is on the bottom wall
		doorPos.y += EDGE_OFFSET;				// set its y position to be on the bottom wall
		doorPos.x += door_offset;				// set its x position to be a random spot along the bottom wall
		below = doorPos.x + worldX;				// cache the offset location of this door in world cordinates to be used for wall placement
		break;
	}

	this->doors.push_back(doorPos);				// add the door to the list of doors for this room
	last->addDoorEntrance(doorPos);				// add a door on the previous room that connects to the new door on this room
}

void Room::addDoorEntrance(Coord doorPos) {
	/* flip the direction of the door so it lines up with the door on the new room */
	if (abs(doorPos.x) == EDGE_OFFSET) doorPos.x = -doorPos.x;
	else if (abs(doorPos.y) == EDGE_OFFSET) doorPos.y = -doorPos.y;

	/* cache the offset location of this door in world cordinates to be used for wall placement */
	switch (getDoorSide(doorPos)) {
	case 0: left = doorPos.y + worldY; break;
	case 1:	above = doorPos.x + worldX; break;
	case 2:	right = doorPos.y + worldY; break;
	case 3:	below = doorPos.x + worldX; break;
	}
	this->doors.push_back(doorPos);				// add the door to the list of doors for this room
}

int Room::getDoorSide(Coord door) {
	if (door.x == -EDGE_OFFSET) return 0;
	else if (door.y == -EDGE_OFFSET) return 1;
	else if (door.x == EDGE_OFFSET) return 2;
	return 3; //else if (door.y == EDGE_OFFSET) , This will never be somthing else because a Door will always have 1 coordinate = EDGE_OFFSET
}

bool Room::posEquals(int x, int y) {
	if (this->x == x && this->y == y)
		return true;
	return false;
}

FTransform Room::getWorldPosition() {
	return FTransform(FVector(this->x * ROOM_SIZE, this->y * ROOM_SIZE, 0));
}

std::vector<FTransform> Room::getDoorPositions() {
	std::vector<FTransform> doorPositions;														// stores the FTransform for all doors in this room
	for (int i = 0; i < this->doors.size(); i++) {												// for every door
		Coord door = doors[i];
		FTransform trans_door = FTransform(FVector(door.x + worldX, door.y + worldY, 0));		// create the FTransform using the world coordinates of the door
		trans_door.SetRotation(FQuat::MakeFromEuler({ 0.f, 0.f, getDoorSide(door) * 90.0f }));	// rotate the door to match the wall it is against
		doorPositions.push_back(trans_door);													// add the FTransform to the list
	}
	return doorPositions;																		// return the list of FTransforms for all the doors
}

std::vector<FTransform> Room::getWallPositions() {
	std::vector<Coord> wallPositions;
	/* calculates the range of world coordinates that this room occupies */
	float startX = worldX - EDGE_OFFSET;
	float endX = worldX + EDGE_OFFSET;
	float startY = worldY - EDGE_OFFSET;
	float endY = worldY + EDGE_OFFSET;

	/* Calculate wall positions around the edge of the room */
	/* This is kindof hard to explain step by step but basically it puts a wall on every tile that is on the edge of room
	 * if the distance from the cached door location for that wall and that particular peice of wall is less than the width of the door then dont place that wall peice.
	*/
	Coord wall;
	for (int i = 0; i < ROOM_SIZE; i += TILE_SIZE) {
		/* place wall peice on the top wall */
		wall.x = startX + i;
		wall.y = startY;
		if (abs(wall.x - above) >= DOOR_WIDTH) wallPositions.push_back(wall);
		/* place wall peice on the bottom wall */
		wall.x = startX + i;
		wall.y = endY;
		if (abs(wall.x - below) >= DOOR_WIDTH) wallPositions.push_back(wall);
	}
	for (int i = TILE_SIZE; i < ROOM_SIZE - TILE_SIZE; i += TILE_SIZE) {		// top/bottom walls cover the corner peices so dont also do corners with the left/right
																				/* place wall peice on the left wall */
		wall.x = startX;
		wall.y = startY + i;
		if (abs(wall.y - left) >= DOOR_WIDTH) wallPositions.push_back(wall);
		/* place wall peice on the right wall */
		wall.x = endX;
		wall.y = startY + i;
		if (abs(wall.y - right) >= DOOR_WIDTH) wallPositions.push_back(wall);
	}

	/* create an FTransform for each wall and return them in a list */
	std::vector<FTransform> walls;
	for (int i = 0; i < wallPositions.size(); i++) {
		Coord wall = wallPositions[i];
		walls.push_back(FTransform(FVector(wall.x, wall.y, 0)));
	}
	return walls;
}

First of all don’t post any new quastion about this, try to compile everything in to this one since it seems everything is related to same issues

Ok, let me start with basic issues with you code, not sure if this gonna fix your issies, but might help

1.Don’t use standard C++ library use UE4 APIs as much as you can, insted of std::vector use TArray ot other UE4 contianer it is compatible with UE4 reflection system. Not to mention if you place any UObject in std::vector it refrence won’t be visible by the engine and if object is not refrences anywhere it will be garbage collected

2.In case of non-UObject clases use F prefix so FRoom, if we in matter of style function names should start with upper case in UE4 code. Would be good if you also consider to use UObject for rooms as it will help you with debuggin, rooms will be visible by the engine and you will be able to reference those rooms in blueprints

3.Don’t do global functions and global variables, use static functions insted (they even supported by the blueprints) and try to avoid global and static variables at all

4.Don’t use int, due to size difference between compilers of this type UE4 uses size defined ints insted (int8, int16,int32, uint8, uint16, uint32), blueprint integer is int32 and bluprint byte type is uint8, other int types aren’t supported by blueprint and property editor but you can still use them in C++. You should get UnrealHeaderTool errors if you use int.

In other words in light of UE4 conventions and styles sorry to say that but your code is a mess and compilation issues might be related to that as you coding in away that engine and it’s tools don’t expect you to code that way. AMapLayout aspecially is style mess, you use 3 diffrent naming conventions in same time. You seem to have C++ knowlage, so i recommand you to looks on example project or engine code it self so you can get fimilar with the style. Think that you working for Epic and you need to adopt this style so other Epic employees (but technically anyone who code in UE4) can understand your code. There also docs:

As for hot reload lot loading and thigns not showing at first time, this is normal. Hot reload is not perfect and sometimes mess ups and oyu need to restart the editor, this aspacially happens when you introduce new properties and classes, if you do so compile without editor. But before jurging that please clean up your code as your issues related to hot reload might be also related.

Ok, thanks. I will rewrite my code to fit UE’s model. as for coding, I am actually not too much of a C++ developer, I work primarily in Java and Assembly.

also as far as the hot reload goes, my best guess is that my code does compile successfully, but when the engine tries to load the new DLL it fails because it didn’t compile right (might be coding style-related) and then when I re-open the editor it will by default try to open the most recently created DLL, that DLL being the broken one.

Thanks for your response though! Hopefully that will resolve my issue.

I have found out what the issue was, I had done all your suggestions and still no fix. I ended up attaching a debugger to the unreal engine editor then opening the project, turns out there was a divide by 0 in Room because TILE_SIZE wasn’t assigned a value before the DLL tried to calculate its value. after correcting this the editor opened with the debugger and seemed to be working properly, I am currently testing if it will open without a debugger.