Real Game Programmers

Started by
45 comments, last by zilch_ 21 years, 7 months ago
It''s funny how people are noteicing faults in the replay handling in commercial games.
I actually didn''t think they existed.

zilch_
Advertisement
quote:
hate to burst your bubble, but

#include <iostream>#include <stdlib.h>#include <conio.h>using namespace std;int main(){ float x = 0; x += 1.0f/3.0f; cout << x << "\t"; x += 1.0f/3.0f; cout << x << "\t"; x += 1.0f/3.0f; cout << x << "\t"; system("pause"); return 0;}


the output of this is:
0.333333 0.666667 1


Fair enough, good proof

At least for that case, I wonder if it works in all similar cases?

I gave it a go in VB


    Option ExplicitSub Main()Dim n As LongDim result As BooleanDim passes As LongDim fails As LongConst MAX_TEST As Long = 100    For n = 1 To MAX_TEST        result = Test(ByVal n)        If result Then            Debug.Print n & " has PASSED the test!"            passes = passes + 1        Else            Debug.Print n & " has FAILED the test!"            fails = fails + 1        End If    Next    Debug.Print "There have been " & passes & " passes and " & fails & " fails."End SubFunction Test(ByVal n As Long) As BooleanDim i As LongDim x As Single    For i = 1 To n        x = x + (1 / n)    Next    Test = (x = 1)End Function    


The final output was:
There have been 25 passes and 75 fails.  


Tests 1 to 9 worked OK but after that most numbers started to fail the test. So, at different FPS, different delta-times would be involved, and would therefore be rounded differently, giving different outcomes in a game.

quote:
but the timespan is not the same


I don't understand, 2 frames @ 2 FPS is the same length of time as 3 frames @ 3 FPS, both are 1 second exactly.

quote:
- TimeFrame model. (for_each(ms) { Update(); })
You *will* have errors, but they will be the same on all computers. Rounding errors also aren't a problem since they will be the same on all computers. Afaik, Quake uses this model.


I concede that I was wrong before.

I did point out that for_each(ms) wouldn't work because in reality the amount of milliseconds passed wasn't an exact integer. On second thoughts, it doesn't matter, since any lost fractions of milliseconds lost will be made up for later. This seems like the perfect solution! I think I am going to try to use this model. It might take alot of CPU though. Assume a game runs at a normal 50 FPS, thats 20ms per frame, so we would be repeating out physics calculations 20 times each frame. Ofcourse, we don't have to use 1ms as our time step, and maybe we could adjust the time step for more speed and less accuracy.

Thanks to everyone who posted, I've learnt alot and I think Cygon summarized the topic very well

[edited by - bazee on September 10, 2002 2:29:18 PM]
Theres a forgotten aspect to this argument - the scale of the world.

In Quake, one unit is very very small; it can afford to be inaccurate, because unless it is off by whole numbers, you aren''t going to notice. In a game such as Jedi Knight, where the units are very large (One unit equals about 20 feet in that game...) you get noticable artifacts in the collision detection.

The problem of how accurate your physics scheme can be easily avoided by just making rounding errors less noticable. Ultimatly, however, you will still need some method to fix these errors. In a racing system, for instance, you could ''granulize'' the vehicle position when it hits something (Calculate the ''bounce too'' position, and... round it off!).

For the ''for_each_ms'' thing - theres a great example of this system out there already. Halflife. I''m fairly certain that HalfLife runs on 100ms intervals. It keeps two positions for the moving objects, and interpolates between the positions smoothly while rendering during those 100ms. Every 100ms, it does the physics, and updates the ''next'' and ''old'' positions, which are used during the next 100ms of rendering.

If the player fires their weapon, and it has to create a new projectile, the ''interval'' can be ended immediatly, and the calculations done so that the new projectile operates properly in the next ''interval''. In this case; the first interval is just cut short. The next starts immediatly and is 100ms long.
1) commercial games dont have to be 100% accurate. Q3 has rounding errors such that there are particular jumps that can be made only if you update at a certain frames per second (the sweet spot). The problem will only arise if you assume that different machines are 100% accurate.

Obviously replays are one area that you could (mistakenly) have running at the same speed. Driver on Playstation1 I gather was notorious for its difference between gameplay and its replay (ie, succeed a mission, watch the replay and then fail).

Games such as Q3 however already have seperated the idea between the actual gamestate and what is being rendered - the rendering being an interpolation and/ or extrapolation of the actual gamestate. And the actual gamestate can be modified in retrospect at any time, both of which are requirements of non-fixed platform applications and network applications. So I find it unsurprising that their replays work 100% of the time on any machine at any speed. (not they just store pad inputs though)


Robert Swan
My company - Aah Games - http://www.aahgames.com
Continuing on what I was saying about ''granularization'' - lets take a 2d example.

The 2d sprite has a float position that it uses for it''s motion, to keep the motion nice and smooth. But when rendering and doing collision detection, you can round it off to the nearest pixel. This works quite well, and makes pixel-perfect collisions much easier.

There is another way to get rid of rounding errors. The error will always be a very small different, like 0.00001. So, instead of using == to compare floats, you make yourself a little macro like so -

#define float_compare(a,b) a > b-0.00001 && a < b+0.00001

Then, in code, instead of a==b, do if (float_compare(a,b))

In this case .99999 = 1.
I think it was Quake where running the game at different frame rates would allow you to jump further due to rounding. If you locked the fps to a number slightly below a harmonic of the refresh rate, I think it would allow you to jump slightly higher.

A solution (as already mentioned) is to run the logic engine at a constant frame rate with the graphics only being rendered if there is enough time left. For replaying, just have a couple of keyframes throughout just in case

Trying is the first step towards failure.
Trying is the first step towards failure.
quote:Original post by Cygon
For a slow computer, each car would move larger steps per frame and the collision could occur somewhere else because car A was moved and ran into car B before car B could perform its movement/collision detection.




this wouldn't be a problem if you updated every object and then checked for collisons

edit: and when a collision occurs you can take the amount the two objects overlap by and then it would be simple to work out the exact point of collision by using the two velocities as a proportion of that overlap. well, for a head on collision it would be simple (ie 1 dimension) but for 3d would be harder but same principle. not entirely sure if that would be exactly the same on every machine but i'm pretty certain.


[edited by - necromancer_df on September 12, 2002 6:38:34 PM]
Don''t you just let the game slow down when the time between intervles exceeds the preset loop time.

Say 100ms steps.

If game loop < 100ms then wait until 100ms has passed before starting next loop.

Else start next loop and reset counter to show that 100ms has passed.

Of course using this method in multi player will cause the game to run as slowly as the slowest player...

--------------------------
Dreamstars 3: A space combat game with depth?!
http://icarusindie.com/Dreamstars
--------------------------Dreamstars 3: A space combat game with depth?!http://dreamstars.jaishaw.com
not sure if ur question was answered before, but this should do it anyway. instead of adding velocity * time_passed_last_frame to object''s position every frame, do that every 50 ms (or some other value). for example, if 16 ms passed, you do nothing. 46 ms passed? still nothing. but as soon as 50 or more ms passed, you do a ''tick''. bah, why explain this again, when there''s a great article on it. it''s called a fixed time step model, instead of (what you were talking about) a variable time step model. here''s the article. the point is that the outcome will be 100% the same if you play on any computer. on a very slow one, it will have to do more than one world-state-update per frame. sorry, it''s 0240 hours () here, so i might not be making sense...

---
shurcool
wwdev
Tombstone uses that method. I actually "invented" it for Tombstone. It''s great for tile based games where you can''t really do fractions of moves like you can in real 3D. You basically have a max frame rate with a fixed interval. It takes only a couple if statements and it will skip frames as well if the computer is too slow. It behaves exactly like time based movement except you have a maximum frame rate. Yombstone is a monster but it will run even on a P200 because of this method.

Tombstone runs at several thousand interations per second but will only put 30 frames to the screen. The server runs at 500,000 iterations per second but only puts a frame to the screen once every 5 seconds.

Quake: framerate == iterations per second
Tombstone: framerate is independent of iterations per second until the IPS dips below the maximum framerate in which case frames start dropping so the IPS can maintain at least 30 so everything stays synced.

Ben


IcarusIndie.com [ The Rabbit Hole | The Labyrinth | DevZone | Gang Wars | The Wall | Hosting | Dot Com | GameShot ]

This topic is closed to new replies.

Advertisement