I'm trying to understand how doom2.exe handles player and projectile speed. Here is some info I've been able to gather.
Gamestate is updated 35 times per second.
Normal forward run speed is 50. Rocket speed is 20. But these speeds must be on different scales, because rockets travel faster than a player running straight forward (no strafe run).
Also, the player has a width of 32 and height of 56, but horizontal and vertical scales must be different because the player looks to be about twice as tall as he is wide (on the screen), yet 56/32 = 1.75.
So how do these distances and speeds relate to each other? Perhaps if I can get answers to two questions, I can figure the rest out:
1. How does doom2.exe translate a player speed of 50 into horizontal units per second? It seems to be about 410 horizontal units per second, but I'm not sure about this.
2. How does doom2.exe translate a rocket speed of 20 into horizontal units per second? It seems to be about 520 horizontal units per second, but I'm not sure about this.
I downloaded the game source code, but it's a lot to sift through for someone who doesn't know C. Still, if anybody can point me to the relevant file(s)/routine(s), I'd appreciate that as well.
Thanks,
nogame
doom2.exe distance and motion calculation
- Caligari87
- Admin
- Posts: 6236
- Joined: Thu Feb 26, 2004 3:02 pm
- Preferred Pronouns: He/Him
- Contact:
Missile speed is straight speed. Player speed is actually an acceleration. The player's speed is added to their current velocity every tic, but they are also subjected to friction every tic to slow them down. Eventually, the player reaches an equilibrium between acceleration and friction, and they move at a constant speed. I would have to look in the source, which I don't have handy right now, to get the amount of friction.
Eureka??
Thanks for the help, people. After poring over the source code and learning lots of C++ along the way, I think I may have figured something out. Here's an explanation and a list of relevant files/modules.
The maximum user input for forwardmove is 0x32 (50 base 10). forwardmove is then multiplied by 2^11 and added to momentum. Momentum is broken into x and y components. The same calculation is done for sidemove, which normally has a maximum user input of 0x28 (40 base 10), except for a bug that allows a user input of 0x32 when SR50 is activated. However, for this explanation, I'll assume only forward run entirely in the x direction.
In an open field going at full forward run, the game attempts on every tic to advance the player by a number of units equal to the momentum, which is subject to a maximum value that is never reached under normal running conditions. After advancing the player, the game simulates drag by multiplying momentum by the fraction 0xe800/2^16 (29/32 base 10).
To get the cruising speed, in units per tic, use the sequence
s_0 = 0
s_k = 50 * 2^11 + (29/32) * s_k-1
Solving the sequence gives s_k = V * [ 1 - (29/32)^k ], where V = 50 * 2^11 * 32 / 3 ~= 1092267 is the limiting value of the sequence. This is how many units a player advances per tic going at full forward run. It takes about 4.57 seconds to technically reach full speed, but for all intents and purposes, full speed is reached in under one second, e.g. at one second, the player is at about 97% of full speed.
Multiplying V by 35 (b/c there are 35 tics per second) and dividing by 2^16 (b/c player and object dimensions are expressed in FRACUNITs, where FRACUNIT = 2^16) gives a player forward run speed of 583.3 units per second. From this, one may also compute other speeds, e.g. strafe running is about 747 units per second.
These speeds agree with rocket and plasma speeds. Rockets have a speed value of 20, so rockets advance 20*35 = 700 units per second. Plasma has a speed value of 25, so plasma advances 25*35 = 875 units per second. Of course, in Doom2, it's possible to catch up to a rocket by strafe running, but not plasma, i.e. these numbers seem reasonable. I also did a few trial runs using Dwango 5 Map 5, and the results were in the same ballpark. So, I feel somewhat confident about these numbers, except for two things...
First, this model predicts it takes 160 units (5 player diameters) and 1.6 seconds to come to halt from a full run after releasing the forward button. This seems too high. I'm hoping that I've simply overlooked something in how doom2.exe handles coasting to a stop. Also, the numbers I get do not agree with the following article:
http://www.doomworld.com/10years/demos/demos07.php
In the article, the author states: "Moving forward + run + strafe_left + strafe_on + turn_left makes you move about 580 units per second."
In other words, his SR50 speed is slightly less than my SR40 speed. I'm hoping it was a typo. I e-mailed the author, but so far no response.
Here are the source code files/modules I used:
g_game.c: G_BuildTiccmd
info.c: MT_ROCKET, MT_PLASMA
local.h: constants
m_fixed.c: FixedMul
m_fixed.h: constants
p_mobj.c: P_MobjThinker, P_XYMovement,
p_user.c: P_Thrust, P_MovePlayer
tables.h: angles and trig functions
Well, that's some of what I know. I also figured out that damn binary angle measurement, but meh, wasn't much help. Okay, any corrections here are welcome.
nogame
The maximum user input for forwardmove is 0x32 (50 base 10). forwardmove is then multiplied by 2^11 and added to momentum. Momentum is broken into x and y components. The same calculation is done for sidemove, which normally has a maximum user input of 0x28 (40 base 10), except for a bug that allows a user input of 0x32 when SR50 is activated. However, for this explanation, I'll assume only forward run entirely in the x direction.
In an open field going at full forward run, the game attempts on every tic to advance the player by a number of units equal to the momentum, which is subject to a maximum value that is never reached under normal running conditions. After advancing the player, the game simulates drag by multiplying momentum by the fraction 0xe800/2^16 (29/32 base 10).
To get the cruising speed, in units per tic, use the sequence
s_0 = 0
s_k = 50 * 2^11 + (29/32) * s_k-1
Solving the sequence gives s_k = V * [ 1 - (29/32)^k ], where V = 50 * 2^11 * 32 / 3 ~= 1092267 is the limiting value of the sequence. This is how many units a player advances per tic going at full forward run. It takes about 4.57 seconds to technically reach full speed, but for all intents and purposes, full speed is reached in under one second, e.g. at one second, the player is at about 97% of full speed.
Multiplying V by 35 (b/c there are 35 tics per second) and dividing by 2^16 (b/c player and object dimensions are expressed in FRACUNITs, where FRACUNIT = 2^16) gives a player forward run speed of 583.3 units per second. From this, one may also compute other speeds, e.g. strafe running is about 747 units per second.
These speeds agree with rocket and plasma speeds. Rockets have a speed value of 20, so rockets advance 20*35 = 700 units per second. Plasma has a speed value of 25, so plasma advances 25*35 = 875 units per second. Of course, in Doom2, it's possible to catch up to a rocket by strafe running, but not plasma, i.e. these numbers seem reasonable. I also did a few trial runs using Dwango 5 Map 5, and the results were in the same ballpark. So, I feel somewhat confident about these numbers, except for two things...
First, this model predicts it takes 160 units (5 player diameters) and 1.6 seconds to come to halt from a full run after releasing the forward button. This seems too high. I'm hoping that I've simply overlooked something in how doom2.exe handles coasting to a stop. Also, the numbers I get do not agree with the following article:
http://www.doomworld.com/10years/demos/demos07.php
In the article, the author states: "Moving forward + run + strafe_left + strafe_on + turn_left makes you move about 580 units per second."
In other words, his SR50 speed is slightly less than my SR40 speed. I'm hoping it was a typo. I e-mailed the author, but so far no response.
Here are the source code files/modules I used:
g_game.c: G_BuildTiccmd
info.c: MT_ROCKET, MT_PLASMA
local.h: constants
m_fixed.c: FixedMul
m_fixed.h: constants
p_mobj.c: P_MobjThinker, P_XYMovement,
p_user.c: P_Thrust, P_MovePlayer
tables.h: angles and trig functions
Well, that's some of what I know. I also figured out that damn binary angle measurement, but meh, wasn't much help. Okay, any corrections here are welcome.
nogame
Eureka??
Yes, SR50 is faster than SR40. By my calculations, forward run ~= 583 units per second, SR40 ~= 747 units per second, and SR50 ~= 825 units per second. I'm hoping that the discrepancy with the doomworld.com article is due to a typo, i.e. that the author intended to say normal run, not SR50, makes you move about 580 units per second. This would be in line with the results I got.