C++ Components, modify in actor details

Hey guys, I’m throwing this out there in case anyone can help. I’m not a C++ guru by any means, and I’ve only been working with UE4 for less than a year, so forgive me if my code looks abhorrent.

What I want to do is pretty simple.

I am trying to populate a list of ‘spawn points’ which the level designer can edit and will spawn AIs at runtime. I have created a struct to store information about the spawn point, as well as a pointer to the mesh component which represents the type of AI I want to spawn. (imagine a mesh representing a person that you could move around in the editor to change where it appeared).

Unfortunately, my results have fallen somewhat short of this goal , as it would appear that while the code works, I cannot actually edit any of the components I create in c++ in the editor, despite making liberal use of the “UPROPERTY” specifier. (see below screenshots for my struct definition, and the code where I actually create the objects).

So I guess my question is… Am I going about this wrong? Is there an obvious reason why I can’t actually move the mesh components around in the editor ? Or is this just an engine limitation and I would be better of coming up with another solution? (One possibility that springs to mind is creating the components in blueprint). I would prefer not to use blueprint for this type of stuff though, as I find C++ easier to maintain.

Any feedback is welcome! Thanks!

Any values you want to be modifiable within the editor need both “EditAnywhere” and “BlueprintReadWrite” included in the UPROPERTY macro.

//within your struct definition:

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnPoint")
UMeshComponent* SpawnPreviewMesh;


//within your spawning actor/object .h

UPROPERTY(EditAnywhere, EditFixedSize, BlueprintReadWrite, Category = "Spawning|Preview")
TArray<FAISpawnPoints> SpawnPoints;

Also, if you want them to be editable outside of the Actor’s blueprint (i.e. within the Level blueprint) you need to make the properties public. If you want them editable only from inside the Actor’s blueprint make them protected. If they are private you can’t modify them within the blueprint editor at all.

The “PreviewComponent” is dynamically created in the code you show in your screenshot; that’s not going to be modifiable within the editor because it has no UPROPERTY macro.
Rather than declare a new variable immediately before populating it, you can include a UMeshComponent* property in your header and include the same EditAnywhere, BlueprintReadWrite statements in the UPROPERTY macro for it:

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning|Preview")
    UMeshComponent* PreviewComponent;

If you need to have multiple PreviewComponent values, place them in an array of their own:

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawning|Preview")
TArray<UMeshComponent*> PreviewComponents;

Then, add your preview components to the array after they’ve been spawned:

if (CDOSkeletalMesh)
{
    USkeletalMeshComponent* PreviewSkeletalMeshComponent = NewObject<CDOSkeletalMesh>(this);
    PreviewSkeletalMeshComponent ->SetSkeletalMesh(CDOSkeletalMesh);
    PreviewComponents.Emplace(PreviewSkeletalMeshComponent );

}
else if (CDOStaticMesh)
{
    UStaticMeshComponent* PreviewStaticMeshComponent = NewObject<CDOStaticMesh>(this);
    PreviewStaticMeshComponent ->SetStaticMesh(CDOSkeletalMesh);
    PreviewComponents.Emplace(PreviewStaticMeshComponent );

}

That should give you the ability to modify the values you’ve highlighted in the editor.

You also may want to change the BlueprintReadOnly value in the SpawnClass property of your USTRUCT to BlueprintReadWrite; allowing you to modify the spawning class in the editor as well.

But what you need to be able to modify in order to control which mesh is spawned are the CDOSkeletalMesh and CDOStaticMesh variables; these seem to be derived from “CDOMesh” - so it looks like if you want to be able to dictate which mesh is spawned in the editor, you’ll need to include a UPROPERTY macro for the CDOMesh value; unless the CDOMesh is derived from the SpawnClass or SpawnPreviewMesh defined in the spawn points; in which case making your array of spawn point BlueprintReadWrite should give you what you need.

But that’s just a guess from the screenshots you’ve shown. I’d have to see more of your code to better help if this doesn’t point you in the right direction.

Very informative, thanks! I’ll give this a try and report back!

Well, I tried your suggestions and I can edit the properties of the preview mesh, but not in the components list of the actor. i.e I can manually set the translation by expanding the appropriate categories, but I can’t select the component in the outliner and just move the widget to translate it.

I have done a little digging and seen similar issues reported in these threads

The only way I have been able to create a component that is directly editable from the actor components list is to create the component in the constructor as a “default sub object” . using the “New Object” method appears to restrict it from being editable in this way, regardless of whether the object in question is marked as a UPROPERTY and has the appropriate specifiers.

Also, found this in FComponentEditorUtils::CanEditNativeComponent() , " // A native component can be edited if it is bound to a member variable and that variable is marked as visible in the editor
// Note: We aren’t concerned with whether the component is marked editable - the component itself is responsible for determining which of its properties are editable

^ It looks like you should only need ‘VisibleAnywhere’ , and not ‘EditAnywhere’ ?

The problem appears to be that FComponentEditorUtils::CanEditNativeComponent() checks the CDO for the class that the component belongs to and only returns true if the component matches a visible Uproperty of the same name in the CDO. This would explain why the objects I am creating aren’t considered editable, they aren’t created in the constructor, so they wouldn’t be in the CDO …?

The Blueprint viewport shows the object as it exists immediately after the constructor is called. BeginPlay and other events have not yet been called.
If you don’t create your components within the constructor, they are not native components, they are dynamically created components, and thus are not directly editable in the details panel.

What you can do is create spawning parameter properties within the native objects you have created that are then used during the preview mesh spawn, and those properties would be modifiable. As it stands right now, you should be able to add and remove members of the SpawnPoints array, as well as modify their internal properties.

Rather than trying to move the spawned preview mesh directly, you can create an FVector in your FAISpawnPoint struct that determines the spawn position of your preview mesh. You can set this property to display a transform widget, so your level designers can directly modify this value in the editor viewport by including “Meta = (MakeEditWidget = true)”.

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SpawnPoint", Meta = (MakeEditWidget = true))
FVector SpawnLocation; 

You already have a SpawnPreviewMesh value that should be editable (or you can have that value dynamically assigned by reading a modifiable version of your SpawnClass, or include a “PreviewMesh” property within the SpawnClass itself and forego the preview mesh property in your struct altogether).

The only trick left is to tell the editor to respawn your meshes if these properties are changed. You do that via PostEditChangeProperty.

void AYourSpawningActor::PostEditChangeProperty(struct FPropertyChangedEvent& e)
{
    if (e.Property && e.Property->GetFName() == GET_MEMBER_NAME_CHECKED(AYourSpawningActor, SpawnPoints)) {
        YourSpawningMethod();
    }
    Super::PostEditChangeProperty(e);
}

You can include any other property modifications you need to perform on these dynamic components in the SpawnPoint struct and programmatically apply them after they’ve been spawned. Since the spawning structs only exist as helpers you can empty the array and free their memory after BeginPlay (or at another appropriate time after they’re no longer needed) - so you don’t need to worry about redundant data storage.

Interesting work around! I wasn’t aware of the ‘MakeEditorWidget’ Meta data, that’s cool.