📜 ⬆️ ⬇️

Bible movements doom. Part 2

Welcome to the Doom Movements Bible! In the second part, as in the first, all the quirks and whims of the movement code in Doom, including intricate stunts describing their work, are sorted out and categorized.


SlideMove: slippery moments


Up to the present moment, all the considered tricks with the character's movement in the game relied on the discrete nature of movement in the DOOM engine. Now we will dig the game code much deeper, and get to the function with the innocent name P_SlideMove. The function contains a comment warning the careless reader of the code that there is “total confusion” in front of it. I don’t know whether John Carmack or Bernd Kreimeier added this commentary, but it’s very, very accurate.

WALL RUNNING


We have already analyzed how a large SPT (“State of Attempted Teleportation”, which can be perceived as a directional impulse of movement of the character, discussed in Part 1) can lead to bugs, for example, line skip (crossing the border). Fortunately, the game engine is not so stupid and tries to take into account such errors! So, if the bugs start, if the character moves too fast, the perfect solution would be to simply divide one move into several suitable sizes, wouldn't it? And this is exactly what the game engine does: if the X or Y vector of movement exceeds 15 units (which, as you can see, is one unit less than the player’s radius), the engine tries to divide the player’s movements into two parts in order to calculate each of them separately. Thus, if the speed is too high, the program checks if the character crosses the obstacle in the "intermediate" state and, if necessary, does not miss it. Since the program conducts two movements instead of one, we will call this process “double SPT”.

“Wait a minute!” - the reader will be indignant here. - “Recently you talked about crossing borders at SPT> 15, and now you are describing a code that does not allow you to do this! How can it work at all? ” Well, then let's just look at the code:
')
f (xtap > 15 OR ytap > 15) 

Did you notice? Well, what happens when a character moves south or west? X-, as well as Y- suddenly appear ... Negative! And, for unknown reasons, the game engine will not verify this curious fact.

But back to the main idea. So, the engine uses the SPT to determine where to "teleport" the character in the next step. If the “drop zone” is occupied, the game code calls the P_SlideMove function, whose task is to check the fact of a collision with an obstacle and, if that happened, to ensure the character’s movement so that the collision looks natural. To start, the engine determines the direction of the player’s movement and calculates three vectors emanating from the three leading corners of the character box. Then it checks each vector for collision with an obstacle. If a collision is detected, the program determines the percentage of the path to the obstacle and makes the character move to the point found. And then he takes the rest of the SPT, directs it parallel to the wall, which the player hit, this part of the movement is the “slide” along the wall.



And it works, more or less. There is a small internal problem hiding here: to calculate the “slide”, the subroutine uses the initial SPT of the character, even if it was divided into two separate movements due to the player’s high speed.

Now that we know this, let's step through the entire process:


To summarize the above: if a player moves north or east with a sufficiently high speed (X-SPT or Y-SPT exceeds 15 units), the initial movement attempt is blocked by an obstacle, the engine performs two complete moves in one tick! Note that as a result, the SPT or something else does not change; it just happens two moves during one.

THING RUNNING



trick at 0:12

Now that we have mastered “wallrunning” (“running against the wall”), let's think about it: what if we replace the wall with any impassable object, and better with a long line of such objects?

The engine will try to teleport the player to a new position and detect an obstacle in the landing zone. Then the program will call the P_SlideMove known to us, and she will draw three vectors from the corners and find out: there is no collision. And what will the engine do now?

Strangely enough, the specified function does not actually check the character's collision with objects, it only checks the intersection of the boundaries of the objects with the vectors drawn by it. And if P_SlideMove could not find the line blocking the movement, it uses its last secret weapon: “stairstep” (running by a ladder). The program tries to move the player exactly along the Y axis, and if it could not, along the X axis. Let me remind you that all the objects in DOOM are enclosed in a “box” with the sides parallel to the axes of coordinates. Thus, if a player moves along an obstacle and the stairstep program decides to move it horizontally, the player will slide along one side of the object.

Now, having put the above in mind, it is easy to understand that the thingrunning is exactly the same as wallrunning: the player moves north or east with “speeding”, the movement test divides each movement into two equal parts, and each half gets the full initial vector PST with a generous P_SlideMove feed (as long as the initial landing point is blocked). The character successfully makes two complete movements instead of one for each tick, simply sliding at double speed along the “edge” of objects. This trick is best known for its use on Map23, where a player can accelerate by doing thingrunning along a number of barrels, and as a result, jumping over part of the map in a seemingly impossible way.



WALLRUN "AIR CONTROL"



trick at 10:17

When I first saw this trick, he stunned me so much that I decided to write an article about the magic of the DOOM engine - the very guide that is now before your eyes.

One of the easiest and most understandable rules for a player to move to DOOM is: “air control does not exist!” This means that when a character’s feet do not touch the ground, the character’s movements depend only on local laws of physics, but not on the player. And how I was amazed when, when viewing the Map14 speed crane, I saw that the player performed wallrunning high in the air, ran along the wall directly to the exit, and at the end simply turned above the ground and headed towards the south door, although before that it was just running east.

At first, I just couldn’t realize how such a trick is possible - the player's PTA was directed exactly to the east! He had zero Y-TSP, how could he start going south! But, as always, the seemingly understandable code contains surprises and dirty tricks.

It turns out that if the player is close to the obstacle, the engine will not try to bring the player closer to the barrier. He will simply use his last resort, the already familiar “stairstep”. Here we get to know the magic number 3.125%. If one of the vectors drawn by the program from the player’s corners crosses the wall in less than 3.125% of its length, the program does not even attempt to make the player move in the initial direction. Skipping all the extra steps, the engine will use the "stairstep". Well, the code of the ladder, as we remember, is able to move the character strictly horizontally (or vertically), without at the same time not changing the initial SPT of the player.

Now that we have reviewed the logic of the process, the incredible “turn in flight” can be explained. The player "goes for takeoff", pressing "up" against the wall, while having a high X-SPT and a small southern Y-SPT. Each tick, while the player is above the ground, the engine tries to move the character to the east and a little to the south, but detects a wall at the end point of the move. The engine instantly gives up and calls for help "stairsteps" and sends the character to the east, while not changing the vector of SPT in any way. As a result, Y-TSP remains slightly negative throughout the “air run”. And in the final, when the passage to the exit is reached, the movement check is successful, and the player changes the direction of movement in the air, which would seem impossible in the realities of DOOM physics.



MOMENTUM PRESERVATION


This trick is not as obvious and entertaining as described above, and it was difficult for me to find a suitable name for it. I have heard the name “door trick”, and it is very often used by speedrunners while waiting for the door to open.

The trick is trivial - resting the impassable wall in the right way, the player can keep the accumulated impulse of movement (SPT) at the maximum value, remaining motionless (the result is easy to detect visually, observing the fast oscillation of the weapon in the player's hands, as at full speed of the race. Therefore this trick is called “ wobble glide "- swaying glide). With a speedran, this means that the player maintains the maximum speed while waiting for the door to open, and when the passage becomes large enough, the bullet rushes further, saving precious milliseconds.



But how does this trick work? Usually, if a character runs into a wall, its speed (its SPT) is instantly reduced by the engine. This can be detected by changing the animation of the weapon in the hands of the player. To understand the nature of what is happening, we will again have to delve into the code of the DOOM engine and understand how it defines collisions.

Recall that usually when encountering a wall, the subroutine P_SlideMove known to us calculates the percentage of the motion vector directed into the wall (or skips this part if the collision is found at a distance of less than 3.125% of the vector), and then redirects the “remainder” of the movement parallel to the wall. In a normal situation, this quite reasonable algorithm will quench your speed - if you hit a wall run at a right angle, the slip speed will be proportional to the cosine of the angle difference between the direction of movement and the direction of the wall, and cos 90 is zero.

But let's look deeper. How exactly does the engine determine the future collision with a wall? To solve this problem, the program solves a simple mathematical equation: take a line representing an impassable wall, take a motion vector directed from one of the corners of the character box and find the intersection point. These calculations can be made using vectors, matrices, and other mathematical tools. But the DOOM engine uses the simplest method: take the end points of segment A and check which side they are relative to segment B. Then we check which side of A are the extreme points of segment B. If in both cases the extreme points are on different sides of the lines, the segments intersect.

To perform such a simple test, we need a little division and multiplication. And here the game engine reveals another aspect. The engine stores all data in the form of 32-bit values, and, most importantly, in the form of numbers with a fixed comma. In this case, one bit is used to indicate the sign of the number (positive / negative), 15 bits store the integer part and the remaining 16 bits are fractional. 15 bits is quite a bit; in fact, the allowable range of values ​​is from -32768 to 32768. So, if you need to multiply two numbers, you have to be very careful that the result is not out of range. The square root of 32768 is just 181, which means that checking the intersection of two lines of 200 units each will overflow the memory cell. And how to cope with such a puzzle?

Here is a part of the code from the “crossing check” function that copes with the problem:

 left = (line->dy / 256) * (dx / 256); right = (dy / 256) * (line->dx / 256); 

As you can see, it simply divides all values ​​by 256 before the start of calculations (to be perfectly accurate, it performs a bitwise right shift by 8, although this does not change anything). In this simple way, the engine ensures that the result of the calculations will be small enough to avoid overflow. At the same time, we reduced the fractional part from 16 to 8 bits, so there are no problems, is it?

Well, in most cases there are no problems. But what happens if the SPT in one of the directions is extremely small? For example, a player runs into a corner, having an X-TPS 20 and a Y-TFS 0.001. Let's take a closer look:


To sum up: if a player runs, resting on a corner, with an extremely small Y_SPT or X-SPT, he can maintain a high SPT, as a result of an erroneous zeroing of the vector.

VOID GLIDE



trick: 0:20

And now it's time to withdraw the heavy artillery. Instead of breaking the conventional lines of separation, we will ignore the absolutely impassable lines.

At first glance, this trick is simply impossible. To pass through the wall, the character must instantly move at least 32 units. Then, even after dividing into two parts, each “half” will be> = 16 units (character size). We already know that the maximum speed that can be reached with the use of the SR50 is 23.57 units per tick, but even if the limit is reached, the engine will split 23.57 into two separate movements. Moreover, the game code contains a hard speed limit of 30 units, which is applied at the very beginning of the movement calculation. And how in such conditions to achieve the speed of movement of the character to 32 units in one movement?

In general, it is true that 23.57 is the maximum speed achievable with conventional character controls, but what if “conventional means” is not the only way to increase the SPT? Armed with this knowledge, you can squeeze out much, much more from the DOOM engine. (I will not intentionally describe the damage boosts - it is difficult to control and test. Of course, you can get a speed of more than 23.57, for example, by undermining yourself with a rocket, but there is a much simpler and safer way).

It's time to go back to P_SlideMove. As I already described, in a collision, after performing a partial displacement in the direction of the vector, the rest of the SPT is redirected parallel to the obstacle. So, how is this piece of logic implemented? The following three steps are performed:


All these calculations are very simple to perform using sines and cosines. But it is here that the code seems to forget about the existence of trigonometry and uses just a brilliant simplification. Instead of determining the value of the remainder of the SPT correctly, it calls the function named P_AproxDistance. And what does this P_AproxDistance (Approximate Distance) do?

 return (the longer of XTAP or YTAP) + (half the shorter of XTAP or YTAP) 

Yes, yes, you were not mistaken. We do not need sines and cosines, square and cubic roots are not needed; addition is enough for us. Just add one axis to half the second axis. You can imagine how approximately this "aproximation" (approximation).

Thus, the function gives the player a very serious bonus. The fact is that the result of such a calculation is consistently more than the correct result. Let's check with an example: calculate the player's “slip” vector when X- = 3 and Y- = 4. The Pythagorean theorem tells us that the size of the slip vector is 5: this is the hypotenuse of a right triangle with sides 3 and 4. But the P_AproxDistance function returns size 5.5. And here, we received acceleration for 10% simply because of an inaccuracy of calculations! The size of such a bonus depends on the angle of movement of the player, and unfortunately, if the angle is 45 degrees (the usual situation with the void glide trick), the bonus will be a measly 6%. And this is more than enough.



However, a little more effort is needed to achieve an amazing result. Usually P_SlideMove does this:


Now the logic of what is happening has become obvious: if a player is in a corner, the first collision with the wall causes a slide to the second wall. A second collision occurs immediately there, and the algorithm is restarted, taking as the initial data the existing RTS - that is, the remainder of the “slip” from the previous cycle. The whole trick comes down to creating a situation where P_SlideMove constantly fails when trying to move. This is what happens:


But this is not all: if during a “run in the corner” a player’s SPT exceeds 15 units and is directed north or east, the described process will occur twice, thanks to splitting the movement into two parts. That is, an increase in PTA will occur four times in one tick!



Now we saw the whole process. You just need to choose the correct position and the code will enter the cycle, which gives a constant increase in TPS. Each SPT tick is increased twice (or four times) by a relatively small amount due to a rough approximation. At the end of each tick, an SPT grown up, of course, is reduced by "friction", but as long as the increase in TPS per tick is at least 10%, this is enough to exceed the decline due to friction and to ensure a constant increase. It remains to wait until SPT exceeds 32 units.

« 30 !» — . Yeah, right. « » 30 , , 32 . -, 37 38 .

: , . X- Y-, . , void glide .

ELASTIC COLLISIONS


void glide , : «elastic collisions» ( ). , , .



elastic collisions . – 15 , -15, - . , - , , .

elastic collisions, DOOM. , , ? : - . . , : X- Y- 16 , 16.

«, ?» — . , , -: , . , , .

, , . : , . line intersection ( ): , . DOOM , .

, . , , . , «» , . – , . wall gliding — . , , , , .

elastic collisions, ! . , «», .

, . , , . , 0 ( ) 180 ( , ).

: , , - , ?

 if (deltaangle > ANG180) deltaangle += ANG180; 

: « » – , , . , , ?

, , ( , «» ) – ? «» , .

, , ? , , , . , : 16 , , , ( ) ( ). « » - . , , .



, , . , ?

BLACK FRIDAY :30% discount on the first payment on the promo code BLACK30% when ordering for 1-6 months!

These are not just virtual servers! This is a VPS (KVM) with dedicated drives, which can be no worse than dedicated servers, and in most cases - better! We made VPS (KVM) with dedicated drives in the Netherlands and the USA (configurations from VPS (KVM) - E5-2650v4 (6 Cores) / 10GB DDR4 / 240GB SSD or 4TB HDD / 1Gbps 10TB available at a uniquely low price - from $ 29 / month , options are available with RAID1 and RAID10) , do not miss the chance to place an order for a new type of virtual server, where all resources belong to you, as on a dedicated one, and the price is much lower, with a much more productive hardware!

How to build the infrastructure of the building. class c using servers Dell R730xd E5-2650 v4 worth 9000 euros for a penny? Dell R730xd 2 times cheaper? Only we have 2 x Intel Dodeca-Core Xeon E5-2650v4 128GB DDR4 6x480GB SSD 1Gbps 100 TV from $ 249 in the Netherlands and the USA!

Source: https://habr.com/ru/post/342286/


All Articles