Closed clintbellanger closed 10 years ago
I think that makes sense, as long as the "closest" interpolation isn't determined per-animation but based on the global clock. Basically I'd want to avoid 122344
always being the solution (we'd probably see weirdness in the animation?) but rather based on what the global timer really is, that same animation could end up 112334
sometimes.
Not sure I really explained what I mean, but hopefully you get the gist.
Would it make sense to change animation durations to represent the duration of the entire animation, instead of the duration of a single frame? Given a total duration and the total number of frames, we should easily be able to calculate approximate frame duration based on our fps cap. Sure, this can be inaccurate sometimes (a 15 frame animation lasting 500ms would actually be 495ms), but I think the interpolation solution would hit the same issue when frames can't be spaced nicely.
Think about the Bresenham algorithm here, which is an algorithm for drawing lines in a pixelized environment. Now imagine the y axis as the index of the animation and the x axis as the time, so we maybe want to have something like this:
@stefanbeller that's exactly what I meant! I guess a picture is worth a thousand words.
Here is the link I forgot, http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
I'm not sure if Bresenham would be the right solution. Given the previously mentioned 200ms, 4-frame animation...
At 30 fps it seems alright:
Duration: 200ms | Frames: 4 | FPS: 30
122334
But then 60 fps isn't optimal, spending too much time on the 2nd and 3rd frames:
Duration: 200ms | Frames: 4 | FPS: 60
112222333344
Maybe we need a 'balancing' bresenham? So to pick up the example with 4 frames and duration 200 milliseconds, at 30 fps we'd have 6 frames in 200ms, but just 4 sprites to render, so the solution was
122334
but this would put too much weight on the 2 and 3 as well?
Maybe we'd need to alternate to 112344
instead or rather 112334 122344
I'm less concerned about the 30 fps example, since 4 doesn't go evenly into 6. It's acceptable to have some imbalance, and I think we should keep the imbalance consistent instead of alternating it.
I was focusing more on the 60 fps example where 4 goes evenly into 12. Each frame should be 3 ticks. Instead we get two frames with 2 ticks, and two frames with 4 ticks.
Heh, I assumed the bresenham would produce an 'even' line with 4 times 3 ticks for 12. Just tested and you're right, it's likely not a good idea to employ the pure bresenham algorithm.
I've worked on getting the engine & data to a state where this can be experimented with. The total duration of each animation in flare-game is evenly divisible by the number of frames. I did just that and set the number of ticks per actual frame as duration/frames. I provided the Bresenham algorithm as a fallback, but we don't see its effects with the current game data.
Anyway, here are the branches for engine and data. Please be aware I haven't touched devlab and minicore in the data yet: https://github.com/dorkster/flare-engine/tree/60fps https://github.com/dorkster/flare-game/tree/60fps
Right now, the game seems perfectly playable when switching between 30fps and 60fps without changing any other code. However, here's what still needs to be done:
I want to take one more look at this for Flare 1.0, just in case we can figure out a better way than what we have.
We started to make this transition, but maybe our approach was off.
I want to express all animation timing in terms of milliseconds. I also want the same data to work at 30fps and 60fps (maybe beyond). Right now if we wanted to change Flare Game from 30fps to 60fps it requires halving all frame and speed variable in all of the data. When really we should be able to flip one variable (the max static FPS) and all the game's content data still works.
The problem I was running into was with Animation timing. Internally we took that desired millisecond timing, compared to the current fixed frame rate, and converted those ms timings into frame timings. But frame timings for animations are using a single framecount duration for all the frames in an animation.
Example. Let's say we create an animation with each frame at 50ms timing. At 60fps this works predictably, with 50ms timing being the same as 3 frames (16.6ms per frame). But if we run that same animation at 30fps, the closes frame counts are 1 frame (round down to 33ms) or 2 frames (round up to 66ms). Changing a 50ms animation to run at 33ms or 66ms feels drastically different, and obviously wrong, which is when I scrapped the idea.
Maybe animations need a more sophisticated internal counter to help get around this. Internally animations could track total milliseconds elapsed, and use that to calculate the nearest frame each frame (instead of once for the whole animation).
E.g. if we have a 4-frame Running animation that we want at 50ms, this is how the frames display at 60fps:
At 30fps we have two less than ideal options, if we convert that 50ms to a fixed framecount.
When really, a staggered frame timing would give a better approximation of the desired 50ms, so it may be calculated like:
Some frames display once, others display twice. But the whole animation loop lasts 200ms -- now it's the same pace at 30fps or 60fps.
I think the basic logic would resemble this (pseudocode):
This wouldn't be perfect any single frame, but over a few frames it should keep about the right animation speed.
If frames can be skipped entirely, we would need to tweak the "Active Frame" trigger so it still triggers on a skipped frame.
I'd like to hear thoughts on this, whether this makes sense or falls apart in certain cases.