Hello,
I’m a total beginner with unreal and C++, so i’m probably doing things the wrong way…
I’m trying to make a simple 3D platformer / parkour game with C++. For that, i want a “random” urban environnement. The first thing i’ve made is a building generator, so that i can spawn buildings with “random in range” number of floors.
So, i made a C++ class called Building based on Actor. It contains a scene as root component, a static mesh for the base floor and an arrow for the location of the next floor, it picks a random uint8 in a range and enters a for loop to call a function that spawns instances of other classes (for now only one other class, a simple box with a few spotlights representing a floor, called Floor), and finally spawns a last instance of another class (inherited from the Floor class called Roof, with slightly different graphics, so that i know which is which). So far, everything works as expected, when i spawn a Building, i get a building with a random number of floors, and a roof.
So i went further, with a Cell class, and it works the same way. It contains a scene as root component, a few static meshes to make roads, and arrows to spawn Buildings. The problem is that when i spawn a Cell, my base floors are placed as expected, on their repective arrows, but all the floors of all the buildings don’t move with their base floor. I’ve attached what i get with a 4 Buildings Cell :
Here are relevant parts of my code in Building.cpp:
ABuilding::ABuilding()
{
// 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;
//Create Scene
Scene = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
RootComponent = Scene;
// Create Meshes
WallMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Wall Mesh"));
static ConstructorHelpers::FObjectFinder<UStaticMesh> StaticMeshOb_building(TEXT("StaticMesh'/Game/Geometry/Meshes/BuildingMesh.BuildingMesh'"));
if (StaticMeshOb_building.Object) WallMesh->SetStaticMesh(StaticMeshOb_building.Object);
static ConstructorHelpers::FObjectFinder<UMaterial> MaterialOb_building(TEXT("Material'/Game/StarterContent/Materials/M_Concrete_Tiles.M_Concrete_Tiles'"));
if (MaterialOb_building.Object) WallMesh->SetMaterial(0, MaterialOb_building.Object);
FloorMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Floor Mesh"));
if (StaticMeshOb_building.Object) FloorMesh->SetStaticMesh(StaticMeshOb_building.Object);
if (MaterialOb_building.Object) FloorMesh->SetMaterial(0, MaterialOb_building.Object);
// Attach them
WallMesh->SetupAttachment(RootComponent);
WallMesh->SetRelativeLocation(FVector(0, 0, 150));
WallMesh->SetRelativeScale3D(FVector(18, 18, 3));
FloorMesh->SetupAttachment(RootComponent);
FloorMesh->SetRelativeLocation(FVector(0, 0, -10));
FloorMesh->SetRelativeScale3D(FVector(20, 20, 0.1));
// Create Arrow for next floor
NextFloor = CreateDefaultSubobject<UArrowComponent>(TEXT("Next Floor"));
NextFloor->SetupAttachment(RootComponent);
NextFloor->SetRelativeLocation(FVector(0, 0, 300));
// other things like that not shown
BuildFloors(NumberOfFloors);
}
/** We build enough floors to match our NumberOfFloors */
void ABuilding::BuildFloors(uint8 Number)
{
// The base floor is part of the Building so it's already here.
// We set the position of the next floor to our arrow position.
UArrowComponent* Next = NextFloor;
// We have to check if we have to build more floors before the roof.
if (Number > 2)
{
// Now we build the next floors starting with that position
for (int i = 0; i < Number - 2; ++i)
{
// And we set Next to the next floor position as we build a new floor
Next = BuildFloor(Next);
//NextFloor->AddLocalTransform(Next->GetRelativeTransform());
}
}
// Finally, build the roof
BuildRoof(Next);
}
/** We build an intermediate floor from its position, giving back the next one's position*/
UArrowComponent* ABuilding::BuildFloor(UArrowComponent* Position)
{
auto NewFloor = Cast<ABuildingFloor>(UGameplayStatics::BeginDeferredActorSpawnFromClass(this, ABuildingFloor::StaticClass(), Position->GetRelativeTransform(),ESpawnActorCollisionHandlingMethod::Undefined,this));
if (NewFloor != nullptr)
{
UGameplayStatics::FinishSpawningActor(NewFloor, Position->GetRelativeTransform());
NextFloor->AttachToComponent(Position, FAttachmentTransformRules::SnapToTargetIncludingScale);
Position = NewFloor->GetNextFloorArrow();
}
return Position;
}