/** * 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 }