Smooth Interp To

#Kismet

A custom node that smoothly interpolates an actor to a destination!

Interpolates an actor's location, rotation and, when possible, the camera FOV, to the given destination.

In addition, this also gracefully handles the case when the actor and destination are hard-attached to the same base.

NOTE: You may want to edit the value of ObjCategory to something unique, so you can tell these nodes apart from other copies in other mods.

Drew_SeqAct_SmoothInterpTo.uc

[RAW] [Download]

/**
 * Smoothly interpolates one or more actors to a given destination.
 */
class Drew_SeqAct_SmoothInterpTo extends SeqAct_Latent;
 
var() Actor Dest;
var() float LocationInterpSpeed, RotationInterpSpeed, FOVInterpSpeed;
var() bool RotationInterpShortestPath;
 
var bool Stopped;
 
event Activated()
{
    // NOTE: This event activates multiple times for some reason (possibly every frame...?). Don't do something outside of any input link checks!
 
    if (InputLinks[1].bHasImpulse)  // Stop
    {
        Stopped = true;
        ActivateOutputLink(1);
    }
    else if (InputLinks[0].bHasImpulse)  // Start
    {
        Stopped = true;
        if (Dest == None) return;
        Stopped = false;
        ActivateOutputLink(0);
    }
}
 
event bool Update(float DeltaTime)
{
    local Object Target;
    local Actor ActorTarget;
    local CameraActor CameraTarget, CameraDest;
    local Vector TargetRelativeLocation;
    local Rotator TargetRelativeRotation;
    local bool UsingRelativeTransform;
 
    if (Stopped) return false;
 
    foreach Targets(Target)
    {
        ActorTarget = Actor(Target);
        if (ActorTarget == None) continue;
 
        // Turns out, when using bHardAttach, the engine uses these RelativeLocation and RelativeRotation variables,
        // so the attachment doesn't lose its precise relative transform. While cool, I REALLY wish they made that clearer...
        // Either way, in this case these variables should be modified directly, if we want this updated movement to be permanent
        // (and not lost when the Base ends up moving).
        UsingRelativeTransform = false;
        if (ActorTarget.bHardAttach && ActorTarget.Base != None)
        {
            UsingRelativeTransform = true;
            if (ActorTarget.Base == Dest.Base && Dest.bHardAttach)
            {
                TargetRelativeLocation = Dest.RelativeLocation;
                TargetRelativeRotation = Dest.RelativeRotation;
            }
            else if (ActorTarget.Base == Dest)
            {
                TargetRelativeLocation = vect(0,0,0);
                TargetRelativeRotation = rot(0,0,0);
            }
            else
            {
                // Can't resolve based movement, so just do plain Move and SetRotation, even though it's not quite correct.
                UsingRelativeTransform = false;
            }
        }
 
        if (RotationInterpSpeed > 0.f)
        {
            if (UsingRelativeTransform)
            {
                if (ActorTarget.RelativeRotation != TargetRelativeRotation)
                    ActorTarget.SetRelativeRotation(RLerp(ActorTarget.RelativeRotation, TargetRelativeRotation, FMin(1.f, DeltaTime * RotationInterpSpeed), RotationInterpShortestPath));
            }
            else
            {
                if (ActorTarget.Rotation != Dest.Rotation)
                    ActorTarget.SetRotation(RLerp(ActorTarget.Rotation, Dest.Rotation, FMin(1.f, DeltaTime * RotationInterpSpeed), RotationInterpShortestPath));
            }
        }
        if (LocationInterpSpeed > 0.f)
        {
            if (UsingRelativeTransform)
            {
                if (ActorTarget.RelativeLocation != TargetRelativeLocation)
                    ActorTarget.SetRelativeLocation(VLerp(ActorTarget.RelativeLocation, TargetRelativeLocation, FMin(1.f, DeltaTime * LocationInterpSpeed)));
            }
            else
            {
                if (ActorTarget.Location != Dest.Location)
                    ActorTarget.Move((Dest.Location - ActorTarget.Location) * FMin(1.f, DeltaTime * LocationInterpSpeed));
            }
        }
        // Custom logic in case this is a CameraActor...
        CameraTarget = CameraActor(Target);
        CameraDest = CameraActor(Dest);
        if (CameraTarget != None && CameraDest != None && FOVInterpSpeed > 0.f)
        {
            CameraTarget.FOVAngle = Lerp(CameraTarget.FOVAngle, CameraDest.FOVAngle, FMin(1.f, DeltaTime * FOVInterpSpeed));
        }
    }
 
    return true;
}
 
defaultproperties
{
    ObjName = "Smooth Interp To"
    ObjCategory = "Give me a custom category!"
    bCallHandler = false
    bAutoActivateOutputLinks = false
    AllowReActivation = true
 
    InputLinks(0) = (LinkDesc="Start")
    InputLinks(1) = (LinkDesc="Stop")
 
    OutputLinks(0) = (LinkDesc="Started")
    OutputLinks(1) = (LinkDesc="Stopped")
 
    VariableLinks(1) = (ExpectedType=class'SeqVar_Object', LinkDesc="Dest", PropertyName=Dest)
 
    LocationInterpSpeed = 7
    RotationInterpSpeed = 7
    FOVInterpSpeed = 7
    RotationInterpShortestPath = true
}