How do you save dynamically created UObjects on ActorComponents from C++ in Blueprints.

I promise you, this is not an easy question. I will preface this with I almost have this working. I have built a trait system where each trait is a UObject. Rather than Define Each trait as a variable, I want to have them stored in an array that is editable on a Trait Manager Component. That way I can add and remove traits dynamically. Programatically no problem, I build this in C++ and it’s easy. The problem comes from a designer perspective with no C++ background. I want the Designer to open a character blueprint, Add or Remove Traits, and then See the outcome of that change reflected in the characters more volatile traits such as Health, stamina, Damage.

Attempt 1:
Initially I thought I could do “EditInlineNew” plus “Instanced” on the Array. This sort of works. There are two problems with this. The first is any C++ defined Trait always appears in the drop down list and is created with out a problem. Any Blueprint however, must first be loaded by the Editor, saved and recompiled before it shows in the drop down.

The Second problem is so much as making a single tweak to the values of that object as editing the whole thing and tweaks and changes do not get reflected for default values later.

Attempt 2:
The first time I tried to do this I build a system that worked decent enough. But it required that I have a special object I called “Trait Builder” that had all the traits and values for the character on it. The Designer worked directly with it. The problem is this object needed to exist when the game ran, then initialize the properties of the character on startup. The biggest issue came when developing the Trait Calculator that takes the current list of traits and derrives various things such as Health, Stamina, and Damage. I effectively needed to keep a separate calculation for showing the designer what the traits would be and actually calcualting the traits in game. Kind of an upkeep problem when you change the calculation of something you have to change it in two places.

Attempt 3:
A best of both worlds approach. I used an Array of structs that existed on the Trait Manager(Component the array of traits is kept/managed), This acts as a replacement to my Trait Builder plus it only exists in the editor (using #if WITH_Editor). Using PostEditChangeChainProperty I populate the Uobject array based on what is in the struct array. This was great because it kept my character “light” because when the game ships no extra objects would be attached to it, it also allowed a designer to work with the struct array to set trait properties, and it had a class object which meant I could change the trait type for any trait at any time. At first this appeared to work beautifully as I could add any number of traits and they would populate the array as desired.

Then would compile, and everything would be blown away. Epic fail (pun).

I would love to have Attempt 3 working. It is almost exactly what I want I seem to be missing some crutial step that registers the newly created object to the blueprint for saving. Please Help.

Wow when I asked a tough question I really ask a touch question it seems. Can anyone help? Amazing people in the community? Awesome Dudes at Epic? Anyone? This would be amazing to know.

I must say I’m having some trouble understanding the whole architecture of your setup. Is this (roughly) what it looks like?



UTrait : UObject
{
}

ACharacter : AActor
{
	class UTraitManager* TraitManagerComponent;

	TArray<class UTrait*> CharacterTraits;
}

UTraitManager : UActorComponent
{
	void PostEditChangeChainProperty()
	{
		// update owning character's UTrait array
	}

	TArray<struct TraitStruct*> Traits; // that map to UObject classes?
}


(With the relevant UCLASS/UENUM/UPROPERTY macros of course)

Firstly, I’m guessing there’s a reason you’re using a UObject based approach? I don’t know what your traits do exactly, but my own approach would be something a bit more simple, for instance



enum ETrait
{
	Outgoing,
	Alcoholic,
	AllergicToDairy,
	LikesNickelback
}

ACharacter : AActor
{
	void DoSuff()
	{
		// take some action depending on the character's traits
	}

	ETrait Traits; // Can combine traits by using the enum as a bitmask
}


Since 4.12 you can use this method in both C++ and blueprints!

This makes your actors much more lightweight, since you don’t need a component and an array of UObjects for each character. If you need more complex trait functionality, you can create a (non-UObject) class and delegate functionality to that, which the actors can then use.

Anyway, that’s just a suggestion. If you do have a reason to stick with UObjects for the traits, here are some blind guesses as to what’s happening (or not):

  • Are you constructing your traits with NewObject()? You may also want to try NewObject(FName) with a unique name, in case it is the garbage collector interfering.
  • Are you calling PreEditChange() and PostEditChange() on the owning actor when updating the array?
  • Are you calling Super::PostEditChangeProperty() / Super::PostEditChangeChainProperty() if you have overriden those methods?
  • Did you forget to mark anything as a UPROPERTY() that should be?

Honestly it’s a bit hard to figure out what might be going wrong without seeing the code. If you could post it (or the relevant parts) that would be helpful.

Method 1 should work and seems the best approach to me (assuming, as Mattiwatti says, that you do indeed need the flexibility of each trait being a UObject).

The first problem that you mention with it is an issue, but it’s expected behaviour. The dropdown can only show all compatible UClasses in memory. UClasses for C++ classes will always be in memory so long as the module is loaded, but those for blueprints are only in memory if they’ve been explicitly loaded, or generated as a result of compiling the blueprint. I did a workaround for this myself at one point, but it’s a fair bit of work and not ideal - from what I remember, the way asset data is stored meant that the only solution was to force load a bunch of stuff in advance. I might be able to dig some of the code out if you’re interested. It was actually something I was thinking of posting a wiki on but I haven’t had chance to get around to it.

Your second problem with method 1 I didn’t understand.

Method 3 I don’t fully follow, but it sounds like you’re essentially duplicating the editable data across both structs and UObjects. If you genuinely need UObjects for this, then different traits will have different properties, and keeping multiple structs/UObjects in sync as you develop would likely become a major hassle.

Thanks for the response, sorry I have not gotten back to you. Unfortunatly I must stick with UObjects. Traits need to be subclassed and replicatable. While you can subclass structs it is only possible through C++, and unfortunately enums are not flexible enough. I’ve implemented a lot of safty logic within them to reduce bugs and tampering while allowing the value of a trait to fluctuate greatly based upon equipment and any magic that may be effecting the character. I’ll Try to better explain what is going on.

First I have a UTrait object that contains some very basic information about a given trait such as it’s name. This UTrait object is actually Derrived from a UNetworkObject that allows the UTrait object to be replicated over the network (along with any subobjects which the UTrait does not have).

The UTrait is then subclassed to UNumericTrait. The difference between these two classes is in the name. UNumericTrait Handles strictly traits that have numeric values, traits that can be modified or simi-permanently damaged (they can be restored back to their maximum value). Between UTrait and UNumericTrait these two classes form the foundations of all other traits. Not all of them are used in the same “system” for instance Attributes and Perks.

So UNumericTrait is then subclassed into UAttributeTrait and some special logic goes into the behavior of Attributes such as how they are increased and leveled. Attributes are then contained within a UAttributeComponent that keeps an Array of the UAttributeTrait objects and is charged with allowing access to them and their replication to the client.

Finally the UAttributeComponent is placed on a Character. Like I said from a code perspective this all works. But I’m thinking about Designers and making this modular to use in other games of mine. I want to dynamically add and define Attributes at the UAttributeComponent level. And yes I could hard code the attributes and other traits but their still stands another issue with my Items. You see my Items use a similar system to trigger their “effects”. Effects are like DamageTypes but with a bit more logic, Items have various effects contained within an EffectInstance. An Effect is responsible for Handeling What the Effect Does while the EffectInstance is responsible for how powerful the effect is. My Items are also UObject derrived and they have a list of EffectInstances that contain a single Effect each.

The Character’s AnimInstance Seems to do this kind of behavior. You pick the AnimInstance Class and it creates a new AnimInstance and initializes it. I tried looking at how that works but I cannot find anything special about it.

So the Idea in Attempt 3 is to have an Array of these Structs, they are the exact same structs I use to save to the disk and load from the disk. They are a form of Memento object that holds the current values of the UNumericTrait Objects. They also contain the important fields for the type of Class to build and Value of the Attributes/Traits. My Idea was that a Designer can Open up the Character, click on the AttributeComponent and Add or Remove Attributes or change the attribute’s values directly from the editor. This sort of worked. I was able to open the character, click the attribute component, and add to the array. The On Edit Change would occur, and Create a new UAttributeTrait Object based on the struct and you could see it clearly in the editor. Then I would compile and save and it would all vanish (except for the struct). The reason this was Ideal was because the Struct Array was Wrapped “#if With_Editor” meaning that once the game was built or shipped the Struct Array would be compiled out.

To answer questions:
I am most definatly using NewObject
I am most definatly calling the Super::PostEditChangeProperty() / Super::PostEditChangeChainProperty()
Everything that should be UPROPERY is UPROPERTY
I am not explicitly calling PreEditChange() and PostEditChange() on the owning actor when changing the array… Not sure how or when I would do that but it seems promising! Because Everything actually appears as it should until I recompile.

Psudo Code:


USTRUCT()
struct FTraitData
{
// BAsic Struct initialization code here

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="TraitData")
TSublcassOf<UTrait> TraitClass;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="TraitData")
FText DisplayName;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="TraitData")
int32 Value;
};

UCLASS()
class UAttribute: UObject
{
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Attribute")
FText DisplayName;

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Attribute")
int32 Value;

void InitializeData(const FTrait & data)
{
this->DisplayName = data.DispalyName;
this->Value = data.Value;
}
};

UCLASS()
class UAttributeComponent : UActorComponent
{

#if WITH_EDITOR
UPROPERTY(EditDefaultsOnly,BlueprintReadWrite, Category="Attributes")
TArray<FTraitData> attributes;
#endif

UPROEPRTY(EditAnywhere, BlueprintReadWrite, ReplicatedUsing="OnRep_Traits", Category="Attributes")
TArray<UTraits*> traits;

#if WITH_EDITOR

virtual void PostEditChangeProperty(struct FPropertyChangedEvent & e) override
{
this->UpdateAttributes();
Super::PostEditChangeProperty(e);
}

virtual void PostEditChangeChainProperty(struct FPropertyChangedChainEvent & e) override
{
this->UpdateAttributes();
Super::PostEditChangeChainProperty(e);
}

void UpdateAttributes()
{
    this->traits.Empty();
    for(int32 i = 0; i < this->attributes.Num(); ++i)
    {
        UAttributeTrait * attribute = NewObject<UAttributeTrait>(this->attributes*.TraitClass, this->GetOwner());
        if(attribute != nullptr)
        {
            attribute->SetData(this->attributes*);

//Do I need to do Owner->PreEditChange() and Owner->PostEditChange() Here?
            this->traits.Add(attribute);
        }
    }
}

#endif

};

@Kamrann
In Method 1 The first approach works decently minus the loading issue. The second problem is this:
Lets say I create a trait UStrength and add it to my array of traits. By default UStrength has a name of “Str” and a value of 1. I then change the UStrength object in the Array to have a value of “100”. This is registering to the class as a change of the entire object. So not lets say I go back to my UStrength object and change it’s name to “Strength”. That value does not get updated in the Array.

If your traits are sub-classed from a UObjects it cannot be replicated by default, since UObject doesn’t have replication built in, unless you built your own subclassed uboject that supports replication by overriding the virtual replication function called IsSupportedForNetworking. Also, i do not believe it has object wide replication, like replicating the entire uobject, i believe you can only replicate members from uobjects. Where as AActor is the first class in the hierarchy that default supports replication, and supports replication object wide.

As for the loading issue, surely you essentially have the same thing with the struct solution? If the designer has to choose a trait class from a dropdown for the TSublcassOf<UTrait> TraitClass member of your struct, presumably this dropdown will also only show blueprint trait classes if they’ve been previously loaded?

I’m guessing you have added your component to the actor inside a blueprint, rather than as a default component in C++? This means it will be recreated as part of the construction script process, which is triggered when any property on the actor changes, even properties on the component in question. The internal code responsible tries to record all component properties that differ from defaults before recreating the component and then reassigning the property values. It’s super complex though and it wouldn’t surprise me if there were issues with this relating to instanced objects in components. In fact now I think of it, I think you may need to override UActorComponent::GetComponentInstanceData() to deal with stuff like this. That’s getting pretty deep though - first I’d try just making an actor class that creates your component as a default subobject in its constructor, then deriving a blueprint from it and seeing if that fixes the issue. If so, you’ll probably need to implement the above function if you want to be able to add the component at the blueprint level.

When you say ‘go back to my UStrength object’, are you talking about modifying the default value (either in C++ constructor or blueprint defaults)? From what I understand about how UE4 properties work, I’d have thought it would get updated since I was under the impression it just gets stored as ‘same as default’ rather than an explicit value. After making the change, does the little ‘reset to default’ arrow show up next to the property, and if so does clicking that update it to the new default? What about if you just reload the blueprint or restart the editor? Maybe it just won’t update until it next gets loaded.

It does support full replication. There are two or three things you need to override, but it works fine and it sounds like OP has already done this.
Info is here if you’re interested.

Well, that is not what an epic developer told me, they have said user enabled uobject replication works on members only, not the entire uobject.

Anyway regardless The problem is, op shouldn’t be using uobjects, as there is no real reason as far as i can tell from this thread.
he could accomplish the same task with less effort and less code by using UStructs.

Surprisingly no, if I am looking only for a Class object than the dropdown TSubclassOf actually has all class objects even those built in BP. If I am looking to create an inline instance of a class then it does not load unless I manually go, open the file, recompile, and force it into memory. Then it shows up.

My Component is created in the Constructor of the my custom character actor, and has a DefaultSubObject assigned to it via the ObjectInitializer. This was so I could test to see if the Array of struct was causing the creation of the UTraits I needed. This seemed to be working at first but the moment I recompile the blueprint the UTraits vanish.

Yes sorry for being unclear here and there it was late when I typed these up :). Yes when I say “go back to my UStrength object” I do mean modifying the Blueprint defaults. What is occurring (and this is only when using Method 1 which is working with InlineEditNew and dynamically creating new Traits directly on the component) is that the by editing the newly created trait it flags the entire thing as changed, so because I changed one value the editor thinks the entire UTrait has changed. At least thats what it is showing me. It could be that the editor is making this a completely new UTrait object sort of like a blueprint within a blueprint (maybe not completely sure how InlineEditNew works).

@SaxonRah

My UNetworkObject is my base class, it allows replication of it’s own properties as well as any subobject I have created on it. This has been tested and works fine. Also the amount of Effort and code is moot, it’s been written and developed and tested. There is no issue with the code, it works. It’s the editor and making it Designer Friendly and robust that is the issue. The Editor is resetting the Component back to Default when I compile, like it is not registering the changes I have made to the blueprint and properly saving them. This is at the Character level, when I load up the Character, Click the Attribute Component, and try to manipulate it there.

Ah I think I see what you’re saying. It doesn’t inherit defaults of individual properties if the subobject has been changed from the default in any way? Sounds strange, but maybe the expected behaviour is only implemented for components and not UObjects in general.

Seems you’re right about the TSubclassOf dropdown. I’m going to look into that to see how it works differently from the instanced version.

yep that is sort of the problem with Method 1. I would have liked it if I could change the base objects without having to worry if all the blueprint simplemented this way changed as well. I mean having 20 NPCs would requirement to regenerate all 20 of them. I’d still like to know how to get Method 3 working as I really liked the flexibility of that one…

From what you described, I think it can be solved by using UDataAsset, your game designer can make any number of them in the content browser and then plug them to the character blueprint. As the trait is an asset, which is transactional and not an archetype object, any changes to it is very simple and straight forward. The only drawback is the trait data asset should be a static data at runtime, but it’s easy to work around that by making instance classes, just like MaterialParameterCollection that has MaterialParameterCollectionInstance at runtime. If you want the game designer to be able to edit this trait in character blueprint, then you should add the keyword Instanced to the trait array. As for showing the changes to the character, you can override the OnConstruction function in C++.



UCLASS(BlueprintType)
class UTrait : public UDataAsset
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Trait")
	float HealthModifier;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Trait")
	float StaminaModifier;

	...

};


UCLASS(Blueprintable)
class AMyActor : public AActor
{
	...

	// function to be called when modifying traits at runtime
	UFUNCTION(BlueprintCallable, Category="Trait")
	void AddTrait( UTrait* Trait ) { 
		Traits.Add( Trait );
		UpdateTraitsImplications();
	}

	UFUNCTION(BlueprintCallable, Category="Trait")
	void RemoveTrait( UTrait* Trait ) {
		Traits.Remove( Trait );
		UpdateTraitsImplications();
	}

	virtual void OnConstruction() override {
		Super::OnConstruction();
		UpdateTraitsImplications();
	}

protected:

	UPROPERTY(EditAnywhere, Instanced, Category="Traits")
	TArray<UTrait*> Traits;

private:

	void UpdateTraitsImplications()
	{
		MaxHealth = BaseHealth + SumOfAllHealthModifiersInTraitStack();
		MaxStamina = BaseHealth + SumOfAllHealthModifiersInTraitStack();
	}

	...	

};


If the trait objects only changed in the editor, then I would use UDataAsset for the trait, something like this:



UCLASS(BlueprintType)
class UTrait : public UDataAsset
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Trait")
	float HealthModifier;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Trait")
	float StaminaModifier;

	...

};


UCLASS(Blueprintable)
class AMyActor : public AActor
{
	...

	// function to be called when modifying traits at runtime
	UFUNCTION(BlueprintCallable, Category="Trait")
	void AddTrait( UTrait* Trait ) { 
		Traits.Add( Trait );
		UpdateTraitsImplications();
	}

	UFUNCTION(BlueprintCallable, Category="Trait")
	void RemoveTrait( UTrait* Trait ) {
		Traits.Remove( Trait );
		UpdateTraitsImplications();
	}

	virtual void OnConstruction() override {
		Super::OnConstruction();
		UpdateTraitsImplications();
	}

protected:

	UPROPERTY(Replicated, EditAnywhere, Instanced, Category="Traits")
	TArray<UTrait*> Traits;

private:

	void UpdateTraitsImplications()
	{
		MaxHealth = BaseHealth + SumOfAllHealthModifiersInTraitStack();
		MaxStamina = BaseHealth + SumOfAllHealthModifiersInTraitStack();
	}

	...	

};


UDataAsset is simple and straightforward for object that doesn’t change throughout the game, but if the data is simple enough then i would just use UStruct in a data table. Sorry if this is not the kind of solution you’re looking for, i still don’t clearly understand your particular problem.

Maybe a paradigm shift would help: use ActorComponent as a base class for your UTrait and simply add traits as components instead of using an array of objects in the detail pane. Then you can have your trait base class add itself to the array on it’s owning actor during component initialization for easy blueprint access. Will save you tons of boilerplate code, you can still create blueprint traits, your traits can tick if you want and you get easy access to binding multicast delegate event dispatchers.

Holy Hedgehogs I figured it out. I’d love to just say Thank EVERYONE for all the support and Ideas you have thrown out, including some of the alternative approaches, which I’d like to take a quick moment to address.

@Fathurahman
The UDataAsset is a great idea unfortunatly the traits are not static, there are ranks that increase as the character levels up and grows. But I learned about a new toy I plan to utilize in the future. Thanks for the idea.

@Manoelneto
The use of ActorComponents would have also been an interesting idea. Similar to the Enum Idea except with more functionality inherently.

The Solution: (v4.11.2)
As a quick recap, I am trying to make the construction of some UObjects within a Component Designer friendly, so that Designers can add Game Data to a Component that then Generates the appropriate UObject. The Designer can Open up the Character Blueprint, Select the Component and then Add/Edit/Remove UObjects from a list within the Component. The hierarchy appears like this:

Character

  • ActorComponent
    – UObject Array

Quick Solution:
If you are in the editor and plan to construct objects dynamically using PostEdit events then be aware that the outer parameter in the NewObject function must be the object that will directly reference the object.


UObject * Obj = NewObject<UObject>(outer, type);

Technical Overview:
The way it will work is a Designer works directly with a Struct. This struct is a Memento object that is also used to save data to the disk and load from it as well. If you are unfamiliar with a memento it is an internal representation of an object without giving direct access to the internals of that object. Using PostEditChainChange functions you iterate through the struct array and build new objects with the values specified by the designer. Benefit to this is if all your code to manage the construction of these objects is wrapped in the #if WITH_EDITOR then it will only exist when you are using the editor. Your game will not contain the struct array used by the designers and will only have the Created Real UObjects your game runs off of.

Alternative Approaches:
One possible method to implement this would have been to allow Designers to Directly edit the UObject by setting the UObject to be InlineEditNew and the Array to be Instanced. This actually worked pretty well except for a minor bug/problem. Lets say you Derive BP_MySpecialObject. Then add your BP_MySpecialObject to the Array of UObjects. The Editor flags the entire object as changed even if you haven’t changed any value, My rational is that it is treating this as an Instance of a completely new Blueprint object. If you go back and edit BP_MySpecialObject it will not update the instance in your Character. The Second problem is that if the BP_MySpecialObject is not already loaded into memory it will not show up as an option for the Drop down to create a new instance, you will have to open the BP and recompile it to force it into memory and use it (As of version 4.11.2).

The Problem:
The problem I was having was after everything was constructed and ready to go, recompiling caused all the dynamically created objects to vanish.

Solution:
It is simple. There seems to be a requirement/bug that when you dynamically construct a UObject nested within another object in the editor (for example a UObject within a Component while editing the Character Blueprint) the object must have an “outer” set to the object that the Post Edit event is being fired for (it seems). My problem was I was constructing the objects using the Owner of the Component. The moment I set the outer parameter of the NewObject function to be the Component itself the data was saved.

Code:
What follows is my test code for making this work.

MyObject.h



#pragma once

#include "Object.h"
#include "MyObject.generated.h"

USTRUCT(Blueprintable, BlueprintType)
struct MY_PROJECT_API FMyObjectData
{
    GENERATED_USTRUCT_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Data|MyObject")
    FText DisplayName;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Data|MyObject")
    int32 Value;
};

UCLASS(Blueprintable, BlueprintType)
class MY_PROJECT_API UMyObject: public UObject
{
	GENERATED_BODY()

public:
	void GetData(const FMyObjectData& data);
	void InitializeFromData(const FMyObjectData& data);

protected:

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ACS|Trait")
	FText DisplayName;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ACS|Trait")
	int32 Value;
};


MyObject.cpp



#include "Apollo.h"
#include "Trait.h"

void UMyObject::GetData(const FMyObjectData & data)
{
	data.DisplayName = this->DisplayName;
	data.Value = this->Value;
}

void UMyObject::InitializeFromData(const FMyObjectData & data)
{
	this->Identifier = data.Identifier;
	this->SetData(data);
}

MyObjectComponent.h



#pragma once

#include "Components/ActorComponent.h"
#include "MyObject.h"
#include "MyObjectComponent.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class MY_PROJECT_API UMyObjectComponent: public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UMyObjectComponent();

	// Called when the game starts
	virtual void BeginPlay() override;
	
	// Called every frame
	virtual void TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction ) override;

#if WITH_EDITOR	

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Objects")
	TArray<FMyObjectData> genObjects;

	virtual void PostEditChangeProperty(FPropertyChangedEvent & e) override;

	virtual void PostEditChangeChainProperty(FPropertyChangedChainEvent & e) override;

	virtual void UpdateObjects();

	virtual void AddObjects();

#endif

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Instanced, Category = "Objects")
		TArray<UMyObject*> myObjects;
	
};



#include "Apollo.h"
#include "MyObjectComponent.h"


// Sets default values for this component's properties
UMyObjectComponent::UMyObjectComponent()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	bWantsBeginPlay = true;
	PrimaryComponentTick.bCanEverTick = true;
	// ...
}


// Called when the game starts
void UMyObjectComponent::BeginPlay()
{
	Super::BeginPlay();

	// ...
	
}


// Called every frame
void UMyObjectComponent::TickComponent( float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction )
{
	Super::TickComponent( DeltaTime, TickType, ThisTickFunction );

	// ...
}

void UMyObjectComponent::PostEditChangeProperty(FPropertyChangedEvent& e)
{
	Super::PostEditChangeProperty(e);
}

void UMyObjectComponent::PostEditChangeChainProperty(FPropertyChangedChainEvent& e)
{
	FName PropertyName = (e.Property != nullptr) ? e.Property->GetFName() : NAME_None;
	if (PropertyName == GET_MEMBER_NAME_CHECKED(UMyObjectComponent, genObjects))
	{
		this->AddObjects();
	}
	else if (PropertyName == GET_MEMBER_NAME_CHECKED(FMyObjectData, DisplayName) || PropertyName == GET_MEMBER_NAME_CHECKED(FMyObjectData, Value))
	{
		this->UpdateObjects();
	}

 	Super::PostEditChangeChainProperty(e);
}

void UMyObjectComponent::UpdateObjects()
{
	for (int32 i = 0; i < this->xTraits.Num(); i++)
	{
		FMyObjectData * data = &genObjects*;
		UMyObject* myObject = this->myObjects*;

		if (myObject != nullptr)
		{
			myObject ->SetData(*data);
		}
	}
}

void UMyObjectComponent::AddObjects()
{
	this->myObjects.Empty();

	for (int32 i = 0; i < this->genObjects.Num(); i++)
	{
		FMyObjectData* data = &genObjects*;
		UMyObject* object= NewObject<UMyObject>(this, UMyObject::StaticClass());

		trait->InitializeFromData(*data);
		this->myObjects.Add(objuect);
	}
	
}

1 Like

I forgot about my posts here, they were my first posts in unreal forum, I was wondering why my post did not appear and wrote a new one, apparently for new member the post needs to be moderated first.

Just setting the outer is not enough for the data to be saved, for an object to be saved to a package, it must meet these condition:

  • The object must be in the same package and not marked as Transient. (And of course the package itself must not be a memory only package, e.g: Transient package or Script package)
  • The UObjectProperty of the object reference must be marked as Export (Instanced will also do the same thing as it is basically saying an object is both Export and EditInline )

But well, since you already set your property to Instanced then there would be no problem. But in case you don’t want to show the property in the details panel, just set it to Export instead of Instanced.

I have to ask, seriously. How? Where is this information because I’ve hunted through the documentation, googled, and stared at the source code and have absolutely no idea still how the editor itself works. You can ask me nearly anything about building a game system, Replication and Multiplayer, Asset/Level Streaming, Blueprints, Art Pipelines, etc and there will be a fairly decent chance I’ll know it but ask me about some behavior in the editor and I got nothing… Properties… Functions… Magic. Thumbing through the Editor source code sounds… well not something I want to do really as my focus has been game development, but would love a decent place to actually start.

For instance try to google “UE4 package”, “UE4 C++ Package”, “UE4 Editor Package” and you get “How to package your game” or documentation page on “Assets and Packages” which none of it has anything to do with a “Script Package” which even that does not get anything useful… I mean as I go along I “stumble” into things and sort of figure their meanings and purposes out, but it is a process.

For instance I have recently discovered that the above method works as long as the component I add is not created in the constructor. It has to be manually added via the editor window of the Character blueprint. This is okay but it is still a minor irritation. The problem seems to some function delegate called



// The component or its outer could be pending kill when calling PreEditChange when applying a transaction.
// Don't do do a full recreate in this situation, and instead simply detach.
check(EditReregisterContexts.Find(this));


This is occuring in the PreEditChange function. It fails when I try to edit an FText. I have no idea why only the FText object fails this operation. The way I stopped this assertion from happening is to temporarily set the bRegistered variable to false and set it back at the end of PreEditChange… who knows that that will do…

Anyway thanks for that info, it’s pretty helpful

I learned about what a Package is by reading the engine code, the outermost of all UObject is a Package, some package are only in memory (such as TransientPackage), but most of them are Packages that will be serialize into file, these are what we would call asset file (*.umap and *.uasset), All objects created within a package by default is private, which means it can not be seen in the editor by object in another package, unless it is marked as public by setting the object flag RF_Public; When you create an asset, e.g: a UDataAsset “MyData” in the folder “/Game/GameData” the editor will create a package “/Game/GameData/MyData”, and then it will create a UDataAsset instance “MyData” in that package and set RF_Public in the object flags (along with RF_Transactional), having these two flags the object can now be ‘seen’ by other object in the other packages. The object path name for the UDataAsset that you have just created will be “/Game/GameData/MyData.MyData”.

I have written a blog post that touch a bit about it in : UObject Constructor, PostInitProperties and PostLoad – heapcleaner , but I am still learning so things in they may not be correct, so read with caution if you choose to read it.

1 Like