Hello and welcome to the Doom Movements Bible! The article sorted out and sorted into categories all the quirks and whims of the movement code in Doom, including intricate tricks describing their work.
Metrics
Let's start with the basics. The engine code for Doom player movements is as follows:
Collecting player baseline data
Applying driving force vectors to the player based on raw data
Squeeze the speed of the player if it is too high
Check, calculations and movements
Using acceleration through friction for the next movement of the player
Consider all the details. Thanks only to this and a couple of variables we will be able to determine the possible maximum speeds of the player’s character. ')
For obvious reasons, the maximum player speed is achieved when the acceleration specified by the player is equal and opposite to the deceleration from friction.
For each tick, the player receives a push equal to 0.03125 units in the calculation of the value of the entered command. Since teams are (usually) limited to a maximum of 50. This gives us a maximum acceleration of friction of 1.5625 units / tick ^ 2.
After calculating the movement for a tick, the player slows down using the friction coefficient. The specific value is 0.90625 - in other words, each player’s movement slows down by 90.625% of his current speed.
For the equivalence of acceleration and deceleration of each tick, with a speed of 50, we need to find the "x" at (1 - 0.90625) x = 1.5625. This leads us to a theoretical peak of a progressive speed of 16.666 units per tick. (Note that the actual empirical maximum speed, for certain reasons, is a little less than this value. But it is nevertheless close enough to the indicated indicator).
Also note that the player has a constant maximum speed given by the code. Even before the actual calculation of the player’s movement is performed, at the beginning of the code, the game checks whether the X- and Y-coordinates do not exceed a speed indicator of 30 units / movement ^ 2 (or less than -30). If you exceed, the game reduces the speed to 30 (or -30, respectively). We will not dwell on the speed limit, but you should note that this happens only before the start of the movement code and does not interfere with reaching a speed above 30 inside the movement code itself.
Summing up, I should note the following. The collision box of the player is actually square, and, more importantly, the bounding box is always aligned on the coordinate axes, regardless of which path the player follows. Later, this detail becomes very important for many things.
Strafe running
Another oddity of the Doom engine (as in essence and many other game engines) is that the X- and Y-coordinates are stored and calculated separately. This leads to one small but amusing effect. When a player moves forward and to the side at the same time, both motion vectors are used in the calculation and the direction of the general motion gets a greater magnitude than in the vectors separately.
In a normal gameplay, Doom Streyf has a maximum driving force of 50 units, and in lateral movement - 40. Thus, with a streyfe, an indicator of driving force per tick reaches 1.25 units and a maximum speed of 13.333 units / movements. If we consider the lateral and direct motion vectors as the sides of a right-angled triangle, you will find that the maximum theoretical velocity with a strafe is 21.34 units per motion. And this is 28% higher than normal speed, which is not very bad.
"SR50" strafe running
But there is a way to move even faster. According to some features of the code, it is possible to simultaneously hold several keys at once in such a way as to obtain a driving force when stretching 50 units, instead of 40. Thus you have the opportunity to accelerate to a maximum speed of 23.57 units / movement (that is 41% higher normal speed with normal running and 10% - with strafe running).
Such a variety of run stretch is usually associated with the abbreviation "SR50" for rather mysterious reasons. Professionals in speeding through the games have created various tools that are used to analyze demo versions. And with the help of some tools you can disassemble every tick. Actually, one of the creators was called “SR” - strafe right (strafe right), followed by the command argument. Since this technique made it possible to achieve a running run with a score of 50 instead of 40, the tools showed “SR50” instead of “SR40”. It was the “SR50” that became widely used as a synecdoche for this technique.
Oh, and, so to speak, for future generations - let's calculate the maximum running speed in the "real world":
23.57 units / movement * 35 movements / second = 825 units / second 825 units / second / 16 units / foot (total conversion factor) = 51.5 feet / second 51.5 feet / second = 35 miles per hour (about 56 km / hour).
Skip glide
trick at 0:02
Now let's go to specific techniques. The first of these is considered the most difficult to implement in practice. But at the same time, its easiest to explain from the position of the engine. I call this trick “skip glide” (skipping jump), although sometimes it is also called “bar glide”, which can create confusion because there is another trick with the same name.
To understand this trick, you first need to disassemble the basic way in which Doom tries to process movements. In each tick, DoomGuy has a defined x- and y-coordinates of the pulse (inertia direction). However, I will not call this “impulse”, since such a term implies certain properties that Doom does not have (you can move beyond the possibilities of “impulse”, or vice versa, you may have an “impulse” even if there is no movement at all) . Instead, I will speak about the “impulse” from the point of view of a potentially new position to which the player wants to go and refer to it as the “Teleport Attempt Position” or the SPT. (From this point on, when you see the abbreviation “SPT” in the text, you can mentally replace it with “impulse.” But I hope the use of another term will help you remember that in reality this is not a “real” impulse, but some fancy pseudo- An analogue that has other properties.)
The reason is that the player is essentially inactive during a tick. Instead, the game simply removes the player from one point on the map and instantly puts it to another. This kind of discrete movement is probably universal for games. But at the same time, a reasonable question arises: how to determine that you ran into something in front of you?
This feature helps to understand exactly how skip glide works. Consider the canonical example of Doom 2 when passing Map 21. The player takes a completely natural position right in front of the diagonal barrier, which closes the gap. On the next tick, the engine conducts a test to determine whether it is possible to teleport a player to a position immediately * behind * a diagonal barrier. With careful choice of direction, you can designate a position that does not contradict the conditions of the "teleport" and the engine will transfer the player to the desired point without any problems.
Squeeze glide
trick at 0:25
Also in the arsenal there is another, even more common trick, which is associated with the reception of "bar glide", because it is also based on the movement between the barriers, located at a distance of 32 units. For me, the term “squeeze glide” (sliding with “squeezing”) seems to be more adequate for memorization and less confusing.
For squeeze glide, the player must be perfectly aligned with a gap of the same size as the character (32 units). Then, after a series of unsuccessful attempts, which seem to last forever, he finally manages to squeeze into this gap!
In the theory of “squeeze glide” it should be quite simple and clear: the player has a width of 32 units (along the axes). And that means if you find a space that is clearly aligned along the axes and with a width of 32 units, you should be able to pass through it. But with the Doom engine rarely everything is so simple.
The main problem is that the motion in the Doom engine is quantized and, moreover, it is not completely quantized. I will not go into details and tell how the player's motion vector was calculated. But in short, the vector uses sines and cosines to combine the direction of the player with the motion vectors to match the final X and Y vectors of the driving force. For example, when a player pushes a key forward and heads north, the X-coordinate of the motion vector should be a value multiplied by the cosine of 90, which is necessary for zeroing. Unfortunately, Doom is not strong in saving angles and cosines, it uses calculated tables of values ​​and, as it turned out, the sine / cosine table does not actually include 0 as a value (the closest is ± 0.0002).
Thus, even at the maximum approaching to the north, a quantity appears that is still too small for a side step to the east or west.
So, knowing how SPT works for players, you can see what happens:
The player is trying to move in space directly between obstacles.
The engine uses SPT, which has a certain quantized coefficient of elongation, and determines the possibility of moving to one or another position
Since the gap has the same width as the player, the latter must EXACTLY match the space being traveled (inaccurate values ​​of the angle make the task really difficult). This means that for successful passage you need with perfect accuracy of 0.0002 units to be located west of the place between obstacles.
Understanding how to make squeeze glide work smoothly is not science, but real art. Speed-wise, hour after hour, learn the subtleties of moving to perform it in a reasonable amount of time.
Line skipping (south / west)
trick at 19:06
Understanding how the engine uses SPT players to test a new position will only bring us a little closer to understanding the line skip. It may seem that the player does the impossible when he performs a line skip: runs directly across the border line without actually activating it.
To understand how this works, you need to look at the order of events in the engine:
He uses the SPT player to test him in a potentially new position.
As part of the process, the engine makes a list of any overlap of a segment of a potentially new position.
If possible, move them to the position in question.
It draws the connecting line between the centers of the old and new player positions, and then checks each boundary in the already compiled list to see if the connection line crosses one of them.
If it crosses, checks these lines for special actions with their further activation.
Thus, as you can see, there are two possibilities for crossing the special boundary line: 1) touching the boundary line at the updated position, and 2) the border intersects with the line connecting the old and updated positions.
So, the algorithm is set. How can you get around it? Somehow like this.
Tick ​​1: SPT of players seeks to move them to the point where their center practically, but still not quite, crosses the border. Tick ​​1: the engine determines that this new point touches the border and adds it to the list Tick ​​1: after a successful move, the engine throws the intersection line, but it does not cross the border, so it does not matter Tick ​​2: SPT of players moves them far enough, so that they move entirely * beyond * the border line and no longer touch it Tick ​​2: the special boundary line is not even in the new compiled list of crossed borders, as a result of which it cannot be activated.
So you see that while you are moving faster than half the player's width per tick (having a good location and a bit of luck), it’s not unbelievable to slip the border line without activating its special actions. However, remember that the player's bounding box is always aligned with reference to the coordinate axes. In other words, this technique is easier to perform along the axes, where the player’s radius is 16 units. However, even diagonally at 45 degrees, where the player’s radius is 22.6 units, the SR50 run with his 23+ units per move is still done.
I want to draw your attention to the fact that the above describes only the movement to the "south / west." For reasons that we have not yet discussed, this trick WILL NOT WORK when moving north or east.
Item bumping
trick at 0:16
Here is another absurdity that occurs because of Doom’s strange and bizarre testing of a potentially new player position. The algorithm is built as follows:
Used SPT player to determine a potential new position.
Checks if there are any obstacles in the landing zone in order to occupy this point.
At the same time, a check is made for the presence of any other things that overlap the landing zone.
And also, if one of these things can be reached, the player picks them up
Do you see the problem? The player will take things concerning the potential SPT landing zone, even if the movement does not ultimately occur. This means that if you rush at full speed to an impassable border with an object located directly on the other side of it, the engine (at the time of the collision with the border) will attempt to move the player (SPT) to the other side of the wall. This will give the opportunity to pick up any things that will fall on the site of a potential landing.
Monsters opening doors
trick at 0:09
Despite the fact that this paragraph does not apply to the direct movement of the player, it is still remarkable and is largely guided by the same principles. For the most part, the engine considers the movement of a monster identical to that of a player. In other words, the SPT tracks the monster, and then uses this SPT to test a potential landing zone for further movement of the monster.
In this case, the reception is triggered if the door was created in a way where one side, for example, is locked with a key, and the other side is a “normal” door. While the fast monster (like the archvile) passes near the door, its huge SPT indicates the engine’s attempts to place this monster at a considerable distance from the unclosed side of the door. And so the monster is able to open it.