Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to FlxCamera#followLerp #1220

Merged
merged 6 commits into from
Jul 27, 2014
Merged

Improvements to FlxCamera#followLerp #1220

merged 6 commits into from
Jul 27, 2014

Conversation

MSGhero
Copy link
Member

@MSGhero MSGhero commented Jul 11, 2014

The elapsed's get cancelled in the numerator and denominator anyway. Probably doesn't jitter anymore when followLerp = 0, but I didn't check for it.

The elapsed's get cancelled in the numerator and denominator anyway. Probably doesn't jitter when followLerp = 0, but I didn't check for it.
@MSGhero MSGhero changed the title Removed extra math Removed extra math in FlxCamera Jul 11, 2014
@Gama11
Copy link
Member

Gama11 commented Jul 11, 2014

Is this supposed to fix some issue or simply code cleanup? If the latter is the case, it should proudce the same results, which it doesn't.

I think it's pretty important for FlxG.elapsed to be taken into account, removing it from the equation doesn't seem like good idea.

@MSGhero
Copy link
Member Author

MSGhero commented Jul 11, 2014

The issue is that elapsed isn't being taken into account in the original case, other than creating truncation/roundoff jitter. Mathematically, the elapseds cancel each other out:
x / (x + lerp * x) = x / (x * (1 + lerp)) = 1 / (1 + lerp)

@MSGhero
Copy link
Member Author

MSGhero commented Jul 11, 2014

The differences between the two exist in the last few decimal digits if there's any difference at all.

No elapsed x, elapsed x, no elapsed y, elapsed y
image

@gamedevsam
Copy link
Contributor

Is there a demo we can check to make sure this doesn't break anything?

It seems like a good fix.

@Gama11
Copy link
Member

Gama11 commented Jul 17, 2014

We have to verfiy that the FlxG.elapsed variables really cancel each other out in this equation. IIRC, I did a test earlier and got signifcantly different results. (I might have made a mistake though)

Either way, I don't think we can simply leave the elapsed time out of the equation, all movement in flixel has to take it into account.

@MSGhero
Copy link
Member Author

MSGhero commented Jul 17, 2014

I think FlxG.elapsed is already taken care of because of the velocity. If you want your player to move 300 pixels per second, at 30fps his x increments by 10, and at 60 fps it would be 5 (300 * FlxG.elapsed).

Do you want a unit test, or like the FlxCamera demo using both ways?

@Gama11
Copy link
Member

Gama11 commented Jul 17, 2014

You could have a moving camera target without any FlxObject#velocity being involved though, if the user sets the position directly.

If this fixes the camera jittering mentioned in that comment, that fix should probably be removed?

@MSGhero
Copy link
Member Author

MSGhero commented Jul 18, 2014

I can't tell with certainty that the jittering is gone. It -might- be gone, but I don't notice it either way. Could @JoeCreates upload the source to the final Zilla test from #797?

Directly setting the position is iffy. Without lerp, the 30 fps version takes 1/30 seconds to move, and the 60 fps version takes half as long. Same thing if I said player.x += 5: the 60 fps player moves twice as far in the same time. I think you'd have to use a tween to get the same behavior at different fps.

@JoeCreates
Copy link
Member

This math was definitely wrong. The elapseds certainly do cancel out, so it looks like elapsed has never been taken into consideration for camera follow lerp. This means the existing values for followLerp have been a bit arbitrary, with no consistent meaning.

Specifying it in terms of seconds might be a bit tricky, though, seeing as the follow speeds change so quickly. That's not to say it couldn't be made consistent with respect to FPS, though, which would look something like:

scroll.x += (_scrollTarget.x - scroll.x) * FlxG.elapsed / (60 * (1 + followLerp));
- scroll.y += (_scrollTarget.y - scroll.y) * FlxG.elapsed / (60 * (1 + followLerp));

(Which would make the follow speed consistently what it used to be on 60FPS games.)

People would have to convert their lerp values if they weren't using 60fps, though, and that wouldn't be so easy.

On a side note, is it just me, or would it be easier to use a ratio (from 0 to 1) for lerp? Currently the value for lerp means something like "1 / ratio + 1", or "move a (lerp plus 1)th toward the target each frame". As such, I've always tended to pick a lerp value entirely on trial and error. :P

@Gama11
Copy link
Member

Gama11 commented Jul 18, 2014

@JoeCreates This definitely needs to be fixed, and I think most people use 60 fps to begin with. We just have to make sure to mention this in the Changelog / upgrade guide.

A range from 0 to 1 does make more sense to me. I think @prog4mr originally implemented this, any thoughts?

@MSGhero
Copy link
Member Author

MSGhero commented Jul 18, 2014

@JoeCreates 0 lerp makes sense, but what does lerp=1 correspond to? On one extreme, lerp=0 means the camera moves instantly. On the other, lerp=1 means the camera takes infinity long to get there?

I think it would be FlxG.elapsed * 60 / whatever. You're dividing by 60 and multiplying by 1/60 in your code, assuming 60 fps.

@JoeCreates
Copy link
Member

@MSGhero Either that or it could be the other way round, i.e. lerp=0 means don't move, lerp=1 means get there instantly.

You're right about the *60, by the way. Elapsed is roughly 1/60 at 60fps so it needs to be multiplied by 60 again to normalize, not divided as I had done.

@MSGhero
Copy link
Member Author

MSGhero commented Jul 19, 2014

@JoeCreates so would the end result be scroll.x += (target.x - scroll.x) * 60 * FlxG.elapsed * lerp? We don't want a lerp of >1 because that interferes with leading. Will have to change the camera demo and any other things that use lerp. Not sure if "linear interpolation" is still an accurate var name, though.

If so, the existing jitter fix probably needs to stay.

Unsure about jittering right now
@MSGhero
Copy link
Member Author

MSGhero commented Jul 19, 2014

The jitter fix no longer works since lerp=1 could be at 30 or 60 fps.

I had to add FlxG.random.float() to test since that was changed recently, should I remove that change from this patch?

@Gama11
Copy link
Member

Gama11 commented Jul 19, 2014

By manually fixing the FlxG.random calls, you created a merge conflict - you need to pull from the current dev branch and fix those.

@MSGhero
Copy link
Member Author

MSGhero commented Jul 20, 2014

Not sure if jittering will be an issue again. 60 * FlxG.elapsed and followLerp are both Floats.

Only place I could find followLerp or the 4th param of FlxCamera.follow() was in the FlxCamera demo.

@MSGhero
Copy link
Member Author

MSGhero commented Jul 21, 2014

Checked in the original zilla demo, and visually it looks the exact same as it did with the jitter fix. There's always a bit of bitmap tearing in flash, but ignoring that, camera movement was smooth. Nothing like the original swf he uploaded.

@@ -1096,6 +1090,14 @@ class FlxCamera extends FlxBasic
return _point.set(flashSprite.scaleX, flashSprite.scaleY);
}

private function set_followLerp(Value:Float):Float
{
if (Value < 0) Value = 0; // lerp is bounded between 0 and 1 inclusive
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have FlxMath.bound() for this. :)

return followLerp = FlxMath.bound(Value, 0, 1);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, of course there is.

@JoeCreates
Copy link
Member

@MSGhero The jitter fix on followLerp is old and completely unrelated to the jittering in the zilla demo. I have a feeling the jittering will be an issue with followLerp = 1, especially if FlxG.fixedTimestep = false, so I'd probably say keep the fix in.

@@ -62,8 +61,10 @@ class FlxCamera extends FlxBasic
public var targetOffset(default, null):FlxPoint;
/**
* Used to smoothly track the camera as it follows.
* Valid values between 0.0 and 1.0.
* A value of 1 means no camera easing. A value of 0 means the camera does not move.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, "easing"... Makes me wonder, would it be useful to allow using FlxEase functions for followLerp?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's interesting. How would that be handled if the camera is following a moving player? Seems like the ease parameter would get reset each time.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AS3 library TweenLite has a "throwProps" plugin for when something is being altered each frame while being tweened, But it's proprietary, not on github :(

@MSGhero
Copy link
Member Author

MSGhero commented Jul 21, 2014

Issue:

FlxG.elapsed = 1/30; lerp = .5 has the same result as lerp = 1 assuming the fix gets put back. If not, lerp = 1 causes leading: scroll.x = deltaX * 60 * elapsed(1/30) * lerp(1) = deltaX * 2. With the fix which sets scroll directly, lerp = .99 has drastically different behavior than lerp = 1.

elapsed * 60 * leading could be bounded to <= 1, but then 60 fps moves farther than 30 fps again.

@JoeCreates
Copy link
Member

@MSGhero That is a bit of an issue . . . It would also mean that we shouldn't cap the lerp at 1, seeing as the framerate might be higher than 60.

The fix would have to be applied with the condition if (lerp >= 60 * FlxG.elapsed)? Perhaps it would even be better to take the actual framerate rather than elapsed, so if (lerp >= FlxG.updateFrameRate / 60)?

@MSGhero
Copy link
Member Author

MSGhero commented Jul 22, 2014

I'll see what Gama says, it's starting to look a bit messy. It's a bit like we're manually tweening the camera but without being given a duration. The original lerp basically said "the camera will take followLerp extra frames to reach its destination." What is it trying to say now?

@JoeCreates
Copy link
Member

@MSGhero Actually, that's not what the original said. Remember that every frame the distance moved toward the target decreases. For example, if the original followLerp were 2, you'd move 1/3 of the total way to the target in the first frame, then 1/3 * 2/3 = 2/9 more on the second frame (totaling 5/9), 1/3 * 4/9 = 4/27 on the third frame (totaling 19/27), and so on . . . It said "move 1/followLerp+1 of the way toward target each frame ". If you ask me, that's pretty cryptic, and it's also dependent on frame rate.

Now what lerp would be saying is "move followLerp of the way toward target each 60th of a second".

That's the plan, anyway.
@MSGhero
Copy link
Member Author

MSGhero commented Jul 23, 2014

Added those changes. The FlxCamera demo doesn't do the lerp justice anymore since 1 -> .9 is much less drastic than .2 -> .1 or even .1 -> 0.

@Gama11
Copy link
Member

Gama11 commented Jul 25, 2014

Hm... it seems very strange to me that the range of values for followLerp varies depending on the framerate. That's not exactly framerate-indepedant, is it?

@JoeCreates
Copy link
Member

@Gama11 Perhaps it's just better to leave it framerate dependent, then? :P It's not that tricky for people to account for framerate differences themselves.

If we could come up with a more flexible solution with easing it might be a bit better, but that seems pretty tricky, too.

@Gama11 Gama11 changed the title Removed extra math in FlxCamera Improvements to FlxCamera#followLerp Jul 27, 2014
Gama11 added a commit that referenced this pull request Jul 27, 2014
Improvements to FlxCamera#followLerp
@Gama11 Gama11 merged commit 07cbf1b into HaxeFlixel:dev Jul 27, 2014
Gama11 added a commit to HaxeFlixel/flixel-demos that referenced this pull request Jul 27, 2014
@Gama11
Copy link
Member

Gama11 commented Jul 27, 2014

Hm... I've merged this for now, but it just occured to me that the max possible value on the update fps might be able to lead to some very confusing behaviour when the user changes the fps at runtime, after the creation of the camera where the followLerp values is determined.. Might wanna revisit this later.

@xaedes
Copy link

xaedes commented Dec 18, 2018

I have previously done the math behind this very case: Lerp with variable time step.
The code before this merge was on the right way, but it was not quite complete.

TLDR: The mathematical correct solution for your kind of lerp with variable timestep is:
scroll += (scrollTarget - scroll) * (lerp * elapsedTime) / (lerp*elapsedTime - lerp*(1/60)+(1/60))

Where does the (1/60) come from?
A lerp like scroll += (scrollTarget - scroll) * lerp only makes sense with constant timestep. So there is some assumption about the constant timestep baked in the single lerp parameter. From the code I guess, it was meant to be at 60 FPS, hence (1/60).

For details see: https://xaedes.github.io/tech-gain-filter.html#h2_applications
It starts from an equation corresponding to scroll += (scrollTarget - scroll) * lerp.
Then this is transformed into a gain filter formulation. See website for what that is, basically just x_new = y * gain + (1-gain) * x_old.
Now the on the website derived adaption of the gain filter to variable timestep is used to compute lerp for a variable timestep. The result needs a reference lerp in [0..1] with a reference timestep as input. I think this should be the lerp in code and (1/60).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants