简体   繁体   中英

Unreal Engine: How can I create UStaticMeshComponent in a loop?

I am trying to create multiple UStaticMeshComponent by using for loop but Unreal Engine keeps triggering breakpoint on CreateDefaultSubobject in Object.h (Not in my code, it's from UE4 core API). When I'm creating a single component it's working fine. I am pretty new to UnrealEngine and C++ so I might be doing something dumb but please be easy on that:)

Thank you so much for your help.

Header

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "FlowSphere.generated.h"

UCLASS()
class CPP_PRACTICE_3_API AFlowSphere : public AActor
{
    GENERATED_BODY()

public: 
    // Sets default values for this actor's properties
    AFlowSphere();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public: 
    // Called every frame
    virtual void Tick(float DeltaTime) override;

private:
    //UPROPERTY()
    //TArray<UStaticMeshComponent*> StaticMeshComponents;

    UStaticMeshComponent* test;

};

C++

#include "FlowSphere.h"

// Sets default values
AFlowSphere::AFlowSphere()
{
    int32 amount = 100;

    RootComponent = CreateDefaultSubobject<USceneComponent>("SceneComponent");

    for (int32 i = 0; i < amount; i++) {

        //WHEN I INCLUDE THE LINE BELOW, UE4 START MAKING BREAKPOINT
        UStaticMeshComponent* item = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Sphere"));
        item.AttachTo(this->RootComponent);

    }

    //THIS WORKS FINE
    this->test = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("HELLO")); 
    PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AFlowSphere::BeginPlay()
{
    Super::BeginPlay();

}

// Called every frame
void AFlowSphere::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

Where it triggering breakpoint on the Object.h from UE4 API

    /**
    * Create a component or subobject
    * @param    TReturnType                 class of return type, all overrides must be of this type
    * @param    SubobjectName               name of the new component
    * @param    bTransient                  true if the component is being assigned to a transient property. This does not make the component itself transient, but does stop it from inheriting parent defaults
    */
    template<class TReturnType>
    TReturnType* CreateDefaultSubobject(FName SubobjectName, bool bTransient = false)
    {
        UClass* ReturnType = TReturnType::StaticClass();
        return static_cast<TReturnType*>(CreateDefaultSubobject(SubobjectName, ReturnType, ReturnType, /*bIsRequired =*/ true, /*bIsAbstract =*/ false, bTransient));
    }

You used the same name for two different calls to CreateDefaultSubobject.

I ran your code in a new UE4 C++ project and got the following error message:

Fatal error: [File:D:\\Build++UE4\\Sync\\Engine\\Source\\Runtime\\CoreUObject\\Private\\UObject\\UObjectGlobals.cpp] [Line: 3755] Default subobject StaticMeshComponent Sphere already exists for FlowSphere /Script/MyProject.Default__FlowSphere.

Also, the code you posted didn't compile.

item.AttachTo(this->RootComponent);

should have been:

item->AttachTo(this->RootComponent);

and you also needed to include "Components/StaticMeshComponent.h". Here is the corrected code:

#include "FlowSphere.h"
#include "Components/StaticMeshComponent.h"

// Sets default values
AFlowSphere::AFlowSphere()
{
    int32 amount = 100;

    RootComponent = CreateDefaultSubobject<USceneComponent>("SceneComponent");

    for (int32 i = 0; i < amount; i++) {

        FName name = *FString::Printf(TEXT("Sphere %i"), i);
        UStaticMeshComponent* item = CreateDefaultSubobject<UStaticMeshComponent>(name);
        item->AttachTo(this->RootComponent);

    }

    this->test = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("HELLO"));
    PrimaryActorTick.bCanEverTick = true;

}

So I had to do the same thing recently and ran into a lot of issues but the two major things to fix are:

  1. use UInstancedStaticMesh instead of UStaticMesh.
  2. add a property to your static mesh.

Below is the correct code.

#include "FlowSphere.h"
#include "Components/StaticMeshComponent.h"
#include "UObject/ConstructorHelpers.h"

// Sets default values
AFlowSphere::AFlowSphere()
{
    int32 amount = 100;

    RootComponent = CreateDefaultSubobject<USceneComponent>("SceneComponent");

    for (int32 i = 0; i < amount; i++) {

        FName name = *FString::Printf(TEXT("Sphere %i"), i);
        FName c_name = *FString::Printf(TEXT("ChildSceneComponent %i"), i);

        UPROPERTY(EditAnywhere)
            USceneComponent* ChildSceneComponent
            = CreateDefaultSubobject<USceneComponent>(c_name);

        // instead of the regular static mesh, create and instance static mesh object with name assigned
        UInstancedStaticMeshComponent* item = CreateDefaultSubobject<UStaticMeshComponent>(name);
        item->RegisterComponent();

        // assign property to mesh if not you'll get a static mesh null property error like i did 
        auto MeshAsset = ConstructorHelpers::FObjectFinder<UStaticMesh>(TEXT("StaticMesh'/Engine/BasicShapes/Sphere.Sphere'"));

        if (MeshAsset.Object != nullptr)
        {
            item->SetStaticMesh(MeshAsset.Object);
        }
        item->SetFlags(RF_Transactional);

        // add instance to actor level
        this->AddInstanceComponent(item);

        // make the scene component for the mesh to be visible
        RootComponent = Root;

        // attach mesh to the scene component of the actor in form of a Hierarchy

        item->AttachToComponent(ChildSceneComponent, FAttachmentTransformRules::KeepWorldTransform, m_name);
        ChildSceneComponent->AttachToComponent(Root, FAttachmentTransformRules::KeepWorldTransform, c_name);

        // set mesh transform based on the ith position
        FTransform t(FVector(250 * i, i, i));

        // activate the new copy of the mesh
        Mesh->AddInstance(t);

        // Offset and scale the child scene from the root scene as a precaution
        ChildSceneComponent->SetRelativeTransform(
            FTransform(FRotator(0, 0, 0),
                FVector(250 * i, i, i),
                FVector(0.1f))
        );


    }


    PrimaryActorTick.bCanEverTick = true;

}



    

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM