Page 1 of 1

Player Interpolation Jitter While Strafing?

Posted: Mon Aug 28, 2017 9:16 pm
by AFADoomer
I think this has been around since uncapped framerate was introduced, but I don't know if it has ever been fully addressed...

When FPS is uncapped, objects that move relative to the player map object's position will 'jitter' when the player is strafing. They move roughly in sync with the player, but appear to move back and forth between 2-3 map units of space horizontally on the screen.

You can see this with a weapon that fires continuous railgun shots, or with an actor that is always warped to the player's location... I *think* this is related to the player map object's actual interpolation being timed differently from the view's interpolation (so you see the object at its original location and at its new location at the same time)... Not sure, though. Screenshots are impossible, because the screenshot doesn't capture until the tic is complete.

You can see the 'jitter' on the actual player sprite as well - make sure cl_capfps is false, go to chasecam view, and strafe back and forth. The player sprite will flicker and move back and forth, always in approximately the right location, but very choppily, despite the floors and walls appearing to move smoothly. With cl_capfps turned on, this doesn't happen - the player sprite moves smoothly, as expected.

I'm not sure if this is really two separate issues (the view interpolation versus actor interpolation), or if both are the same issue...

Working example available here. Two weapons are included - an (overly complicated) in-work Ghostbusters-style proton pack that uses an actor that constantly warps to the player's position (actor name: ProtonPack), and a minimal railgun that fires a constant stream of particles while you hold down fire (actor name: 'RailTest'). Both are in weapon slot 6. In both cases, holding down 'fire' and strafing back and forth will show the jitter with cl_fps=false, but smooth movement with cl_fps=true.

Re: Player Interpolation Jitter While Strafing?

Posted: Thu Aug 31, 2017 2:26 pm
by RaveYard
I've never looked into the source code but...

Interpolation is happening between previous and current frame/position.
That means your camera, etc. are lagging behind their actual position which is why projectiles will seemingly appear slightly ahead the player.
If you can give them proper interpolation properly(i.e: https://zdoom.org/wiki/A_Warp), they should appear alright.

But speaking of bad interpolation...

Try jumping, flying up/down with the proton beam :(

Re: Player Interpolation Jitter While Strafing?

Posted: Sat Sep 02, 2017 12:02 am
by Graf Zahl
Is this in the last official build or the latest devbuild? I already addressed the case of actors being moved around by outside forces which this seems to be some time ago.

Re: Player Interpolation Jitter While Strafing?

Posted: Sat Sep 02, 2017 9:29 am
by AFADoomer
RaveYard wrote:If you can give them proper interpolation properly(i.e: https://zdoom.org/wiki/A_Warp), they should appear alright.

But speaking of bad interpolation...

Try jumping, flying up/down with the proton beam :(
That's essentially what I'm doing with the proton beam example - the beams originate from a point that warps to the player's location at specific offsets (in ZScript using SetOrigin, not A_Warp).
Graf Zahl wrote:Is this in the last official build or the latest devbuild? I already addressed the case of actors being moved around by outside forces which this seems to be some time ago.
Just tested this in the latest dev build, and the issue is still present.

I'm starting to think that there are different issues with different pieces of this, though.

You can see the jitter on the player mo's movement by going to chasecam and strafing. With capped framerate, the walking animation is smooth, and with uncapped it's not.

The easiest way to see the what I'm talking about with the "back and forth" movement in the player view is to switch to the RailTest weapon in the link above (slot 6, uses rocket launcher sprite), which fires a steady stream of 1-tic duration railgun beams as you hold down fire, then strafe back and forth. Regardless of capped/uncapped framerate, the 'beam' of particles will move very dramatically left and right, completely out of sync with the center of the player's view - this leads me to think that view interpolation is out of sync with the player mo's interpolation. Maybe it's supposed to be like this?

For the Proton Pack weapon, I attempted to negate this 'beam swaying' effect by spawning an actor that warps to the player's location every tic, and by spawning the beam from that actor instead of the player. This works almost perfectly *if* framerate is capped. If not, the effect flashes jerkily left and right across the player's screen, similar to the RailTest weapon. This effect is obviously far worse if I use SetOrigin with the second parameter as false instead of true...

For the Proton Pack, I've found that I can negate the "bad interpolation" effect by making the beam actor invisible on spawn, and only making it visible after a Tick has passed, and it has been moved to the correct location... But that seems like a step I shouldn't have to take - the SetOrigin call happens in the Tick() override on the beam actor, and I was previously making the beam visible in the same function call, and getting the beam still showing at the old position. Calling SetOrigin before or after Super.Tick makes no difference, either.
Spoiler: Sample code
Both solutions get the same result with capped framerate.

Re: Player Interpolation Jitter While Strafing?

Posted: Thu Sep 28, 2017 10:19 am
by Major Cooke
You gave some of the actors +DONTINTERPOLATE. All functions, even if you give the flags to interpolate or booleans, it never actually will interpolate. That's why you have so much jitter. Remove that and suddenly, it becomes a lot smoother after the initial spawn and first jitter.

Next, when you gutted MovingTrailBeam, you also took out the part where some anti-interpolation to avoid that jitter at the start.

Take a look at my old version's Process state.

Code: Select all

Class MovingTrailBeam : Actor 
{
    const NoSpawn = 256;
    double user_dist;
    int user_t;
    int user_zoffset;
    double user_angle;
    double user_pitch;
    Vector3 pa, pb;
    double user_xa, user_xb;
    double user_ya, user_yb;
    double user_za, user_zb;
    double user_time;
    double user_scale;
    int user_pflags;
    int user_neg;
    Default 
    {
        +NOINTERACTION
        +FLATSPRITE
        +BRIGHT
        +ROLLCENTER
        YScale 0.25; //Don't touch this! Otherwise the trail will become broken up!
        XScale 0.03; //This modifies the width of the beam. It is safe to play around with.
        Alpha 0;
        RenderStyle "Add";
    }
    // DO NOT add a translation. If you need to change the colors, make a sprite
    // entry in TEXTURES lump and use BLEND. NEVER TRANSLATE!
    States
    {
    Spawn:
        X202 B 0 NoDelay
        {
            // Sets how long to split the scaling-in effect.
            user_time = 3.0;
            user_zoffset = 16;
            // Set this to the actor's same name. The TID will prevent it spawning infinitely (tid * 256).
            A_SpawnItemEx("MovingTrailBeam",0,0,0,0,0,0,0,SXF_NOCHECKPOSITION|SXF_TRANSFERPITCH|SXF_TRANSFERSCALE|SXF_TRANSFERPOINTERS,tid * 256,1);
        }
        Goto Process;
    Process:
        "####" "#" 0
        {
            if (!user_pflags)    { user_pflags = WARPF_INTERPOLATE|WARPF_NOCHECKPOSITION|WARPF_ABSOLUTEOFFSET;    }
            A_Warp(DefPtr,0,0,user_zoffset,0,WARPF_NOCHECKPOSITION);
            // If it's a secondary trail (vertical)
            if (tid > 0)    
            {    
                Thing_ChangeTID(0,0);
                roll = pitch + 90;
                pitch = 90;
                angle += 90;
            }
        
            // Save our current information so we know how to properly interpolate whenever
            // the owner moves.
            user_neg = (Scale.X < 0) ? 1 : 0;
            user_scale = Scale.X;
            user_angle = angle;
            user_pitch = pitch;
            user_dist = GetDistance(false, TargetPtr);
            pa = pos;
            
            // Move to the shooter so we can get just how far out we are.
            A_Warp(TargetPtr,0,0,0,0,WARPF_NOCHECKPOSITION);
            pb = pa - pos;
            
            // Now move back without interpolating. Otherwise it'll look weird.
            A_Warp(TargetPtr, pb.x, pb.y, pb.z, 0, WARPF_NOCHECKPOSITION|WARPF_ABSOLUTEOFFSET);
            angle = user_angle;
            pitch = user_pitch;
        }
        "####" "#" 1 
        {
            A_FadeIn(1.0,FTF_CLAMP);
            A_Warp(TargetPtr, pb.x + vel.x, pb.y + vel.y, pb.z + vel.z, 0, user_pflags);
            angle = user_angle;
            pitch = user_pitch;
        }
        "####" "#" 1 
        {
            // Keep 'shrinking'. NEVER fade out!
            if (user_neg)
            {
                A_SetScale(Scale.X - (user_scale / Max(1.0, user_time)), Scale.Y);
                if (Scale.X >= 0)    { return ResolveState("Null"); }
            }
            else
            {
                A_SetScale(Scale.X - (user_scale / Max(1.0, user_time)), Scale.Y);
                if (Scale.X <= 0)    { return ResolveState("Null"); }
            }
            A_Warp(TargetPtr ,pb.x + vel.x, pb.y + vel.y, pb.z + vel.z ,0, user_pflags);
            angle = user_angle;
            pitch = user_pitch;
            return ResolveState(null);
        }
        Wait;
    }
}