variable's value modified in AnimBP's EventGraph is different in AnimGraph!

Hi, I am asking for help as I have not found a solution after many experiments. :sob:
I created a TMap<int32, float> type variable in my animbp (child of animinstance), which is passed as a parameter to the node I created in animgraph.


I created a blueprint interface and added a function ‘SetBodyCalibraition’ that sets the values of the TMap<int32, float> type variables in my animbp. My animbp registered this blueprint interface and implemented the function SetBodyCalibraition.

Calling the ‘SetBodyCalibraition’ function is done by the BP (child of the Actor) that I created. In the BP, I perform the logic SkeletalMeshComponent->GetAnimInstance->CastTo MyAnimBP->Function SetBodyCalibraition. This logic passes meaningful values to the TMap<int32, float> type variables in my animbp.

In this situation, when I log my node in the animgraph to see if it is receiving the parameters, I see that the parameters are empty.
In my animbp’s EventGraph, when the SetBodyCalibraition function is called, it passes a value to a TMap<int32, float> type variable and then logs how many elements it contains. The log shows a good count of valid elements.

void FAnimNode_ModifyEndBone::Evaluate_AnyThread(FPoseContext& Output)
{
	InputPose.Evaluate(Output);
    
    const FBoneContainer& BoneContainer = Output.Pose.GetBoneContainer();
    const FCompactPose& Pose = Output.Pose;
    TArray<int32> StartBI = StartBoneIndexArr.BoneIndexes;

    FString temp = FString::FromInt(StartBI.Num()) + " | " + FString::FromInt(EndBoneIndexNRatio.Num());
    UE_LOG(LogTemp, Warning, TEXT("!!!Num:%s"), *temp);
//It repeatedly outputs 'LogTemp: Warning: !!!Num:0 | 0'
    if (StartBI.Num() != EndBoneIndexNRatio.Num() || StartBI.Num() == 0)
        return;

    int32 i = 0;
    for (TTuple<int32, float>& IndexNRatio : EndBoneIndexNRatio)
    {
        if (IndexNRatio.Value != 1.0f)
        {
            FCompactPoseBoneIndex StartBoneIndex(StartBI[i]);
            FCompactPoseBoneIndex EndBoneIndex(IndexNRatio.Key);
            if (!BoneContainer.Contains(FBoneIndexType(StartBoneIndex.GetInt())) || !BoneContainer.Contains(EndBoneIndex.GetInt()))
                break;

            FVector StartBoneLoc = Pose[StartBoneIndex].GetLocation();
            FTransform EndBoneTrans = Pose[EndBoneIndex];

            FVector Direction = StartBoneLoc - EndBoneTrans.GetLocation();
            float CurDistant = Direction.Size();

            if (CurDistant > KINDA_SMALL_NUMBER)
            {
                FVector NewEndLoc = StartBoneLoc + (Direction.GetSafeNormal() * (CurDistant * IndexNRatio.Value));
                EndBoneTrans.SetLocation(NewEndLoc);
                Output.Pose[EndBoneIndex] = EndBoneTrans;
            }
        }
        ++i;
    }
}

I don’t know why a variable that is set correctly in the EventGraph comes up empty in the Animgraph.
If anyone has any clue, I’d appreciate it.

Can you explain better what you need the map for? Then from these screens I can’t really understand honestly. However one of the reasons why the variable gives you problems could be because you have used the set too many times. The set overwrites the values. Also, between get Bone index array and Set Body Calib, maybe a for each loop would help where you iterate with a new array as an intermediary. Have you created a dictionary map that has the bone names as keys and the reference to a bp structure as values?

1 Like

Thanks for the first answer! My goal is to reposition certain bones on the character to meet a certain number of values.

The indexes of these specific bones and the values to use to reposition them are obtained by a one-time calculation in my BP.

  1. in my BP, calculate the numbers needed to modify the position of each specific bone and record them in a TMap<int32, float> type variable.
    The key is the index of the specific bone, and the value is the number to use to modify the position of that specific bone.
  2. my BP accesses my animBP directly with this recorded value and passes it to the animBP’s TMap<int32, float> type variable.

Steps 1 and 2 are only done once, as described above, and for step 2 I am using the Blueprint interface.

In the animbp’s animgraph, the ‘ModifyEndBone’ node uses the TMap<int32, float> variable to modify the position of that particular bone. The ‘ModifyEndBone’ node repeats this action until the end of play in the project.

I’m a little confused about what you’re saying below: are you saying that the parameters of the ‘ModifyEndBone’ node, ‘FBoneIndexArray StartBoneIndexArr’ and ‘TMap<int32, float> EndBoneIndexNRatio’, are being overwritten with new values too often?

you have used the set too many times.

Are you suggesting that I use a for each loop between the ‘get Bone index array’ function and the ‘Set Body Calib’ function and pass the elements one by one? Sorry for the hassle. I just figured that if I’m going to pass parameters anyway, it makes sense to pass a TArray and a TMap<int32, float> at once…

Also, between get Bone index array and Set Body Calib, maybe a for each loop would help where you iterate with a new array as an intermediary.

Should I replace TMap<int32, float> with TMap<FName, float> like you said?

Have you created a dictionary map that has the bone names as keys and the reference to a bp structure as values?

1 Like

Even if the reception happens all at once, it is convenient to add each element taken to an array, which collects the data and sorts it for each type of bone. In this way you can have an array that does not only take into account the last value, but that contains them all, and then assigns them to the appropriate bones. If I understood what you want to do. I gave you those suggestions because a few weeks ago I was working on a map and I had problems similar to yours, the counter integer was updating, but the value actor reference was always the last one, it ignored all the others. And I solved it in the ways I suggested before. So to avoid losing the values, always providing only the last ones taken as input, it is better not to iterate directly in the map, but to update the map through a structure type bp, and reference it in the right column of your map as value. In this way the struct can contain multiple values ​​without losing them, and consequently structure the updated map. But I do not understand, why would you want to reposition bones with numbers? And according to which logic are these numbers generated?

1 Like

No, I was saying create an external bp of type structure, inside it add 2 parameters, one int and one float and then use all the bp structure as values, so you will have TMap(Name,Bp structure)

1 Like

Thank you very much for your reply. I’m not sure if my understanding is correct, but I’m going to try and think like below.

Your method:
You passed a TMap-type variable with a valid value to animbp’s TMap-type variable in its entirety, but only part of the value was passed correctly.
So you created a separate struct with int32 and float attributes (MyStruct, for example). In animbp, you created a TMap<int32, MyStruct>-type variable (MyMap, for example).
You accessed animbp’s MyMap externally and iterated through the for each statement, adding the int32(key)-MyStruct(value) elements you wanted to add to MyMap one by one.
You passed this populated MyMap as a parameter to animgraph’s custom node.

I’m going to try the above method, thank you very much! I hope I succeed… :crying_cat_face:

1 Like

I share some screens on my structure maybe they will help you understand better, always considering that in my case there are different values ​​for a different application. But the concept is always the same, collect and sort data to then have the engine perform actions.




@seungmki

1 Like

But do you want to manipulate only specific bones or all bones? It’s still not entirely clear to me, especially how numbers are handled.

1 Like

Oh, I’m just trying to change the positions of certain bones.

Ah… The method I tried above failed;_;. I experimented quickly, so my BP node got messy, so please understand…
I’m worried that the anim custom node I created might be the problem and not be able to receive variables…


USTRUCT(BlueprintType)
struct FBoneIndexNRatio
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	int32 BoneIndex;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float BoneRatio;
};
///////////
void FAnimNode_ModifyEndBone::Evaluate_AnyThread(FPoseContext& Output)
{
	InputPose.Evaluate(Output);
    
    FString temp = FString::FromInt(Result.Num());//FString::FromInt(StartBI.Num()) + " | " + FString::FromInt(EndBoneIndexNRatio.Num());
    UE_LOG(LogTemp, Warning, TEXT("!!!Num:%s"), *temp);
   //'LogTemp: Warning: !!!Num:0' is repeatedly printed.
//...
}
//////////
USTRUCT(BlueprintInternalUseOnly)
struct MyTest_API FAnimNode_ModifyEndBone : public FAnimNode_Base
{
	GENERATED_USTRUCT_BODY()

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
	FPoseLink InputPose;

	UPROPERTY(EditAnywhere, Category = Calibration, meta = (PinShownByDefault)) //PinHiddenByDefault = false, ExposeOnSpawn = true
	FBoneIndexArray StartBoneIndexArr;

	UPROPERTY(EditAnywhere, Category = Calibration, meta = (PinShownByDefault))
	TMap<int32, float> EndBoneIndexNRatio;
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Transform")
	TEnumAsByte<EBoneControlSpace> BoneSpace;

	UPROPERTY(EditAnywhere, Category = Calibration, meta = (PinShownByDefault))
	TMap<int32, FBoneIndexNRatio> Result;

	FAnimNode_ModifyEndBone();
	virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
	virtual void CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) override;
	virtual void Update_AnyThread(const FAnimationUpdateContext& Context) override;
	virtual void Evaluate_AnyThread(FPoseContext& Output) override;
};
1 Like

There is a lot of confusion in the nodes now. Let’s proceed in order. First of all, if you insert the values ​​directly into the map with add, without a control branch and without an update operation, you will still have an overwriting of data. So the logic should be, take a current bone, if it is not already contained as a key in the map, add it to the map, otherwise take the data already recorded under that bone and update them. So you will need 2 branches, one where from the map you do contain, and check if the bone you receive has already been registered in the map, and a branch where from the map you do find, in case the bone is already present. So instead of redoing add and overwriting everything, you make distinction and then manage the update without losing the other data. Make the bones variable, so you don’t have to manually enter anything for the keys.
If the current bone (for each loop) is not already registered, then do make array, make structure and add to map
If the current bone is already registered, do map dictionary - find (current bone), so you can retrieve the data already registered on it, and you can update it with another for each loop, where the data taken is added to an array (created manually), to then be put back into the structure with make and add map. Avoid sets, and avoid iterating add map without branches.

1 Like

Oh, before proceeding as you said, I think it would be good to explain what I forgot above!

First, the logic to obtain the bone indices (int32) and ratios (float) in my BP only works once. After obtaining them, I access and assign them to my animBP variable (currently TMap<int32, FBoneIndexNRatio> Result) only once.

After that, TMap<int32, FBoneIndexNRatio> Result will never be assigned a new value!
My anim custom node will continue to use the Result, which will never change after applying a valid value only once.

What really confuses me is that when the ‘result’ value changes in my animbp, if I use the ‘print string’ node to print the number of elements in result, it prints a valid value (e.g. 23) correctly.

However, when I print the number of elements in the ‘result’ variable in my anim custom node that uses this result variable, it strangely prints 0…

1 Like

My case too, is a single call, which occurs when the map is complete, therefore a single call, which transmits all the data at once. However, if you need your data to change the position, how can they not change? So either I didn’t understand your goal, or you’re using the wrong logic. I repeat, my case too is a single call, but an array contains several values ​​together, therefore the values ​​must be treated individually as a variable. Then, regarding the print string, if it first prints correctly and then prints 0, the error must be between the input received (which prints correctly) and the map update (which prints 0, therefore it doesn’t happen and empties?). It is likely that at some point the input is null, the map is overwritten once with the last value taken and gives in print string 0.

1 Like

Maybe you should use structure and map, to collect based on the current bone, its position, and then later from that position value set a new one. But still the problem that your data structure is null. So in my opinion, you should use is valid, branches and use each element from the array as a variable, avoid sets after the values ​​are taken. This is what I can advise you. Otherwise I’m afraid I can’t help. :frowning_face:

1 Like

Thank you so much for your detailed and kind answer…! As you said, I will try to treat the int32(key), int32(value), and float(value) that make up TMap as one variable and store them one by one in TMap. Thank you! :smiley_cat:
I’ll share the results here too! There might be others who struggle like me!

1 Like

But why do you use int as key and not the names of the bones? Let me know if you solve it, I’m curious, also because I think what you’re trying to do is cool! haha. Anyway it’s a pleasure to try to help, they make the engine, we make it unreal! :muscle:

1 Like

Maybe it’s just me, but I figured that when I’m looking for a bone I want to reposition out of the 200+ bones in my character, it would be more efficient to search by index rather than name!

Sir, I succeeded!!!;O; The lengths of the variables in animbp are finally being printed correctly in the anim custom node I created.
The game thread and animation update thread were working together, so I was getting alternating valid lengths and length zeros…

LogTemp: Warning: !!!Num:0| is empty:true | EndBoneIndexNRatio:0 | StartBoneIndexArr:0
LogTemp: Warning: !!!Num:23| is empty:false | EndBoneIndexNRatio:23 | StartBoneIndexArr:23
LogTemp: Warning: !!!Num:0| is empty:true | EndBoneIndexNRatio:0 | StartBoneIndexArr:0
LogTemp: Warning: !!!Num:23| is empty:false | EndBoneIndexNRatio:23 | StartBoneIndexArr:23
LogTemp: Warning: !!!Num:0| is empty:true | EndBoneIndexNRatio:0 | StartBoneIndexArr:0
LogTemp: Warning: !!!Num:23| is empty:false | EndBoneIndexNRatio:23 | StartBoneIndexArr:23
//Output two sentences alternately....

The way I pass values ​​to the parameters of animbp has not changed from what I showed you in the screenshot earlier. I decided that the anim custom node I wrote was not receiving the variables properly, so I fixed the anim custom node first. If this method fails, I tried to use the method mentioned above to store the int32 (key), int32 (value), and float (value) that make up TMap as a single variable.

I have an anim custom node that I made using the Transform Modify bone node as a reference and confirmed to work properly. I used this to recreate the node that I kept failing at and it solved the problem.

void FAnimNode_ModifyEndBoneLoc::EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms)
{
	DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(EvaluateSkeletalControl_AnyThread) //Used for detailed profiling
	check(OutBoneTransforms.Num() == 0);

	const FBoneContainer& BoneContainer = Output.Pose.GetPose().GetBoneContainer();
	const FCompactPose& Pose = Output.Pose.GetPose();

	for (const FCompactPoseBoneIndex& BoneIndex : Pose.ForEachBoneIndex())
	{
		FBoneReference& BoneReference = BoneToModifies[BoneIndex.GetInt()];
	}

	FString temp = FString::FromInt(Result.Num()) + "| is empty:" + UKismetStringLibrary::Conv_BoolToString(Result.IsEmpty());
	temp += " | EndBoneIndexNRatio:" + FString::FromInt(EndBoneIndexNRatio.Num());
	temp += " | StartBoneIndexArr:" + FString::FromInt(StartBoneIndexArr.BoneIndexes.Num());
	UE_LOG(LogTemp, Warning, TEXT("!!!Num:%s"), *temp);
}

This time, I tried to make it in a different way… The old one is a good one. :sneezing_face:

1 Like

I’m glad you overcame this problem! Great work! What was the problem? Why did the last string print 0?

1 Like

When I connect a print string node to the blueprint update animation node in the eventgraph of my animbp and print the length of my variables, I get the same output as above. In other words, it alternates between valid length and length 0.

I also looked into the reason, and I found out that it is because the game thread and animation update thread each maintain instances for my animbp.

Since the logic for changing the variable values ​​of my animbp is done in the eventgraph, the game thread will be the one that prints the valid length. It seems that the values ​​of my animbp variables are not synchronized in the animation update thread, so they are printed as 0…

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.