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;
}