I have an array of UObject which are being set to null every time I restart the editor. The array is set as Instanced and the UCLASS has the EditInlineNew specifier - for example;
When I add a new instance of my object to the TArray, in the editor I get a dropdown where I can choose between None and ExampleObject. This always defaults to None and I must select the latter in order to access the settings it contains and tweak them.
After making my changes, compiling and saving and then restarting the editor the TArray still has the same number of elements - but they have all been defaulted to None.
Iāve been trying to Google-fu this for a while now as Iām sure Iām doing something wrong, but I canāt seem to figure out what.
on the TArray you can try adding SaveGame to the arguments in the UPROPERTY() macro.
EditInlineNew allows you to pick it from a drop down box in the property editor. it doesnāt do anything about writing to disk either in Editor, or at runtime.
None is null/nullptr, Epic decided that None is an easier concept then null (partly āthanks javaā, but also I can understand that is often how it is described in CS), and pointers that have not been initialized will have a value of null.
if these are not to be directly accessed by Blueprints (the UCLASS only cares about the details window, and the Array has not blueprints specifier) could your UExampleObject be a struct instead, you lose the ability to call functions on them from blueprints, but you can use intermediary functions in the containing class if you need. (you will probably still need SaveGame)
Thanks for the suggestion, sadly the results remain the same. I do want them to be UObjects so I can execute functions and do some stuff on tick (physics).
I could get away using Scene Components but I dislike working with them as they just get messy and I prefer iterating over an array of structs. Chaos does something similar for their movement component.
Edit:
It seems like BlueprintType on the array like you mentioned may have been the secret sauce. However, a visualizer I use to help edit the properties of those objects stops working when I add that specifier.
Iām not getting any errors or warnings, so looks like I have a fair bit of debugging ahead of me.
Edit 2:
Eh, I thought it worked. Seems like in some very rare occasions it does work. Iām all out of ideas, may just try and adapt and use scene components instead.
could you maybe use a TArray of structs that point to these UObjects.
my thinking is this:
TArray holds the structs, and one of the members is a pointer to the UObject(does it need to be supervised by Garbage Collection be able to be serialized/Saved) / ActorComponent(does it need to be in the Actors Hierarchy, and be able to GetOwner()) / SceneComponent(does it need a transform and to exist in space) these could even be other Actors (if you need)
in PostInitializeComponents() (or even PreInitializeComponents()) step though your array of Structs then instantiate the pointer at that time, and feed in the needed data if you need
when you go to iterate over the Structs get the pointer in the struct to the actual Object if you need to work with that, or the struct members
there would be intermediate functions on the container that end up calling functions on the structs, or retrieve the pointer/reference to the actual object if you wanted to return them for blueprints.
doing edits to a post does not send notifications to those that have bookmark or notifications for the thread
They do in fact need a position, so I guess using SceneComponent does make sense. I decided to try and go down the route of using a single ActorComponent to reduce the amount of clutter in the components tab of the blueprint. And instead relying on a visualizer to assist in giving feedback where they theoretically are located and move them around.
I think itās a matter of me overcomplicating things and not fully understanding how or if UObjects are being saved. I tried moving things around a bit, but the UObject is still defaulted to null upon editor restart.
So Iāll try and clean things up a bit and just deal with using scene components for now, should still be able to make a visualizer for them.
Hmm, well I am a bit confused. I played around with this a bit more after dinner and taking a walk to clear my head, it seems like the Constructor of my object is firing - but thatās not enough?
I added a custom method; Init() which initializes the same way the constructor did. Only difference now is that it does save.
For example;
UMyExampleObject::UMyExampleObject()
{
UE_LOG(LogTemp, Warning, TEXT("UMyExampleObject CONSTRUCTOR"));
StructA = FExampleStructA();
StructB = FExampleStructB();
StructC = FExampleStructC();
bMyVariable = true;
// This is executed, but the object is still nulled
}
void UMyExampleObject::Init()
{
UE_LOG(LogTemp, Warning, TEXT("UMyExampleObject INIT"));
StructA = FExampleStructA();
StructB = FExampleStructB();
StructC = FExampleStructC();
bMyVariable = true;
// This is executed, but the specified settings and the object itself is stored
}
I then trigger this method via my ActorComponent constructor;
if (MyObjectArray.Num() > 0)
{
for (auto AnObject : MyObjectArray)
{
AnObject->Init();
}
}
Am I missing something obvious here?
Edit:
Another issue I narrowed down is if I modify the structs (in this case a FVector member), the object will always default to null when the editor is restarted.
To clarify, my UObject has a struct which contains a FVector, used to determine the location of the object relative to the owner. I edit this via my visualizer;
the constructor is only called the first time the instance is instantiated (so when you pull it out from the content drawer into the level, or when you call CreateDefaultSubObject<T>() or SpawnActor() these invoke the constructor.
for things that are supposed to happen when the World comes into existence, or just before regardless of PIE, or Packaged. use PostInitializeComponents() (keep in mind that because this is fired both on the Editor loading, PIE loading, and the Packaged game running, anything requiring the UWorld might crash the engine/Editor, and try not to spawn/create here as it can also cause crashes)
if the thing is supposed to be changed in the editor then you want to use
Interesting, though Iām not sure how I should utilize PostEditChangeProperty for my use case. Should I be using that in MyActorComponent or inside MyExampleObject?
I assumed that was what NotifyPropertyModified was for and I could swear it was working before I started using UObjectā¦
I can use my visualizer to move the āobjectā around, the transform is updated and so is the property in the details panel. I compile, save and restart the editor and itās nulled.
So it seems like me trying to modify the object via HandleInputDelta breaks it somehow.
I found this thread which I think may be relevant;
But I have no idea how to implement this, that method is also deprecated in favour of one that uses FObjectPreSaveContext - little to no documentation other than trying to look through engine source.
Thanks for letting me know, that is inconvenient. I saw this post a while ago but I know little more than I did in my PostEditChange topic. If I remember correctly, that post I made was specifically because I was writing data from an external editor as a form of automation. This is not something you normally do. I was not willing to set all properties manually because I had updated data available in CSV format, so I needed to inject the data.
Can you describe in short what specific goal you need to achieve in the editor? Because there might be other ways to get there. Injecting your own logic during editor events / editing / saving has been a hacky process for me which I like to avoid.
The constructor is only meant to set up the class itself. Any values set there will be the default values to the editor and to blueprints. The constructor in c++ must not be confused with the blueprint construction script, which is not an actual constructor and has very different behavior. Even in the blueprint construction script some things are not available, like certain debugging features.
Other than that, you are working with UObject* pointers. These are of the āhard pointerā types, meaning that the object they point to must exist. When this object is deleted or not referenced as UPROPERTY in unreal engine the hard pointer will automatically be nulled.
When you work in the editor, there are multiple objects for what you think is the same thing, but is not. There are instances on the editor level, instances copied from the level to PIE, and instances for the asset editor per asset. Here I lack accurate information / documentation (which I would love to have), but I think you are editing a temporary object which is trashed, or you are setting pointers to objects which stop to exist. I hope to be of more help if I see what exactly you try to achieve.
When you want to modify a class and save it as a default, you want to be saving for what is called the CDO (Class default object). This serves as a template for all new instances. This is what happens when you edit a class through the editor windows the normal way, but misbehaved when I attempted to inject data in my post you linked.
In my experience, if you can do something with components then do it. Place an actor on the map in the editor, attempt to avoid doing things with it in the blueprint constructor (try to do everything after BeginPlay).
In the editor you can add components to an actor and in the level you can edit the instance to move the components around. Then during BeginPlay:
Doing so makes your work 100% compatible with the implementation of UE and you can skip storing pointers. The only pointer that is mostly save to be set in the editor is of the soft type, when you want to load an object yourself. The soft type acts more like a path which you can load on demand, providing a newly created object and a new hard pointer. the use case for that is when you want to provide a āSpawnActorClassā in the above example without having an instance of that class / actor and without having it loaded in memory already.
Please correct me if Iām wrong. Iām not entirely sure about this. When you create a hard pointer in blueprints the entire class is loaded into memory even if you donāt access the property. During the unreal editor splash screen (initial engine and module startup) and apparently just when running Visual Studio a lot seems to be loaded and running already.
Thank you so much for the great reply! Instead of using pesudocode, Iāll try and explain black and white what Iām trying to do.
Iām working on refactoring a suspension system for tanks, in the older version it relies on using a lot of scene components and while it works it becomes an absolute chore to set anything up since you canāt work with categories and having empty SceneComponent as a means of having categories seems counter-intuitive at best.
So my idea was to instead have 1 ActorComponent where you define each suspension via an array and since those will be based on a UObject they can have their own methods and whatnot. That way instead of relying on like 16 scene components all ticking individually, you have one actor component that ticks and loops through an array to do all the physics calculation (from what I can tell this is how the wheeled chaos vehicle works when setting up wheels, except their setup is 100 times more complicated).
Since those are UObjects, I visualize their location via a Visualizer module - drawing them as points and whatnot.
This all works exactly as I had intended it to except for the fact that once I edit the FVector via the widget and as such update the location via C++ - they break. If I instead alter the position of the spring via the vector entry in the detail panel - it saves as expected.
So thereās definitely some weird discrepancy here that I am unaware of, so much like @Roy_Wierer.Seda145 says - me being confused about the fact there being different versions of the same object perhaps. And to my knowledge there is 0 documentation for this aside from the source code which I have tried to go through using the SplineComponent + SplineComponentVisualizer as a basis. But I am still none the wiser.
They seem to use some push model, marking properties as dirty but as far as I can tell - thatās for networking.
Hereās a screenshot that may explain it better than a thousand words;
Also, thank you so much for taking the time to help me @Roy_Wierer.Seda145 and @gardian206, if it werenāt for you two Iād have given up and lost all my hair some time ago
ha, you canāt? I actually had this experience with structs where nested categories would not show up or even cause properties to go invisible on a datatable editor. In short, if you need an object to just hold data, use a struct. A struct (being a class) can technically hold methods but this does not work properly with blueprints, in that case use UObject. When you need to add modules of logic to an actor, use components. There is no case in which this is not true, even though it might look inconvenient because of UEās editor.
Consider it similar to sockets on a skeleton. While this seems overkill (āwe only need a position and an ID hereā), consider there are 999 things you get in this engine with UObjects and other types you donāt need but getting anyway. Performance wonāt be affected.
Edit Donāt underestimate the power of sockets btw if you see a use for them on the skeleton itself. You can retrieve socket transforms by name of the socket. If you are building an actor with a mesh (or building an actor containing such actors) consider giving the spring skeletal mesh sockets so you can get your spring info directly from the mesh.
You can disable tick per component and only update them when you want to. Make your own āUpdate()ā method called by the 1 ActorComponent. Tick itself is not a concern for performance at all until you actually run into a measured performance hit. Right now keep it simple, optimize another day when itās working and ready.
I have had the exact same experience in a project. I wanted to alter position of an object in the editor window, after changing a property. After user input some values would have to recalculate to find a position on a spline. I saw properties update visually but the new data would not be processed at all. I realized that what I was attempting to do was more of an āeditor utilityā (not proper game code) that didnāt play nice with the editor implementation and should be removed. I rewrote my code to work on BeginPlay and quit attempting to hijack the editor. I then found my solution at the cost of less editor visualization before PIE.
I think you refer to āUObject::Modifyā which causes the āstarā icon to appear on your asset to mark that it can be saved, but it doesnāt seem to do anything. I did that in the post you linked initially.
I think you should make a copy of your project (backup) and go the component route, where a SceneComponent its position replaces your āSpringLocationā of that struct, so you can actually move it around in the editor. I think you can keep the rest of the struct as is, just put it on the component. This component will be your āSpringComponentā.
Components are not all bad, if you can look past the editor windows themselves. It would decouple the spring logic as well. One day you make a vehicle with no springs and have the 1 ActorComponent stuck with spring logic if you donāt decouple this.
If you really donāt want to go the component route, keep the logic and struct on your 1 ActorComponent but replace your āSpringLocationā property with a reference or ID to get a matching scenecomponent on your actor which does nothing else but exist as a position marker.
for notification: Edited last reply to provide a possible solution where included spring meshes contain sockets to provide your spring position / transform info.
It does work in the variables menu as far as Iām aware, but in the actual components outliner - no. Youād have to use an empty scene component and rename it something. It messes with this hierarchy and is just a pain in the rear to navigate.
In my case it very much is an editor utility since it uses visualizer components which is a editor-specific module. I donāt know if this truly is counter to how Epic wants you to use Unreal, but it seems like a massive missed opportunity as these visualizers are not only easy to work with but immensely powerful.
The whole idea is for the setup to be convenient, with most important information available to you directly in the viewport as opposed to having to navigate menus back and forth.
It does work, just the saving doesnāt. And I donāt know if thatās by design or if Iām missing something - probably the former at this point which would make me quite sad.
Negative, I know about Modify, what Iām referring to is a macro;
At first I thought this was the secret sauce, but it seems to be network related unless Iām mistaken.
Iām considering it, I did try making my own scene component with the values I need but even here the results were the same. If I were to try and alter it via C++ when moving it using the widget - it doesnāt save, gets corrupted(?) and nulled on restart.
At this point I might just look at whatās necessary for a scene component to work at the barest minimum and just use that as a way of having categoriesā¦
I know Iām working against the engine here, but I really wanna know if this is possible. It would unlock a lot of cool stuff you could do with visualizers.
Hope someone might give us both more information / documentation on this, now the method I was using is also deprecated. Itās hard to tell what is by design. For example, I was running some code which was basically an editor utility but written on the construction script and visual debugging was failing because it depended on a player and HUD being present (obviously not present before PIE). This was one of multiple debuggers, all with their flaws. Sometimes the design is not clear at all.
Not familiar with this. I checked the Network Compendium which shows nothing about it.
I suppose setting the relative position of the SceneComponent itself through c++, being a property, then also does not give the results. Wish I had the fix . I donāt really remember what I discovered back then but on my old post I said this:
If you are lucky to work with an editor extension (detail panels etc.) you can make use of property handles and property nodes, but these are otherwise inaccessible (strange indeed, as theyād make automation really easy). Setting values on a preview object during PreSave seems to write them permanently to the CDO as well.
This will likely have been at UE4.2.7 where I hoped to simulate āa user editing valuesā for this exact reason. Apparently I thought there was a way through detail panels (Link, Link_2) if working with an editor extension for what is basically your editor util code. The idea being if you modify the editor panel instead of the object property, the panel will take care of the process the natural way. Itās an idea, but Iām running out of ideas .
Well Iām fairly certain that NotifyPropertyModified is the secret sauce and should be what you need, but it doesnāt seem like it works on arrays of uobjects (or at least, on the structs inside those uobjects). I looked at the implementation and it contains all the property propagation;
Collapsed, lots of code
void FComponentVisualizer::NotifyPropertyModified(UActorComponent* Component, FProperty* Property, EPropertyChangeType::Type PropertyChangeType)
{
TArray<FProperty*> Properties;
Properties.Add(Property);
NotifyPropertiesModified(Component, Properties, PropertyChangeType);
}
void FComponentVisualizer::NotifyPropertiesModified(UActorComponent* Component, const TArray<FProperty*>& Properties, EPropertyChangeType::Type PropertyChangeType)
{
if (Component == nullptr)
{
return;
}
for (FProperty* Property : Properties)
{
FPropertyChangedEvent PropertyChangedEvent(Property, PropertyChangeType);
Component->PostEditChangeProperty(PropertyChangedEvent);
}
AActor* Owner = Component->GetOwner();
if (Owner && FActorEditorUtils::IsAPreviewOrInactiveActor(Owner))
{
// The component belongs to the preview actor in the BP editor, so we need to propagate the property change to the archetype.
// Before this, we exploit the fact that the archetype and the preview actor have the old and new value of the property, respectively.
// So we can go through all archetype instances, and if they hold the (old) archetype value, update it to the new value.
// Get archetype
UActorComponent* Archetype = Cast<UActorComponent>(Component->GetArchetype());
check(Archetype);
// Get all archetype instances (the preview actor passed in should be amongst them)
TArray<UObject*> ArchetypeInstances;
Archetype->GetArchetypeInstances(ArchetypeInstances);
check(ArchetypeInstances.Contains(Component));
// Identify which of the modified properties are at their default values in the archetype instances,
// and thus need the new value to be propagated to them
struct FInstanceDefaultProperties
{
UActorComponent* ArchetypeInstance;
TArray<FProperty*, TInlineAllocator<8>> Properties;
};
TArray<FInstanceDefaultProperties> InstanceDefaultProperties;
InstanceDefaultProperties.Reserve(ArchetypeInstances.Num());
for (UObject* ArchetypeInstance : ArchetypeInstances)
{
UActorComponent* InstanceComp = Cast<UActorComponent>(ArchetypeInstance);
if (InstanceComp != Component)
{
FInstanceDefaultProperties Entry;
for (FProperty* Property : Properties)
{
uint8* ArchetypePtr = Property->ContainerPtrToValuePtr<uint8>(Archetype);
uint8* InstancePtr = Property->ContainerPtrToValuePtr<uint8>(InstanceComp);
if (Property->Identical(ArchetypePtr, InstancePtr))
{
Entry.Properties.Add(Property);
}
}
if (Entry.Properties.Num() > 0)
{
Entry.ArchetypeInstance = InstanceComp;
InstanceDefaultProperties.Add(MoveTemp(Entry));
}
}
}
// Propagate all modified properties to the archetype
Archetype->SetFlags(RF_Transactional);
Archetype->Modify();
if (Archetype->GetOwner())
{
Archetype->GetOwner()->Modify();
}
for (FProperty* Property : Properties)
{
uint8* ArchetypePtr = Property->ContainerPtrToValuePtr<uint8>(Archetype);
uint8* PreviewPtr = Property->ContainerPtrToValuePtr<uint8>(Component);
Property->CopyCompleteValue(ArchetypePtr, PreviewPtr);
FPropertyChangedEvent PropertyChangedEvent(Property);
Archetype->PostEditChangeProperty(PropertyChangedEvent);
}
// Apply changes to each archetype instance
for (const auto& Instance : InstanceDefaultProperties)
{
Instance.ArchetypeInstance->SetFlags(RF_Transactional);
Instance.ArchetypeInstance->Modify();
AActor* InstanceOwner = Instance.ArchetypeInstance->GetOwner();
if (InstanceOwner)
{
InstanceOwner->Modify();
}
for (FProperty* Property : Instance.Properties)
{
uint8* InstancePtr = Property->ContainerPtrToValuePtr<uint8>(Instance.ArchetypeInstance);
uint8* PreviewPtr = Property->ContainerPtrToValuePtr<uint8>(Component);
Property->CopyCompleteValue(InstancePtr, PreviewPtr);
FPropertyChangedEvent PropertyChangedEvent(Property);
Instance.ArchetypeInstance->PostEditChangeProperty(PropertyChangedEvent);
}
// Rerun construction script on instance
if (InstanceOwner)
{
InstanceOwner->PostEditMove(PropertyChangeType == EPropertyChangeType::ValueSet);
}
}
}
// Rerun construction script on preview actor
if (Owner)
{
Owner->PostEditMove(PropertyChangeType == EPropertyChangeType::ValueSet);
}
}
So I either need to rethink my strategy here or scavenge those methods and build my own propagation which seems like more work than itād be worth.
For now, unless someone chimes in with their brilliance to help a lost coder in need - Iāll heed @Roy_Wierer.Seda145ās advice and make a backup for this and start over with a clean slate - trying with structs and if that fails; simply use scene components.