Setting FVector Property using Reflection only ever changes the "X" member

Hello!

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.

1 Like

Hi!

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);

	...
1 Like

I had a hunch that was the issue, but I was using the wrong ptr!

I was using a FStructProperty* FVectorPropPtr here…

void* VectorAddress = Prop->ContainerPtrToValuePtr(ObjectWithVector);
void* VectorYAddress = ChildProp->ContainerPtrToValuePtr(FVectorPropPtr);

instead of void* VectorYAddress = ChildProp->ContainerPtrToValuePtr(VectorAddress);

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:

        FVector Vector;
        std::memcpy(&Vector, VectorAddress, sizeof(FVector));

Works like a charm:

LogTemp: Warning: X is: 110.371984
LogTemp: Warning: Y is: 25.058952
LogTemp: Warning: Z is: 1.518790