Download

Adding a UStaticMeshComponent to a pawn in constructor via a SceneComponent

I’ve been trying to create ladders to use in all my blueprint pawns since I need dozens of them per ship and these are moving ships. Creating an actor class works fine and it creates the static mesh in the level, I’m using SmartfirstPersonController for this.

So I thought I could create a scene component class that would create a Static Mesh Component from a UPROPERTY() UStaticMeshComponent* TheMeshToAdd and it would add it to the list of components during runtime on the blueprint.

I was wrong. Even my UPROPERTY set with meta tags EditAnywhere, BlueprintReadWrite etc. doesnt even show up. It just looks like a blank scenecomponent despite having all this constructor logic for collider box, static mesh etc.

I’ve tried in the constuctor and on BeginPlay using CreateDefaultSubObject<UStaticMeshComponent>(TEXT(“LadderMesh”); followed by LadderMesh->SetupAttachment(…);

It seems no matter what I do I cant have a component spawn in a static mesh during runtime, I can set its location via another function called during BeginPlay (although setting it in editor would be great but then I might as well just spawn the Actor class version of this that does work using a loop that holds the vectors of all locaitons I need them.

Heres the .cpp with allth e tuff I’ve tried. It compiles but when added to any blueprint its just as if it was a blank scenecomponent (transform, LOD, physics, navigaton and thats about it)



FKismetEditorUtilities::AddComponentsToBlueprint(GetOwner(), UStaticMeshComponent , false, nullptr);
	// Our root component will be a sphere that reacts to physics
	USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
	SphereComponent->InitSphereRadius(40.0f);
	SphereComponent->SetCollisionProfileName(TEXT("Pawn"));
	// Create and position a mesh component so we can see where our sphere is
	UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("VisualRepresentation"));
	SphereVisual->SetupAttachment(GetOwner()->GetDefaultAttachComponent());
	static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
	if (SphereVisualAsset.Succeeded())
	{
		SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
		SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
		SphereVisual->SetWorldScale3D(FVector(0.8f));
	}

	bWantsBeginPlay = true; // Set this component to be initialized when the game starts.
	PrimaryComponentTick.bCanEverTick = true;

	UStaticMeshComponent* LadderMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("LadderMesh"));
	LadderMesh->SetupAttachment(GetOwner()->GetDefaultAttachComponent());
	static ConstructorHelpers::FObjectFinder<UStaticMesh> LadderMesh(TEXT("/Game/Meshes/Static/Props/MilitarySet1/Ladder1.Ladder1"));
	if (LadderMesh.Succeeded())
	{
		LadderMesh->SetStaticMesh(LadderMesh.Object);
		LadderMesh->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
		LadderMesh->SetWorldScale3D(FVector(0.8f));
	}

}

// Called when the game starts
void ULadderComponent::BeginPlay()
{
	Super::BeginPlay();
	LadderMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("LadderMesh"));

	//OwningClass = nullptr; // Ensure its set to nullptr so we dont get crashes if its not set properly
	BoxCollider = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxCollider"));
	BoxCollider->SetupAttachment(LadderMesh);
	BoxCollider->BodyInstance.SetCollisionProfileName("Trigger");
	BoxCollider->bGenerateOverlapEvents = true;
	BoxCollider->OnComponentBeginOverlap.AddDynamic(this, &ULadderComponent::OnTriggerEnter);
	BoxCollider->OnComponentEndOverlap.AddDynamic(this, &ULadderComponent::OnTriggerExit);
        // LadderMesh = ConstructObject<UStaticMeshComponent>(UStaticMeshComponent::StaticClass(), GetOwner(), NAME_None, RF_Transient);
       //
	
}


Heres the .h



// (C) 2016 Tek ltd

#pragma once

 #include "Components/SceneComponent.h"
#include "LadderComponent.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class OPTRIDENT_API ULadderComponent : public USceneComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties CONSTRUCTOR UE4 DEFAULT
	ULadderComponent();

	// Called when the game starts UE4 STOCK FUNCTION
	virtual void BeginPlay() override;
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;


	/********************************************/
	/*		Begin Overlap trigger Interface     */
	/********************************************/
	UFUNCTION(Category = "Ladders|Overlaps") // Called when the BoxCollider collision shape is overlapped by anything with overlap events on
		void OnTriggerEnter(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
	UFUNCTION(Category = "Ladders|Overlaps") // Called when the actor in the colission shape exits it
		void OnTriggerExit(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
	/* The StaticMesh this ladder uses */
	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Static Mesh") //HideCategories=(Mobility)
		UStaticMeshComponent* LadderMesh;
	/* The cube collision shape used for overlaps events */
	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Ladder")
		UBoxComponent* BoxCollider;


private:

	/* The sound cue used when moving up/down the ladder */

	/* The class that 'owns' this component ie BP_Odax */
	AActor* OwningClass;
		
	
};


First problem here is that you are using BeginPlay as if it were a constructor. The BeginPlay is called when gameplay begins (hence its name), and is not called in the editor itself.

In your .h, replace GENERATED_BODY with GENERATED_UCLASS_BODY.

Then in your .cpp, you will need to define the constructor:


ULadderComponent ::ULadderComponent (const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer.SetDefaultSubobjectClass<UBaseCharacterMovement>(ABaseCharacter::CharacterMovementComponentName))
{
    // Constructor logic here
}