I have implemented a latent function to go together with my ledge climbing system. Since I am making it in C++, I would prefer to avoid using Blueprints for this particular part (since it will be part of core functionality), which is why I got myself a latent function from the KismetSystemLibrary. Now, the function is working properly except for one downside; I cannot seem to find a way to get it to fire something that I can use to determine whether the latent function has stopped or not. I prefer not to resort to using the Tick function to check every single tick.
The code in question;
void UMyClass::MoveComponentTo(USceneComponent* Component, FVector TargetRelativeLocation, FRotator TargetRelativeRotation, bool bEaseOut, bool bEaseIn, float OverTime, TEnumAsByte<EMoveCompAction::Type> MoveAction, FLatentActionInfo LatentInfo)
{
if (UWorld* World = ((Component != NULL) ? Component->GetWorld() : NULL))
{
FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
FInterpolateComponentToAction* Action = LatentActionManager.FindExistingAction<FInterpolateComponentToAction>(LatentInfo.CallbackTarget, LatentInfo.UUID);
const FVector ComponentLocation = (Component != NULL) ? Component->RelativeLocation : FVector::ZeroVector;
const FRotator ComponentRotation = (Component != NULL) ? Component->RelativeRotation : FRotator::ZeroRotator;
// If not currently running
if (Action == NULL)
{
if (MoveAction == EMoveCompAction::Move)
{
// Only act on a 'move' input if not running
Action = new FInterpolateComponentToAction(OverTime, LatentInfo, Component, bEaseOut, bEaseIn);
Action->TargetLocation = TargetRelativeLocation;
Action->TargetRotation = TargetRelativeRotation;
Action->InitialLocation = ComponentLocation;
Action->InitialRotation = ComponentRotation;
LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, Action);
}
}
else
{
if (MoveAction == EMoveCompAction::Move)
{
// A 'Move' action while moving restarts interpolation
Action->TotalTime = OverTime;
Action->TimeElapsed = 0.f;
Action->TargetLocation = TargetRelativeLocation;
Action->TargetRotation = TargetRelativeRotation;
Action->InitialLocation = ComponentLocation;
Action->InitialRotation = ComponentRotation;
}
else if (MoveAction == EMoveCompAction::Stop)
{
// 'Stop' just stops the interpolation where it is
Action->bInterpolating = false;
}
else if (MoveAction == EMoveCompAction::Return)
{
// Return moves back to the beginning
Action->TotalTime = Action->TimeElapsed;
Action->TimeElapsed = 0.f;
// Set our target to be our initial, and set the new initial to be the current position
Action->TargetLocation = Action->InitialLocation;
Action->TargetRotation = Action->InitialRotation;
Action->InitialLocation = ComponentLocation;
Action->InitialRotation = ComponentRotation;
}
}
}
}
The function that I believe it is calling when finishing a latent action;
void FLatentActionManager::TickLatentActionForObject(float DeltaTime, FActionList& ObjectActionList, UObject* InObject)
{
typedef TPair<int32, FPendingLatentAction*> FActionListPair;
TArray<FActionListPair, TInlineAllocator<4>> ItemsToRemove;
FLatentResponse Response(DeltaTime);
for (TMultiMap<int32, FPendingLatentAction*>::TConstIterator It(ObjectActionList); It; ++It)
{
FPendingLatentAction* Action = It.Value();
Response.bRemoveAction = false;
Action->UpdateOperation(Response);
if (Response.bRemoveAction)
{
new (ItemsToRemove) FActionListPair(TPairInitializer<int32, FPendingLatentAction*>(It.Key(), Action));
}
}
// Remove any items that were deleted
for (int32 i = 0; i < ItemsToRemove.Num(); ++i)
{
const FActionListPair& ItemPair = ItemsToRemove*;
const int32 ItemIndex = ItemPair.Key;
FPendingLatentAction* DyingAction = ItemPair.Value;
ObjectActionList.Remove(ItemIndex, DyingAction);
delete DyingAction;
}
// Trigger any pending execution links
for (int32 i = 0; i < Response.LinksToExecute.Num(); ++i)
{
FLatentResponse::FExecutionInfo& LinkInfo = Response.LinksToExecute*;
if (LinkInfo.LinkID != INDEX_NONE)
{
if (UObject* CallbackTarget = LinkInfo.CallbackTarget.Get())
{
check(CallbackTarget == InObject);
if (UFunction* ExecutionFunction = CallbackTarget->FindFunction(LinkInfo.ExecutionFunction))
{
CallbackTarget->ProcessEvent(ExecutionFunction, &(LinkInfo.LinkID));
}
else
{
UE_LOG(LogScript, Warning, TEXT("FLatentActionManager::ProcessLatentActions: Could not find latent action resume point named '%s' on '%s' called by '%s'"),
*LinkInfo.ExecutionFunction.ToString(), *(CallbackTarget->GetPathName()), *(InObject->GetPathName()));
}
}
else
{
UE_LOG(LogScript, Warning, TEXT("FLatentActionManager::ProcessLatentActions: CallbackTarget is None."));
}
}
}
}
Now, I got to the point of the log writing out the “could not find latent action resume point.” I implemented a callback function simply known as Callback and even though the linkage should have been fine (CallbackTarget and InObject both returned the class that contained the Callback function), I still got that log message. I suspect this function has been made with Blueprint in mind (the whole pin thing), but I want to know a way through which it will call a c++ function instead.