I’ve been soaking in this issue for a while and it’s finally time to kneel at the alter of the most high forums.
I have not tested this for other structs besides FVector - that being said. . . .
If I have a Uobject that has a member MyVector and I want to change the Y or Z member in MyVector - it will only ever change X. (Reading this value back by also yields the X value).
This is how I’m currently trying to achieve this process - based on code from this thread
float UPropertyUtilFunctionLib::SetObjectPropertyByName(UObject* ObjectWithVector, const FName& PropName, const float& NewValue)
{
FName VectorMemberName = FName("Y");
// Get property representing struct
FProperty* 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 (FStructProperty* StructProp = CastField<FStructProperty>(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
FProperty* ChildProp = ScriptStruct->FindPropertyByName(VectorMemberName);
// Cast to FloatProperty
if (FFloatProperty* ChildFloatProp = CastField<FFloatProperty>(ChildProp))
{
// Get
float OldValue = ChildFloatProp->GetFloatingPointPropertyValue(StructAddress);
// Set
ChildFloatProp->SetFloatingPointPropertyValue(StructAddress, NewValue);
return OldValue;
}
}
}
return 0;
}
On 4.27.2 this code will always change the X value not the Y value - I’m purposefully avoiding checking tags and calling object->PostEdit() to keep the code to a minimum. But I have tested with yielding the same result.
You are getting/setting the float value with the address of the vector property which always points to the beginning of the data structure. The beginning is the first member (which is X) so that’s the only thing that pointer will ever change. You need to use the address of the Y member of the vector, not the address of the vector itself.
// Cast to FloatProperty
if (FFloatProperty* ChildFloatProp = CastField<FFloatProperty>(ChildProp))
{
// Address of the vector property itself which starts with member X so that's what it points to.
void* VectorAddress = Prop->ContainerPtrToValuePtr<void>(ObjectWithVector);
// Address of the float member Y of the vector...
void* VectorYAddress = ChildProp->ContainerPtrToValuePtr<void>(VectorAddress);
// ...which is just the address of the property + some offset (4 byte for Y, 8 byte for Z because they are floats), so this call will yield the same address:
void* VectorYAddressToo = (uint8*)VectorAddress + ChildProp->GetOffset_ForInternal();
// Get
float OldValue = ChildFloatProp->GetFloatingPointPropertyValue(VectorYAddress);
// Set
ChildFloatProp->SetFloatingPointPropertyValue(VectorYAddress, NewValue);
...
I was struggling to access different components of FVector depending on what component was changed. So, I came up with this easy solution. Once you got the FVector address, just take XYZ values from it: