Created
December 6, 2015 21:52
-
-
Save jchildren/6ff0c21affea9e07d63d to your computer and use it in GitHub Desktop.
Unreal engine instanced static mesh manager for floors and walls
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Fill out your copyright notice in the Description page of Project Settings. | |
| #include "LevelGeneration.h" | |
| #include "LevelManager.h" | |
| // Sets default values | |
| ALevelManager::ALevelManager() : | |
| MaxX(10), | |
| MaxY(10), | |
| MaxZ(1), | |
| TileSize(400), | |
| LevelHeight(400), | |
| FloorThickness(10), | |
| FloorTypes(0), | |
| WallTypes(0), | |
| RandomSeed(0) | |
| { | |
| // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. | |
| PrimaryActorTick.bCanEverTick = true; | |
| Levels.SetNum(1); | |
| ResizeLevels(); | |
| RootComponent = CreateDefaultSubobject | |
| <USphereComponent>(TEXT("RootComponent")); | |
| MeshRootComponent = CreateDefaultSubobject | |
| <USphereComponent>(TEXT("MeshRootComponent")); | |
| FloorsRootComponent = CreateDefaultSubobject | |
| <USphereComponent>(TEXT("FloorsRootComponent")); | |
| WallsRootComponent = CreateDefaultSubobject | |
| <USphereComponent>(TEXT("WallsRootComponent")); | |
| MeshRootComponent->AttachTo(RootComponent); | |
| FloorsRootComponent->AttachTo(MeshRootComponent); | |
| WallsRootComponent->AttachTo(MeshRootComponent); | |
| InitAllComponents(); | |
| InitInstances(); | |
| } | |
| void ALevelManager::BeginDestroy() | |
| { | |
| ClearAllInstances(); | |
| ClearAllComponents(); | |
| } | |
| // Called when the game starts or when spawned | |
| void ALevelManager::BeginPlay() | |
| { | |
| Super::BeginPlay(); | |
| } | |
| // Called every frame | |
| void ALevelManager::Tick(float DeltaTime) | |
| { | |
| Super::Tick(DeltaTime); | |
| } | |
| void ALevelManager::ClearAllComponents() | |
| { | |
| ClearComponents(FloorComponents); | |
| ClearComponents(WallComponents); | |
| } | |
| void ALevelManager::ClearComponents(TArray<UInstancedStaticMeshComponent*> &Components) | |
| { | |
| UnregisterComponents(Components); | |
| Components.Empty(); | |
| } | |
| void ALevelManager::UnregisterComponents | |
| (TArray<UInstancedStaticMeshComponent*> &Components) | |
| { | |
| for (UInstancedStaticMeshComponent*& Component : Components) | |
| { | |
| Component->DetachFromParent(); | |
| Component->UnregisterComponent(); | |
| } | |
| } | |
| void ALevelManager::ClearAllInstances() | |
| { | |
| ClearInstances(FloorComponents); | |
| ClearInstances(WallComponents); | |
| } | |
| void ALevelManager::ClearInstances(TArray<UInstancedStaticMeshComponent*> &Components) | |
| { | |
| for (UInstancedStaticMeshComponent*& Component : Components) | |
| { | |
| Component->ClearInstances(); | |
| } | |
| } | |
| void ALevelManager::InitAllComponents() | |
| { | |
| InitComponents(FloorComponents, FloorTypes, | |
| TEXT("Floor Component"), FloorsRootComponent); | |
| InitComponents(WallComponents, WallTypes, | |
| TEXT("Wall Component "), WallsRootComponent); | |
| } | |
| void ALevelManager::InitComponents | |
| (TArray<UInstancedStaticMeshComponent*> &Components, int32 Count, | |
| FString BaseComponentName, USphereComponent* ParentComponent) | |
| { | |
| Components.SetNum(Count); | |
| for (auto Component(Components.CreateIterator()); Component; ++Component) | |
| { | |
| FString ComponentName = BaseComponentName; | |
| ComponentName.AppendInt(Component.GetIndex()); | |
| *Component = AddInstancedStaticMeshComponent(ComponentName, ParentComponent); | |
| } | |
| } | |
| void ALevelManager::AddComponents | |
| (TArray<UInstancedStaticMeshComponent*> &Components, int32 Count, | |
| FString BaseComponentName, USphereComponent* ParentComponent) | |
| { | |
| for (int32 i = Components.Num(); i < Count; ++i) | |
| { | |
| FString ComponentName = BaseComponentName; | |
| ComponentName.AppendInt(i - 1); | |
| Components.Add(AddInstancedStaticMeshComponent(ComponentName, ParentComponent)); | |
| } | |
| } | |
| void ALevelManager::PopComponents | |
| (TArray<UInstancedStaticMeshComponent*> &Components, int32 Count) | |
| { | |
| for (int32 i = Components.Num(); i > Count; --i) | |
| { | |
| Components[i - 1]->DetachFromParent(); | |
| Components.Pop(); | |
| } | |
| } | |
| UInstancedStaticMeshComponent* ALevelManager::AddInstancedStaticMeshComponent | |
| (FString ComponentName, USphereComponent* ParentComponent) | |
| { | |
| UInstancedStaticMeshComponent* ComponentPtr; | |
| ComponentPtr = NewObject<UInstancedStaticMeshComponent> | |
| (FloorsRootComponent, FName(*ComponentName)); | |
| ComponentPtr->AttachTo(ParentComponent); | |
| ComponentPtr->RegisterComponent(); | |
| return ComponentPtr; | |
| } | |
| void ALevelManager::InitInstances() | |
| { | |
| // We use iterators rather than ranged based for loops | |
| // here as we need the index of the TArray to determine | |
| // the location of each static mesh intance | |
| for (auto Level(Levels.CreateIterator()); Level; ++Level) | |
| { | |
| FTransform Transform; | |
| Transform.SetRotation(FQuat(FRotator(0.0f, 0.0f, 0.0f))); | |
| int32 z = (LevelHeight + FloorThickness) * Level.GetIndex(); | |
| for (auto Floor(Level->Floors.CreateIterator()); Floor; ++Floor) | |
| { | |
| switch (*Floor) | |
| { | |
| case 0: | |
| break; | |
| default: | |
| int32 x = (Floor.GetIndex() / MaxY) * TileSize; | |
| int32 y = (Floor.GetIndex() % MaxY) * TileSize; | |
| Transform.SetLocation(FVector(x, y, z)); | |
| // We subtract 1 as Floor == 0 is empty | |
| FloorComponents[*Floor - 1] | |
| ->AddInstance(Transform); | |
| break; | |
| } | |
| } | |
| for (auto XWall(Level->XWalls.CreateIterator()); XWall; ++XWall) | |
| { | |
| switch (*XWall) | |
| { | |
| case 0: | |
| break; | |
| default: | |
| int32 x = ((XWall.GetIndex() / 2) / (MaxY + 1)) * TileSize; | |
| int32 y = ((XWall.GetIndex() / 2) % (MaxY + 1)) * TileSize; | |
| Transform.SetLocation(FVector(x, y, z)); | |
| // We subtract 1 as Wall == 0 is void | |
| WallComponents[*XWall - 1] | |
| ->AddInstance(Transform); | |
| break; | |
| } | |
| } | |
| Transform.SetRotation(FQuat(FRotator(0.0f, 90.0f, 0.0f))); | |
| for (auto YWall(Level->YWalls.CreateIterator()); YWall; ++YWall) | |
| { | |
| switch (*YWall) | |
| { | |
| case 0: | |
| break; | |
| default: | |
| int32 x = ((YWall.GetIndex() / 2) / MaxY) * TileSize; | |
| int32 y = ((YWall.GetIndex() / 2) % MaxY) * TileSize; | |
| Transform.SetLocation(FVector(x, y, z)); | |
| // We subtract 1 as Wall == 0 is void | |
| WallComponents[*YWall - 1] | |
| ->AddInstance(Transform); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| void ALevelManager::ResizeLevels() | |
| { | |
| for (FStructLevel& Level : Levels) | |
| { | |
| Level.Resize(MaxX, MaxY); | |
| } | |
| } | |
| #if WITH_EDITOR | |
| void ALevelManager::PostEditChangeProperty(FPropertyChangedEvent& e) | |
| { | |
| FName PropertyName = (e.Property != NULL) ? e.Property->GetFName() : NAME_None; | |
| if (PropertyName == GET_MEMBER_NAME_CHECKED(ALevelManager, MaxX)) | |
| { | |
| ResizeLevels(); | |
| ClearAllInstances(); | |
| InitInstances(); | |
| } | |
| else if (PropertyName == GET_MEMBER_NAME_CHECKED(ALevelManager, MaxY)) | |
| { | |
| ResizeLevels(); | |
| ClearAllInstances(); | |
| InitInstances(); | |
| } | |
| else if (PropertyName == GET_MEMBER_NAME_CHECKED(ALevelManager, MaxZ)) | |
| { | |
| Levels.SetNum(MaxZ); | |
| ResizeLevels(); | |
| ClearAllInstances(); | |
| InitInstances(); | |
| } | |
| else if (PropertyName == GET_MEMBER_NAME_CHECKED(ALevelManager, FloorTypes)) | |
| { | |
| if (FloorTypes == 0) | |
| { | |
| ClearComponents(FloorComponents); | |
| } | |
| else if (FloorComponents.Num() == 0) | |
| { | |
| InitComponents(FloorComponents, FloorTypes, | |
| TEXT("Floor Component "), FloorsRootComponent); | |
| } | |
| else if (FloorTypes > FloorComponents.Num()) | |
| { | |
| AddComponents(FloorComponents, FloorTypes, | |
| TEXT("Floor Component "), FloorsRootComponent); | |
| } | |
| else if (FloorTypes < FloorComponents.Num()) | |
| { | |
| PopComponents(FloorComponents, FloorTypes); | |
| } | |
| } | |
| else if (PropertyName == GET_MEMBER_NAME_CHECKED(ALevelManager, WallTypes)) | |
| { | |
| if (WallTypes == 0) | |
| { | |
| ClearComponents(WallComponents); | |
| } | |
| else if (WallComponents.Num() == 0) | |
| { | |
| InitComponents(WallComponents, WallTypes, | |
| TEXT("Wall Component "), WallsRootComponent); | |
| } | |
| else if (WallTypes > WallComponents.Num()) | |
| { | |
| AddComponents(WallComponents, WallTypes, | |
| TEXT("Wall Component "), WallsRootComponent); | |
| } | |
| else if (WallTypes < WallComponents.Num()) | |
| { | |
| PopComponents(WallComponents, WallTypes); | |
| } | |
| } | |
| else if (PropertyName == GET_MEMBER_NAME_CHECKED(ALevelManager, Levels)) | |
| { | |
| ClearAllInstances(); | |
| InitInstances(); | |
| } | |
| Super::PostEditChangeProperty(e); | |
| } | |
| #endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Fill out your copyright notice in the Description page of Project Settings. | |
| #pragma once | |
| #include "GameFramework/Actor.h" | |
| #include "LevelManager.generated.h" | |
| USTRUCT() | |
| struct FStructLevel | |
| { | |
| GENERATED_USTRUCT_BODY() | |
| /* | |
| */ | |
| UPROPERTY(EditAnywhere, EditFixedSize, BlueprintReadWrite, NoClear) | |
| TArray<int32> Floors; | |
| /* | |
| Wall values are broken up into two seperate arrays | |
| corresponding to their rotation, such that each instance | |
| will be on the line equal to a constant integer * tile size | |
| XWalls and YWalls | |
| x = constant y = constant | |
| Previously this was handled with % 2 and a single array, | |
| but became overly complicated to edit values manually | |
| Values of 0 imply that no corresponding instances should | |
| be created in the location, while and integer less than | |
| the number supported wall types in the object (WallTypes) | |
| will correspond to an instance of a Instanced Static Mesh | |
| at that location, which can be set in the editor | |
| These numbers are agnostic to the wall type, but should | |
| correspond to the type of wall the generator used intends | |
| to create (i.e. plain walls or those with doors and windows) | |
| */ | |
| UPROPERTY(EditAnywhere, EditFixedSize, BlueprintReadWrite, NoClear) | |
| TArray<int32> XWalls; | |
| UPROPERTY(EditAnywhere, EditFixedSize, BlueprintReadWrite, NoClear) | |
| TArray<int32> YWalls; | |
| void Resize(int32 x, int32 y) | |
| { | |
| Floors.SetNumZeroed(x * y); | |
| XWalls.SetNumZeroed(x * y + y); | |
| YWalls.SetNumZeroed(x * y + x); | |
| } | |
| FStructLevel(int32 x, int32 y) | |
| { | |
| Floors.SetNumZeroed(x * y); | |
| XWalls.SetNumZeroed(x * y + y); | |
| YWalls.SetNumZeroed(x * y + x); | |
| } | |
| FStructLevel() | |
| { | |
| Floors.SetNumUninitialized(0); | |
| XWalls.SetNumUninitialized(0); | |
| YWalls.SetNumUninitialized(0); | |
| } | |
| void Destroy() | |
| { | |
| Floors.Empty(); | |
| XWalls.Empty(); | |
| YWalls.Empty(); | |
| } | |
| }; | |
| UCLASS() | |
| class LEVELGENERATION_API ALevelManager : public AActor | |
| { | |
| GENERATED_BODY() | |
| public: | |
| // Sets default values for this actor's properties | |
| ALevelManager(); | |
| // Begin asynchronus clean-up | |
| virtual void BeginDestroy() override; | |
| // Called when the game starts or when spawned | |
| virtual void BeginPlay() override; | |
| // Called every frame | |
| virtual void Tick(float DeltaSeconds) override; | |
| UFUNCTION() | |
| void ClearAllComponents(); | |
| UFUNCTION() | |
| void ClearAllInstances(); | |
| UFUNCTION() | |
| void InitAllComponents(); | |
| UFUNCTION() | |
| void InitInstances(); | |
| UFUNCTION() | |
| void ResizeLevels(); | |
| protected: | |
| UPROPERTY(EditAnywhere, Category = "Map Size") | |
| int32 MaxX; | |
| UPROPERTY(EditAnywhere, Category = "Map Size") | |
| int32 MaxY; | |
| UPROPERTY(EditAnywhere, Category = "Map Size") | |
| int32 MaxZ; | |
| UPROPERTY(EditAnywhere, Category = "Instance Size") | |
| int32 TileSize; | |
| UPROPERTY(EditAnywhere, Category = "Instance Size") | |
| int32 LevelHeight; | |
| UPROPERTY(EditAnywhere, Category = "Instance Size") | |
| int32 FloorThickness; | |
| UPROPERTY(EditAnywhere, Category = "Mesh Types") | |
| int32 FloorTypes; | |
| UPROPERTY(EditAnywhere, Category = "Mesh Types") | |
| int32 WallTypes; | |
| UPROPERTY(EditAnywhere, EditFixedSize, NoClear, Category = "Data") | |
| TArray<FStructLevel> Levels; | |
| UPROPERTY(EditAnywhere, Category = "Level Generation") | |
| int32 RandomSeed; | |
| /* | |
| Component structure is as follows: | |
| RootComponent | |
| FloorsRootComponent | |
| FloorComponent0 | |
| FloorComponent1 | |
| ... | |
| FloorComponent[n] | |
| WallsRootComponent | |
| WallComponent0 | |
| WallComponent1 | |
| ... | |
| WallComponent[n] | |
| */ | |
| UPROPERTY(Instanced, NoClear) | |
| USphereComponent* MeshRootComponent; | |
| UPROPERTY(Instanced, NoClear) | |
| USphereComponent* FloorsRootComponent; | |
| UPROPERTY(Instanced, NoClear) | |
| USphereComponent* WallsRootComponent; | |
| UPROPERTY(EditAnywhere, EditFixedSize, Instanced, NoClear) | |
| TArray<UInstancedStaticMeshComponent*> FloorComponents; | |
| UPROPERTY(EditAnywhere, EditFixedSize, Instanced, NoClear) | |
| TArray<UInstancedStaticMeshComponent*> WallComponents; | |
| private: | |
| // Utility functions | |
| UFUNCTION() | |
| void ClearComponents(TArray<UInstancedStaticMeshComponent*> &Components); | |
| UFUNCTION() | |
| void UnregisterComponents(TArray<UInstancedStaticMeshComponent*> &Components); | |
| UFUNCTION() | |
| void ClearInstances(TArray<UInstancedStaticMeshComponent*> &Components); | |
| UFUNCTION() | |
| void InitComponents(TArray<UInstancedStaticMeshComponent*> &Components, | |
| int32 Count, FString BaseComponentName, USphereComponent* ParentComponent); | |
| UFUNCTION() | |
| void AddComponents(TArray<UInstancedStaticMeshComponent*> &Components, | |
| int32 Count, FString BaseComponentName, USphereComponent* ParentComponent); | |
| UFUNCTION() | |
| void PopComponents(TArray<UInstancedStaticMeshComponent*> &Components, | |
| int32 Count); | |
| UFUNCTION() | |
| UInstancedStaticMeshComponent* AddInstancedStaticMeshComponent | |
| (FString ComponentName, USphereComponent* ParentComponent); | |
| public: | |
| #if WITH_EDITOR | |
| void PostEditChangeProperty(FPropertyChangedEvent& e); | |
| #endif | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment