USceneComponent* goes null in editor

Here is a completely contrived example of the problem I am having…

  • I’ve got a completely empty c++ project.

  • I create a c++ USceneComponent component UFoo.

    • UFoo has a float UPROPERTY called myValue. It is EditAnywhere, BlueprintReadWrite. It’s default value is 1.0f.

  • I create a c++ class derived from UObject called UTweaker.

    • UTweaker has a UFoo* UPROPERTY called myTarget. It is EditAnywhere, BlueprintReadWrite.

    • UTweaker has a float UPROPERTY called currentSetpoint. It is EditAnywhere, BlueprintReadWrite. It’s default value is 10.0f.

    • UTweaker has a UFUNCTION called Tick(deltaTime). It is BlueprintCallable.

    • When the Tick(…) function is called it sets the myValue property of the UFoo instance it points to the currentSetpoint value.

  • I create a Blueprint class derived from GameModeBase called BP_MyGameMode.

    • I add the UFoo component to it.

    • I add a variable of type 'Tweaker Object Reference" and call it myTweaker. I make this public.

    • In BP_MyGameMode’s “Event BeginPlay” I construct a “Tweaker” instance and assign the result to myTweaker.

    • I then set myTweaker’s myTarget property to the UFoo component added a few steps ago. (again… this is done in the BeginPlay event.)

    • I then set myTweaker’s currentSetpoint property to 100.0f.

    • In BP_MyGameMode;s “Event Tick” I call tick on myTweaker.

  • I create a level.

    • I go to the world settings and I set the GameMode to BP_MyGameMode

  • I hit play.

    • In the world outliner I can see that a BP_MyGameMode instance has been created.

    • I can see that everything above connects correctly.

    • I can select the BP_MyGameMode instance in the world outliner, navigate in the details panel, see the UFoo instance now has a new myValue… pushed into it by the tweaker… of 100.0f. The value I set in 4.5 above. If I put a breakpoint in UTweaker’s tick function I see it getting hit every frame.

    • **(NOW HERE IS WHERE THINGS GO SIDEWAYS...)**
    • I select the UFoo component from the BP_MyGameMode’s component list.

    • I click on the myValue field and change it there in the details panel using mouse and keyboard. I set it to 12.

    • All of a sudden, the UTweaker instance can no longer set the myValue in the UFoo component. I mean… it should continuously be ‘pounding it’ to its currentSetpoint… 100.0f. What has happened?

After some debugging I can see that the UFoo* that the UTweaker instance is holding in ‘myTarget’ has “gone stale”. For a few frames it is pointing to a UFoo instance that is soon to be garbage collected, and then ultimately the garbage collector sets the UFoo* in the UTweaker instance to nullptr. So… the act of modifying the value of myValye in the details panel for the UFoo instance has caused another instance to be spawned, and the UFoo instance that the tweaker instance is pointing to gets deleted.

Uuuum. Clearly this is not the functionality I want or was expecting. How do I keep myTweaker’s myTarget property pointing to the “active” instance of the UFoo component on the BP_MyGameMode instance?

  • Thanks

Can you just post the code? To be honest it’ll make it a lot easier to figure out what’s going on if we can look at it. Explanation just makes it harder :stuck_out_tongue:

One thing to be aware of is that if an object has the same name (and I think same outer?) as another object, it’ll confuse the hell out of the garbage collector and cause the wrong one (or both?) objects to be destroyed if the other object is flagged for garbage collection.

UE4 spawns and deletes transient objects all the time when editing objects in the editor. Is it possible that when this transient object is being created and destroyed when you edit myValue, it’s creating its own instance of your tweaker with the same name, and is causing both to be garbage collected once it flags the transient editor object for deletion?

When you create components programmatically in Editor and don’t register the component and its package properly then things like that happens.

This is just a byproduct/limitation of UE4’s construction script behaviour. All components added via the Blueprint editor get recreated every time their actor’s construction script is run, and the construction script is run every time you edit a property on the actor (or one of its components) in a details panel. I think this really limits the potential for tools and testing involving realtime property changes during Simulate in Editor; I’ve brought up the idea of suppressing the construction script when PIE is live with an Epic dev, but I don’t expect it to happen.

You can see the same effect breaking even built in engine components, for example if you add a physics constraint component to a blueprint, then change a property on the actor at runtime, the constraint will break (at least until a couple of engine versions ago, I suspect still the case but haven’t tested). Some components deal with this by implementing GetComponentInstanceData, but this doesn’t help deal with other things maintaining a reference to the component, as in your case.

You can deal with this by adding a handler to the GEditor->OnObjectsReplaced delegates, that gets called in the case of component reinstancing and allows you to update your references. A much simpler solution though, if you can live with it, is to just create a C++ base for your actor class and add the component in question using CreateDefaultSubobject in the constructor. C++ components do not get reinstanced by the construction script.

2 Likes

This I don’t quite understand. In my case UFoo is a c++ component, but it is getting reinstanced.

That said, the rest of your description fits perfectly for what I am seeing. Your physics constraint issue description matches my case pretty closely.

Thanks

Sorry, wasn’t clear. I meant components that are added to actors in C++ (via CreateDefaultSubobject in the actor’s constructor). So if you are able to tie down all actor types that will want a UFoo at the C++ level and don’t need the flexibility of designers being able to add UFoo’s via the Blueprint Editor to arbitrary actor types, then that will get around this issue.

I suspect this behavior is related to the behavior the OP talks about:

  • Create a subclass of AActor (say AHotReloadTest), and give it a simple float UPROPERTY:


// Header:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Testing")
float SomePublicValue;

// Constructor:
SomePublicValue = 1.0f;


  • Drag this class in to the map to create an instance, and change *SomePublicValue *to 2.0f.
  • Create a Blueprint Class that subclasses AHotReloadTest.
  • Drag the blueprint in to the map and change *SomePublicValue *to 2.0f.
    *- *Save the map.
  • Edit the AHotReloadTest header to add/remove/change any UPROPERTY:


// Header:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Testing")
float SomePublicValue;

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Testing")
float SomeOtherPublicValue;


  • Build.

The blueprint instance in the level will have SomePublicValue reset back to 1.0f, while AHotReloadTest will have 2.0f.
If you re-load the map (do not save after the re-compile), the blueprint instance will have 2.0f and the AHotReloadTest instance will have 2.0f.

This does not seem like intended behavior. I would expect both instances to keep the values they had before the re-compile.