Friday, February 17, 2012

Bad Architecture 101

Finally, the poor design of Kaiken's internals is giving me headaches.

For a while, the sloppiness gave me no real troubles. But now, the various design flaws are starting to make progress difficult. My main goal tonight was a tough problem, one I should have saved for a day when I've done enough coding to get back into the groove, one that could have waited until I did some study on the subject and potted a reasonable course of action.

Instead, I dove headlong into frame-rate independent animation and movement. Up until now, Kaiken was strictly based on frames. Every frame, tell the level to update itself, which would, in turn, tell each object in that level (players, enemies, etc.) to update itself. These updates include such key things as changes in position or momentum, as well as checks for collisions. Everything was perfect; every frame, tell each object to move based on its momentum. If the momentum was {5,10,0}, the object would move 5 pixels to the right and 10 pixels up before the next frame was rendered.

This simple design is easy to code, but it has flaws. The biggest being that not every computer runs at the same speed. While modern computers can chug through the entire update and render process at about 3 to 6 milliseconds, this shouldn't be counted on. If the system is under load, it may take longer, and when the game slows down, it literally slows down. Even under optimal circumstances, there are subtle variations in games speed.

The rate the game runs at should not have an impact on how fast objects move in the game. Even running at 20 frames per second, an object should cover the same area as an instance of the game running at 60 frames. It will look choppier, but appear to be going the same speed.

The simple way of doing this transition (if one does exist) is to, rather than move the object by its momentum every update, move the object by its momentum multiplied by the time that has passed.  Find the number of milliseconds that have passed since the last update and make that a factor in how far the object will move in the current update.

A simple concept, truly, but it changed an entire portion of my shabby little engine, most notably the Physics class. This class is simply a bunch of functions that can be called to handle collision checking and movement. For instance, the move() function would take an object and add its momentum to its location, thus moving it to a new location based on how it was moving. The function now also accepts a number of milliseconds, which is now factored into this equation. No big deal.

Too bad the rest of the functions weren't this easy. Applying friction and gravity this way will require a complete re-write of these functions. Well, I guess that isn't completely honest. I've got gravity working pretty well, but friction isn't going so great, but I've got a couple ideas up my sleeve.

On the worst end is my collision checking code. It checks the momentum of the object in motion to guess what its previous position was. Because the momentum is now pixels per second, not pixels per frame, it's allowing things to get inside other things in a way nature never intended. The way I do physics will have to be completely different. Perhaps I might get it working by storing the object's previous position instead of trying to backtrack it with momentum.

Hopefully Kaiken will be more robust and clean by the end of this disaster. I'm not expecting that to be the case, however.

No comments:

Post a Comment