ReX's Nexus: Creating a Multi-Floor Elevator

Creating a Multi-Floor Elevator

In this tutorial I lay out the essentials of creating an elevator that generally functions in a manner similar to a real elevator. In other words:

What this tutorial does not cover are the following: I know that Item 1 on the "does not cover" list can be achieved by complex scripting, but I didn't feel up to the task of implementing such a system, which also requires creating additional custom textures. I'm reasonably certain that Items 2 and 3 can also achieved by complex scripting, but I was able to achieve my objectives within the limitations imposed. As such, I did not make the additional effort.

A. The Parameters of the Elevator

First, let's lay down the parameters of this specific construct. It is a 4-storey building with an elevator that can stop at each floor. This is a true 3D structure, and the entrance to the elevator on each floor is above/below the others. The bottom floor is at 0 height, and each successive floor is 160 units above the previous one. The map starts with the elevator on Floor 3, and can be called down or up from relevant floors using an "outside" switch on each floor (which is common on all floors, i.e., it's the same linedef that you activate on all the floors). The doors are really a single polyobject, which means they open on all floors simultaneously. While you're inside the elevator you have access to a wall switch on each floor (named the "inside" switch), and you can use the same switch to determine whether to ride the elevator up or down. Again, this switch is common on all floors, i.e., it's the same linedef that you activate on all the floors. I have set up the script on the outside switch to check the height of the player and compare it to the floor at which the elevator is. The elevator moves up or down until it gets to the player's floor, before opening the doors. For the inside switch I wanted to be able to control the script to ride up or down an elevator using a single switch. In other words, if I want to ride the elevator up I would press the top part of the switch, and if I want to ride down I'd press the bottom part.

The parameters of this elevator you can change with relative ease are the number of floors, the floor at which the elevator is at map start, and the height differential between respective floors (making sure that the height differential between floors is the same).

B. Pre-Requisites

To follow this tutorial, previous knowledge of the following topics is required:

C. A Quick Examination of the Geometry

The two pictures below respectively show the elevator with its doors shut and open. This gives you a sense for the overall geometry of the area. What you see in these two pictures is virtually exactly the same as what you'll see if you were operating the elevator from a lower or higher floor (with the exception of any wall/floor/ceiling texture variations you may introduce).

The picture below shows the elevator area in a map editor. I have marked, in yellow text, some of the relevant aspects of the constructs that are controlled by the scripts. Please note that the polyobject and its anchors are not shown in the picture (as they are drawn in another part of the map), but must be included in your map. Line_SetID = 2 represents the "outside" switch and Line_SetID = 3 represents the "inside" switch. I have also highlighted the sector with Tag = 83, as this plays a role in controlling the movement of the elevator's 3d control sectors. You can create a link between two or more sectors so that they will move together. This allows for the creation of complex lifts comprised of many sectors that will hold together if their movement gets blocked by the player or another actor. The sector tagged 83 serves this linking function (more on it in the discussion of scripts below).

D. The Mechanics of Motion & Control

Now let's look at the scripting that went into operating the elevator from the outside:

//****
// Script 1
//****

script 1 OPEN
{

}

An OPEN script is executed upon map start, and is therefore used to establish any specials that must be activated independent of the player's actions. In this OPEN script, the three sectors that make up the elevator (tags 80, 81, and 82) are linked to a single sector (tagged 83) using Sector_SetLink. In addition, the OPEN script sets up the "outside" (Line_SetIdentification = 2) and the "inside" (Line_SetIdentification = 3) switches to activate Scripts 3 and 4 respectively. These scripts are discussed below.

//****
// Script 3: Raises/lowers elevator from outside; opens door to elevator
//****

int LiftFloor = 3;
script 3 (void)
{
int PlayerFloor = GetActorZ(PlayerNumber())/10485760+1;
if (LiftFloor>PlayerFloor)

Some important notes: First, one unit of height in ZDooM has a fixed point value of 65536. Therefore, the 160 units of height that separate any two floors of the building translate to 160 * 65536 = 10485760. [If you set up your floors to have a different height differential, you'll need to modify the value accordingly. Second, you'll need to make sure that you have the same height differential between each set of floors. If you vary the height differential (e.g., height differential between Floor 1 & 2 = 160, but between 2 & 3 is 192) it will mess us this script. Third, GetActorZ yields an absolute value of actor's height. The script above will not work when any of the floors have an absolute height value below 0. This is very important, and will affect your map planning before you implement the elevator.

One of the common functions of a real elevator is that when you're outside it, and you press a button to call it, the elevator knows at which floor it needs to stop. This, in turn, is based on some value assigned to the button you press. Therefore, before the script for your in-game elevator can move the elevator to your floor, it needs to know which floor it's on. This is achieved by the following line of code:

This tells the script that the elevator is starting at Floor 3 of 4. (Note, that this is simply an initial value that the script requires; its value is reassigned by the script each time the elevator moves to a new floor. If you initialize your elevator to be at a different floor upon map start, make sure you change this value appropriately.) Next, it needs to know which floor you're on. This is achieved by the following line of code:

As the building has 4 floors, this yields a value between 1 and 4 depending on the floor height of the player that called the elevator. In this way, the script knows which floor you're calling the elevator from. Finally, it needs to know if the floor you're on is above or below the floor where the elevator is "parked". This is achieved by the following line of code:

If the elevator is above the player, it checks to make sure that that this condition continues to be met while executing the following part of the script, namely making the relevant sectors (in this case, sector with tag = 83) move down by an appropriate amount. Note that the script is set up to move in increments of 160 units, which corresponds to the height difference between each floor. In other words, the elevator will move down with a speed of 64, continuously checking if the elevator is still above the player's floor, and stop when it reaches the proper floor. The switch texture needs to be defined based on whether the elevator is moving down or up. In this case, as it's moving down, the texture changes from the default one to the one showing the elevator is moving down, as shown in the images to the left.

A couple of important points. First, as with a real elevator, you need to make sure that the elevator doesn't begin moving until the doors have shut completely from any previous elevator use. This is achieved through the line of code "PolyWait (6);". Via the line of code "TagWait(83);", the script waits until the relevant sector (i.e., with with tag = 83) has stopped moving before proceeding to the next part of the script, namely setting the texture back to the default, as shown in the images to the left. Meanwhile, the script has recalculated the value of the floor at which the elevator has stopped, via the line of code "LiftFloor--;".

Finally, the script opens the door by sliding the relevant polyobject sideways, and prevents the script from being activated further until the polyobject has completed its range of movement (i.e., the doors have shut again after opening). In addition to emulating the behavior of a real elevator, there's another reason to ensure that the elevator does not move until the doors have shut - the player can fall/jump into the elevator shaft and get stuck in there. Now let's look at the second segment of the script:

[Continued from script, see above]
else if (LiftFloor < PlayerFloor)

}

The first segment of the continued script above is virtually identical to that of the script previously discussed, except that the player is now calling the elevator from a floor that is above that of the elevator. As such, the direction of the elevator's travel is reversed so that it's now moving upwards. In addition, the switch texture is changed from the default one to the one showing the elevator is moving up. Once the elevator reaches the desired floor the texture switches back to the default one. These are illustrated in the images to the left. As before, the script has recalculated the value of the floor at which the elevator has stopped, via the line of code "LiftFloor++;", only this time it has incremented the values instead of decrementing them.

The second segment of the continued script above is for the instance when the elevator is already at the floor from which the player is calling it. In this case, the script simply opens the doors, pauses, and shuts the doors. As before, the script checks to ensure that the elevator doors do not open before they are completely shut from a previous elevator operation, and then sets it up so that any futher elevator operation is prevented until the doors are completely shut again. The third segment simply allows the doors to be repeatedly opened from the floor where the elevator has stopped.

One thing to keep in mind is that if you have a set of stairs nearby, you can call the elevator when it's 2 or more floors above/below, run up/down, and by the time the elevator reaches your original floor and the doors open, you are on another floor; when the doors open you can jump on top of the elevator or into the pit below. I found that by:

I can prevent the player from reaching any elevator door on a different floor while the doors are still open. At those speeds the door behavior doesn't seem unnaturally quick, and as the player doesn't see the rate at which the elevator is descending/ascending, this does not become an issue.

Finally, let's take a look at the operation of the elevator from the inside:

//****
// Script 4: Raises/lowers elevator from inside; opens door to elevator
//****

script 4 (void)

{
if (GetActorPitch (0) > 0)

else if (GetActorPitch (0) < 0)
else if (GetActorPitch (0) == 0)
}

This entire script hinges on the direction the player is looking when activating the switch. Looking/pointing at the upper part of the switch will tell the script to move the elevator up, and vice-versa. If the player is pointing at exactly the middle of the switch, the script will merely open the doors without moving the elevator. [However, by trial-and-error I've discovered that it's virtually impossible to press the switch at the exact middle when using free-look.] The script relies on the GetActorPitch special to determine which way the player is pointing. Note that looking up produces a negative value and looking down produces a positive value; 0 is looking straight ahead. Everything else is controlled in the same way as the script for operating the elevator from the outside.

One thing to keep in mind is that for the script to work properly, the switch needs to be placed at exactly the correct height. For example, at the bottom floor the in-game 3D sector that represents the switch has a floor height of 48 and a ceiling height of 64; the floor of the elevator is at a height of 0. Because the player's height is 56, which is the mid-point between 48 and 64, this floor/ceiling height combination works well.

So there you have it. Go forth and conquer!