UE4 Blueprints makes it very easy to drag and drop objects onto each other, but sometimes you want everything already set up.
I’ve been working on a procedural level generation system, which is designer assisted. So blocks that will be assembled to build a level need to be marked up by the designer with locations to spawn items and other things.
Doing this in Blueprints is very easy, make a new Blueprint extending Actor, then press AddComponent, pick BillBoardComponent, set it’s Sprite in it’s details panel and you are done. But, it’s a Blueprint itself which means code that runs it will have to go looking for blueprint instances. Additionally, it gets messier when the Blueprint extends Blueprint Component, and then these need to be added to each block.
So, moving all this to C++.
The result will be that when the designer is modifying a Block they will press add component, and select the one they want directly, then position it.
It actually is very simple, but because it wasn’t directly documented it took me a while to figure it out. Here’s the code.
This is the base header file:
#pragma once #include "Components/ActorComponent.h" #include "Components/SceneComponent.h" #include "Components/BillboardComponent.h" #include "MarkerComp.generated.h" UCLASS(Blueprintable, ClassGroup = (Marker), meta = (BlueprintSpawnableComponent)) class MYGAME_API UMarkerComp : public USceneComponent { GENERATED_BODY() protected: //A UBillboardComponent to hold Icon sprite class UBillboardComponent* BillboardComponent; //Sprite for the Billboard Component class UTexture2D* SpriteTexture; public: // Sets default values for this component's properties UMarkerComp(const FObjectInitializer& ObjectInitializer); // Begin ActorComponent interface virtual void OnRegister() override; // Called when the game starts virtual void InitializeComponent() override; // Called every frame virtual void TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction ) override; };
I didn’t actually choose to make the Billboards in it’s .cpp, but in it’s derived classes. I use multiple because each type of marker in my system has different variables that need to be set as well as functions that will be called as the level is being generated, so it is more than just a different sprite.
#include "MyGame.h" #include "MarkerComp.h" // Sets default values for this component's properties UMarkerComp::UMarkerComp(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features // off to improve performance if you don't need them. bWantsInitializeComponent = true; PrimaryComponentTick.bCanEverTick = true; //IconPath = "/Game/Lemons/Procedural/Blocks/Blueprints/icons/AmmoMarker"; bVisualizeComponent = true; } void UMarkerComp::OnRegister() { // If we need to perform a call to AttachTo, do that now // At this point scene component still has no any state (rendering, physics), // so this call will just add this component to an AttachChildren array of a the Parent component AttachTo(AttachParent, AttachSocketName); Super::OnRegister(); }
Here is the extending header.
#pragma once #include "Components/ActorComponent.h" #include "MarkerComp.h" #include "AmmoMarker.generated.h" UCLASS(ClassGroup = (Lemon), meta = (BlueprintSpawnableComponent)) class MYGAME_API UAmmoMarker : public UMarkerComp { GENERATED_BODY() public: // Sets default values for this actor's properties UAmmoMarker(const FObjectInitializer& ObjectInitializer); // Called when the game starts virtual void InitializeComponent() override; // Called every frame virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; };
Here is the .cpp of an extending class that actually makes the Billboard Component.
#include "MyGame.h" #include "AmmoMarker.h" // Sets default values UAmmoMarker::UAmmoMarker(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. bWantsInitializeComponent = true; PrimaryComponentTick.bCanEverTick = true; // Structure to hold one-time initialization struct FConstructorStatics { // A helper class object we use to find target UTexture2D object in resource package ConstructorHelpers::FObjectFinderOptionalMarkerTextureObject; // Icon sprite category name FName ID_CatagoryName; // Icon sprite display name FText NAME_DisplayName; FConstructorStatics() // Use helper class object to find the texture // "/Engine/EditorResources/S_Note" is resource path : MarkerTextureObject(TEXT("/Game/Lemons/Procedural/Blocks/Blueprints/icons/AmmoMarker")) , ID_CatagoryName(TEXT("Marker")) , NAME_DisplayName(NSLOCTEXT("SpriteCategory", "AmmoMarker", "AmmoMarker")) { } }; static FConstructorStatics ConstructorStatics; BillboardComponent = ObjectInitializer.CreateEditorOnlyDefaultSubobject (this, TEXT("Billboard"), true); SpriteTexture = ConstructorStatics.MarkerTextureObject.Get(); BillboardComponent->Sprite = SpriteTexture; BillboardComponent->AttachTo(this); }
This post you made 6 1/2 years ago really saved my neck today. Thanks for this!
Glad it helped. I refer back to some of these sometimes myself. I check in but am not actively writing these tutorials.