HaxeFlixel / flixel

Free, cross-platform 2D game engine powered by Haxe and OpenFL
https://haxeflixel.com/
MIT License
1.98k stars 439 forks source link

Native fps is different from flash fps #439

Closed Gama11 closed 11 years ago

Gama11 commented 11 years ago

Previously mentioned in #436 by

@crazysam:

60 fps native runs very differently from 60 fps flash. In my project I had to set native fps to 100 in order to get the same perceived game-speed. I think this is something caused by the accumulator timer that Flixel uses.

and @Beeblerox:

there are some weird behavior with framerate on native targets: framerate will be much less than expected if you set it to 60, but if set it to something like 63 or 65 (i don't remember exactly, you'll need to experiment with values) then it should work much smoother and faster. it's a quite old "feature" of NME

PaulGene commented 11 years ago

Done some tests on Windows and iPhone 4 and i'm not getting this problem.

AndreiRegiani commented 11 years ago

If can confirm this, with 60 fps it does NOT run smoothly on native, I set it to 100 now and it's running much much better. Obviously it's something with the rendering engine since my monitor updates at 60Hz.

AndreiRegiani commented 11 years ago

Hmm I noticed something semi-related but very serious, my game speed was originally adjusted for 60fps, now I set it to 100fps and ALL movements are slower! even using FlxG.elapsed! This shouldn't be happening. I tested with:

x += 100 * FlxG.elapsed; // should move 100px every second, independently of frame rate, but it's varying, the higher the FPS the slower it's moving

Same thing happens moving by: velocity = 100;

All Tweens are also affected.

@Beeblerox

Beeblerox commented 11 years ago

That's not good :( That's totally not good

i'll just go mad

Beeblerox commented 11 years ago

i think i know where is the problem, will try to fix it today

goshki commented 11 years ago

@AndreiRegiani are you sure that the game is able to maintain the target FPS? If not, then you'll experience a slowdown because FlxG.elapsed is calculated based on the target FPS but if the actual FPS is lower then the game will be updated less frequently.

I've seen a similar behaviour on a weak Android phone that was not able to maintain even a mere target 30 FPS. Everything moved like in slow motion.

This can be mitigated by removing this condition:

if (_accumulator > _maxAccumulation)
{
    _accumulator = _maxAccumulation;
}

in https://github.com/HaxeFlixel/engine/blob/master/src/org/flixel/FlxGame.hx#L353 but then you loose fixed-step updates.

AndreiRegiani commented 11 years ago

@goshki @Beeblerox I tested more deeply, the problem occurs only when FPS is set above 60:

PaulGene commented 11 years ago

I just noticed something weird that might be related, the FPS you put in the xml window tag doesn't seem to work. Tracing Lib.current.stage.frameRate always returns 60.

AndreiRegiani commented 11 years ago

@PaulGene I think the configuration that counts are those on FlxGame.new(..., GameFramerate, FlashFramerate)

PaulGene commented 11 years ago

Yes i realise that, i've been passing Lib.current.stage.frameRate to FlxGame.

AndreiRegiani commented 11 years ago

More tests, FlashFramerate is affecting on native platforms, I'm getting 60/100 FPS because I'm setting GameFramerate AND FlashFramerate to 100, it runs smooth but slows, if I set GameFramerate to 100 and FlashFramerate to 30, it runs at solid 30 FPS on NATIVE platform. Hope this can help to figure out, FlashFramerate shouldn't be affecting on native, right?

AndreiRegiani commented 11 years ago

SOLVED for me: I'm getting now a smooth 60 fps, I just needed to set GameFramerate to 60 and FlashFramerate to 100 super(gameWidth, gameHeight, GameplayState, ratio, 60, 100);

Seems it was only a naming confusion, FlashFramerate doesn't mean Framerate for Flash Player.

GameFramerate: "How many times you want your game to update each second." FlashFramerate: "How many times you want your game to update each second. NOTE: This is NOT the same thing as the Flash Player framerate!"

@Beeblerox

sergey-miryanov commented 11 years ago

I'm not sure, but maybe we should rename it to UpdateRate and RenderRate?

AndreiRegiani commented 11 years ago

@sergey-miryanov Renaming is indeed necessary to avoid future confusions, but why are two rates needed?

sergey-miryanov commented 11 years ago

Look this - http://gafferongames.com/game-physics/fix-your-timestep/, especially Free the physics section

AndreiRegiani commented 11 years ago

From the forum: FlxG.elapsed as a constant? Is semi-fixed timestep really a good idea? FlxG.elapsed should return the difference from the last frame, which would make animations really frame-independent.

I'm testing a "60FPS" game on old computers/devices, it reaches ~30, but instead of "skipping frames" and keeping normal speed, it slows down the speed and renders all frames, that makes the game unplayable! Wouldn't happen with (regular) fixed time step." -- http://haxeflixel.com/forum/help/how-to-calculate-the-best-fps-in-realtime

When I have time I'll implement real fixed time step ("Delta Time") and post results here.

AndreiRegiani commented 11 years ago

Done (fast implementation): FlxGame https://dl.dropboxusercontent.com/u/21553459/FlxGame.hx

Results are excellent, game running at 20FPS still playable, I will use it on my product final release.

What you guys think?

AndreiRegiani commented 11 years ago

These are the lines changed on update()

var time:Int = Lib.getTimer(); FlxG.elapsed = (time - _lastTime) / 1000; _lastTime = time;

// old implementation ("semi-fixed time step") //FlxG.elapsed = FlxG.timeScale * stepSeconds;

Gama11 commented 11 years ago

I'm not sure why Adam chose to use fixed timestep, but I'm sure he had a good reason for it. Could very well have been the issues that start to occur with physics like the article @sergey-miryanov linked claims.

Anyway, I have noticed in the past that games in flixel that don't run at max framerate feel horribly slow, to the point of being unplayable, so that definitely is an issue. This is worth considering and more testing should be done-

That code you posted has a flaw - you shouldn't use Lib.getTimer(). It's explained a few lines above:

#if !FLX_NO_DEBUG
if (FlxG.debugger.visible)
{
    // getTimer() is expensive, only do it if necessary
    mark = Lib.getTimer(); 
}
#end

I think @crazysam pointed this out originally, so I'm not sure how much of an impact this really has. But at the very least, this shouldn't be done twice per frame, no reason for that.

I tested this with FlxBunnyMark. It really makes a significant difference:

FlxBunnyMark, space to switch between semi-fixed (default) and fixed timestep.

There's some weird behaviour when switching between the two modes.

PaulGene commented 11 years ago

He probably went fixed timestep because of the recording / playback system. Please don't replace fixed timestep with this, i need fixed / frame skipping ! . An option to use it would be good though.

Gama11 commented 11 years ago

@PaulGene Not sure about that. After all, the recording system was only introduced in 2.5.

This could be made optional fairly easily.

Why do you need this exactly? Do you use the vcr?

PaulGene commented 11 years ago

Exactly... older flixel used delta time...

Gama11 commented 11 years ago

@PaulGene Oh, ok, good to know (I actually didn't). Still, why exactly do you need fixed timesteps? For the vcr?

PaulGene commented 11 years ago

Not the VCR no but something similar, the async multiplayer in Renegade Racing mobile is basically a recording / playback system and unless you do things frame based it gets a bit messy. Also fixed time step is better for physics engines.

Gama11 commented 11 years ago

@PaulGene I'm wondering if this actually messes with nape integration, or if nape has its own update cycle?

I guess the delta time could be made the default setting, since the vcr is turned off by default. In case FLX_RECORD has been set, or another flag FlxG.fixedStep maybe, that logic is used. More testing, especially with physics, needs to be done though.

AdamAtomic commented 11 years ago

so modern-ish Flixel does use fixed timestep, but still provides the flxg.elapsed variable to allow you to do framerate safe coding still.

for example, you could just ignore flxg.elapsed completely, and just increment variables, etc, and it will work fine, but if you were to change the framerate later, then you'd have to alter everything in the whole game. if you use a fixed timestep and flxg.elapsed, then you can change the framerate later in development, and not have to worry about breaking everything you wrote so far (if you're worried about that)

its a bit weird but its handy for some things! i think to get true delta time, or get a nice range of min/max framerates, you'll probably have to override flxgame and make some adjustments, and yes, fixed framerates are a huge win for physics, for frame stepping, for replays, for a lot of things...

hope that helps!

gamedevsam commented 11 years ago

Running in a fixed (or semi-fixed) time step is important for physics and networked simulations. In addition, not skipping frames can give the game a "retro" feel (old games chugged and slowed down to process every frame), and a lot of games made with Flixel are "retro-like" games that seem to target that nostalgia factor.

I think we could introduce a new haxedef: FLX_NO_FIXED_TIMESTEP

This would make the game loop update and render as fast as possible, and FlxG.elapsed would represent the "real" time elapsed between frames: FlxG.elapsed = FlxG.timeScale * (elapsedMS / 1000);

This would break recording and frame stepping, but will be disabled by default.

I need to speak with Beeblerox to make sure he's OK with it.

gamedevsam commented 11 years ago

Made a pull request that implements this feature (optionally of course). https://github.com/HaxeFlixel/flixel/pull/526

AndreiRegiani commented 11 years ago

Thanks @AdamAtomic for responding my tweet, very nice to have you here. Thanks @crazysam for the final implementation :)

@Gama11 the weird behavior when switching modes was because in my (test) implementation _time was set to '0' initially, when subtracting to getTimer(), FlxG.elapsed was set to a high value like '22867.493' in the first frame instead of a 0.015ish.

gamedevsam commented 11 years ago

Using variable timestep is now possible by setting FlxG.fixedTimestep = false;. This should fix the issues where perceived game speed is different on different platforms. I'm not sure it addresses the original issue though, that the same fps setting makes the game run differently on Windows and Flash targets. We should investigate this more.

impaler commented 11 years ago

Since we now have FlxG.fixedTimestep this issue which is marked as a bug, should be closed. Great discussion here guys.