Last time I mentioned that I had made a stick figure to move around. I implemented four degrees of movement for it, but I still don’t have a timer to make it move at a sane rate. Since this project is late anyway, I figured I might as well learn about implementing frame rate independent movement.
I actually looked at my GiD entry and found that it was handling the timer code badly. Basically, the game would update every time an SDL Event occurred. Normally the event would just be the timer callback and would run fairly regularly. But if you pressed a key or released a key, an extra update and draw would occur. Now, I programmed it to allow you to hold the key down to move so it only speeds up when changing directions and may not be as noticeable; however, if you press keys multiple times, it becomes very obvious. For my first GiD, I suppose it isn’t that big of a deal.
For Oracle’s Eye, I decided to create frame rate independence. It basically means that the rendering happens independent of the game world updates. The game can go from 30 frames per second to 100 frames per second to 15 frames per second, but the game world will still update once every 10 milliseconds or whatever I want to set it to. It should result in smoother animation and movement.
When I started this project I didn’t think that I would be working on such an “advanced” topic. It sounded a bit complex and beyond my abilities. Well, screw that thinking. It can’t be THAT far beyond what I can currently do. In fact, when I think about it, making games in general is still a challenge I haven’t decisively overcome yet, so I’m charging forth. Screw you, Pessimism!
Luckily someone just posted about this topic on the SDL mailing list and so now I have a link: Game Physics: Fix Your Timestep!. Reading it gave me some idea about what needs to be done, but variable names like t and dt and functons that come out of nowhere make it hard to follow. I was able to find another article on gamedev.net called Achieiving Frame Rate Independent Game Movement. It is a bit shorter, more to the point, and easier to follow.
Problem # 1: It is a good thing that I haven’t gotten too far with my game project because implementing this feature is going to require a redesign. I originally envisioned moving my characters about by telling them to move so many pixels directly. For example,
player->moveX( moveSpeed );
should move the player sprite to the right by moveSpeed distance. If moveSpeed is 5, then the sprite will move to the right five pixels.
It would have been fine, but with FRI movement, I would need to do something different. As I understand it, I would have to know where the sprite started and where it will end up. Then I have to calculate at any given moment where it is along that path. Whereas before I would have moved the player sprite an arbitrary number of pixels, now I will need to determine how many pixels it moves each second then calculate how far to move it per frame. If a player should move 60 pixels per second, then the total movement of the sprite in all frames in that second should add up to 60 pixels. The trick is figuring out the movement each frame.
Problem #2: FRI movement works much better in 3D than in 2D. In 3D, the actual 3D model is just math. You can interpolate the animation of the model, and it will look smooth no matter how slow or fast you run it. I’m using sprites, and sprites have only as many frames of animation as I created. Let’s say I have three frames of animation for a walking sprite. Normally, I would just update every so often and make the animation step forward each time, but with FRI movement, how do I handle the partial movements AND animate at a normal pace? I can’t update each movement because then it looks like the player sprite is running way too fast. Do I have to figure out how far it should move before each frame gets updated? Does it even make sense to have FRI movement in a 2D game?
Starting this, I felt a bit overwhelmed. That’s a lot of things to take in, and all I wanted to do with Oracle’s Eye is make a simple game. Still, I forged ahead. I figured it is better to learn about it sooner rather than later.
I found a few more articles: Main Loop with Fixed Time Steps on Flipcode and Frame Rate Independent Movement on Gamedev.net. I also found a few threads on gamedev.net that discuss “time based movement”.
It actually doesn’t seem so bad now, at least for Problem #1. In fact, I don’t have to change my existing code all that much. I changed the moveX() and moveY() functions to take floats as arguments instead of ints. I added some timer variables: one for the delta, one for the current time, and one for the last time.
In my PlayState::update() function:
gTemp_currentTime = SDL_GetTicks();
gTemp_deltaTime = (gTemp_currentTime - gTemp_lastTime) / 50.0f;
gTemp_lastTime = gTemp_currentTime;
Then for each of the directions, I just change the code from:
gTemp_player->moveX( -gTemp_speed );
to
gTemp_player->moveX( -gTemp_speed * gTemp_deltaTime );
The 50.0f is fairly arbitrary. The code I got it from used 1000.0f to convert from milliseconds to seconds, but I found that the delta almost never became anything greater than 0, which meant that nothing moved. Using 50.0f seems to be fairly fast and smooth on my system. Of course, it depended on gTemp_speed which is in pixels per second. If I set it to 50, it ran quickly. Setting it to 25 slowed it down quite a bit, but it was also jerky, and setting it to 100 made it too fast.
It also didn’t take me too long to implement it, so I got suspicious that I did something wrong. After all, it shouldn’t matter how fast the sprite is moving. I will likely have sprites moving at different speeds, so what gives?
I found yet another tutorial, and this one actually made use of SDL. It turns out that I don’t do much differently. I ended up having to add SDL_Delay( 10) to the draw() function to get it to slow down enough, but I think that is a bad kludge. I believe that the reason it won’t move the sprites between each frame is because there isn’t enough to do between frames. The time between frames looks like 0.0 and so nothing happens. The delay allows changes to happen, but there is still the ugly jerkiness every once in awhile.
The next day, I tried again. I changed it so that it will constantly loop until deltaTime is something other than 0. It worked and looked great, but I found out that I forgot to remove the SDL_Delay() line. When I removed it, it ran incredibly slowly again.
So I changed the loop. I basically checked to see if the start and end times were 1/30th of a second apart since I wanted the game to update game logic at 30 frames a second. If the difference was less than that time, I delayed. It seems to work out well. 1/60th of a second looks even nicer, but I’ll have to do more testing to verify.
Unfortunately the use of floats/doubles means that when movement gets converted to ints for Kyra’s sprite movement the precision gets lopped off. I think this loss of precision is the reason why some of the movement might look a bit jerky. Of course, Problem #2 might be a tougher issue, but I’ll have to tackle both of them another time.
I am fairly pleased with what I have done in a few hours over the course of a couple of days. To think that I originally believed that I wasn’t up to the task and would have skipped it. I win.