Stuttery movement


#1

I’m making a little game with cinder. So far I’ve got everything working just fine, but when I build and run the thing, animation is stuttery.
I expect this has something to do with keyboard input: animation is fairly smooth, but when I hold a key things seem to start stuttering.
I found this: https://forum.libcinder.org/topic/keypresses-repeated-by-windows-make-my-app-lag

This seems to be about the same problem I’m having. The only difference is that the problem on my program persists, even with VSync turned off. (I tried turning vsync off through the nvidia control panel and with gl::enableVerticalSync(false).).

I do feel like this issue is VSync-related though: whenever the stutter happens, the framerate seems to get halved.
I’m using a laptop, and used this little piece of code:
extern "C" { __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; } extern "C" { __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; }

This requests to use an AMD or NVidia GPU when possible, because I couldn’t get line smoothing to work on my Intel (integrated) GPU.

Has anyone else experienced something similar before? And if so, would you happen to know why this happens or how to fix it?


#2

Hi,

the stuttering may have something to do with what you do in your keyDown( ci::app::KeyEvent event ) method.

Windows sends a single WM_KEYDOWN event when you press a key. If you hold the key, Windows starts sending more WM_KEYDOWN events until you let go of the key and a WM_KEYUP event is sent.

Cinder detects the system events for you and calls your application’s keyDown() and keyUp() methods. If you’re doing a lot of processing in those methods, this may lead to stuttering.

I know there have been reports of stuttering, even though the key methods didn’t do much. It happened to me, too. But for what it’s worth: I just ran a demanding Cinder application while holding the q key and it did not stutter. One of my tools is a frame rate monitor and it showed a steady 60 fps. So, at least for me, repeated keys don’t cause any stuttering.

I don’t think it’s related to enabling your discrete GPU.

If you think it might be something in your keyDown() method, perhaps you can share some code?

-Paul


#3

Thanks for the answer!
I’m not using the keyDown and keyUp methods, but a little KeyListener class.
Anyway, here’s how every button gets ‘listened’ for:

bool KeyListener::downKey() { return(GetAsyncKeyState(VK_DOWN) != 0); }
This is a simple example for the down arrow, but every key (up,left,right,‘C’,…) I need has the same code.

Here’s the things that happen when an arrow is pressed:

void Player::movestd(KeyListener &a, double dt) {
	double standardt = (1 / 60.0);
	if(a.xKey() && energy > 7){
		energy -= 0.16;
		if (spedUp == false) {
			spedUp = true;
			maxVel *= 1.9;
		}
	}
	else {
		if (spedUp == true) { 
			maxVel /= 1.9;
			spedUp = false;
		}
		if (!a.xKey()) { energy += energy < max_e ? 0.4 : 0; }
	}
	if (a.upKey()) {
		if (vel > maxVel) { vel *= 0.83; }
		vel += (vel >= maxVel ? 0 : maxVel/10)*(dt/standardt);
	}
	else if (a.downKey()) {
		if (vel < maxVel) { vel *= 0.83; }
		vel -= (vel <= -maxVel / 2 ? 0 : maxVel / 10)*(dt / standardt);
	}
	else {
		vel *= 0.83;
	}
	if (a.rightKey()) {
		the += M_PI / 60.0 * (dt / standardt);
		if (the >= 2 * M_PI) {
			the = 0;
		}
	}
	if (a.leftKey()) {
		the -= M_PI / 60.0 * (dt / standardt);
		if (the < 0) {
			the = 2 * M_PI;
		}
	}

	xp += cos(the)*vel*(dt/standardt);
	yp += sin(the)*vel*(dt/standardt);
}

void Player::behave(KeyListener &a, std::vector<Bullet> *bulletList) {
	if (xp < 0) {
		xp = ci::app::getWindowWidth();
	}
	if (xp > ci::app::getWindowWidth()) {
		xp = 0;
	}
	if (yp < 0) {
		yp = ci::app::getWindowHeight();
	}
	if (yp > ci::app::getWindowHeight()) {
		yp = 0;
	}
	
	if (a.cKey()) {
		if (!shot && energy > 12) {
		Bullet * bull = new Bullet(xp, yp, the, bulV, 5);
		energy -= 8;
		bulletList->push_back(*bull);
		delete bull;
		}
		shot = true;
	}
	else {
		shot = false;
	}
}

Maybe I’m doing some weird stuff with pointers etc., but I’m still very much learning C++ :slight_smile:
(little off-topic: I’m using standardt and dt to adjust movement to the fps the program runs at, that seems to work pretty OK)

This is every piece of code that uses the keylistener, so I don’t think any other code in my program interacts with the listener.


#4

Hi,

apart from potential logic problems, timing problems and coding style (none of which are important at the moment), I think your code is OK. You’re relying on GetAsyncKeyState, effectively circumventing the system events. Your animations simply want to know if a key is pressed and change their behavior based on that. So all looks good.

In order to rule out timing issues, you may want to make sure you either have vertical sync enabled (at a steady 60 fps) or use Cinder’s built-in frame rate limiter (setFrameRate( 60.0f );) and then use a fixed dt when you call:

movestd( keylistener, 1.0 / 60.0 );

If that removes the stutter, you should look into the way you measure elapsed time and the way you apply that to your equations. Otherwise, nothing really stands out as wrong to me.

-Paul

P.S.: an alternative to using dt can be found in this excellent article.


#5

Hi

Thanks for the answer!
Setting a 1/60s timestep and limiting the framerate gives about the same result as relying on framerate-based animation (which I did at first) and then setting the framerate to 60: things move the way they should.

I think I’m going to try to change the way I’m measuring time: the little ‘framerate-monitor’ i put into the program often records ‘framerates’ (the time between calculations actually) of up to a couple hundred (even with the framerate set to 60), so I’m thinking that’s where the issue is at… Thanks :slight_smile:

I’ll also clean up my code a bit, I made it to just kind of work, thanks for reminding me.

The article you linked is really nice, I’d read it yesterday as well, but thought that just the dt would be good enough, considering the only computer this ever has to run on is mine, and I’m not trying to simulate anything very precise. Turns out I was wrong :slight_smile:

I’ll try to clean up my code as well as I can, implement the timestep fix from the article, and if that doesn’t work, try to look for another way of measuring time.

Thank you very much for the answer!
-Arthur


#6

Problem solved (mostly, I think).

I used the semi-fixed-timestep -solution from Fix your timestep, but tweaked it a bit: I changed ‘frametime’ to be the difference in time between 20 frames instead of one, divided by twenty again. My thinking here was that that might correct for eventual inaccuracies that occur when asking for one frame, by taking the average over twenty frames.

Slightly to my surprise, but this seems to have worked pretty well :smiley: .

Obviously this isn’t a perfect fix: sometimes things speed up or slow down a bit, but it’s good enough for now.