USpringArmComponent lag vars not abiding origin shifting (proto-fix included)

G’day,

It seems that if you use bEnableCameraLag == true with the spring arm component, the internal state variables “PreviousDesiredLoc” and “PreviousArmOrigin” are not updated when the world origin shifts. The effect of this is a very unpleasant location snapping effect every time the world origin is reset, because the component tries to interpolate from what is now a very wrong previous location.

I was able to address this locally by subclassing the component and adding something like this:

void UMySpringArmComponent::ApplyWorldOffset(const FVector& offset, bool bWorldShift)
{
   Super::ApplyWorldOffset(offset, bWorldShift);
   PreviousDesiredLoc += offset;
   PreviousArmOrigin  += offset;
}

Following that, the interpolated movement remains smooth on origin rebasing. I believe the original component should do this though; I can’t think of any conceivable reason you’d want the weird behavior it exhibits now. I imagine it’s a simple matter of hoisting the above code into the USpringArmComponent.

(Separately but relatedly, the TemporalAA has some origin rebasing artifacts, which I haven’t investigated yet).

Other details: UE 4.8.3 on Linux

Hey -

I’m not sure what you mean by shifting the world origin. Can you provide further explanation of the issue and/or detailed steps I could follow to reproduce the problem on my end?

Cheers

Hi ,

Sure, I’ll try. The high level topic is as described here. I have a FVector holding my observer location, and when that drifts too far from the world origin, I reset the origin as follows:

   // New location, but ignore Z axis
   if (FMath::Abs(location.X) > OriginRebaseThreshold || FMath::Abs(location.Y) > OriginRebaseThreshold)
      GetWorld()->(FIntVector(location.X, location.Y, 0.0f) + GetWorld()->OriginLocation);

This works as expected: the ApplyWorldOffset() virtual method is called on various objects, with the new origin set close to the viewpoint. In principle, I don’t think there should be a visually observable effect from this. A number of UE4 provided classes are overloading this method to do various internal housekeeping: for instance, USceneComponent::ApplyWorldOffset updates bounding box positions and whatnot.

The USpringArmComponent has not only its primary location to worry about, but some internal hysteresis variables for smoothing the camera location from wherever it just was, to wherever you are asking it to be. Those variables are not being updated. You probably wouldn’t notice if you updated the observer location and the world origin at once, but if you set the world origin and the spring arm is “tensioned” so to speak, it jumps abruptly as the smoothing goes wonky.

I’m brand new to UE4, and it’s very possible I have missed something important! But this fixes the weird spring arm behavior for me, and other UE4 classes are updating their own internal state similarly.

Thanks for looking into this! If there’s anything else I can do to help, let me know. I can privately send you the APlayerController subclass which encountered this, though it wouldn’t compile straight away since it has tentacles into some of my other code.

BTW, if you want to try to repro it without my code, you might:

(1) Set up a spring arm attached to an object you can drive around with the keyboard via PIE. It’s essential that the spring arm have bEnableCameraLag = true.

(2) While actively driving the spring arm around - it’s essential it’s actively going through the interpolation code around SpringArmComponent:101 - set the world origin to a spot around 100m away via (). You should see the camera do some weird things, because the internal state variables above are not getting updated when the origin is shifted. The spring arm tries to interpolate from a spot in the old world reference frame.

Hey -

Where are you using the () function? Is this being called in the same class that you setup your sprint arm component? When you say to set the world origin 100m away, are you referring to 100m away from the default world origin or from the location of the actor with the spring arm component? If possible could you include the code where you have your spring arm component set up for reference?

Cheers

Hi ,

I’m calling from with a derived class of APlayerController, from a timer function called every half second (and then only if the camera has moved far from the origin). Though I believe the misbehavior is identical if it call it from the Tick() function.

When I say “100m away”, I just mean 100m away from where the origin was previously. The absolute location doesn’t matter: what matters is that it moves some distance from where it was, which causes the spring arm state variables to go stale. Similarly, the absolute distance isn’t important: 10km would also show the problem.

And sure, I’ll include the code I think you’re talking about. If this isn’t what you meant let me know and I’ll try again. This happens in a derived class from APawn, in its constructor:

   SpringArm     = CreateDefaultSubobject<UMySpringArmComponent>(TEXT("CameraRigSpringArm"));
   RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("CameraRigRoot"));

   SpringArm->AttachTo(RootComponent);
      
   SpringArm->SetRelativeLocationAndRotation(FVector(0.0f, 0.0f, 0.0f), FRotator(-50.0f, 0.0f, 0.0f));
   SpringArm->bAbsoluteRotation        = true;
   SpringArm->TargetArmLength          = 2000.0f;
   SpringArm->bEnableCameraLag         = true;
   SpringArm->bEnableCameraRotationLag = true;
   SpringArm->bDoCollisionTest         = false;
   SpringArm->bInheritPitch            = false;
   SpringArm->bInheritRoll             = false;
   SpringArm->bInheritYaw              = true;
   SpringArm->CameraLagSpeed           = 5.0f;
   SpringArm->CameraRotationLagSpeed   = 16.0f;
   SpringArm->CameraLagMaxDistance     = 10000.0f;

Where “UMySpringArmComponent” is the derivation I made to include my fix above, but you can replace it with the stock spring arm to show the trouble.

I do have a spring arm feature request too, but I’ll put that in another reply to avoid conflating it with this topic :).

Hi ,

Do you have a sample project this is occurring in? I’d be happy to take a look and see what may be occurring.

Hi ,

Over the w/e perhaps I can come up with an example project by trimming down the full project I have. Shouldn’t be too hard.

I have an adequate workaround (from my post at the top of this thread), so it isn’t a big problem for me. I just thought you might want to roll a similar fix into the main engine, if you end up believing it’s a real bug once you see it yourself locally. I’ll try to get you an example project soon…

,

I have a test project that shows this. How do I give it to you?

Hi ,

You can send it to me either here by posting the . or in a private message on the forums.

Alrighty. I’m quite new to UE4 and not entirely clear on what subset of the project to package up, so I took a SWAG at it, and if you need something else, I’ll try again.

There’s a file README-SpringArmPossibleBug.txt included which explains the situation. Line endings are Linux form for both code and the readme.

link text

Hey -

I can see the jump you mention when moving the actor in game. Overriding the ApplyWorldOffset as you’ve done in your spring arm component is what needs to be done to correct this in custom classes as stated in the documentation. Doing the same thing in a custom camera class could help eliminate the jump/hitch more.

Hi ,

Is there other documentation for the spring arm than the reference entry here? I looked there and didn’t see anything about origin rebasing or ApplyWorldOffset. It’s a bit on the terse side.

Anyway, I know I can fix it by overriding that method, but I was thinking that generally the other internal UE4 classes seem to handle rebasing their own state, rather than leaving it up to custom derivations that might not even have visibility into or knowledge of their internal state. Arguably, the spring arm ought to do the same. As a user of the spring arm, I didn’t even know about those variables until I went to debug the problem. I’m fine either way, but from an API perspective I think it’s better, and might save other new users similar head-scratching.

Hi ,

Is there other documentation for the spring arm than the reference entry here? I looked there and didn’t see anything about origin rebasing or ApplyWorldOffset. It’s a bit on the terse side.

Anyway, I know I can fix it by overriding that method, but I was thinking that generally the other internal UE4 classes seem to handle rebasing their own state, rather than leaving it up to custom derivations that might not even have visibility into or knowledge of their internal state. Arguably, the spring arm ought to do the same. As a user of the spring arm, I didn’t even know about those variables until I went to debug the problem. I’m fine either way, but from an API perspective I think it’s better, and might save other new users similar head-scratching. Also, I think an argument could be made that origin rebasing should be as seamless as possible.

Anyway, thanks.

Hi ,

I am still looking into this. I’ll post here when I have additional information.

Okay, great.

Hi ,

I’ve entered this into our system as a feature request, UE-21057, to be considered by our development staff.

Cool.