x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Refresh / destroy an Actor's Components in c++ changing a property in the engine

Hello, I'm trying to refresh an actor when I change a property in the engine. The actor owns a property ("Spawning Rooms") telling how many rooms it should spawn. Through PostEditChangeProperty I can refresh the actor, regenerating the rooms. To do so, I call a DestroyComponent() to the pointer to the previously saved SceneComponents, and then regenerate the whole dungeon.

Doing so, the rooms initially created (and only them) disappear forever, while the rooms generated later appear and disappear correctly.

I have a main actor "Floor" and a SceneComponent "Room".

Floor.h

 #include "GameFramework/Actor.h"
 #include "YARGRoom.h"
 #include "YARGFloor.generated.h"
 
 UCLASS()
 class YARGPROTO_API AYARGFloor : public AActor
 {
     GENERATED_BODY()
     
 public:    
     AYARGFloor(const class FObjectInitializer& ObjectInitializer);
 
     virtual void BeginPlay() override;
     
     virtual void Tick( float DeltaSeconds ) override;
 
     virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
 
     virtual void GenerateFloor();
     virtual void CleanAndGenerate();
     
 protected:
 
     TArray<UYARGRoom*> RoomsArray;
 
     UPROPERTY(EditAnywhere, Category="Floor Options")
     uint32 TileSize;
     UPROPERTY(EditAnywhere, Category = "Floor Options")
     uint32 WorldSize;
     UPROPERTY(EditAnywhere, Category = "Floor Options")
     uint32 MinRoomSide;
     UPROPERTY(EditAnywhere, Category = "Floor Options")
     uint32 MaxRoomSide;
     UPROPERTY(EditAnywhere, Category = "Floor Options")
     uint32 SpawningRooms;
     UPROPERTY(EditAnywhere, Category = "Floor Options")
     uint32 SpawningEllipseWidth;
     UPROPERTY(EditAnywhere, Category = "Floor Options")
     uint32 SpawningEllipseHeight;
 
 private:
 
 };

Floor.cpp

 #include "YARGProto.h"
 #include "YARGFloor.h"
 #include "YARGMath.h"
 
 
 AYARGFloor::AYARGFloor(const class FObjectInitializer& ObjectInitializer)
     : Super(ObjectInitializer)
     , TileSize(100)
     , WorldSize(10000)
     , MinRoomSide(2)
     , MaxRoomSide(5)
     , SpawningRooms(5)
     , SpawningEllipseWidth(5000)
     , SpawningEllipseHeight(5000)
 {
     GenerateFloor();
     PrimaryActorTick.bCanEverTick = true;
 }
 
 // Called when the game starts or when spawned
 void AYARGFloor::BeginPlay()
 {
     Super::BeginPlay();
 }
 
 void AYARGFloor::Tick( float DeltaTime )
 {
     Super::Tick( DeltaTime );
 }
 
 void AYARGFloor::PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent)
 {
     CleanAndGenerate();
 
     Super::PostEditChangeProperty(PropertyChangedEvent);
 }
 
 void AYARGFloor::CleanAndGenerate()
 {
     for (UYARGRoom* pRoom : RoomsArray)
     {
         pRoom->DestroyComponent();
     }
     RoomsArray.Empty();
     GenerateFloor();
 }
 
 void AYARGFloor::GenerateFloor()
 {
     RoomsArray.Init(SpawningRooms);
 
     for (uint32 uiRoomIndex = 0; uiRoomIndex < SpawningRooms; ++uiRoomIndex)
     {
         UYARGRoom* NewRoom = NewObject<UYARGRoom>(this, *FString::Printf(TEXT("Room %u"), uiRoomIndex));
         RoomsArray[uiRoomIndex] = NewRoom;
         NewRoom->AttachTo(RootComponent);
     }
 }

The SceneComponent Room has nothing specific.

Initial situation:
alt text
I correctly see all the 5 default rooms.

From there, if I set more than 5 rooms:
alt text

while if I set less than 5 rooms:
alt text

Note: if I set 6 , 8 and again 6, i see 1, 3 and 1 rooms, so it doesn't seems a name conflict.

Any help is appreciated, thanks in advance.

Product Version: UE 4.10
Tags:
morethan5.png (36.0 kB)
lessthan5.png (33.2 kB)
more ▼

asked Jan 13 '16 at 11:33 PM in C++ Programming

avatar image

Mewster
35 4 8 11

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

1 answer: sort voted first

Hi Mewster. Dynamically creating components in C++ is a bit more involved than just creating the UObject and attaching it. You have to call a few other procedures as well. Here's an example snippet from some code that I use to dynamically create static mesh components, along with a separate hit box component for each, for something like a chess board Actor:

             const FTransform LocalTransform(
                 FRotator::ZeroRotator,
                 Position + FVector(0.f, 0.f, VisualHeight),
                 FVector(1.f, 1.f, 1.f));
             const FTransform SpawnedTransform = LocalTransform * GetTransform();
             auto* NewMeshComponent = NewObject<UStaticMeshComponent>(this);
             NewMeshComponent->CreationMethod = EComponentCreationMethod::Native;
             if (TileMesh) NewMeshComponent->SetStaticMesh(TileMesh);
             NewMeshComponent->OnComponentCreated();
             NewMeshComponent->AttachTo(RootComponent);
             NewMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
             NewMeshComponent->bGenerateOverlapEvents = false;
             NewMeshComponent->SetRelativeTransform(TileMeshLocalTransform * LocalTransform);
             NewMeshComponent->SetMobility(EComponentMobility::Static);
             NewMeshComponent->RegisterComponent();
             auto* NewBoxComponent = NewObject<UBoxComponent>(this);
             NewBoxComponent->CreationMethod = EComponentCreationMethod::Native;
             NewBoxComponent->SetBoxExtent(TileHitBoxExtents);
             NewBoxComponent->OnComponentCreated();
             NewBoxComponent->AttachTo(RootComponent);
             NewBoxComponent->SetRelativeTransform(TileHitBoxLocalTransform * LocalTransform);
             NewBoxComponent->SetCollisionObjectType(COLLISION_INTERACTION);
             NewBoxComponent->bGenerateOverlapEvents = false;
             NewBoxComponent->SetMobility(EComponentMobility::Static);
             NewBoxComponent->RegisterComponent();
more ▼

answered Jan 14 '16 at 03:24 AM

avatar image

cancel
2.4k 101 72 124

avatar image Mewster Jan 14 '16 at 07:35 AM

I can see some methods I didn't call on my snip. Anyway, I followed the code on https://docs.unrealengine.com/latest/INT/Programming/Tutorials/Components/1/index.html so I thought that was all I needed. What about the destruction of the component? And in your case, do your components get a name in the editor? Is there some difference in creating the components in the costructor of the actor, or there is a better time for that?

avatar image cancel Jan 15 '16 at 02:52 AM

It's a bit tricky. If you're creating the component in the constructor/initializer for your Actor, you don't have to call as many procedures -- the engine will do it for you when the Actor is spawned. However, components created in the constructor are limited to so-called "default subobjects": the components are considered an important part of the actor, will show up for editing in the Blueprint editor/defaults editor, and you can only use default values to determine what happens. Basically, you can only read from the default values of your class in a constructor, so you are pretty limited in terms of procedural generation for what you can do with default subobjects. Plus, there is usually a fixed number of default subobjects in a class created in a constructor -- you'd rarely create an array of them, especially one of arbitrary size determined by some other data (I'm pretty sure something bad will happen if you try this).

However, you're free to create components elsewhere in your Actor, such as when parameters are modified, provided you do the correct things for your component's lifecycle. This means calling a few more procedures manually and setting some data.

As for destruction, the regular DestroyComponent will work just fine -- it handles everything for you.

avatar image Mewster Jan 15 '16 at 08:00 AM

So, if I'd want to create a component in a constructor, provided that it won't change later, what should be done?

I made a few changes: put the GenerateRoom() in the BeginPlay(), used both Attach and Register, and the ConstructObject to generate the Rooms. This way the Floor behaves correctly . except when in the editor's viewport because it shows nothing, and only after a PostEditChangedProperty it spawns the correct number of subcomponents. I saw that BeginPlay doesn't get called in the editor; is there an equivalent method?

I thought that every component should have a name provided, but I noticed yours haven't. Is there a difference?

avatar image cancel Jan 15 '16 at 10:20 AM

Try overriding OnConstruction and PostEditPropertyChange (can't remember if that's the right name or not).

If you don't provide a name to a component, one will be generated for you. Explicit names are only required for default subobjects.

(comments are locked)
10|2000 characters needed characters left
Viewable by all users
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question