Set/Get UProperty inside of USTRUCT directly

Hi,

I’d like to set&get ustruct property value. What I have is

UProperty* Property - property that I want to get (float)

UObject* PropertyOwner - owner of this property (UStruct in this case)

For example: I’m trying to get FilmContrast value from FPostProcessSettings struct inside camera component.

So normally I would do something like this: *Property->ContainerPtrToValuePtr(PropertyOwner) to get this property value. But because PropertyOwner is a UStruct the line above crashes.

I’ve seen some examples in the source code that uses ContainerPtrToValuePtr method but I’m not sure if this is the right way to get into ustruct properties and how exactly can i do it.

Any help/advice will be greatly appreciated,

Cheers

I want to get/set current value of the property in this struct instance, not the default value.

I’m not sure of what you are trying to do, you want to get the value from a default property of a struct? Or just access the property value from an instance of a struct? Or something else?

Have you tried casting it to UFloatProperty and then calling GetPropertyValue_InContainer?

Structs are a little different than UObjects, but you can get and set their members like so:

float GetSetVectorX(UObject * ObjectWithVector)
{
	FName PropName = FName("MyVector");
	FName VectorMemberName = FName("X");
	float NewValue = 42.0;
	
	// Get property representing struct
	UProperty * Prop = ObjectWithVector->GetClass()->FindPropertyByName(PropName);
	
	// Ensure ObjectWithVector actually has a myVector member
	if (Prop)
	{
		// Get struct address
		void * StructAddress = Prop->ContainerPtrToValuePtr<void>(ObjectWithVector);
		
		// Ensure MyVector really is a vector
		if (UStructProperty * StructProp = Cast<UStructProperty>(Prop))
		{
			// We'll get struct properties through a UScriptStruct, not a UObject
			// ScriptStructs are just templates and don't hold any of your data
			UScriptStruct * ScriptStruct = StructProp->Struct;
			
			// Get the vector's "X" property
			UProperty * ChildProp = ScriptStruct->FindPropertyByName(VectorMemberName);

			// Cast to FloatProperty
			if (UFloatProperty * ChildFloatProp = Cast<UFloatProperty>(ChildProp))
			{
				// Get
				float OldValue = ChildFloatProp->GetFloatingPointPropertyValue(StructAddress);
				
				// Set
				ChildFloatProp->SetFloatingPointPropertyValue(StructAddress, NewValue);
				
				return OldValue;
			}
		}
	}
	return 0;
}
1 Like

I should note that (at least in my environment) Structs created in blueprints have invisible extensions added to their member names. So a member called “CrazyName” might look like this:

CrazyName_2_8879DD6E49624206B15E6DB35EFD0E09

It’s not exactly elegant, but I parse all the Struct’s members into a map beforehand:

UScriptStruct * ScriptStruct = StructProp->Struct;

// FriendlyNames => BlueprintNames
TMap<FString, FString> BlueprintNames;

// Is this Struct not made with Cpp?
if (!ScriptStruct->IsNative())
{
	// Get Struct's first member
	UField * Field = ScriptStruct->Children;
	while (Field)
	{
		// Get Blueprint name
		FString FieldName = Field->GetName();
		
		// Chop off the second-to-last underscore (_)
		int index = FieldName.Len();
		for (int i = 0; i < 2; ++i)
		{
			index = FieldName.Find(FString("_"), ESearchCase::CaseSensitive, ESearchDir::FromEnd, index);
		}
		
		// Get the friendly name
		FString MemberName = FieldName.Left(index);

		// Add to map
		BlueprintNames.Add(MemberName, FieldName);

		// Go to next member
		Field = Field->Next;
	}
}

Then I get and set the Struct’s members as normal:

// Only difference:
FString LiteralName = FString("CrazyName");
FName MemberName = FName(*(ScriptStruct->IsNative() ? LiteralName : BlueprintNames[LiteralName]));

// Normal business
UProperty * Prop = ScriptStruct->FindPropertyByName(MemberName);
// ... Get / Set logic ...

I know this is an old question, but I arrived here while trying to do something similar, so I’ll add what worked for me. If the object in question has a UPROPERTY that is a struct, then you can just cast it to a UStructProperty and then ContainerPtrToValuePtr does what you need.

For example, for an object with an FVector property inside it:

    if (UStructProperty *p = Cast<UStructProperty>(Property))
    {
        FVector *v = p->ContainerPtrToValuePtr<FVector>(PropertyOwner);
        v->X = 10.0;
        v->Y = 12.0;
        v->Z = 3.0
    }

If for some reason you need to be able to serialize and deserialize your UStructProperty, or any other type of property for that matter, regardless of what type it is, then you can use the following to get its value as a byte array.

TArray<uint8> ByteArray;
FMemoryWriter MemoryWriter(ByteArray);
FObjectAndNameAsStringProxyArchive Ar(MemoryWriter, false);
Ar.ArNoDelta = true;

Property->SerializeBinProperty(Ar, PropertyOwner);

In theory you should be able to then type cast it to your struct type if you know what it ought to be

Then to load the property from your byte array you could do

FMemoryReader MemoryReader(ByteArray);
FObjectAndNameAsStringProxyArchive ReadAr(MemoryReader, false);
ReadAr.ArNoDelta = true;
			
Property->SerializeBinProperty(ReadAr, PropertyOwner);

This is mostly useful if you’re creating something like a save system

1 Like