This guide will cover TF2 mapping to take you from zero knowledge to being able to create maps on your own. It is a long and comprehensive guide that will attempt to teach you every single fundamental and best practice you will need for creating maps.
If you have any questions specific to this guide, ask them in the replies. I expect parts of this guide will need to be slightly amended as people use it.
This guide is intended to be read in order. Each section will build upon knowledge from previous sections. If you skip sections, you will miss important information.
This guide is hands-on. The intention is to follow along in hammer as you read through. Installing and running hammer is explained in the guide.
Introduction
Mapping is the process of using Hammer Editor to design levels for TF2. The Hammer Editor is the only real tool that exists to create levels, but it's fairly easy to understand the basics of how to use it.
While not directly related to level creation, other aspects of mapping may include asset creation (custom textures, models, or sounds) or scripting (VScript using the Squirrel programming language).
The purpose of this series of guides is to teach you how to perform level creation. As of writing this I do not plan on putting guides for asset creation in this post, they will likely be in other posts (with VScript possibly being in a separate subforum).
The goal of these guides is to take you from having no knowledge of how to use hammer, to being able to have all the tools you need to make a Valve quality map. This does not mean the maps you create will be Valve quality, but you'll have the tools to do so. Whether or not you can create a good map comes down to your own creativity, inspiration, and understanding of the game.
There is no "secret" to making a good map, you will just need to try out what you can until you land somewhere that works. In all likelihood, your first map will not be good unless it is heavily tested, and this is fine, getting it right the first time is a nigh impossible feat without a lot of support from others. All the Valve maps you know and love went through heavy testing and layout scrutinizing during their design phases, they were not just "one and done" maps. To make a good map you'll need a good understanding of level design theory for TF2 which includes designing for gamemodes and classes. You will also need to know how to optimize your map, which goes hand in hand with the visuals and making sure there's not too much going on aesthetically in your map to cause performance issues. I hope that by the end of this guide you'll have an idea of how to do all of this to a reasonable degree, such that you can make up the gap by studying maps you enjoy to see what they do right and what they do wrong.
Overall this guide series will cover
Installing the necessary programs and setting up Hammer
Basics of using hammer and how to create the aspects of a level
Optimizing your maps so they perform well even for players with older hardware
All basic entities and map making processes needed to make a good quality map
The tools of the trade a mapper will need
Decompiling and studying existing maps
Mapping is a very enjoyable creative process and I hope you'll come out of this liking it as much as I do. Depending on how you like to work and do projects, maps can take anywhere from a few weeks to a few months, whether you do on-and-off work or consistent work. Don't burn yourself out, just remember there's not (usually) a deadline to making your map, so take your time and enjoy the process. Once you understand mapping you'll also have a greater appreciation for existing maps, the effort that went into them, as well as being able to perform a better analysis on maps you like.
Program Installation/Requirements
At a most basic level, the only program you need in order to develop maps is Hammer editor, which comes preinstalled if you have TF2 installed. However, alone it isn't really enough to make mapping easy, so there are some other programs you should install additionally.
Firstly, it should be noted that you may find it useful to create a shortcut to your Team Fortress 2 directory, as you will be navigating here very frequently and a shortcut is helpful.
Windows Shortcut
Your steam/steamapps/common/Team Fortress 2 directory can be bookmarked on your file browser to navigate there easier
Linux Shortcut
While you can add the TF2 directory to your file browser (if you have one), I would advise symlinking your TF2 directory to home, this has easily been one of my most used symlinks and I have gotten great use out of it. It will make navigating to the directory easier
Code: Select all
ln -s /home/<your_user>/.steam/steam/steamapps/common/Team\ Fortress\ 2 /home/<your_user>/TF2From here on going forward in this guide, the location of your Team Fortress 2 directory will be referred to as the "TF2" folder. If this guide mentions a folder like TF2/bin/ or TF2/tf/, it will instead be Team Fortress 2/bin/ and Team Fortress 2/tf/ respectively, however this shorthand will be used to make file paths easier to parse.
Programs
The following programs are all very useful for mapping and you should download them before we begin.
Hammer++: This is an upgrade to regular Hammer which adds more features as well as stability (it crashes less and autosaves more, for instance). There are no downsides to using H++ and anyone mapping should be using it.
Tools++: These are upgrades to some of the build programs that you use to compile maps. As with Hammer++, these are straight upgrades that make compiling faster with better features, there's no reason not to use them. For the time being, all the exes can go into your TF2/bin/x64/ folder. The only ones you need are: VBSP++, VRAD++, VVIS++, and BSPZIP++.
CompilePal: This is a program to make compiling easier, as well as providing some important tools like asset packing (for custom assets), automatic cubemap generation, and so on. While you can get by without it, it's highly advised to use this as it simplifies the process. Without it, you will need to use alternate methods of asset packing which will not be covered in this guide.
BAMF: This is a set of materials and tools that will help you out when mapping. The most important part of this is the FGD. Other useful things include decompiled valve maps, prefabs, and additional dev textures. The only things necessary/recommended for this tutorial however are the FGD and Valve VMFs.
This guide will assume you are using all of the above programs. If you choose not to for any reason (you are highly advised to use them) you will need to adapt the guide to your setup.
Installation - Windows
Hammer++: Check the README.txt file within the download for details on exactly where to put it, but generally speaking the files will be in your TF2/bin/ dir.
Tools++: All files within the tools_plusplus folder should go inside your TF2/bin/x64 directory, both the exes and the fgd.
CompilePal: This is a standalone program which does not need to go near your TF2 directory, you can install it wherever you'd like. Keeping it near your TF2 directory (for instance, creating a folder named "tools" in your TF2 directory and then installing it there) is advised so you don't lose track of it.
BAMF: Check out the existing installation guide. Note that the only things you need for this guide are the FGD and Valve VMFs, all other features are optional and can be installed at your own discretion.
Installation - Linux
Hammer++: Check out my tutorial here to install and use Hammer/Hammer++ through Proton.
Tools++: All files within the tools_plusplus folder should go inside your TF2/bin/x64 directory, both the exes and the fgd.
CompilePal: Install version 28.4, later versions don't work. This is a standalone program which does not need to go near your TF2 directory, you can install it wherever you'd like. Keeping it near your TF2 directory (for instance, creating a folder named "tools" in your TF2 directory and then installing it there) is advised so you don't lose track of it. CompilePal runs with Wine, but it will need access to your TF2 directory. You will need to symlink your TF2 directory into the wine prefix CompilePal uses. Read the above H++ Linux tutorial for an example of what that might look like based on where your wine prefix is. A full tutorial will not be covered as this guide assumes a bare minimum level of Linux knowledge to be able to perform this yourself
BAMF: You can try to use the existing installer first by running it with wine. If that doesn't work, you can instead clone the github repo and move the contents of the FGD folder into your TF2/bin/ dir, and the entire Decompiled Valve Maps folder inside of your map folder (TF2/sourcesdk_content/tf/mapsrc/) for use later.
To launch Hammer, you'll navigate to TF2/bin/x64/ and run hammerplusplus.exe. Depending how you installed it you will launch differently, but generally for windows you launch the exe directly (and can shortcut it to your task bar) and for Linux you'll run it through Steam with Proton.
At this point in the tutorial, the Linux and Windows differences essentially stop so there will be no OS specifics given unless necessary.
Hammer Settings
There are a few settings that will make your life a lot easier that you should enable before we start.
Navigate to Tools > Options
Game Configuration:
Game Data: Click Add and select TF2/bin/tf-brokk.fgd (it should drop you in the TF2/bin/x64 directory by default, so you will need to navigate out and into the regular bin dir. Alternatively, copy this fgd file into your x64 dir). Also add TF2/bin/x64/toolsplusplus.fgd
Default PointEntity: prop_static
Default SolidEntity: func_detail
Default texture: 0.25
Default lightmap: 16
General:
Enable Autosave: Enabled (configure as you like)
2D Views:
Default to default degree rotation: Enabled
Default Degree: 15
3D Views:
Back Clipping: 8000-10000 (render distance)
Build Programs:
BSP executable: vbspplusplus.exe
VIS executable: vvisplusplus.exe
RAD executable: vradplusplus.exe
Hammer++:
Enable microgrid: Disabled
Snap newly created brush points to grid: Enabled
Hammer Basics (Your First Map)
In this guide we are going to be going from zero knowledge of how to use hammer, to making an extremely basic but fully functional KOTH map. KOTH is chosen due to ease of gamemode logic for this guide, other guides will cover the specific logic for other gamemodes.
Hammer
First off, navigate to File > New and load into a new map.
Your window should be shown in a 2x2 grid. Three of these grids are 2D views at different angles, while one grid is the 3D view. If you don't have them set correctly, look to the top left of each window to see the window type (such as top (x/y) or front (y/z)). Right click this to change the window type.
Generally speaking you want to have the following windows:
3D Shaded Textured Polygons
Top (x/y)
Front (y/z)
Side (x/z)
Your layout should look something like this by default. Some important areas are labeled below

On the left of the program are your tools. To get started we don't need all of them, but some of the basics are:

Selection tool: This lets you select, move, and resize brushes and entities, or create selection zones by dragging in one of the 2d views.

Block Tool: By creating a selection zone with this tool and pressing enter, a brush of that specified size will be created. It can be resized and molded afterwards

Entity Tool: This tool is used to create entities like props as well as internal game logic, such as gamemode logic, lights, player spawns, etc.

Toggle Texture Application: This tool is how you will apply textures to brushes in most cases
Basic Brush Creation
We are going to be creating the floor brush, but first off we want to know what texture it should be. Whenever you use the block tool to create a brush, whatever texture you currently have selected will be applied to all faces of the brush. It is generally considered best practice to make sure that any textures not visible to players are set to nodraw, however testing a map with nodraw is difficult. Instead, we can use a dev texture and mass replace it later in the development of our map once we get to texturing.
In the Current texture section of the Right sidebar, select Browse, then filter for measuregeneric, and double click on dev_measuregeneric01b (the gray grid) to select it.

Before creating a brush, check the bottom right corner to ensure Snap is set to On. If it isn't, press Shift+W to cycle it to On. Generally speaking, you always want grid snap on.
Next, select the Block tool, and on your top (x/y) window, drag a box to be size 2048x2048. If you align it with the center of the map, you'll notice it lines up perfectly with the orange lines. On either your side or front view, ensure the brush is thin, around 16-64 Hammer Units tall. Make sure the brush is underneath the origin, ideally flush with the blue axis line.
If you're having trouble lining it up easily, you can use the bracket keys [ and ] on your keyboard to change the grid size. Check the bottom right corner to see your current size. For this exercise, stick between 16-64 unless otherwise specified.
Your selection should look like this. Some labels are applied to help understand the grid and 3D views

Press Enter to create the brush.
To navigate the 3D view, press z to toggle freecam controls. Scroll wheel can be used to move forward and backwards, as well as WASD to move.
If you created the brush in the right spot, you'll see the origin in the 3D view like this.

As with most editing programs, Ctrl + z will undo the last action you took which can be helpful if you misclicked or did an action inadvertently.
Now, let's create some map geometry. Using the same method as before, go ahead and create four walls around the floor brush that are 512 units tall. Note that when creating a brush in the 2D view, the unseen third dimension will equal the length of the last brush you selected. You can see this for yourself by selecting the floor brush, then trying to make a wall in either direction within one 2D view. This selection shortcut only applies to manually selected brushes, so once you create a brush if you want to make another brush of the same height/width, you'll need to reselect it.

Spawn Entities
In order for the map to be able to be compiled, it needs entities within the playable space. The easiest first entity to add is a player spawn.
In the 3D view, navigate inside of your room, then click on the floor using the Entity Tool. The entity tool does not work correctly when used on the 2D view, so you'll be using the 3D view whenever you use this tool.
If you set up the setting correctly previously, you'll have created a red box on the floor (though it's only red because it's selected). This is an uninitialized static prop, which is what we set our default entity to in the options. Double click to open the settings for it to be sure

In order to change this to an entity we want, at the top you'll select Class, highlight the currently class (prop_static) then input info_player_teamspawn. Press Enter or hit Apply to convert the entity

Within this entity we can check the Keyvalues to see what it's configured to by default. As you can see in the Team key, the default value is 0 or None. This means the entity has no associated team with it, which in the context of a team spawn means either team can spawn here. However, we don't want players getting stuck on each other. Perform the same process again to create another teamspawn entity and place them a little bit apart like so.

Note that for spawnpoints, they are required to be floating off the ground a bit. When a player spawns in, they're snapped to the floor, but if the teamspawn is flush against the floor it becomes invalid.
In each spawnpoint, select the Value for team, and on the right click the dropdown. Set one spawnpoint to Red and one to Blu.
Compiling
We're almost ready to compile our map. Use Ctrl + s to save the map. Maps are by default stored in the .vmf format, and can be found in the TF2/sourcesdk_content/tf/mapsrc/ folder. Name your map something like first_map and press Save.
Now we can compile the map. Go to File > Run Map, or just press F9. Once in this menu, press Expert as you will generally always want to be in the expert menu.
You'll want to make sure that bsp, vis, light, and copy are all selected. These respectively perform the map geometry generation, the visibility generation, light generation, and moving it to your map folder. You'll want to make sure $game_exe is unchecked. Generally speaking it's easier to just have your game open and then compile on the side than to have the compiler open your game. Additionally, on Linux, this feature does not work at all as Hammer can't open your game.

Once ready, press Go to start the compile.
Unfortunately, this map doesn't compile! If we check the compile window, it will mention that
Code: Select all
Entity info_player_teamspawn leaked!So let's figure out why this error occurred.
Identifying Leaks
Press any button to exit the compile window, then go back into hammer and navigate to Map > Load Pointfile. If there's a vbsp (geometry) compilation issue, Hammer will create a pointfile that shows you exactly what went wrong.
As you can see in your pointfile, the entity that "leaked" has a line pointing towards where it leaked.

But what is a leak? In Source, all maps must be completely self contained. If any line can be drawn from the inside of a level to the outside, then it "leaks." Think of your map as being like a spaceship, and leaks like hull breaches. Even a small hole will let out all the air.
You can fill this leak using a dev texture brush. Technically, any world brush will seal a leak (aside from some special tool textures like clips). But if all maps had to be enclosed inside rooms, you wouldn't be able to have levels outdoors. Therefore, we can use a skybox brush to create a sky for our map.
You can unload the pointfile the same way by using Map > Unload Pointfile
Skybox Brush and Brush Duplication
Select the tools/toolsskybox texture (you can filter for tools/ to make finding tool textures easier). Note that you should select the light blue toolsskybox texture, NOT the purple toolsskybox2d texture.
While we could create another brush from scratch, we know the ceiling is going to be the same size as the floor, so instead let's just duplicate the floor and change the texture.
When holding Shift, any movement applied to a brush or entity (resize, movement, rotation) will cause the movement to instead create a duplicate of that brush.
Select the floor, then while holding Shift the entire time, move the ceiling up to the top of the walls. You can do this either by dragging it with your mouse in the 2D view, or (based on what grid your mouse is focused on) the arrow keys. If dragging with your mouse, hold shift for the entire duration of the movement. If using arrow keys, only hold shift for the first arrow press, as each subsequent one will create multiple duplicates. Note that this same principle applies to entities, you can go back and redo the creation of the second teamspawn entity by holding shift and moving the first one as a demonstration of this.
You can also use Ctrl + c and Ctrl + v to copy paste brushes, however using Shift tends to be more precise.

Apply Current Texture
Once you have the ceiling in place, select it and click the Apply Current Texture button. Unlike the regular texture tool which is more precise, this is a quick and easy way to apply your current texture to all faces of a brush indiscriminately.
Once done your room should look like this

Loading Into Your Map
Following the previous steps, you should be able to compile now without errors.
Once compilation finishes, you can load into your map by booting up TF2 and in console typing
Code: Select all
map <name_of_vmf>which would be whatever you named it earlier in this guide. For example, first_map.vmf can be loaded into (after compilation) with
Code: Select all
map first_mapNote that when you use the "map" command in console, it starts up a local server each time. If you are already in your local server (for example you loaded the map previously) and want to cut load times, you can instead use
Code: Select all
changelevel <name_of_vmf>which merely changes the map on the server instead of restarting the server itself.
When loaded into your map it should look something like the below. You should be able to join a team and run around in this map now. If you spawn under the ground or stuck in the void, refer back to the spawn entity creation section.

We have a functional map that you can load into but this map has nothing in it. Firstly we'll create some respawn rooms for each team.
Respawn Room and Clipping Tool
We are only going to make one respawn room for now, so don't worry about mirroring any of this just yet.
Pick one wall you want to be the RED spawn. The specific one does not matter, though if you want to follow this guide exactly, the left wall (from the top view perspective) will be used.
We will be using the clipping tool. This tool allows you to cut one or more objects along a plane. Most of the time you will use it as a straight cut along an axis, so that's what we'll use it for during this guide, though note that you can make more interesting diagonal cuts by moving the cut vertices in your 2D grids.
Select your wall and then in one of your side 2D grids, click and drag to create a cut line horizontally. Having grid snap on for this step is important so verify it, as having cuts not aligned on the grid will introduce a lot of microgird errors later.
After the initial line is created you can freely drag it around with the vertices. Your initial line does not need to be in the exact right spot, just get it close enough and then after move it into the right location. For the time being, don't drag any of the cuts on your top 2D grid as this will make the cut a 3D diagonal one which we don't want to do.

You can see on the side the size that each brush will be after the cut. Get the bottom brush to 128.
You will notice that one brush is red and the other is white. Depending on your cut setting, cut can be set to either keep both brushes, or delete one. The red parts will be deleted, while the white parts will be preserved. To cycle this, simply click on the tool again, or anywhere on the 2D grid. There are 3 settings (delete left, delete right, keep) so just click until both the top and bottom brush are white. Once ready, press enter to perform the cut.

Next, let's create the door hole. Select the bottom brush, then this time create a vertical cut in either your top view, or the side view which has the correct face depending on which wall you picked. Cut it right down the middle so that both sides are equal. Perform the same operation as previously for the cutting settings, keeping both brushes.
For each brush, select it and drag it to one side so that an opening is created in the wall. The final width of the door should be 192, which you can verify by dragging a selection box across the width after you resize the walls.

Unfortunately there's one problem, the brushes now have skybox textures on the side (if you followed this guide exactly). This is because when you use the clipping tool, the newly created faces on the brushes will be set to whatever texture you have active.
To fix this, use the Apply current texture tool on each brush on the wall after selecting the dev grid again.
Below you can see what one of the brushes looks like after fixing the texture on it. A good rule of thumb is to have the texture you want to use in the cut selected beforehand. When doing map development this usually means a dev grid or nodraw, to ensure newly created faces use dummy textures just in case they aren't seen, and can be retextured later if they are.

Creating the Room
Create a room using the previous principles. When creating a brush a good rule of thumb is that it should only be as big as it needs to be, so for instance you don't want a floor brush to extend beyond the walls. Practicing this will make texturing much easier in the future. For example, in the area within the door itself, create a small brush just for that floor rather than dragging the whole spawn room floor over.

Find the player spawn in the middle that is set for RED team and drag it over into your spawn room. After moving it, you can use Shift to duplicate it a few times. Generally speaking all spawn rooms for all maps will have multiple spawn locations, this is because the game will first try to fill up each spawn before placing players in the same spawn location, and stacking everyone in one spot gives poor visibility. Unless you intend your map to be played on servers with >32 players, the respawn room spawnpoints for a team doesn't need to add to more than 12 cumulatively as a rule of thumb.
There are a few aspects to spawn rooms we will need to create. The spawn room boundary, respawn lockers, and the spawn door.
Firstly, the spawn boundary. Select the tools/trigger texture and create a brush that completely fills the inside of your spawn room.
You will also want to create a trigger that takes up the full space in the doorway. The need for this will be clear shortly.

Brush Entities and the Respawn Room Trigger
We are now going to be creating a Brush Entity. Unlike other entity types, created with the Entity Tool, a brush entity is instead created from one or more brushes. While both are entities, the types of entities that can be brush entities are entirely separate from other "point" entities. For example, a door or trigger would be a brush entity, while a prop is a point entity.
There are two ways to create entities, either with Ctrl + t or by clicking the Entity button in the brush conversion section on your right sidebar. However, they do not do the same thing.
Ctrl + t will turn each brush you select into an individual entity. For example if you select your two triggers and use ctrl + t, they will both be turned into individual func_detail entities. You can verify they are separate by using the selection tool on one of them and seeing that the other is not selected as well, as they are entirely separate entities.
The Entity button will instead take all selected brushes and consider them all one brush entity. If you ctrl + z the previous change to your triggers and then instead use the Entity button, the two trigger brushes will instead be considered one brush entity. You can again verify this by using the selection tool and seeing that clicking on one brush selects both. It's also worth nothing pressing the Entity button will combine multiple brush entities, though we won't need that yet.
This is a useful and important distinction because there are times for both use cases, though generally combining multiple brushes into one brush entity is preferred if possible. For instance if your spawn room has two exit doors, then the triggers for the doors (which we will go over shortly) would be better separate, because if they were combined, touching either door would open both doors which is incorrect. On the other hand, something like our two triggers here would be better as one brush, since they'll be the same respawn room entity, and we want them to have the same settings. Generally speaking unless you want brushes to be able to move independently or do different things, they can be combined for efficiency.
In the case of our respawn room, this is all the same room and therefore we want this to be a single respawn room entity. Select both trigger brushes and press the Entity button to convert them to func_detail.

func_detail is just the default brush entity type, similar to how prop_static is the default point entity type, so we'll change it to the entity we need. Turn your trigger into a func_respawnroom and set the Team keyvalue on it to RED.

func_respawnroom does a few things, but the most important ones for now is that it allows players to swap class instantly without dying, it prevents engineers from placing buildings, and prevents players from carrying flags (such as in ctf) within them. Any spawnroom you create should have a func_respawnroom within it's boundaries.
The reason we use the second respawnroom brush in the doorway is specifically for the prevention of engineer buildings. If you put your door in the middle of the doorway, an engineer would still be able to build on the other side, allowing them to build exits in spawn and thus allow enemy spies inside your spawn room. You'll want to adjust the respawnroom brush to be as far forward as you want engineers to build near the door, but generally right in the middle of the door entity itself (or in our current case, the doorway) is sufficient. It should not extend too far from the door, as players could switch classes outside of spawn if so.
Brush Resizing and the Vertex Tool
But you'll notice that we created our brush in the full size of the doorway, so let's fix that. Select the brush and then drag it to be sized in half the doorway.
Unfortunately, you'll notice a new problem - the full entity itself got resized, not just the piece we selected. You can see the main respawnroom now isn't flush with the wall closest to the doorway.

Go ahead and undo that change and we'll do this properly.
There are two ways you can go about fixing this, using either the Solid selection mode, or with the Vertex Tool. My personal preference is the vertex tool due to more broad application but for this use case, both will achieve the exact same result. We will do both so you get an idea of how it's done in either case.
In the case of the Solids selection mode, you'll head to the top of your right sidebar to the Selection Type area, selecting Solids. Now when you use the selection tool on the doorway trigger, only that is selected, not the full entity. This selection mode allows you to select individual brushes regardless if they are part of a larger entity or part of a group. You can go ahead and resize the brush now to be half the size of the doorway.

Note that unless you are doing a maneuver like this, you want to usually be on either Objects or Groups, as staying on Solids may cause problems for you. So once you're done, change back to Groups.
Go ahead and undo that change, swap back to Groups, and instead switch to the Vertex Tool. This is a very powerful tool which lets you manually modify the vertices of a brush. While you can use it to make interesting shapes, I find the best use out of it comes from being able to move the boundaries of multiple brushes at once without causing resizing bugs. When selecting something, you can either select the object itself in 3D with the Vertex Tool already selected, OR you can select the object beforehand and then switch to the vertex tool. In this case it doesn't matter which you choose, but note the differences that happen in each case (you can click around your map to see this). Once selected you'll see all corners of the brush highlighted with the edges red.
Like the Cutting Tool, the Vertex Tool has three modes (corners & middles, corners only, middles only). Generally speaking you want to always be in the corners & middles mode (both white and yellow dots) unless you have a reason not to be. In this mode, go into your 2D view and drag a selection box around only the vertices that are on the outer edge of the brush that we want to move. You can then either drag them with your mouse or use arrow keys to move them into the desired location. Note that with the vertex tool you can select vertices with your mouse but unless you have a good reason to, you should generally select your vertices with a drag box first before moving them.
You can see what a selection box for this tool looks like. In this case, I created the box in the top view. Most of the time you won't need to, but you can drag it to be a 3d selection in the side/front views.

After pressing Enter you can see all the vertices within my selection were selected and can be moved. Note that when a selection has a width of zero (such as when first creating it on the 2d grid), it has "infinite" width. It only gains an actual width if you drag it on another view.

Respawn lockers
Now that our respawn room itself is done, let's add another critical part of a respawn room, the respawn locker. The locker consists of two parts, the prop, and the trigger.
Use the Entity Tool to create a prop_dynamic. There are three types of props, prop_static, prop_dynamic, and prop_physics. The majority of props on a map are static, a handful will be dynamic, and only few maps will have physics props, with them generally being unused and a left over feature from half life 2. Static props are things like trees, crates, or bushes, they have a single model and don't move. Dynamic props can have animations or can be moved around, for example the control point is a dynamic prop as it changes model depending on what team controls it.
Generally speaking unless you need a prop to be dynamic, it should be static. The respawn locker has an animation for the doors opening and closing, and therefore should be a dynamic prop. Physics props are things like the tables, chairs, and crates of half life 2 which allow players to hit them around. TF2 maps don't usually use this feature so physics props will be ignored for the sake of this guide.
On your newly created dynamic prop, select the model value, and on the right you'll see an option to browse. This will open up the model browser. The model browser takes a bit to load for the first time (each time you open hammer that is) as it loads in models.
In the bottom left search for resupply_locker. If you don't see anything yet, wait a second or two and then delete/readd one of the letters in your filter, this will make the browser do another search. Double click it to select the model.

Rotating Objects
You'll notice our respawn locker is facing the wrong way, so let's rotate it to face the right direction.
In order to rotate something in hammer, click it again in the 3D view to cycle selection modes. As with previous tools, there are 3 settings for brush movement, scale, rotate, and skew. For point entities, the only relevant one is rotate, as scale and skew do nothing. Scale is identified by having squares on the corners and edges, rotate is identified by having circles on the corners, and skew is identified by having squares only on the edges. Generally speaking you won't need skew mode as it's imprecise and tools like the cutting tool or vertex tool are better. Click your prop until it's in the rotate mode.
Hammer++ has a feature called a pivot point, which is a circle with a cross in the center, usually located at the center of an entity. If you're familiar with 3D transformations or just any sort of modeling, animation, math, etc, you'll know that this is the point around which the object rotates.
To demonstrate this, click and drag the pivot point a short distance away from the prop, and then while in rotate mode, grab one of the corners of the prop and shift it. You'll see the prop rotates around your pivot point. This is a useful feature if you're creating something large and want to rotate around the edges of it, but in our case the only thing we want to do is just change the angle of our prop to be facing the right way.

Undo any rotation you just did and move the pivot point back to the middle of the prop. Now when you rotate it, it'll be just rotating in place. Make sure it's facing the right direction. You'll notice that when you rotate the object, the Angles key value is updated. Changing this value directly can be helpful if you want to hit a specific angle, or if you mess up a prop and want to reset it you can change this to 0 0 0. Note that changing this value does not allow usage of a pivot point because this is just the local angles, while the pivot point is a hammer abstraction.
Note that if you haven't already, you can now rotate your spawn point to face the spawn door.

You can also move the locker so it's flush against the wall. Also make sure it's flush against the floor, you don't want a floating locker!
Respawn locker Trigger
Next up we'll want to create a trigger for our respawn locker, this will define the area where players walking near the locker will get their health and ammo restored. Select the respawn locker and then use the brush tool to create a trigger next to it.
You will notice that if you select your newly created brush, in the 2D view some of the sides will be reported as having fractional lengths. Props typically do not have sizes that correspond to the grid, and as we know having fractional sized brushes is not a good idea. Resize the trigger to be 112 long x 112 high x 80 wide, the typical size used in Valve maps. You will need to change down to grid size 8 to do this.

Since we only have one brush here, we can use either Ctrl + t or the Entity conversion button to create our entity. If you recall, even if we had multiple respawn lockers, we wouldn't want to make it so touching one opens the door of the other, so each locker trigger will be a separate entity. After converting it to a func_detail, change the type of the entity to func_regenerate. This is the trigger that upon being touched, will refill a player's health and ammo to full as well as update their loadout if they swapped weapons.
However, the game doesn't know what respawn locker to open when the trigger is touched, so we need to identify it. Select your locker prop and in the Name section, give it a descriptive name, like locker_prop_red_1. Descriptive names help with searching for entities later, though any name will work in this case as long as it's unique.

In the Associated Model key value on your func_regenerate, set the value equal to whatever the name you gave your prop is. Note that we do not need to give our func_regenerate a name unless another entity needs to call it or interact with it in some way. Since it's the start of the chain, it can function just fine without a name.

Now that you have that set, our respawn locker is done. As with most respawn rooms, you'll want to be symmetrical. Select your locker prop, then while holding Ctrl select the trigger. Now when you hold Shift to drag, it duplicates both at once. Move the new locker to the other side.
You will notice that if you select the newly duplicated locker, it still has the same name as the previous one. With the exception of a few entities (such as path_track), duplicating an entity does not give it a new name. Update the new locker to a new name, like locker_prop_red_2.
You'll also need to edit the trigger, as it'll be using the old name. Select it and change it's associated model to your new locker's name.

Creating the Spawn Door
We now have functional respawn lockers for our spawn room. Now all that's left is the most important feature of a respawn room, the spawn door. This will prevent enemies from getting inside the spawn so that the team can spawn in safely.
A respawn door consists of three parts, the door brush, the door trigger, and the respawnroom visualizer.
Firstly, the door brush. Select the <red dev grid> texture and create your door, It should fill the doorway and not be too thick. Make sure the door is flush against your respawn trigger and they don't overlap, this is usually how it's done in valve maps.
Convert the door brush into a func_door. func_door is a useful entity that has applications beyond just doors but in our case we'll be using it for a simple case. Give your door a descriptive name like spawn_door_red.

func_door is an entity that works by giving it a speed, and a direction, and then it will move back and forth along the direction, for the length of the door in that distance, at the given speed, back and forth between there and it's original starting point.
We want our door to move up like most spawn doors. First let's set the move direction. In the move direction key value, you'll notice there is a -90 0 0 angle selection (it may be another default depending on your fgd), as well as a dropdown, and a circle. The circle itself is relative to the directions in your top 2D grid, while the dropdown has some preset values that are helpful. In this case we want it to move up, so click the dropdown and select Up.
The speed is equal to the number of units the door moves per second. Set it to 300 which is the value Valve maps usually use. You will also want to set Lip to -1 so the door retracts slightly into the ceiling to improve visuals.

Just in case, you'll want to check your door's flags. Select the Flags tab at the top of the entity menu, and simply ensure that Touch Opens is unchecked.
Next we are going to create the trigger boundary for the door. This trigger will determine where a player needs to stand in order for the door itself to open. Select your door then create a trigger brush. You'll want the brush to stick out around 64-80 units from the door, which is a value Valve maps usually use. 176 is a good size for the brush if you made your door 16 thick.
Convert your trigger brush into a trigger_multiple. trigger_multiple is a generic trigger type, which simply put is just an area that can do something specific you define when a player touches it.

Entity Outputs and Making the Door Open
To get this trigger to do what we want, we'll need to give it an Output. Head over to the Outputs tab, you'll see a menu like this.

Outputs on an entity are events that get called by the game when certain game events occur. For example, when a player touches a trigger, when a piece of ammo is picked up, when a round starts, when a breakable takes damage, when a point is captured, etc.
In our case, we want to create an output that fires when a player enters and leaves this trigger, to open and close the door respectively.
Select new and then edit your outputs to look like the following.

In the case of the open action, we want to use OnStartTouchAll instead of OnStartTouch. For a trigger, OnStartTouch fires every single time a player enters the trigger. That means if two players enter a doorway at once, the door would attempt to open twice. On the other hand, OnStartTouchAll fires only for the first player to enter a trigger, then it cannot fire again until all players have left the trigger's bounds. For our spawn door we only want the door to try to open once when someone first enters, not to continually try to reopen every time someone enters it even if it's already open, so we use OnStartTouchAll.
Similarly, we use OnEndTouchAll instead of OnEndTouch. OnEndTouch fires every single time a player leaves the trigger. That means if two players are in the trigger at once, and one player leaves, it would fire while the other player is inside, causing them to potentially get crushed by the door. (door damage?). On the other hand, OnEndTouchAll only fires once the last player in a trigger leaves, when the trigger truly empties. For our spawn door we want the door to only close once everyone is outside it's area, so we use OnEndTouchAll
The target entity is self explanatory in the sense that we want it to target our door. However, it should be noted that if you have two entities of the same name, and you use that name in an output, the output will apply to both. For instance if we had two spawn room doors both named the same and we had this same logic, going near one would cause the other to open as well. It's the same principle as we discussed with the associated model with our respawn lockers.
The output in this case is going to be Open and Close. If you click the dropdown you'll see the entity has quite a few outputs, but for this guide we aren't going to need any of them, so just use Open and Close.
The delay section tells the game how long to wait after the event occurs to run the output. In this case we want both to be zero, as we want the door to begin to open and close as soon as someone enters and exits the area respectively.
Lastly is the number of fire times. This is a feature you will seldom need to use, it tells the game how many times this output can fire before it stops working for the entire rest of the round. -1 just means it can run as many times as we want, and obviously we don't want our respawn door to stop working after so many uses, so leave this -1. Unless you have a good reason to change it, this should always be -1.
Filters
We now have a functional door, however there's two issues. Firstly, enemies are able to use this door, as the trigger accepts any players who touch it, so a blue player walking up to this spawn door would open it. Secondly, even if our door was only able to be opened by red players, there's nothing stopping blues from walking in when it is open.
To prevent enemies from opening the door, we'll use a filter. Create a point entity of type filter_activator_tfteam.
You'll see a few keyvalues that are basically self explanatory, so set the Team to RED in this case.
You will want to give filters descriptive names. You want your filters to be explanatory at a glance, for instance if I see a filter named "filter_red", does that mean it filters OUT reds, not allowing them, or it filters reds in, ONLY allowing them?
I would recommend something like filter_allow_red or filter_red_only for allowing reds, then filter_deny_red or filter_no_red for deny (you will most likely not be using the "deny red" type and instead be using an "allow blue" but if you do, make sure it's descriptive). In this case, let's name it filter_allow_red.
Unless the logic is specific to an area, you should generally centralize your logic entities in one location in the map. I like to put my logic entities near the center of the map, such as above the middle control point. If it's an asymmetrical map, it might be near the first or last point. But generally you want to have all your logic together, as it will make finding it for you (and others) easier, and you won't lose track of it. In the case of this map let's put the filter in the middle of the large room like so.

Now, within our door trigger, we can set the filter value to filter_allow_red. You will notice a dropdown, this shows all the filters you have on the map and is also why giving a descriptive name is so important, because finding it in this list will become trivial.

Respawn Room Visualizer (brush)
The next problem we have to deal with is not allowing enemy players into the spawn when the door is open.
Select the tools/nodraw texture, then create a brush entity the exact same size as the door. nodraw is a special texture that is invisible in game, the game knows not to even attempt to render any brush face that has nodraw on it. In our case, the barrier is largely invisible, so we want it to be nodraw to start off with.
However you will notice a problem. When you try to select the door or the brush entity, you'll have trouble selecting a specific one. In this screenshot I have the newly created nodraw brush selected, but you can't see it due to overlapping with the door.

There are two ways to reconcile this.
The first one is the easier one. Instead of making this new brush the exact same size as the door, extend it a bit into the spawn. You'll understand why once we define the entity itself but this extending into spawn is no problem.

Alternatively, you can keep it that size and use Hide to select it. I would recommend hiding most of the time, so go ahead and ctrl + z if you extended the brush.
The Uses of Hiding
Hide is a very powerful part of Hammer that allows you to temporarily hide objects. When an object is hidden, not only is it hidden in the editor, but it won't be compiled if you decide to compile while you have the entity hidden.
You can press h to hide the selected entity, u to unhide ALL entities, or ctrl + h to hide everything except the selected entity. When you are working on a small area, it can often be helpful to use ctrl + h to select only that area, so the rest of your 2D grid isn't cluttered with other geometry.
In this example I hid the main walls of our larger room so we can see what's going on inside the respawn room.

Unless you're intentionally trying to compile without a specific entity, you should get into the habit of compulsively pressing u before any compile so you don't accidentally compile your map with some of it missing. The above example wouldn't compile because it's missing walls.
In the case of our door here, there's a quick and clever way to only select one of the entities. Firstly, just click and try to select. IF the entity is already the one you wanted, just make the edits you need. IF it isn't, just press h to hide it, then select the other entity underneath that you actually need. This lets you keep the size of your brushes while making them still selectable. Generally it's better to just make sure your brushes aren't the same size as each other, but this is an option if you need two brushes at the same size.
In this example I hid the red door after selecting it and now I have free access to the newly created brush

Respawn Room Visualizer (entity)
Now that we have our nodraw func_detail, go ahead and convert it to a func_respawnroomvisualizer. This is a special entity that is only visible/solid to the enemy team. The team you select in the keyvalues is the team it belongs to, in this case the RED team. To them, this entity will be completely invisible and nonsolid and they can walk right through. Conversely, the BLU team will be able to see any non-nodraw texture on this, and it will be an impassible invisible wall to them.

You may recall that on most maps when walking up to an enemy respawn room, there is a red circle showing that you can't enter. This is a specific texture Valve uses for respawn rooms.
We could use the Apply Current Texture tool to set this texture across the entire brush, however we don't want to use the texture where it won't be seen. Therefore we need to use the regular Toggle Texture Application to apply it only to one face.
The Toggle Texture Application Tool
Select the Toggle Texture Application tool, which I'll refer to here on as the Texture Tool. You will notice if you click any face that your currently selected texture is updated to it, this is the color picking function. The right click will instead apply the current texture to any face you click. To demonstrate this, select the 3dskybox texture on the ceiling, then apply it to one of the walls. Then undo this by selecting one of the other walls and applying it back to that same wall you changed.

The current texture selection is what the texture tool will be when you first open it but it won't adhere to it. Luckily if we need to change on the fly, there is a Browse button on the texture tool that will open the texture browser. Select the overlays/no_entry in the browser, then use right click to apply it to the enemy facing side of the nodraw respawnroomvisualizer. Note that just like when selecting a brush, applying a texture also suffers from the same issue if your brush is flush with another brush. In this case though, merely clicking it and hoping for the best isn't a good outcome. You'll need to go back to the selection tool and use either h or ctrl+h to get the right brush selected. After that, re-open the texture tool (you may need to reselect the blocking texture) then right click the face you want to change.

You will notice that the texture looks off, it's too small and being repeated, it's also not centered. We want to change that so it's the correct size in the correct location.
Left click the texture, and you'll notice that in addition to selecting it, you'll have access to the settings for the texture at that particular face. We can go over advanced texturing later but for the time being, the easiest way to resolve this is to click Fit, then go to the x and y scales, and copy paste the larger of the two into the other. Lastly, press C in the Justify section to Center it. Click apply and you'll see the texture in the right location at the right size.

Our respawn room should be good to go, but let's give it a test before we make the blue one (don't worry, it will be faster than you think!).
Testing our Respawn Room
Compile and load into the map on RED team. Walk up to the door to make sure it opens and closes, check the respawn lockers to make sure they regenerate properly, change classes to make sure you're in a respawnroom, and make sure you can't build a teleporter on your side of the spawn door.
Next change to BLU team and walk up to the door to confirm it doesn't open.
You'll notice that our visualizer texture is Z-fighting (when two brushes are flush with each other and flicker) and so the door looks bad.

To fix this, go back into hammer and instead move the visualizer to be flush with the back side of the door. Unlike what I lead you to believe, Valve maps use this to only show that you can't go past when the door is open, so it shouldn't be visible if the door is closed.

Since we'll be duplicating spawn rooms for BLU side, let's completely finish off the RED side by going over lighting.
Base Map Lighting
Generally speaking there are three light entities you'll be interacting with, light_environment, light, and light_spot. For the purpose of this map we'll only be covering light_environment and light.
To start off with, how light works is that if a map has no light entities at all, it will be rendered in "fullbright." This means no lighting whatsoever, up until now we've been playing our map in fullbright. Adding even a single light entity will stop this and make the entire map need lighting, for example all rooms will become dark. Fullbright is not meant for release maps and is only a way for you to see your map prior to doing lighting.
Virtually every map will need a light_environment. Unless you have a totally indoors map like cp_junction, your map will have some sky that needs ambient light. light_environment generates ambient light from wherever you have skybox textures.
Lighting is in large part personal preference, and doing good lightning is hard. A later tutorial will be dedicated to that, but for right now we're going to just set our light_environment to use the same values 2fort uses.

Pitch however is a non-default value so we need to add it by disabling smart edit

Individual Lights
For our indoor lights, we'll keep it simple. Create a static prop on the ceiling with the models/props_lights/light_fluorescent_farm.mdl model, align it, then create a point entity of type light. This entity just emits light from the location, though it's less of an entity and more of an indicator for VRAD (the light compiler). We'll just set them to these settings as a basic value, you can play around with it as you like. Move the light entity below the prop.

Note that for the brightness, there are 4 values. The first 3 are the rgb values while the fourth is the brightness. In this case we'll just go with 250.
Getting good lightning takes trial and error and you may find some success examining valve maps to see how they do things (explored in the next part of this guide).
We want to move these lights around but it's a pain to have to select both each time we want to clone them. Instead, we can group them. Select both the light and the prop and click the group button at the top.

These two objects are now grouped in hammer. This has no effect in game, it's just for organization within the editor. You can now move them around together. You might notice you can't select an individual object anymore, but there's an easy way around that. The Selection Type has a Groups and Objects section. When Groups is on, whenever you click an object, if it's part of a group the whole group will be selected. Conversely if Objects is selected, groups are ignored completely. Note that Objects and Solids are different as if you have a single entity with multiple brush components (like our respawnroom), that is still one object, as opposed to these editor-only groups that we just demonstrated.
Use shift to create a few lights in our respawn room so that it won't be dark.

Fullbright
Compile and load into the map. You might notice nothing changed, but that's because of a cvar quirk with fullbright. If you load into a map that does not have any lighting, the mat_fullbright cvar is set to 1. If you swap maps to something else it stays, and then all other maps you play will be fullbright. You can confirm this by quickly doing a changelevel to a valve map. The fix to this is to simply reset fullbright by doing
Code: Select all
mat_fullbright 0in console.
Once you do, you should see your level lit up properly.
Duplicating Objects
Next up let's create the blue spawn room. It would be a pain to have to recreate everything we just did manually, so let's do some quick shortcuts to reduce the work.
On the opposite wall to the one you made your RED spawn room, go ahead and delete that wall. Then, go back to your respawnroom, and in the 2D grid, drag select the entire respawnroom itself as well as the wall.

Click on the selection until you've entered rotation mode, then move the pivot point to the middle of the large center room.

Hold shift, then rotate the room to the other side, and finally let go of shift. You'll see the entire respawn room has now been duplicated!

We will need to fix all the entities inside, however. See if you can figure out all the fixes on your own, they are listed below for you to spot check against.
-
Update the team keyvalue for all the spawn points
-
Remove the blue spawnpoint in the middle of the map
-
Create a BLU allow filter
-
Update the team keyvalue for the respawnroom and respawnroomvisualizer
-
Change the door trigger to use your BLU filter
-
Change the texture on the door to use the BLU dev grid
-
Rename the door so it's unique from the red door
-
Update the door trigger to use the new door name
-
Rename the respawn lockers
-
Update the respawn locker triggers to use their new names

Now if you compile and load into the game, you have a respawn room for both teams.
Lastly for this map, let's set this up with a gamemode. The easiest one will be koth, so we can go ahead and set it up.
Control Point Layout
Before we set up the gamemode logic let's create a zone for our control point. Place a prop_dynamic named point_prop with the model models/props_gameplay/cap_point_base.mdl right in the middle. You might have to move the prop down, as often times creating props has them start off floating. Some props don't align very well with larger grid sizes, so you can reduce your grid size to align it with the floor.
One problem with our current setup however is that it's not obvious where the capture zone of our point is due to our floor being a single texture.
We'll be creating some lines on the ground. There are a few ways you can go about this, such as creating very thin brushes and overlaying them, but for this guide we'll instead be cutting into the ground with the clipping tool.
As you may recall the clipping tool textures new faces it creates with whatever your current texture is. Use the texture tool to select the dev grid before we go ahead with this. The reason is that the clips we're about to make won't be visible unless you freecam into the ground, so we should be proactive. Once you have that selected, go ahead and make four cuts in the ground around your control point like so. Do not deselect the brushes at any point, just leave them selected - you'll see the clipping tool will make cuts along multiple brushes at once.

While this is technically fine, in practice you don't want to make square cut holes like this, as resizing the outer areas will introduce issues. Instead, we want to make sure our brushes are as large as they can be whenever possible.
To resolve this we are going to use the merge feature. There are two ways to merge, either with Ctrl + Shift + m or by going to Tools > Merge. Merging combines two solid brushes together. It can onlybe used on world brushes, and any brushes you chose have to share dimensions along the merge line. For instance, below would not be able to be merged

while the below would

You can merge more than one brush at once. To demonstrate this, go ahead and select 3 of the outer brushes we created with our cuts (ideally ones parallel to a spawn door) and then perform the merge. You'll see all the brushes get combined together. Go ahead and do this for the other side as well.

In terms of alternate ways to have done this, you could have also deleted two of the three brushes and resized the remaining brush into the open space, or you could have done your cuts properly in the first place by instead only selecting the middle brush when making your second set of cuts.
Also worth noting, you can use the vertex tool to make your corners diagonal like so

however I would not recommend this for floors, as floors will often need to get stretched and resized which becomes a pain to do when you set up the corners like this.
Once you're done, you are going to repeat the same process but this time only for the brush under the control point, except the cuts you make with the clipping tool will instead be near the edge of the brush.
Your final brush layout should look something like this once you're done clipping

Next up we want to use a texture to demonstrate where our capture area is. For this example we'll use the props/hazardstrip001a texture. Go ahead and texture the top part of your thin brush strips with it by using the texture tool. Remember, we don't want to use apply current texture, as unseen textures (such as the ones under the floor) should never be real textures, only dev/nodraw textures.

Control Point Logic
We are going to need a few logic entities in order to create our control point.
Firstly, we need a team_control_point_master point entity which you can place with your other map-wide logic. Name it something like master_control_point. This entity is in charge of all control points on the map, so you'll need one even if your map only has a single point. You only need one of these entities. The default parameters are fine in our case and we don't need to touch any of its keyvalues.
Next we need a team_control_point point entity. Go ahead and name it something like control_point. If this were a 5cp or a/d map we'd give it a more descriptive name, but as we're making koth, this name will do fine.
Move the entity right to the middle of the control point, and set up the key values as shown below

Next we need the trigger which defines the area where you stand to capture the point. Go ahead and create a trigger that's at least 256 high, using the hazard lines as the boundary for it.
The trigger should be a trigger_capture_area entity. Set the key values as shown below, making sure that the Control Point key value is targeting your control point entity which should be named control_point as per the previous steps.

This trigger also needs two outputs which will change the skin of the control point. Models in source have a concept called skins, which allows them to change their look without needing to have entirely separate props which would be a pain to work with. In this case, the control point has a base skin, a red skin, and a blue skin. Set up the outputs as shown below.

Note that while all props can be given a skin, you need a dynamic prop if you want to change the prop's skin during a round. Some static props like signs or trees will have alternate skins you can set at compile time, but this should only be used if you don't intend for it to be changed mid round.
King of the Hill Logic
We have a control point but there's no gamemode set, so the only thing you can do with it is just capture it back and forth - there's no timer or logic behind it other than the capturing.
First off, create a tf_logic_koth entity. This entity tells the game that the map is a koth map, as well as having configs for how long teams have to hold the point, and when the point unlocks. For our case, we'll leave all the values on their defaults.
Create a tf_gamerules entity named gamerules. While this entity does get automatically created if you don't make one, having one helps illustrate how the logic works. You will not need to touch any of the keyvalues on this, merely having it is sufficient, though you can rename it for ease of use.
Next create a logic_auto. This entity is used for automatically running logic on map/round start and we'll need it to set some of the default values. Set the values to the below

The Goal String is a value which utilizes a base TF2 translation file (in this case, #koth_setup_goal) to give players a HUD indication of what the goal is. Base gamemode all have their own goal strings already in the game. If you're making a custom gamemode without a goal string, you can write the goal here manually, but it won't be translated.
Open up the outputs of our trigger_capture_area and set the outputs as shown below, noting which outputs go for which team cap.

Respawn Wave Time is how long the respawn "waves" are, as players are spawned in groups. In practice you'll expect to see a respawn time between this value, and double it, so in this case the base respawn time will be somewhere between 6 and 12 seconds for most players, and it will grow/shrink depending on who controls the point.
As a general rule, attacking teams on any gamemode will have a shorter respawn time than the defending team. This is because the attackers usually have the tougher job, attacking a fortified position, so they need to be allowed to respawn more frequently, on top of having to walk further to the objective on asymmetrical maps. In KOTH, the almost universally used values are 6 for neutral (before the point is controlled), then 8 for the team holding the point and 4 for the team trying to capture the point. These values are used across basically all KOTH maps and will be fine for our sample map.
Once done you should have all these entities placed and configured

Testing our KOTH Map
Now that we have our map logic ready, let's go ahead and compile and test it out.
If everything was set up correctly, you should be able to capture the point as one team, swap to the other team, and capture it back, while seeing the timer tick down. You can also use this opportunity to check the respawn wave times to confirm they're being set correctly.
If your map is functional at this point, you have successfully completed the beginner guide to making a TF2 map. You, technically speaking, have all the tools you need to make a basic map. Of course it probably won't be a very good map, which is what the guides after this will help rectify, but you should now know how to use the editor and what it takes to make a basic map.
The last thing you'll want to do is go to File > Save As and save your map as koth_first_map.vmf. Your map should have the gamemode name saved up front so the game can determine what the gamemode is on loading screens and in the server browser, as well as for players to know what the gamemode it as a glance of the map name. Secondarily, this will also back up your first map if you want to come back to it later and do your own customizations. We'll be using this koth_first_map.vmf variant for the guide after this one.
CompilePal
Now is a good time to go over CompilePal. This is a tool which makes compiling both easier and gives more features.
Assuming you have followed the installation correctly, you should be able to just launch the program.
The below image gives a quick explanation of each part of the window, though it's largely self explanatory.

Game Configuration
Firstly before we try it out, close CompilePal then go into the directory you installed it, and navigate to the file GameConfiguration/gameConfigs.json.
You should set VBSP, VVIS, and VRAD to use the plusplus variants of the .exe files for the TF2 block
Compiling Processes
Let's go over some of the options available. First are the Presets, the Normal preset mirrors what you have in your F9 menu within Hammer, and other presets will run different process with different parameters. You can create your own presets as well to run whatever you like, or update existing ones.
Each "process" step simply calls an executable, first vbsp.exe to compile the level, vvis.exe to compile visibility data, vrad.exe to compile lighting, copy to move it to your maps directory, and game to launch the game. Note as mentioned above that on Linux compilepal (and hammer) cannot launch your game so uncheck that step, and prefer keeping your game open in the background while mapping rather than opening it and closing it every compile.
For the purposes of this guide, generally speaking you should use Normal when you're testing the map locally, and Publish when you are creating a release to send to other people.
Unless you are an advanced user, generally the steps you are going to want to be using are:
VBSP: Required always. Compiles the level
VVIS: Almost always required. Only turn off for very quick local testing - will not run visibility calculations and map will run slow and be visually buggy.
VRAD: Almost always required. Compiles lighting for your level. Only reason not to use is for quick local testing where you don't need to check lighting.
COPY: When your map is first compiled it's with in the sourcesdk_content/tf/mapsrc/ directory. This step just moves it to your tf/maps/ directory so your game can load it
CUBEMAPS: Optionally performs the cubemap compilation for you, if your level has any cubemaps. Windows only.
PACK: Required if you use custom assets. Packs custom assets (textures/sounds/scripts/etc) into your maps bsp file.
REPACK: Optional but highly recommended for release builds. Compresses your map significantly and reduces the file size.
I would not recommend using GAME and to instead have TF2 open in the background so you can compile while testing and having the game open. Unless you have particularly bad hardware and can't handle running both at once, it's better to have TF2 open than to launch it every compile.
CUBEMAPS, PACK, and REPACK should only be used when creating a version of the map to release to others, as they serve no benefit when doing local testing.
NAV is best done by hand (not as hard as you think!) so running it purely automatically while compiling is not something I would recommend.
As a result, I would recommend you edit your Publish presets and add the Repack step to them, and remove the NAV step.
With new VRAD++ features, I would advise compiling for Both in VRAD, which is automatically selected in the Publish - Both preset.
Skipping recompiling
If there is an issue with your cubemaps, packing, or repacking, don't worry - you don't have to recompile your entire map.
The COPY step after compiling makes a copy of the raw compiled map from your mapsrc dir into your maps dir, which means even if there's an issue packing the one in the maps dir, you can simply copy it again.

CompilePal is overall fairly easy to use and you should get the hang of it. The default settings are sufficient for most people, you can read the VDC pages on the main three compilers for additional flags.
Hammer++ Tips and Tricks
Specific Hiding Buttons
There's a few helpful buttons at the top bar that will help you hide entities based on certain criteria. I am going to be using a few of them during the section after this but they are generally helpful.
Other than texture lock, these are all purely visual and do not affect compiling, merely just how easy it is to see in the editor.

I advise using the Toggle Nodraw, Toggle Tool Brushes, Toggle Displacements, Toggle Prop Collision Meshes, and Toggle Editor Entities the most. The others have some use but are less important.
In retrospect this section probably could have been better combined by naturally using these buttons in other sections but maybe I'll add more to this in the future.
Map Optimization
This guide will cover the basic principles of map optimization and how to make a map that runs well. I will be covering visleaves, areaportals, occluders, prop fading, and other general tips.
This guide assumes you followed the above First Map guide, as we'll be using it during this guide to illustrate optimization principles.
What is Optimization?
To put it simply, optimizing a map means making it perform better for players. But to understand how to optimize a map we have to understand why maps run poorly to begin with.
While this map doesn't run entirely bad, Boardwalk will be a good demonstration point.
In BLU spawn, from this point of view, what do you think the game is rendering?

Obviously anything you can see is being rendered, but if we use mat_wireframe 1 we can see what the game is actually rendering.

There's a lot more than meets the eye to what's being rendered. As you can see, the entire rollercoaster over RED spawn is being rendered, despite barely being in view. While intuitively you may say "only render what's on screen" in practice that's a much harder calculation than you might think, so in reality everything in the photo is being rendered, just one layer on top of another. Which means the rollercoaster is actually being drawn by your GPU, but then it's overwritten by the objects on top of it. This wasted rendering is generally why maps end up unoptimized, as your computer is doing a lot of wasted rendering of objects that aren't even visible to players. Too many objects, and players will noticeably suffer frames per second which causes lag.
Now your kneejerk reaction may be that there's no way the game can handle this many triangles. But modern computers can handle quite a beating as far as rendering goes, so this is closer to a regular workload for the game to render. Tens of thousands of triangles are no sweat for modern GPUs, and most textures/models have LOD levels (Level of Detail) which means they get simpler the further away they are. For instance, on my graphics card, I get about 400 fps in blu spawn here (with medium TF2 settings). But this map is still a good illustration of how map design can affect optimization, and if you make too many mistakes or have a large map, you might end up with an unsalvageable map that can't be optimized no matter how much you try. So getting it right from the start is important.
Source Rendering
I am no expert but I can give a reasonable enough explanation of this to be of use. Rendering in the Source engine is optimized mainly through the visleaf system.
Let's get a real example we can use. In your map, go ahead and create a brush like this in front of one of the spawns:

Compile, then go into the spawn which has the blocker in front of it.

Now if we turn on mat_wireframe 1 you can see what's actually being rendered

So why is the control point being rendered despite being behind a door, and behind a wall?
Visleaves
The vvis.exe program you run when compiling a map generates something called visleaves. These are individual visibility cubes which determine what gets rendered depending on where you are.
To view your current visleaves, go to Map > Load Portal File. This will only work if you have run vvis at least once, and you will need to re-load the file each time you run vvis to update it.
You should see something like this


So now that we see our visleaves, why is the control point being rendered? The key is in which visleaves the game decides to render.
When your player camera is within a visleaf, all visleaves that can be seen from anywhere within your current visleaf are rendered. Additionally, when it comes to vis, only world brushes are calculated. Props, brush entities, and other such objects are ignored for vis (visibility) calculations.

Through this we can understand why the control point is being rendered. The respawn room is two large visleaves, and since a line can be drawn from a point in that leaf, NOT just the player's camera, that means (at least one of) the leaves that the control point resides are rendered in their entirety.
Any entity that has it's bounding box any amount within a visleaf will be rendered if that leaf is rendered. You can see in this example that despite the control point being split between four different leaves, it will be rendered if any one of them is visible, not just if all four are visible. This is crucial and explains why the earlier Boardwalk rollercoaster was rendered at all times, because the whole coaster was a single prop, and despite only being able to see a little bit of the flag on top from blue spawn, it meant the whole prop had to be rendered.
Another example of this principle is below

In this case, someone standing at point A would render someone standing at point B, because the visleaves of both points extend above the wall, which means both visleaves can see each other.
If you are wondering why rendering is done this way, it's because it makes it possible to pre-calculate rendering while compiling. The map is embedded with information that says, for each visleaf, what visleaves are visible, which means players don't need to calculate this information on the fly when doing rendering. It's an efficient system, but from a purely automatic standpoint it has some flaws.
We'll be going over how to take advantage of this system to optimize a map so that, generally speaking, only what's visible will be rendered, which will help improve the optimization of your map.
Optimization through design
The first and easiest way to help your optimization is to optimize through design.
The easiest example I can give is the map garbage_day. While this map is fairly simple and is thus pretty optimized, it's a good example of the earlier principle.

As you can see in this example, due to how open this map is, pretty much no matter where you're standing above ground, it's very likely that the entire map will be rendered at once, because if even a single pixel of the visleaf you're standing in can see another, it will render, and in such an open area, this will be almost guaranteed.
As I said previously, fortunately this map doesn't have too much going on which means that even though it's all rendered at once, the rendering strain is minimal.
Another example is Odyssey. At the second point, a large amount of the map ends up being rendered.

The map creator did a fairly good job of optimizing despite all of this, but less effort would have to have been put in had the design been different, and in all fairness this type of thing would be present on a map like Upward as well, which also has open areas.
The takeaway I want to convey is that unless you have some clever ways to get around it, open maps usually mean poor optimization.
Source as an engine is generally made to work with rooms connected by hallways. These rooms are often not huge, being medium or large on average, with hallways connecting them. The hallways serve as points where you can sever connections between rooms so that there's no visibility.
Dustbowl is a perfect example of this kind of visibility. Here on the first point, you'll notice all the hallways twist and turn such that none of the second point is visible.

Of course on the flipside, this also makes dustbowl a very choke pointy map where most of the combat across the whole map happens in these connecting hallways. It's a fine line to walk and I would encourage looking at how some valve maps do their level design if you want to try and optimize better. In general, you want your level to be a couple large rooms, with hallways and smaller, connecting rooms in between them. These larger rooms then won't render each other, meaning the natural segregation improves performance.
Optimization through areaportals
This is the first step we can take to more intentionally optimize our map. If we look at dustbowl again, we can find these areaportals in the hallways

Areaportals divide the map up into "areas," and the game does special optimization for these areas. For instance, if you are on one side of an areaportal, any prop not directly visible will not be rendered at all, even if the visleaf is visible. Additionally, areaportals can be "closed" which allows them to completely cut off areas from sight. For instance, if you are on one side of a closed areaportal, everything on the other side will not be rendered no matter what, even if you can directly see it.
In some maps, crucial dividing doors will have areaportals inside of them set to close when the door is closed, such that everything on the other side of the door will stop being rendered when the door is closed. If you recall, vis ignores entities like func_door, which means that normally a closed door will not block rendering at all. Attaching an areaportal to it allows you to make the door act like a world brush when it's closed.
Back to our Dustbowl example, we can see how the areaportals in this side hallway create areas.

Note that areaportals need to entirely enclose an area. If a line can be drawn from one side of a portal, to the other side of it, the areaportal is considered to be "leaking" as it's not enclosing any area. If you put a sole areaportal in the middle of nowhere, it will leak as you can easily connect the front and back with a curved line. If you have a respawn room with two exits, and only put an areaportal on one door, it will leak as you can go from one face of the portal to another face by drawing a line through the other door.
Areaportals count as an edict, which means the game can only have a limited number of them. You should not have more than a few dozen areaportals across your map generally speaking unless you really need it.
If you want to close/open areaportals with a door, areaportal entities have a key value they can use to link to a door and will mirror the door's status. An areaportal should always be smaller than the door, if it is larger than the door then the door will become invisible when closed.
If we go back to our first map, we can put an areaportal on the doorway. This one is larger than the door because we aren't linking it to the door itself, simply dividing this into an area provides passive optimization benefits that we want to take advantage of.

Now if you go in game and check the wireframes, you'll notice the control point is no longer rendered if it can't be seen through the doorway, such as if you stand to the side of the room. This is a good first step and means if we had lots of props in our middle area, only props actually visible through the doorway would be rendered, rather than anything within the middle room's visleaves. Note that areaportals will hide direct line of sight to props, but not for (most) geometry.
Areaportalwindows
Another useful entity to note is func_areaportalwindow. This is intended to be used on windows that players can look through up close, but become opaque at a distance.
The following example is from plr_pipeline (with the brushes resized so they don't overlap for demonstration purposes).

As you can see, the window is set so that it fades into becoming opaque between the two visible radii set on it. The brush to the right is the one selected in the areaportalwindow keyvalues, and it's used as the "opacity" that is faded into. Beyond the final range, the areaportalwindow closes but only for that player, meaning if you stand outside of the larger sphere, the spawn room will not be rendered at all. If you have a lot of tiny windows this can become inefficient, and as such I would advise not having lots of small windows and sticking to fewer, larger windows if you want to be able to use this optimization tactic.
Optimization using Hints
However looking at our first map we can still see there's a problem - standing in the doorway still renders the control point despite our areaportal. We could solve this by closing the portal with the door but this won't always be available, such as with transparent doors or with very thin doors. Instead, we can take advantage of hints to guide the compiler to make the visleaves we want.
To start off with, select the tools/toolsskip texture. This is a special texture similar to nodraw, except it is ignored by the renderer and the compiler. You can place skip brushes freely, as they are totally ignored when the map gets compiled. Often times when using hints, you'll want to make the brush a skip brush as the base, then use the hint only on the sides you want.
Create a brush the side of the area, but only 16-32 tall, and have it be flush with the top of our spawn wall. Then, apply the tools/toolshint texture to the bottom like so.

Compile the map, but don't load into it, instead simply reload the visleaves by doing Load Portal File once more.

So what exactly did the hint brush do? The hint texture is used to force the compiler to make a visleaf cut along the hint face. Normally, visleaves are created roughly along the lines of your world geometry. We can tell vvis to make a cut at a specific location by using the hint texture along a plane. In this case, by creating a map wide plane at the height of the wall, we've divided up the visleaves so that leaves above the wall don't reach down below the wall. That means any leaf behind our wall no longer sees leaves on the other side of the wall due to height.

Of course in our case, this doesn't do much on it's own - these leaves extend to the sides of the wall as well which meas you will still see on the other side of the wall.
Our first reaction to this problem might be to do something like this, putting a hint across each side plane of the wall as well.

but this comes with it's own problems. I placed a small crate prop in a specific location to demonstrate why.

If we look at this topdown, you'll see what happened. Our new hint brushes cut the middle of the area, so now the control point's visleaf is completely behind the wall - this means standing on the spawn room side of the wall will not render the control point which is good. However, the box, despite not being visible for most of the area behind the wall, will still be rendered, as a ray can be drawn to it's visleaf from the one behind the wall.
Let's look at a more efficient way of doing this visleaf. If we instead create a skip brush and cut along the diagonal that connects the wall and spawn door like so

we have a more efficient vis cut. Set it to a hint, compile, and then compare

Now you can see that from within the red respawn room, nothing behind the wall will be rendered as a line can't be drawn from within the respawn room to anywhere behind the wall.
You don't want to do this very often. Generally speaking, spamming hints is something you do only for unique map geometry or if your map is poorly designed, as a way to try and salvage it. A well designed map won't need that many hints, only on major corners and around choke points. Additionally, sometimes the area you're trying to optimize doesn't have that many props or objects which means trying to use a hint to remove it is pointless. In the case of our first map, we have a single prop and barely any geometry, which means trying to do optimization like this is not important. If we detailed our map significantly, we might want to put these hints in so that props aren't always rendered.
One example from a map I made, mvm_refinery, illustrates the use of few but powerful hints very well.


This map is divided into a lower and upper level. While this wall could have been a transparent fence, I instead decided to make it a solid world brush, then added a map-wide hint right at the wall's level. The effect this had is to make it so that the upper and lower levels of the map are not rendered as long as you are standing below the top of the wall, which 95% of the map is below, meaning this single hint brush managed to segregate the map in half, providing a lot of optimization.
Optimization using Occluders
The last entity you can use for basic optimization is func_occluder. This entity functions a bit differently than other entities in the sense that it's a purely run time optimization, it doesn't affect compiling whatsoever.
Occluders work by not rendering any model (prop/player/etc) that is completely covered by it. Because of this, these entities tend to be more expensive and if you place too many on a map, the time it takes to perform occluder calculations will outweigh the benefit you gain from placing them. You want to place these sparingly, and at crucial locations where they have the most effectiveness.
To demonstrate, let's go back to our first map. Delete the skip hints, then select nodraw and create a brush the exact size of the wall. If you want, you can drag it down into the floor to make it easier to select later.
Place the tools/toolsoccluder texture on the largest faces of the wall. The occluder calculations run for every face you place the texture on, which means you don't want to waste the texture on small sides that won't be able to cover anything.

Convert the entity to a func_occluder then compile and load into your map. You should notice that despite not having any hints/skips, and the visleaves being able to see each other (you can load portal file to confirm this), props won't be rendered behind the wall. This isn't foolproof but it's usually good enough if placed correctly, move side to side at the edge of the wall to see how the props in the other spawn are hidden as the pass behind the wall.
Optimization using Prop Fading
The last important topic to touch on for optimzation is prop fading. This makes props fade at a distance despite if you can see them or not, which helps optimize for situations where visleaf optimization isn't feasible.
Generally speaking, almost all props on your map should have some amount of fade on them. Props that you have in very well segregated rooms can usually be ignored for this, but in open areas you'll want to use fade to help reduce rendering strain, as props tend to be the largest render strain aside from other players.
To start with, go ahead and delete the occluder we just made. Now, on our mining crate prop, set the fade values to the below

If we load in game, we'll be able to see that if we are right near the spawn door, the crate will not have a wireframe, but it will show up as soon as we enter the larger sphere. Anywhere outside of the large sphere, the prop will not be rendered whatsoever.
This, admittedly, is not a great example of fade, as there's plenty of locations here where the prop fades out while in full view of the player. Instead, look at this sample from Dustbowl.

This rock has a fade distance such that over by the spawn, the rock stops rendering entirely despite likely being inside of a visleaf that can be seen from there.

In this example, the bush stops rendering if a player is standing over by the tunnels.
Generally speaking, the smaller and less important a prop is, the closer the fade can be. Most players will not notice this bush fading away as the stand from a distance because it's in a small, closed off location.

In this last example, we have this bush in Viaduct. Viaduct is a map designed such that visleaf optimization is going to be much tougher, and so prop fading plays a much bigger part in the optimizing of this map than hints/occluders/areaportals do. In this case, the bush fades before you even get to the control point, because the visleaf containing the bush would be visible from the other side of the map most likely due to how open the map is. On the flipside, the train props at the top of the map never fade, as there's nowhere you can stand in the main area where it would render, but wouldn't be an obvious fade.
Therefore, the largest and most impactful prop remains always rendered since it usually will be in view, while smaller things like small bushes and rocks fade relatively quickly as they are less noticed when they disappear. Larger props should either fade later, or not at all, and smaller props can fade closer unless they have gameplay significance (such as crates or barrels which block sightlines, rather than just being decorations).
There's no "correct" way to perform prop fading. My best advice would be to examine Valve maps to see how they fade props, as Valve usually does a good job of fading smaller props.
Optimization using common sense
The last and most important tip is not to go overboard with your map. If you'll notice, a lot of the best looking valve maps don't have that many props. Not every corner needs a whole array of props. The more you detail a map, the worse it will run, so finding a middle ground between enough but not too much detailing will be the best help for your optimization. Ask your self for every prop you place, if you can't fade it away easily, should it be there? Check out the level of detail Valve maps put in to see just how much you need, you'd probably be surprised at how little you need to make an area look good.
Optimizing Hammer
While all the previous tips are regarding optimizing your map so it runs better for players, you can also optimize your map so it runs better in hammer. While there have been improvements recently regarding tools like vvis and vrad so they run much faster, optimizing your visleaves will still reduce your compile time significantly.
For example, let's create some unoptimized brushes. Select the brush creation tool, then in the bottom right select cylinder and set it to 16 sides.

Create a few of them on the floor and then compile and load portal files. You should see something like this

As you can see, these cylinders are creating a ton of visleaf cuts. Every visleaf cut is going to make vvis take longer, and if you have a huge number of leaves across your map, it's going to slow your compiling down significantly. vvis has been known to take hours on complicated maps, when it can be lowered to just seconds on a properly optimized map.
The biggest tool of our trade here is going to be func_detail. This is a special entity that works the exact same as world geometry in essentially every respect, except it is ignored by vis calculations. This means func_detail entities will not generate visleaves.
If we convert the cylinders into func_detail and then recompile and reload our portal file, you'll see this now

This means vvis is no longer trying to make leaves for these entities. This also means that as far as rendering goes, they are translucent, and you will render anything behind them since they won't make vis cuts and will allow vis rays to pass through.
func_detail is your bread and butter for optimization. You may ask yourself, when is the correct time to use it, and my answer would be if it doesn't need to be a world brush, it should be a detail brush.
func_detail does NOT seal leaks in the map, so the lowest floors, ceilings, etc of a map must still be world brushes otherwise your map will not compile.
Let's look at the middle point of Fastlane for an example

None of the brushes here are going to be dividing areas, you aren't going to be hiding entire areas behind them like the wall of a building would. That means this entire structure (save for the roof which is props) can be func_detail, as from a vis perspective it's pointless.
Optimizing Hammer, after the fact, using visgroups
But, doing this as you go can be tedious. Instead, it might make more sense to do func_detail conversions later on.
Instead, we're going to use the visgroups to show every brush currently affecting our vis.
To do so, perform the following steps in order using the Visgroups tab on your right hand bar.

This process is:
Uncheck Auto
Check World Geometry
Uncheck Displacements (leave Water checked if you have it)
Uncheck all other major categories (Tool Brushes, Entities, Custom, World Details, etc)
*: Re-check Hints/Areaportals under the Tool Brushes category
Unchecking a visgroup will mean that group will not be compiled. Only use this when investigating the map. Get in the habit of pressing "Auto" before every compile to make sure you're not compiling while missing objects
WIth this, the only brushes checked should be World Geometry, Nodraw, and Sky. These are the only categories that affect vis other than the previously mentioned tool brushes. Note that deselecting Auto then reselecting only these categories does not work the same, so follow the above steps instead.
If we do this on Fastlane, we can see this at the middle point

In this case Valve chose to make the ceiling into world brushes, but otherwise this showcases every brush that actually affects vis. The only things that actually affect what you can see are shown here, anything else that was hidden in this process was not a part of visibility checks, including the floor. This is because Displacements (the bumpy brushes used for natural rocks/dirt/grass/etc) are not considered world brushes, which means they don't count for vis nor do they seal leaks.
In the case of Fastlane, this whole center part (the roof and control point concrete) could be func_detail, which makes this a slight optimization mishap on Valve's part. It doesn't affect much and this is only really a problem if you have very complicated geometry. For instance, if the middle building itself was entirely world geometry, it would generate a ton of pointless visleaves.
Another example I can give for func_detail is the following

In this case, the reason why this should be a func_detail is that there's no way it can possibly affect vis for a player. In order for a player to be "behind" this, their camera would need to be on the floor. Therefore this is never going to meaningfully affect vis and can be converted to a func_detail.
If we go back to my mvm map, this is what a properly func_detail optimized map looks like. Only the core geometry is detail, things like the roofs, stairs, ledges, fences, etc. are converted to func_detail as they'd have no meaningful impact on vvis.

You should now be able to fairly competently optimize a map. My biggest suggestion is to design your map around optimization to begin with, as a map with a poor layout will likely be unable to be optimized. Just remember, rooms and hallways.
Out of Bounds and Skyboxes
This will be a brief section going over skyboxes and keeping players in bounds in your level, even if your out of bounds is detailed.
2D Skybox
The 2D Skybox is the single texture used for the sky. To set the skybox material, go into Maps > Map Properties > 2D Skybox Material. Here you can click the dropdown to see a list of skyboxes used in Valve maps, or check the VDC Skies List instead.
For instance, let's change our skybox to use the Hydro skybox. Scroll down until you find it or simply set it manually to sky_hydro_01 then click apply and you can see the skybox will change if you go outside your level within the 3D view.

Out of Bounds
First I want to cover out of bounds areas because this will segue nicely into 3D skyboxes later on.
Let's create a bit of out of bounds for out level.
On one of the empty walls of our map let's open it up and create a location where we can place some props, like below.

Let's place some props here. The ones I used in this demonstration are security_fence512, dumptruck, and keg_large.

However, there's a glaring flaw in our current design - this fence doesn't extend high enough and as a result players can explosive jump into this out of bounds area. We want to prevent that from happening, so we can add some invisible walls.
There are two invisible wall textures you can use, tools/toolsclip and tools/toolsplayerclip. Personally I prefer playerclip, however they function identically in TF2 (slight differences in other source games) so you can choose whatever you like. For the rest of this tutorial I will be using playerclip but you can use regular clip if you want.
Playerclip (or clip) is a special texture which prevents players from passing through it, and from engineers placing through it. However, it allows bullets through as well as projectiles like rockets and grenades. These are the most commonly used textures for invisible walls, as it prevents players from going out of bounds but it doesn't break immersion by causing projectiles to stop in mid-air as well. Player clips are one of the tool textures hidden by the tool texture toggle, so if you are having a hard time seeing your visuals you can use them to temporarily hide them.
Player clips prevent players from going past them, as well as preventing engineers from building through them. Many old maps had exploits where out of bounds clips had single unit gaps in them, allowing engineers to place teleporters and build sentries in out of bounds locations. Making sure you don't have unit misalignment is one of the biggest reasons you want to make sure you have grid snapping on, because if you don't, you might introduce small holes which in addition to making your level look bad, also allow exploiting.
Create a player clip and extend it from the floor to the ceiling, you don't want even a unit of open space on any edge. Make the clip 16 units thick, covering the fence like so

While you might think we're done, let's double check. Certain props have collisions that aren't fully aligned with their model. Use the prop collision toggle so we can see the collision mesh of this fence

As you can see, our fence actually sticks out from our player clip. This means a player could stand on top of the fence and shoot down from below. In our case we don't want this behavior, so let's correct it by dragging the clip forwards

Depending on how you like to do things, you may choose to only drag the clip a few units rather than making it flush with the wall, however getting stuck on corners can be a peeve towards players so I would recommend keeping your walls flush and dragging it all the way.
Now we have a bit of detail in our out of bounds, and players can't reach it but they can still see it. This technique is used extremely frequently by valve mappers to make the level look more open than it actually is.
One thing though about our out of bounds is that it drops off a sheer cliff, which looks strange. Our skybox doesn't have a "floor" so we should add something to make it look more expansive. Before we start working with 3D skyboxes, let's add a bit of a ledge so this doesn't look to be dropping off a sheer cliff.

3D Skyboxes
A 2D skybox was the simple texture in the background of our level, while a 3D skybox is instead a moldable miniature version of our map we can use to create objects outside our map without needing to expand the main level.
Often times you will want to add a 3D skybox to detail the out of bounds areas of your map, but having to make a whole separate area increases the size of the map too much. This is where a 3D skybox comes in, we can make a purely visual addon to our map without needing to physically expand the map.
First off, create a small box somewhere outside your map using the 2D skybox texture. I went ahead and gave myself 512x512x256 space but you can give yourself as much as you want, just note it doesn't need to be very large and you can always expand it later.

The 2D texture is used to prevent recursion issues where the skybox renders itself.
Next, create a sky_camera entity. This entity represents the world origin. Make sure the 3D skybox scale is set to 16 for this guide. The sky camera should be placed on grid, and unless you have a reason otherwise, should be placed near the bottom of your sky box (but not touching the floor)

As mentioned, this sky camera represents the world origin. Anything we place within this box will be scaled up 16x, then displayed in the main level relative to our sky box.
While you can perform trial and error, there is a simpler way to get the dimensions you need for your map. This is somewhat time consuming and I would recommend doing your 3D skybox last after the rest of your level is more or less set in stone.
The easiest way to figure out where to place objects is to measure it. I would recommend using a skip texture just in case, as if you leave this brush in, it won't show up in the final map. But any brush works if you want to perform a measurement.
Drag your brush so that one corner is on the world origin, and one is on the corner you're trying to get the skybox coordinates of.

Now we can see that this corner is 1664, 128, and 320 units away from the origin.
Since our skybox is 16x scale, that means the same corner is going to be 104, 8, and 20 units away from the sky_camera.
By creating a second skip brush in our sky box, we can now measure where that corner will be!

We will also want to know where this outcropping meets the wall, but now that we have one anchor point, we can measure from there instead without even needing a skip brush. In this case we know it can go at most 36 (576/16) units towards our sky camera to be flush with our outside wall.

Now within our sky box, we can create some 3D skybox area that fits snugly with our real geometry.

Compile and check out the out of bounds area. You'll notice two major issues. The first is that our out of bounds actually isn't snug with our walls, and secondly the textures are a lot bigger than they should be.

The easiest fix is our walls. While you can handle this by increasing the size of your skybox walls, I am going to do it simply by expanding the out of bounds area.

The reason for the gap is that the top texture of our walls was touching a skybox, which means it got nulled. Doing this means it will touch our 3D skybox again.
The second thing to fix is our textures. Go ahead and select one of the textures on your walls and note down the values. The texture scale is going to be updated, while the texture value itself and the alignments need to match 1:1. Do not touch UV vectors!

In this case our scale is 0.25, which means a 16x downscale puts them at 0.015625.
Select your brushes in your sky box first with the selection tool, then open up your texture tool. Doing this will show you all the values shared between different parameters.

We want the texture shift to be x = 0 and y = 0, so let's set that to zero. Then, set the texture scale to our new value

The texture has now been scaled down by a factor of 16. If we compile and load back into our map, we should see our textures fixed

There's only one small problem remaining, as you can see this corner has bad lighting. This is because 3D skyboxes are not lit using data from your main level, so that part of the sky box doesn't know it should be in shadow.
Generally speaking you should only use a 3D skybox in hard to reach locations. In this case, this is fairly near the playable area and as such it will be obvious. If we wanted to make this look better, we might make part of this area in our main level, and put the extensions further away so it's harder to see the issues. You don't want the 3D skybox to be too close to the playable area, this is typically why you have certain details like the truck and props in your main level, while details such as buildings in the distance, mountains, or other far away objects in your 3D skybox. You may even choose not to have a 3D skybox based on your level geometry.
One last thing to note is that for skybox props, there are often specifically compiled props that are at 1/16th the normal size that you can use for your skybox. For instance, I am going to add these power towers to my skybox, which are the model models/props_farm/powertower02_skybox.mdl.

Sizing your skybox properly
While the above is an example of putting a skybox in the right spot, I would not recommend you create one that small or close to the map.
Let's look at the skybox for badwater for an example. Over by last we have this out of bounds area

If we open it up in hammer we can see this area is actually in the map, it's not a part of the 3D skybox. Areas close to your map generally should be real even if out of bounds, as we saw with the shadow issue on the corner, having a skybox close up can be buggy.

But that's not all, in many cases we don't even want our skybox to be touching our geometry. If we look at the actual 3D skybox we can see it's both huge, and the map itself floats on top of the skybox

This is more obvious if we go near the bounds of the map in spectator.

Depending on the size of the skybox you want to create, your skybox might be massive. For instance, upward has a massive skybox almost as large as the map itself

whereas dustbowl it's a lot smaller.

Many valve maps perform their skyboxes this way by having displacements cradle the map. How you wish to do it is up to you ultimately.
Advanced Map Geometry
Next up let's go ahead and add some more interesting areas to our level. This is not going to be showcasing best-practices for level design, moreso how to make different objects in a level. A good level should not be this small, and we are going to take some liberties here.
Creating a roof
To make it so standing on our point isn't too difficult, especially with what we're going to add later, let's add a roof to our point similar to koth_king or cp_fastlane.
First let's create some pillars to hold our roof up. I am using the toggle editor objects/toggle tool brushes setting as you can see in the top right to declutter the screen while I work on this geometry. The editor objects toggle is buggy so use at your own risk, it may make your geometry miscolored at certain angles.

Use the clipping tool to create some triangles as well, this first roof will simply be two sided.

We want to make a diagonal brush for our roof. The first step will be making a regular brush. Give it a lip over the sides that will be going off the edges, a roof that is flush with the sides looks strange.

If you made your brush with the same dimensions as mine you might notice our brush doesn't align with the roof. To get it to align, we are going to need to use the same slope the roof does. In this case, it's a slope of 2/3

so in order to align it, we can simply drag the edge of our roof down so that it's the same slope as the roof, just hitting the next point.

You can also reduce the grid size if you want the lip closer to the edge of the roof
To make the other side, you can either create another brush, or clone the existing one (done easily by putting a pivot point at the center and rotating while holding shift)

This roof is fine, but let's demonstrate how to make a more interesting roof by making it four sided.
First, take your two roof sides and use shift to rotate them 90 degrees to create a perpendicular set of roof tiles. Bring the triangles over as well

Make sure to stretch them so the corners meet like so

Next select only the roof parts (not triangles) and make two diagonal cuts right along the center like so

Now you can create two different types of roofs. If you delete the top part and the triangles, you get something like this:

and if you instead delete the bottom triangles, you get something like this:

I will be sticking with this second roof for this tutorial. I'm also going to add a ceiling to this as well as move my logic entities down so they're in the open

func_nobuild
Now, our map is pretty small, and being able to build a sentry up here would be fairly oppressive. To fix this, we can add a func_nobuild trigger. This allows you to block engineer buildings in an area, even specific buildings if you so choose, though it's mostly used as a universal blocker.
In this case let's create a trigger that covers the whole roof - and only the roof. We still want players to be able to build lower than this, so the minimum for the trigger is the bottom of the roof, while the maximum for the trigger should be a few units above the tallest point. If you make it flush with the top, players might be able to build right on the peaks, so we can avoid that by just making it a bit taller

Additional Spawn Exits
Our map is small and would be prone to spawn camping. But generally speaking, most maps are prone to spawn camping, especially asymmetrical modes (as well as modes that can become asymmetrical, like koth when one team controls the point). Such modes will usually have one or two side exits which are a bit slower or lead to flanks, but help prevent a single door from being camped and shutting one team down.
Using your previous knowledge, go ahead and create some exits like this. Ceilings hidden for demonstration purposes.

You can go ahead and duplicate your main spawn door to the other two exits, assuming you made them the same size they should fit perfectly. Remember to rename the brushes and update the outputs!

Make sure to increase the size of your func_respawnroom to include the alt exits by adding more brushes to the main entity, since this area counts as our respawn room and we want to prevent engineers from building here. By pressing the to Entity button when you have an entity and a world brush selected, you can add that world brush to the entity instead of needing to recreate it from scratch.

Creating a tower
To give our players something more to do, and to shelter one of our spawn exits, let's add a sort of tower like structure. This can contain whatever we want, but ideally we'll put some health and ammo kits as a place to fall back, as well as a balcony where snipers can stand.
First off, let's make an interior room. Design it like this, with one of the blocks being square as we'll be putting a staircase conencting to it.

Creating the staircase is going to be a multi-step process. First off, create the individual stairs like so. These dimensions happen to fit this room quite well so we'll be using them.

Next, use either the clipping or vertex tool to create brushes that go right along the slope of your steps like so

For each of the two flights of stairs, select all of the stairs at once by holding ctrl and clicking each, then use the clipping tool to cut them along the same slope as the brush below them

Lastly, we are going to make sure the stairs are smooth. In TF2, players automatically step over any brush or obstacle that is 18 hammer units or less, so players can go up our stairs unimpeded, however its a bit bumpy visually and there are issues with explosives.
To resolve this we will use the tools/toolsblockbullets texture. This is a texture similar to the clip texture in that it is invisible, except it also blocks bullets and projectiles, serving as a solid but invisible brush. You can use the clipping tool to get it to match the slope of the stairs exactly. As mentioned previously players will snap up small hammer gaps, so the small lip created by letting the blockbullets go all the way to the floor here is fine and won't affect our stairs. Though generally speaking in a real map you don't want two flights of stairs this close, so this kind of lip is usually a non issue.

Create the second floor of our tower. Note that I used a second brush for the second floor wall instead of dragging the lower one down, this is because if we want to cut or modify the wall of the second floor, it will be much easier if the brush is separate.

Let's add a balcony outside the front with a door. Don't forget to add a ceiling!

Lastly for geometry, let's add a second balcony here as a secondary point for snipers.

We have the geometry for our building now, but it could use with some supplies so players can fall back here.
Create two entities, an item_ammopack_medium and an item_healthkit_medium and place them on the ground. While you're at it, you can add some lights to each floor.

Place a clip on the roof to prevent players from getting up there, and a models/props_hydro/barrel_crate.mdl to help break up sightlines, and we're done with our tower!

Middleground
Let's add something between this tower and where the other tower eventually will be. I'm going to create a sort of U shaped building which contains a large ammo pack, something valuable players will want to enter the shack to fight for.

Next a roof (note in this example I had to go down a grid size to align my roof, you can size this building however you like)

And finally some boxes to allow Scouts to climb on top

We can also add a light here to make sure it's not too dark

Grouping
Before we go duplicating this tower to the other side, let's do some grouping on our tower.
We want to select everything in the tower. Rather than doing it one by one, simply make a box selection in the 2D like so

then after selecting, deselect what you don't want (the floor, the ceiling skybox, and the door trigger) and group whatever is remaining

Now when you use the Group selection mode, you can click the tower once and it will all be selected.
Duplicating our tower
As in all king of the hill maps, our map needs to be symmetrical. That means we're going to need to duplicate part of our map to the other side. Generally you want to do this at the end, once you're happy with one side. I have a plan for the other exits that won't require any duplication, so we can go ahead and just do our duplication now.
On the side you didn't make the tower on (in my case, red side) delete the walls that were modified in the process of making the tower and adding the alt exits.

Select all the brushes related to the alt exits. Make sure to select each door fully, getting both the door, the trigger, and the spawn room visualizer.

Use the pivot point to rotate the alternate exits into place

Make sure you rename and fix up all of the doors to be for the other team! All three entities of the door need changes.
Now, we can duplicate our tower. Select the tower and shift-dupe it over to the other corner of the map.

However you will notice that try as you might, you can't rotate the tower into place. That's because the tower doesn't need a rotation, it needs a flip.
Go to Tools > Flip Objects > Horizontally or press Ctrl + L. You should notice that you now can rotate the towers into place.

Creating a death pit
Many maps have a concept of a death pit. Usually, open maps with edges where you can push players off the side, such as upward. All of these function the same.
The entity we are going to be using is trigger_hurt. This entity deals damage to players based on the specified amount. While we can set it to low values to tickle players, in our case we want to just kill them.
First off, create the pit. It doesn't need to be too deep. You can decorate the bottom of the pit if you'd like but in this case I'll be keeping it barren.

Create a trigger_hurt brush at the bottom of the pit with the following parameters

We want a high damage number, generally anything above 1000 will work but 9999 is used frequently.
Zero Damage Force is also important, because if it's turned off players will be launched as soon as they touch the trigger when they instead should simply crumple to the floor.
Damage Type is important as it affects what happens to the player when they die. For type 32 damage, Fall, this means players will hear a crunching sound when they die and the killfeed will say a player has died of fall damage. If you were making this a lava pit, you could use a different type of damage (fire for instance). Trains have trigger hurts attached to them with the Train damage type which shows a train in the killfeed. But, in our case, we're going to stick with Fall.
A plain pit with nothing on it isn't very interesting, so let's give players something to work for here.
Add a thin platform that crosses the bridge, weighted slightly more towards the wall. You want it to be at least 64 units away from the wall, in my case I put it 128 from the wall, and 256 from the ledge.

Now we can add something that will make players want to cross the bridge, such as a large health kit.

We can also add some danger signs here, these are often present near kill triggers in many Valve maps to indicate to players they will die if they fall.

Let's also add a bit of a roof above the exit near the pit so it's not so open. This one we can do without func_nobuild, as it'll be fine to let Engineers build up here if they want, due to how open and easily spammable it is.

To block the spawn door itself, we can use some glass. In this case I'm using glass/glasswindow001a with texture scale of 0.5.

Don't forget to add a light or two here!

Lastly, we can put some crates here with small ammo packs. This cove is a good spot for a dispenser and the crates prevent it from being too open. The smaller crates to the side also allow Scouts to jump up on top of both the crates and the roof of the point.

Compile, and you should have a functional koth map with all the workings!

Optimization Pass
Let's perform an optimization pass on our map.
First off, we can use the visgroup trick to see all of our brushes that count towards vis.

You can see here a lot of our map are world brushes. If we load in the portal file...

...then you can see it's pretty messy. A lot of these brushes can't possibly affect vis but are generating unnecessary portals, so let's get optimizing.
When performing all of the following func_detail optimization, note that I am using the Move to Entity button in the right hand bar. If you recall, this takes all selected brushes and puts them into the same entity. In this case we want all of these to be combined, so I'll use this tool to combine them.
Likewise, during the following steps, you can go back to your Auto visgroups and repeatedly uncheck World Details to hide the func_details as you make them.
First off, these two little glass shacks near the pit spawn doors can be entirely func_detail. There's nowhere near here where these could possibly be blocking something. If this was near the top of the map, the ceiling of this might actually be the ceiling of an area, or if this were more expansive, but for this case this won't block vis (since transparent textures allow vis through). The entire thing can be a func_detail.

This bridge we put over the death pit also can be a detail, it's too small to do anything.

The center point can be a func_detail, because no matter where you stand on it or near it, any of your visleaves are going to be able to see the rest of the map, so generating these leaves is pointless.

The roof of this shack doesn't need to be a world brush, as the triangle underneath it (solid, one brush) will do all the vis blocking, this is just going to generate a bunch of strange leaves at weird sizes.

These balconies don't need to be world brushes either

Each flight of stairs can be a single func_detail. The ramps under them can remain world brushes

Your map should now look like this if you only have world brushes selected.

We can do a quick pass with areaportals now as well. If you created an areaportal and left it in from the optimization tutorial previously, then you can go into the Tool Brushes visgroup and click Area Portal to re-enable them.
Each of the spawn doors can use an areaportal on it

The exit doorways to the towers can also use areaportals

And while this is a little overkill, our shack can also use areaportals.

Lastly we can do some hint optimization. Remember to only use a hint on one side of your skip brush, or else you'll be inadvertently creating more cuts than you need to.
If you followed the guide to the exact dimensions, the top floor of your tower should correspond with the barrier in front of the main spawn door, allowing an easy horizontal hint across the map.

While this will most likely already be done by the compiler, it doesn't hurt to add a hint here

We can take advantage of our map's small size to create a big hint like so, dividing the map into 6ths based on the edges of the towers

We can place a hint here so that the visleaves on the opposite sides of our shack can't see each other. The hint doesn't need to extend above the shack as those visleaves will always see each other, over the top of it.

Lastly, we can add the hint here which prevents the center of the map from being rendered while behind the barrier or within spawn.

If you're wondering why we didn't put any hints on the spawn doors, that's because in addition to them being holes and already getting visleaf cuts, Areaportals automatically generate visleaves along their sides.
You can click your Auto visgroup twice now to bring everything back, compile, and your map should now run ever so slightly better than it used to. This level of optimization is essentially unnecessary on a map this small, but it's good to get into the habit of it for the future.
Soundscapes
The next part of any good map is to set up the soundscapes. These are the ambient noises you'll hear in any level, even if you're grown used to them. Every properly made map should have a soundscape no matter how minor it is.
env_soundscape
First we'll use some premade soundscapes then explore making our own.
In one of our spawn rooms go ahead and create an env_soundscape with the following parameters. Place it near the doorway, right around the middle.

The way env_soundscape works is by line of sight. Unlike regular vis, this is direct line of sight, so any level geometry (and even props?) will block it. The closest soundscape OR the most recent will always play for a user. If there are soundscapes within line of sight (and radius) then the closest will be played, if none are visible then the most recent will be played.
This line of sight calculation does not mean on screen, rather it just checks if a line can be drawn between the player and the soundscape. Entities do not block this line of sight.
So, by placing a soundscape right in our spawn like so, when a player on this team spawns they'll start hearing this soundscape and will continue hearing it until they encounter a new one.
Soundscapes are typically divided into inside and outside on most maps, so now let's create an outdoor soundscape. Right outside our front door we can create a soundscape like so

If you think about it you'll understand why we placed it this way. As soon as we step outside, no matter what the outside soundscape will always be closer to us than the inside one.
To see soundscapes in action, let's use
Code: Select all
soundscape_debug 1in console, which will show the line of sight calculations happening visually.
Within our spawn room you should see something like this

The green line indicates the current active soundscape. Red means that while loaded, we don't have line of sight on it, so it can't activate.
If we take a step outside the door while leaving the door open, we will then see this

Now the closest soundscape is the outdoor one, which becomes green. The indoor one turns white, which means it's loaded and in our LOS - just not the closest one to us.
Step behind the barrier right outside of the spawn door and you will now see something like this

The orange indicates a soundscape that is currently active, but isn't in our LOS. If we come across any other soundscape that we can actually see, then this one will become inactive. Orange just means the last one we saw before entering a dead zone.
Soundscape Priority
You might think that it would be as simple as just attaching two soundscapes on our door then calling it a day, as orange will load the soundscape for players all around the map. However, keep in mind players can teleport to different areas where there may be no soundscape visible. The easiest example would be a player dying indoors with the indoor soundscape active, then in their death spectate they are immediately shifted outside to an area with no soundscapes. That player would then be hearing the indoor soundscape while outside.
Generally speaking, you can't avoid this happening all the time. Trying to be adamant about your soundscapes staying in certain areas is a task you will spend a lot more time on than is worth the effort. Therefore I would suggest a "best effort" approach to dealing with soundscapes, wherein 99% of cases are covered. For example, in a map with many rooms, you might simply place a single indoor soundscape in the middle of each room, with two soundscapes when there's an area transition from inside to outside. It would still be possible for a spectator camera to come in behind a prop and thus not activate your soundscape, but it's not a big deal.
To give you another example of why it's fruitless to chase perfection, consider the following example. where we have an orange soundscape and a red one.

Currently the orange is being blocked by this wall, and the red is being blocked by the door. Since the orange is the outside one, and the last one we saw, we are currently playing the outdoor soundscape.
But now what if the door was open?

Now you can see the issue, despite being outside the indoor soundscape is now the only one in view, making it the one loaded.
You might be tempted to fix this by placing multiple outdoor soundscapes around the walls so that there's no angle where the inside one only can be seen. I can assure you this is a pointless, time wasting endeavor. Instead, let's just place a few good soundscapes in common places and worry about edge cases less.
We can place one outside soundscape on the point, and one above the roof like so

Now, pretty much no matter where you are outside, just with these two soundscapes we now prevent issues of orange door soundscapes. You can try compiling and running around to see what areas are orange.
In my opinion, this is more than enough for our case. If you are especially picky about your soundscape transitions, I would recommend one above the ammo shack, and one above the death pit as those will give more coverage.
Entity Report
We have more than just our first spawn door. Let's copy the dual layout on our first spawn door to the remaining 5 spawn doors on the map (make sure you rotate them when pasting to the other side so that the inside/outside aren't swapped!)

Now we have a soundscape at each of our spawn doors, as well as in the center of our map, meaning in the majority of our area we'll have good soundscape transitions.
But, let's say we don't like our current soundscape. Instead of Powerhouse.Outside, I'd like to use 2fort.OutdoorFort. But I don't want to have to select every single soundscape again and edit them, that's a pain. Instead, let's use the Entity Report to get all of them at once.
Select Map > Entity Report. Check By class and input env_soundscape

You'll see we now have a list of every single object on our map that is of the type env_soundscape. You can use shift to select the entire list and you'll see the entities get selected in your view. You can even double click an individual entity to get your 3d camera transported to it. You will still need to double click one of the entities in the 3D view to open the entity menu, however.

You'll notice that when we have everything selected, Hammer will show us b[/b] on entities which have different values for a specific key. But, you can also see that Radius is set to -1, as Hammer will show a value if all entities selected share that same value for a key. If we're going to edit only our outside soundscapes, we'll need to only select them.
While you can just manually select the specifically named ones in the entity report, there's an even better and more surefire way to go about it.
Check By key/value as well, then input soundscape = Powerhouse.Outside.

You'll see that now in the report, only the soundscapes with that value set are shown. If your entities aren't named very well, this is a good way to make sure you are selecting the right things, because if you accidentally had a soundscape_outside named soundscape_inside, you would have missed it on the first pass.
Now by selecting all of our outdoor soundscapes we can update the value to 2fort.OutdoorFort.
Soundscape Proxy
Entity Report is a very powerful tool and you will use it for a lot more than this in the future. But in our case, having to open up the entity report every time we want to edit our soundscapes still isn't easy enough. Instead, let's utilize a different entity to solve this problem, env_soundscape_proxy.
The proxy version of a soundscape ent works very similarly to the base version, the key difference is that the actual "sound" data it carries is instead copied from another entity. What this means is that we can replace most of our soundscape entities with proxies, and whenever we want to change something, we only have to change one ent, and all the others will automatically follow it.
At one of our spawn doors, let's convert the outdoor soundscape to a proxy. Update it to use the following params, make sure to change the name as well!

Now, this soundscape will behave just like any other soundscape, except the sound it decides to play will just be whatever our original one is.
But we don't want to do this manually for all our soundscapes. Instead, let's mass edit them with the entity report.
Select all your outdoor soundscapes again, and deselect one of them. We need a single one to be a regular soundscape, as that one will be where we set the actual sound config on.

Now we can open the entity menu, and by editing these values we are editing all of the selected entities at once. We can convert them to what we need in one pass.

You can also do the same for the inside soundscapes

From what I've observed in how Valve does things, they generally use one door for both their main soundscapes, and all others are proxies. In this case I used the blue door but where you put them doesn't matter as long as you keep track and remember where you did.
Custom Soundscapes
Let's say we don't want to use any of the built in soundscapes and we want to make our own. Soundscapes are pretty easy to make and are only limited by your imagination.
While an excellent tutorial exists on the VDC, the basic idea is that for a given map, you will have a file inside your tf/scripts/ directory named soundscapes_<mapname>.txt. Using the exact map name is important, as the file will not be loaded otherwise. This includes full path versions, for instance soundscapes_my_map_v4_final.txt would be a valid path for a map named my_map_v4_final.
In our case, we're going to want to make a file named soundscapes_koth_first_map.txt. You can do this in your tf/scripts/ directory but I would recommend putting it into the tf/custom/first_map/scripts/ directory, which functions as a segregated version of the scripts directory. You will need to create this if you don't already have it. Then create the map with the aforementioned file name in that directory.
Open the file in a text editor and set up the skeleton of the file like so

In a soundscape file, we can define different soundscapes for our level. Each one of these outer blocks at the base level of our file is a soundscape we can select. So in our case we want an inside and an outside soundscape. I have run into issues using uppercase names for soundscapes so I would recommend lowercase, but if it works for you then you can do whatever case you want.
Sound Browser
We want to find some sounds that we can use for our soundscape, so let's open the sound browser. Go to Tools > Sound Browser.
Turn the volume down! By default, these sounds are loud and you will want to reduce the volume to around 5% with the Sound Volume slider. The browser itself is fairly self explanatory, you can search for a term in the Filter section and all sounds with that word in the file name.

I happen to know ambient\indoors.wav is a pretty good indoors sound we can use. But just to make sure, you can go ahead and click each of the sounds in the list to hear what they sound like, they'll play automatically if you have Autoplay on.
Looping Sounds
We have a sound we want to play, let's go back and add it to our inside soundscape by creating a block inside of firstmap.inside

In this case, we're going to use playlooping, which takes the given sound and plays it forever while you have the soundscape active.
volume as you expect is the volume level, it goes from 0 to 1. Typically you want this at a level below 1, though how high you make the volume depends on how loud your source sound is. In this case we want this to be very much in the background so we're setting the volume to 0.5, or 50%.
pitch is something you usually are not going to be touching so we can leave it at 100, the default.
wave refers to the soundwave that will be looped in this block at this volume. In this case we want to set it to the indoor sound we want to use. Note that you should should use backslashes for the paths to sounds regardless of platform.
We can do the same thing for our outside soundscape. Conveniently there's another good generic outside ambience named ambient/outdoors.wav so we can just insert that.

Back in Hammer, let's make sure to set out soundscapes so that they use the new ones we just created

Now compile and load into the map and you can hear our newly created soundscapes. Instead of pure silence we have a little bit of white noise to fit the environment.
It's a bit hard to hear, so let's go back into our soundscape to confirm it's working. Set both values to 1.0 instead of 0.50 for volume.
Now, unlike a map, a soundscape script does not need a recompile! This file is loaded on demand when you load into the map, but it can also be easily reset by simply running
Code: Select all
soundscape_flushYou can run the command to refresh the current soundscapes. You can also mute and unmute tf2 and you should notice the obvious difference between having no sound and slight ambient noise.
Once you're happy with it, you can reduce the values back to 0.5. These are sounds that aren't meant to be constantly heard, only there subconsciously. We can add some more obvious sounds separately by having them trigger randomly.
Random Sounds
While looping sounds are nice, we want some variety in our soundscape. Let's add some random sounds, for instance a train horn in the distance.
I happen to know there are a few good train sounds we can use, namely ambient/mvm_warehouse/train_01-03.wav. There are three of these sounds each making a slightly different noise. We can play each of them at random.

You will see we have two differences in this block.
playrandom is another block type and is the counterpart to playlooping. In a playrandom block, instead of looping a sound, the sounds will be played a single time at random intervals.
time is the parameter to control the randomness. It takes two numbers, a minimum and a maximum, and chooses a random number in the range and plays the sound after that delay. In this case, our train sound will play somewhere between every 45 and 60 seconds. Each time it plays, a new number is picked, so it could be up to 60 seconds between the next train sound.
rndwave is the list of sounds we want to play. We can have as many sounds as we want, and we can even have only a single sound if there's only one thing you want to play randomly. If you put multiple sounds here, one will be chosen at random.
Depending on what sound you're using, you may want to choose different numbers. In a map with a jungle, you may want wildlife to come in every few seconds so you can set the time to something smaller like 5 or 10 seconds. In this case, we don't want there to be constant trains in the background, just occasional, so we'll use a longer delay. This way we should only get a handful of train sounds each round.
You can run soundscape_flush and check out your new sound. It will take at most one minute after walking outside to hear a train sound, though you can reduce the time to something like "2,5" if you want to be certain it's working.
We can also update our inside soundscape to have a quieter version of this, if you like to have that audible within the spawn rooms.

You should know how to make any soundscape you like. You can create a complicated or a simple one by combing through the sound browser for any sounds you like, playing them at different times and volumes. Generally you want at most one or two "persistent" sounds in the background, like ambience, and only a handful of random sounds.
Keep in mind that soundscapes are not meant to distract the player, in most cases players should not even notice them. They are there to subconsciously boost your maps aesthetics by helping it fit whatever theme you're going for. Usually players are too focused on playing the game to be paying attention to soundscapes, but that doesn't mean we don't need one! That ambience will make downtime such as during setup or when walking to objectives that much more enjoyable.
There are two things to note before we move on from soundscapes, firstly mp3 files cannot be looped. wav files are required for looping so when selecting sounds for your playlooping blocks, only select wav files. You can use mp3 files freely within playrandom blocks however since they don't loop. Also keep in mind wav files are roughly 10x as large as mp3 files, so in the future if you make custom sounds don't go too hard on your soundscape ambience!
Secondly, two sounds cannot play at the same time. For instance, you can't have two playlooping blocks with the same sound at different volumes, one will override the other. This is an engine limitation. If you have a playrandom block with long sounds, make sure your minimum random time is at least as large as the longest sound, otherwise if a sound is picked twice it will not play the second time and you'll need to wait twice as long to get another sound.
My final piece of advice is to examine valve soundscapes for tips. There is a later section dedicated to extracting materials from maps that we will get to eventually if you want to do this.
Misc Entities
These don't really fit into other sections very well, but are still important to know.
Spectator Cameras
Spectator points are achieved using the info_observer_point entity. We want a few cameras scattered around the map so spectators can get a lay of the land. Additionally, you might have noticed that when you were loading into the map, your camera was in the void - this was also due to not having spectator cameras.
You don't want too many cameras, it should never take more than a few seconds to scroll to any particular point, a map does not need 10-20 cameras when 3-4 will do. Every single angle does not need to be covered, just the main parts.
In the case of our map, a single camera is probably sufficient but we'll use a few cameras for demonstration purposes.
Go ahead and create an info_observer_point, I am putting mine above the death put as it's a good central place to get a view of the whole map. You want to make sure your camera is a little bit away from walls, so that the camera isn't inside of a wall by mistake. You don't have to place it very far away, keep it close, but just don't touch the wall with your camera.

Note that I am selecting the Welcome Point setting on this camera. As you can probably guess, that makes it so that the camera will be used when you first load into the map. A good, central view of the map is a good place for a welcome point as it will immediately let players know what map they're on.
We want to rotate our camera down as well, as most spectator cameras face slightly downwards. If you move yourself in your 3D view right in the center of your camera, you can imagine this is what spectators will be seeing. If you look straight forward, half of the screen is just the sky which isn't a very good way to view our level.
In one of your 2D views, you can rotate the camera down a bit, such as by about 15 degrees

Let's also create some team cameras. These cameras will only be visible by one team, which helps dead players stay focused only on their teams areas.

In this case I am putting a red team camera over by the blue tower, as from this position we can look over the area that red will be in.
My camera is facing towards the area I want to be looking at, but you'll notice if you try to rotate it downwards...

...it causes the camera to become misaligned and have strange angle values!
Rather than try and use the 2D views to rotate our camera, instead we can simply change the pitch by editing the angles directly. Undo your transformation so the camera is back to an angle that is something like 0 225 0.
Let's edit the angle field on the entity directly and change it to 15 225 0.

Now you can see the camera is nice and angle aligned. You can still rotate the direction it's facing in your top down 2D grid without causing issues.
You can create a camera for the other team as well on the opposite side near the other tower to finish it out. If you compile and check in game you should notice when you spawn in that you're viewing the map, and when you die you can swap between the two cameras (one neutral and one for your team).
You might have also noticed the Match Summary setting on observer points. When the match is over you can set a specific camera to be viewed during the short end-of-round phase. I did not set one but you could make the neutral camera above the death pit also the match summary point. If you don't select it (or Welcome for that matter) the game will choose one for you.
Cubemaps
This is only a brief introduction to cubemaps. If you want to read more, I encourage you to check out the VDC Page on cubemaps which goes much more in depth.
Essentially, Cubemaps are pregenerated images used for reflections. Wherever there is a reflective surface in the game such as glass or particularly shiny metal/tile, a reflection should be shown to keep the texture looking correct. But generating real time reflections is expensive, and the majority of the time players aren't going to pay much attention to them anyways, rather serving as a background reinforcement that keeps the level looking nice while only being noticed subconsciously. So we don't need to show things like moving entities in our reflections either.
But how does the game know what pictures to use for the reflections? This is where env_cubemap comes into play. If your map has any number of this entity, you can build cubemaps after compiling your map and your game will take a series of screenshots in low resolution that will be used for reflections. These are stored in the map file itself.
A texture will use the closest env_cubemap to it for reflections. If you have none on your map, they will use no reflection or in a worst case may use a missing texture. Thus, if you have any amount of reflective textures (or even if you don't) it makes sense to place some cubemaps in your level. The number you place is partially up to your aesthetic taste, but generally every room should have a cubemap, and larger areas may have multiple depending on how they are divided.
To demonstrate this, let's place a cubemap in our map. Since our only reflective texture is the glass near the death pit, I am going to place a cubemap right above the pit.

You don't need to spam these everywhere, placing them more generally is more than enough. For example, we don't need to place one right next to each window, on the side of each window. This placement will give us a decent enough reflection that players will note it exists but not scrutinize it much beyond that since it will look mostly correct.
After you have recompiled your map, it's time to build cubemaps. Building cubemaps is a separate step from compiling and you do it in game (or with compilepal, though I recommend it manually, also it can't be automated on Linux) by running a series of commands.
From the main menu, run commands in this order:
Code: Select all
sv_cheats 1
mat_specular 0
map koth_first_map
buildcubemaps
disconnect
mat_specular 1
This will disable reflections (mat_specular) which is required for taking the screenshots, load into your map, perform the cubemap building, leave the game, and then enable reflections again. If you see an error about the skybox not having default cubemaps, ignore it - all tf2 skyboxes have this issue. It's a remnant from other source games.
After you do this, you can load back into your map. If you walk up to the glass you should see some reflections.

As you can see these aren't the most high quality reflections. They aren't meant to be. In fact, if you look at most valve maps, there are rarely any large reflective surfaces players can walk up to like this, most glass windows are small or in hard to reach areas. So our map having this big glass wall is unusual. Additionally most windows have rooms on the other side, so on another map you'd likely have a cubemap inside for the inside half of the glass.
The images taken from the cubemap are essentially a 360 around the entity itself, then applied with parallax to your reflective texture.
While you think you might be done here, we aren't - we need more cubemaps to make our map look correct. For example, look at this

The scope of the Machina actually loads cubemaps, you can use this to see what area of the map is using what cubemap. In this case since we only have one cubemap in our map, no matter where we stand (even inside spawn) we will get a reflection of the cubemap by the death pit.
This isn't the only problem with only having one cubemap, if you head over to our control point you'll see it uses cubemaps too.

In this case, it's reflecting the sky when it's under a roof. This looks more noticeably bad.
The last problem with only one cubemap is for futureproofing. Say we wanted to add a window somewhere, such as on the bottom floor of the tower. We'd have to remember to place a cubemap there after the fact, rather than already having a cubemap where we need it.
The way we can fix this is to have more cubemaps in our level. First up, our control point. From what I can tell, almost all control points in valve maps have a cubemap directly on top of them. This is usually a result of control points being in a central location, but also control points being reflective and large, making a nearby cubemap important.

Good cubemap placement usually involves having one in every decently sized room. For example, we can place cubemaps in each of our spawn rooms

As well as inside each floor of our tower

Lastly, on top of the shack roof is a good spot for a cubemap

Conversely, there's a few locations which a cubemap would be mostly pointless. The inside of the shack is too small and wouldn't have use for a cubemap unless you add windows to it. The side exit hallways of spawn also don't need a cubemap of their own, as there probably won't be any reflective surfaces here and the ones in the tower/spawn room cover them well enough.
One last thing to note regarding cubemaps is you can manually set what faces use them. In this case, our glass outside is pretty close to both the spawn room cubemap, and the death pit cubemap. To make sure they use an outside one, we can force it to use
In your cubemap's keyvalues you can click the Brush Faces value and then select Pick to select any faces you want to use the cubemap, regardless of distance.

You can ctrl + click to select multiple at once. In this case I decided to set up both sides of each of my glass brushes to use the outside cubemap.

You can run the cubemap compilation steps again and we can see how our cubemaps look
As you can see, the control point now has correct reflections, as does the machina's scope.

All in all how many cubemaps you place and how strict you are about them is up to you. A good example of cubemaps would be cp_powerhouse, later on in this tutorial when we go over map decompiling and viewing, you can see how the cubemaps are placed there. As a bad example, check out some of the cubemaps in pl_badwater and notice how some of them seemingly for no reason manually apply themselves to reflective surfaces across the entire map. But also note that players think both of these maps look fine and this ends up being not too big of a problem, so don't overthink it too much. Just place them in the middle of decently sized areas to get good reflections, as well as on top of bodies of water as water is reflective.
Our control point still doesn't look very good. This is due to having poor lighting around the point, which we'll fix next.
Proper lighting
While we went over the very basics of doing lighting, this is actually not how lighting is often done in a proper map. The difference is mainly in what light entities are used, and where they are used.
The entity we have used up until now, simply called light, is not really meant to be used for main lighting, at least not for lamps. The entity we want to use instead is light_spot, which is a cone of light we can use to more efficiently light up a room with less lamps.
Let's start off by redoing our spawn rooms. First off let's replace our lights with a new prop, models/props_lights/light_fluorescent_basement.mdl. You can keep the current model if you prefer it, but if you do I'd recommend raising the ceiling height - if you examine many common maps you won't find many rooms this small in height.

I placed two near the center of the room, go ahead and do the same as our example.
Now under each prop let's place a light_spot. Note that the pitch parameter is filled automatically as you rotate it, some entities have their own pitch parameter for whatever reason. You don't need to add it yourself, simply rotate the lights so that they face the ground.

Angles of 30 and 60 are pretty good starting points, I have found most maps have an inner angle of 25-35 and an outer of 50-60.
While we could just compile and load in, this is a rather slow way of doing it. Instead, let's use a feature that Hammer++ provides, lighting preview.
You can change your 3d view to the lighting preview mode in the same way you first selected the 3d camera mode. RIght click it to open the bake window

After clicking full bake you should see something like this

This isn't a particularly high quality preview, but it serves the purpose of just being a preview. The goal is to get a rough idea of what is or isn't lit in our level. In this case, we can see that the edges of our room are pretty dark.
While you don't have to, here's an example of what this looks like in game. The lights and darks are a little more mild in game as you can see but the darkness is still dark.

With four we can get better coverage

but our walls are still dark, even in game

This is where our light entity comes in. This entity is best used to fill in gaps, or to give rooms an ambient level of light that our spotlights don't already provide.

I put two low brightness lights above the height of players and in equidistant locations in the room. This lights up our room a lot nicer, making the walls and ceilings not as dark.

One last adjustment we can do to get a brighter room is to increase the height of our room, as well as the brightness of our lights.

I increased the brightness of the regular light entities to 200, left the light_spot entities untouched, and increased the ceiling by 128.
Obviously, not all rooms are going to be this tall. There will be cases in your maps where you'll have rooms that need smaller ceilings, in those cases you will want to go with a less bright light underneath your light_spot entities.
A good rule of thumb is that wherever you have one or more light_spot, you should have at least one light to help fill out the room. It doesn't need to be very bright, just enough to hit the dark bits the light_spot doesn't hit.
The following areas were done with a light spot using an inner angle of 30, with an outer angle of 60, and brightness 800. Each one has a light below the light_spot with a brightness of 150.



Last but not least, our control point. I chose to place a light entity with a brightness of 200 right at the bottom of the hologram, so it looks like the hologram is lighting up the point.

The reason the point itself looked poor previously was due to the area being poorly lit. You always want your objectives to be well lit, especially areas like control points where being able to see players is crucial. A dark area might make it hard to see the team of an enemy, which isn't fun to play.
My closing remark regarding lighting is that it's largely subjective, so long as you can see the area you're playing in. It should never be ambiguous what team a player is on at a medium distance, any playable space should be well lit, including nooks and crannies. You will be able to get more inspiration for how lighting is done by examining Valve maps, as mentioned later in this tutorial.
You can turn off lighting preview in hammer if you aren't using it, you can always come back to your lighting later. I generally stay on 3D Shaded Textured Polygons unless there's a reason not to be on it.
Displacements
This may not be as long as other sections but I figure it deserves its own section.
Displacements are a special type of brush which can be molded into bumpy shapes, and can blend textures together on them. Areas on maps with grass, rocks, dirt, gravel, are all probably displacements. Depending on the type of map you're creating, this may be one of the most used brushes you will make, especially for floors.
Displacements are not world brushes, meaning they do not seal leaks nor do they block vis calculations. They are something you put on top of an already roughly made set of geometry for your map.
Before we try making anything, it's helpful to see them in action so we can understand how to use them.
Examples of displacement usage
First off let's take a look at dustbowl, in particular the trench at the first point. We can see all of this bumpy terrain is displacements, this includes the floor as well as the walls.

By using the Disp Draw 3D button on the top bar, we can toggle the 3d visualization of displacements in the editor and see what the brushes looked like prior to being molded

It's good practice to get your displacements relatively close to what you want your end product to be prior to doing any molding, as it will make your life a lot easier.
Next to understand shaping let's look at what the world brushes underneath look like. You can untick the Displacements visgroup in your visgroup menu on the right bar to see

This is a good example of world brushes underneath. Remember, since displacements don't block vis, we want our brushes to be very similar to what our end result is going to be, so that the world brushes underneath perform a good approximation of vis blocking. A good example of this can be found at the wall with the tunnels separating the first and second point of this stage

Because displacements don't block vis, if these brushes were not present then standing in the first area would cause us to render the entire area of the second point despite being over a hill.
It's also common to add a skybox brush that divides areas like this, as a skybox brush also blocks vis. For example, rocket jumping in either of the two points does not render the other area.

You can see that the areas are not rendered in game yourself by going up above the wall

The main point here is that displacements are not the fundamental building blocks of your map, world brushes are, displacements are essentially a detail element you add afterwards to make areas look more natural.
Working with displacements
If you want more examples you can look at other valve maps, but we'll make something simple for our map.
The first important thing to note regarding creating displacements is that they must be four sided. Displacements do not work on any brush face that is more or less than four sides, it's an exact number. It does not mean you need to make squares and rectangles only, if we look back at dustbowl we can see all sorts of shapes that are four sided for the floors.

With that being said let's start work on adding some displacements to our level.
To start off with, let's try the occams razor approach and see how it goes - we can take the floor of our level, duplicate it, and make it thinner. Eventually this is going to be flush with the floor but we can move it down later.

Next we'll use the Toggle Texture Application tool. It applies specific textures to brushes and it's also in charge of displacements, in a second tab.
With the full brushes go to the displacement tab, click Create, make sure the power is set to 3, and click Ok

We can see now that our brushes have been turned into displacements

But there's a problem. Displacements are only meant to be used on faces that we see, but if we look closely at our brushes, we can see the sides and bottom faces were converted as well.

Generally speaking, for any displacement you make, there will be a base brush you are converting and only one of the faces is going to have a displacement on it. This makes working with displacements a lot easier in general.
Given that, let's fix this. We can either ctrl + z, or click the "Destroy" button in the displacement menu and it will destroy any selected displacements.
Now if we only select the top faces of our brushes (by clicking them in the 3D view) and then create our displacements, you can see only the top face ended up getting converted. This is what we're looking for

With our displacements still off the ground where we can select them, let's give them a new texture.
Alpha painting with blend textures
Displacements, and displacements alone, allow us to use a special category of texture called a blend texture. Blend textures allow you to blend two textures together across a face without having to make many individual brushes. For example, if you have a grass texture with a dirt pathway, you can "paint" the dirt pathway onto the texture.
While you can use the blend texture you want, in this case I'll be using nature/blendgroundtogravel001 and I encourage you to as well if you want to follow along exactly.
Set the top texture of all your displacements to this texture. Go to one of the spawn doors and select only that displacement then click Paint Alpha. Now if you left or right click you can add gravel to places on the texture.

The current paint mode we have is Raise/Lower which means that, for the given value chosen by the slider (or typed manually), the alpha value (or how much it blends from one texture to the other) will be updated by that amount every frame you hold the button.
If you set the value to 255 and click around you'll see that by left and right clicking you instantly reset it. By lowering it to a value like 10 it's more gradual. You want to choose the value based on your needs.
Both Raise To and Smooth do not work very well for displacements. I'd recommend just using Raise/Lower for whatever you need.
Let's say instead of having the base be dirt, we want the base to be gravel. Some blend textures have specific variants for this, but there's an easier way to do it without swapping textures. By pressing Invert Alpha all values will be reversed. You can see on our current brush that all the gravel parts become dirt and vice versa.

If we want the gravel to form a path you can select another displacement as well and try to paint between them

But try as you might, this isn't going to work very well. This is because our displacements are currently not sized properly. We'll fix this in the paint geometry section next. For the time being I'm going to reset all of my painting to zero by making everything dirt again.
Paint Geometry
This is the main purpose behind using displacements, being able to mold 3D geometry.
For the first and only time in this tutorial I am going to defer to a video as a visual explanation for this is. This video is very well done and is a very competent explanation of how painting works.
The displacement tools only half work on linux and painting is especially affected so I cannot make a proper tutorial for this at the moment.
The main takeaways from this video should be how the various geometry painting tools work, and brush sizing and sewing. Also note that displacement brush selection is broken on linux and I will update this guide when I eventually figure out a solution to fixing it.
Given this knowledge, we can divide our displacements up like so

You can see that I made the decision not to have a displacement inside the towers. The control point itself generally should never be on top of a displacement, so the entire center area under the roof is left untouched. Otherwise, I've used the clipping tool (with nodraw/dev as my selected texture beforehand!) to slice up the various displacement brushes so that for any two given displacements, the edges match perfectly. This means that both alpha painting and geometry painting will be able to sew seamlessly.
Creating our floor
Assuming you have looked at the video and understand sewing properly, and have divided up the displacements into the above configuration, we can now move this down to the level of our floor. I don't think the main map needs many bumps, you can add those later for aesthetics if you choose but let's leave it flat for now.
Go ahead and move the displacements so they're flush with the floor. You will see them z-fighting if you left them all flat.

There are a few ways we can fix this. Let's look at dustbowl for one example

You can see here the decision the mapper made for fixing z-fighting, which was to make the floor itself lower. The supporting brush, the nodraw underneath, is not even touching the displacement. This has a few advantages, mainly being that it allows you to both raise and lower from this point. If our brush is level with the displacement, it can't go any lower.
Let's look at another example with badwater

Here we can see the decision the mapper made was to actually allow z-fighting between the nodraw texture and the displacement. The reason this works while our map doesn't is because while a dev texture is rendered, a nodraw texture is not. This means that despite the displacement sharing the same plane as our nodraw brush we can't see it in game.
We can see the effects this have by checking in game. In cases where the displacement is flush with or even behind the nodraw brush, bullet decals do not show up
First we can see what it looks like in hammer

then in game we can verify the boundaries


Personally, I don't like lowering the floor as it introduces issue with small brushes and visleaves. So for this tutorial I will be doing the flush option.
While we'll be doing this globally later during the texturing section of our tutorial, for now let's just update the floor brushes. You can untick the displacement visgroup for easy access to the floor, then set it all to nodraw. Also make sure the brush inside your towers is a separate one with a dev texture.

And now by turning displacements back on and clicking our nodraw toggle we can see the displacements cover our floor.

Remember, this means bullet decals won't show up on the ground, but I'm going to consider that a loss worth having due to the ease of use here. In a future map if you want to avoid this I'd recommend putting your geometry a bit lower than where you want it in the final product, or even making displacements first and placing nodraws under them after, though both of those are tougher and will not be covered here.
Creating a pit
Let's do some more advanced displacement work now that we have this by adding a more natural fall into the death pit.
To start off, I'm going to create a series of brushes like so, which match up with the surrounding displacement edges

Convert the visible faces to displacement and you can get to painting.

The important things to note is that none of your pit should be walkable, players need to fall down all of it for the pit to work. If there's any triangles on your pit displacements that aren't yellow, then a player can stand there indefinitely.
You will also want to give enough space between the bridge and the wall for players to fall down. Players themselves are a 49x49x83 tall box from a collision perspective, so make a box a bit bigger than that and make sure you can move it down the side.

I am not the best at working with displacements but I found using a cut cube to be very nice for selecting angles with face normal.

With that done I've gone ahead and added some bumps and small alpha changes in various places on the map.

I, personally, am not a very aesthetically gifted person. These displacements I've had you set up are fairly basic and in all honesty are not that great. But your decision to make more interesting choices with displacements is up to your own creativity. Next we'll go over the final part of making our map which is the texturing.
Texturing
Last but not least we are going to be texturing our map. Up until this point we've used dev textures, and it's for a particular reason that has to do with my personal preference for texturing.
Replace Texture and face limits
TF2 has an inherent limit on the number of faces you can have in a map. If you go over it, your map will crash when you try to compile it.
To resolve this, the general rule of thumb is to make any texture you can't see nodraw, as nodraw does not contribute to this face limit. However, we don't want to go around and manually make everything nodraw, especially since we can't be certain which faces we'll miss.
Instead, we can mass replace them. Select the dev tool using your Toggle Texture Application tool then using the Replace tool, update all of our dev textures to become nodraw.

After the replace it will look something like this

Now what we have to do is go through the whole map and replace textures individually with what we want. This is a tedious process but doing it this way helps keep our faces low regardless of the number of brushes our map has.
From this point forward, any brush you create should have nodraw as the base texture, then texture the remaining sides afterwards.
Setting textures
I would recommend toggling off displacements for this next step
To start with let's make the shared wall above the pit the texture concrete/concretewall014. This is a neutral texture used in cp_powerhouse that I think will be a good divider.

To make it most obvious which side is which, we can theme each base differently. Similar to 2fort, I'll use wood for the texture of the red base, wood/grain_elevator_facade_14c

You can see a problem however, which is that our texture repeats. This particular texture gets darker near the bottom but we don't want that. To fix this we're going to cut the top brush with the clipping tool, right where the texture starts to repeat like so

And now, for the upper texture, we can use a different variant which is wood/grain_elevator_facade_14b (variant b instead of c) which does not have a fade at the bottom

You can repeat this process for the blue side with concretewall/concretewall002 and concretewall/concretewall002b

For the bridge over our death pit I'm going to use wood/wood_bridge002

Texture Alignment
But we have a slight issue. As you can see on the edge of our wooden plank, the top texture doesn't carry down, so it doesn't look like proper planks would on the side.
To fix this, we can use a special trick with alt+right click. When you have a face selected with the texture tool, alt+right click will cause an adjacent face to line up with it perfectly, even at an angle. So if we select the top face, then alt+right click the sides...

...you can see it now aligns properly, being a continuation of that texture.
For the red tower, we'll use wood/wood_wall005b and wood/wood_wall005c.

You can see that due to how we set up our brushes, each of these brushes has a small face where you'll need an additional texture. There are two ways around this
The first way is the obvious one, just texture it the same. The benefit here is speed, the downside is introducing a few extra faces for our map.
On the other hand, we can modify our geometry slightly to avoid that. By making the corners of this building diagonal using the vertex or clipping tool, the exposed face is now hidden and we have a fully textured corner without needing an additional face.

This requires a lot of work if you do it across the entire map. As such, I would only recommend doing this if you actually run into texture limits, and otherwise simply adding a few extra faces on corners doesn't hurt your total much.
We can do the same thing for the blue tower, using metal/wall016 and metal/wall016b

For the inside of our tower's top floor I'm going to use wood/wood_floor001 for the floor and wood/wood_beam02 for the ceiling.

For our catwalks outside the tower, I think we should have our wood beams aligned lengthwise. If you were to just set the textures normally, one of the two won't be aligned.

To align it, we can use the rotation parameter in the texture tool. Either 90 or -90 (270) would work here as we just care about it being in the right direction.

Doorways look better with a separate lip on them. Let's add one also using wood/wood_bridge002 rotated 90 degrees

When you add a lip like this, you also should add some clips to make it smoother around them. Players getting stuck on lips of doors makes for a poor movement experience. You can see here that I have some clips on the sides of the door which make the diagonal from the door to the wall smooth, so a player won't collide with the lip and won't even notice the clip itself.

Don't forget to set your door frame to func_detail!

You can add these doorways on your other exits as well

Our roof is less important, but since players can still view up here if they are launched when they die, we should have a roof regardless. I used the same texture as the ceiling

You can repeat the same process for blue side with the following textures
concrete/concretefloor003 - Floor
wood/wood_bridge001 - Balcony, door frames, ceiling
metal/wall021 - Roof

For the inside of the tower I went with nature/dirtground003 similar to 2fort. I also clipped the floor in the entryway and used wood/wood_bridge001 so there's a divider between the inside dirt and outside dirt.

Stairs, particularly on red team, are a bit harder. If we want to make a traditional set of stairs, the commonly used texture is wood/wall020.
We are going to have to do this one stair at a time. Starting with the bottom one, set the texture then click T and R to top right align it. Use the scroll wheel on the Y texture offset to get it to the point where half of the texture is black and half is wood. I found scrolling up to 78 works.

If you try to just apply this to the next stair up, it won't work. We need to set the offset for that one as well. But doing this process over and over each time is tedious. Instead let's do some quick math to figure out how to scale it.
A texture with a scale of 1.0 means each pixel takes up one hammer unit. So a 0.25 scale means each hammer unit is four pixels.
Our stairs are each 8 hammer units tall. If we want to then get a texture that is 8 hammer units above, we need to offset it by 8 hammer units x the number of pixels per unit 4, which is 8x4=32.
If you set the texture to the next stair, then manually input 110 (78 + 32) into the Y offset, you'll see it is lined up correctly.

You can repeat this for the rest of the stairs. The offset of the top stair should be 816 if you did this correctly.
Note: If you deselect your texture tool after a few steps, you'll notice the values get clamped to a smaller value and there's an easy pattern you can use. This is easier than having to continually add every time, you can do this instead!
The top of each stair can be wood/wood_beam03.

Valve also likes to add these little corners to stairs, which helps to make the transition from the stair to whatever side texture you use easier. The corners use the wood_beam03 texture, the sides use wood/wood_wall003 and the top parts connecting the stairs use wood/wood_bridge001

You can once again repeat the process on blue side.
metal/imetal005 - Tops of stairs
metal/metalstair001a - Front part of stairs. Increments by 32 also, from 82 at lowest stair to 818 at highest stair.
metal/metalcrome002 - Corners of stairs
metal/wall014d - Sides of stairs
nature/ground_04 - Ceiling of lower level
metal/wall021 - 90 degree rotated, used for side of the floor that is exposed on the staircase (optional, use whatever you want)

Don't forget to func detail the corners on both blue and red side!

We now have both of our towers textured

Spawn Rooms
These are the textures I used for the red spawn room:
tile/floor_tile_007a - Floor
concrete/concretewall021d - Walls (with the non -d variant for the top parts, if your room is that high)
concrete/concretewall001b - Ceiling

For blue team:
tile/floor_tile_007a - Floor
concrete/concretewall021z - Walls (with the -a variant for the top parts)
concrete/computerwall007 - Ceiling

Outside objects
The walls outside of each spawn door use brick/brickwall006c (non -c for top) for red and brick/brickwall002b (non -b for top) for blue

The control point structure uses:
metal/wall026 - Roof
wood/wood_bridge002 - Roof support
wood/wood_bridge001 - Support beams
concrete/concretefloor003 - Floor of the point itself

The shack between the two towers uses:
metal/wall026 - Roof
metal/wall011b - Walls
nature/ground_04 - Ceiling

The small roofs over the spawns are just textured like the towers.
Red:
metal/wall026 - Roof
wood/wood_wall005b - Roof support
wood/wood_beam02 - Ceiling
wood/grain_elevator_facade_14c - Support beams

Blue:
metal/wall021 - Roof
metal/wall016 - Roof support
wood/wood_bridge001 - Ceiling, Support beams

And although I'll be redoing it soon, our out of bounds area is textured using concrete/concretefloor003 and concrete/concretewall014

I have left the spawn doors untextured for the finishing touches section intentionally as it's less texture based and more entity based.
Overlays
The last thing regarding texturing that we can learn is how to use overlays. Overlays allow us to plaster textures in places without needing to make a separate brush.
Most overlays can be found in the texture browser either under overlays/ (this is usually used for environment stuff like dirt or stains), or signs/ (this is used for vanity signs that go on walls)
Sign overlays often help direct players to where they need to go. While it's largely redundant on a map as small as this, let's just go through the motions.
Using the overlay signs/arrow_red_rt_lt in the texture browser, click on our brick wall with the overlay tool to create an overlay.

It's a big large to start off with so we can resize it. I made mine 48x96 large.
Let's try to add an overlay somewhere else, this time a paint stain. Select overlays/patch003 and apply it on the side of our tower right above where the walkways are.

After we add it you might notice a problem, the overlay is cut off when the brush ends. When you apply an overlay, by default it only applies to the specific brush face you clicked on. If there's multiple brushes, the overlay won't stretch between them.
To fix this, open the entity menu on the overlay by double clicking the box which represents the entity. In the brush faces value you can pick then ctrl+click the relevant faces.

Now we can see our stain has been applied to all 3 brush faces.
We can do the same thing on blue side

Often times things like stains or paint marks are used to make metal, wood, and even dirt look better. It's also used to breathe some life into the surroundings.
For instance, on 2fort, just look at how many overlays we have at the front of red base. Even the tracks on the ground are overlays

You can use overlays to spice up your map and prevent a plain wall from being too plain.
My final takeaway regarding texturing is to make sure you have a consistent theme. Generally your map will be mostly comprised of only a few textures, you don't want every building to be themed differently and use different textures. Each team should have a certain set of textures they use, along with some general textures for neutral areas.
For a brief summary on why you should stick to a consistent theme, consult this video about the original version of Cashworks
Finishing Touches
There's a few more things to go over before we're done.
Spawn Doors
You may have noticed I haven't touched spawn doors yet. That's because our doors can be redone entirely without using a brush, to have the shutter doors you see in most maps.
If we look at a valve map, we see doors are actually made up of a few parts.

func_door (nodraw) - The actual "door" entity is invisible, and is in front of the shutter door. It is thin and has blocking damage to prevent blue players from keeping it open. The func_respawnroom is actually flush with the front of this.
prop_dynamic (shutter door) - The visible door. It is a prop dynamic parented to the func_door. This door is actually non-solid, the invisible func_door is used for collisions. It uses the model door_slide_large_door. Our func_respawnroomvisualizer is flush with the back of it.
prop_static (door frame) - The cylindrical frame at the top of the door is a separate non-solid prop with the model door_slide_large_frame
Armed with this knowledge, let's redo our spawn room.
Firstly, we can take out spawn door and make it a 4 thick nodraw. The only parameter we want to add here is blocking damage, this prevents blue players from standing under the door to keep it open.

Add a non solid prop for the frame, keeping it inside the door frame

Next up our door prop. This is going to be a prop_dynamic, nonsolid, parented to our func_door. Parenting allows this door to follow the movement of our func_door. Make sure to line it up with the door frame prop

Lastly, resize some of our previous brushes. The func_respawnroomvisualizer can be put in one of two places, either right behind the door, or flush with our nodraw door if you want the blocking symbol to show up when a blu player approches the door rather than when it's open. If you bring the visualizer to the front, it also means blue players can't hold open the door by default and are thus less likely to get crushed. On the other hand, the func_respawnroom can be brought flush with the door with no issues. This extends the inherent nobuild properties of it as well, making it so that engineers can't build pixel perfect teleporters to get inside the spawn room.

You can then texture the area around the door similar to the rest of the spawn room

But you can see our spawn door doesn't look very good here as the texture transition is poor.

To fix this let's first add a 4 unit gap around our spawn door like so using the vertex tool

Then we can add a func_detail frame to fill in the gap. The current height already has a gap at the top, so putting a 4 unit frame there works without needing to make space. Remember to make your frame func_detail!

Last but not least, make sure to drag the floor brush in the spawn doorway so that it doesn't leak

With all that out of the way, you can go ahead and copy this methodology for all 6 spawn doors on the map.

Signs
There's a few gameplay-important signs we can include in our map to help players navigate.
Exit signs make it obvious how to leave spawn (along with an overlay arrow pointing out of the main door)


The prop sign_gameplay01 can be used for a lot of different things by changing the skin.
For skins 4 (blue) and 5 (red) we get a resupply sign which lets players know where the spawn rooms are

Skins 2 (blue) and 3 (red) give us a control point sign which tells us where the point is


Ammo/Health Kit Decals
Most maps will use decals to denote where health and ammo kits are, so it's easier to know where they respawn.
overlays/patch007 (size 32x32) is used for health kits, while overlays/patch001 (size 32x32) is used for ammo kits.



A proper 3D skybox
Lastly we can update our 3d skybox to be better.
I went ahead and added a displacement floor that sits below the map and goes off into the distance. There's a few props in there you can find by searching "skybox" in the model browser. Notably, this does not directly connect to the map anymore,

We can also expand our out of bounds to be a more reasonable size.

Then give it a bit of displacements so it seems to go on more than it does

And lastly some extra props to distract players from the abrupt end to our our of bounds

Fixing leaks
Through the redoing of our spawn door, we introduced a leak. By increasing the size of the gap, the areaportal no longer fills it.

You can stretch the portals to fit their new space.
The finale
Now let's compile our map and load into it.
After building our cubemaps, we can see the fruits of our labor

You should now have all the technical knowledge to make a full TF2 map in whichever way you like. Learning how to make maps and what the best practices are is tough, which is why I'd recommend studying valve maps to see how they do layout and visuals.
Decompiling and Studying Maps
The last guide for this introduction will be how to decompile other maps so you can view them in hammer to see their entity settings and brushes.
Decompiling
The tool used for decompiling is bspsrc (github). This converts a compiled bsp file into a vmf file which you can open in hammer.
As mentioned in the requirements steps, having a "tools" directory inside your Team Fortress 2 folder (next to your tf/ folder, not inside it!) is helpful for organizing all your tools in one place.
Windows
On Windows you should be able to simply double click the jar or bat files to launch the program, assuming you have Java installed.
Linux
While a sh script is provided, I simply run the program using
Code: Select all
java -jar bspsrc.jar path/to/map.bspIn either case, the vmf will be generated in the same folder as the bsp. Maps will have a "_d" suffix to note they are a decompiled version of a vmf, for example cp_dustbowl_d.vmf.
If you installed BAMF as recommended earlier in the installation guide, it should already provide you with a directory containing decompiled versions of all valve maps. This is just a utility as they were also decompiled with bspsrc.
When you open a decompiled map, occasionally you will see Hammer complain about certain brushes not being able to be loaded correctly. Some angled brushes and displacements don't get decompiled correctly and thus error out in the editor. You can ignore this error - you'll see the offending brush highlighted in red when you load in but it won't affect your viewing experience. Just know the map won't compile correctly if you then go on to recompile this generated vmf, if you want to recompile this generated vmf you will need to fix the error.
Once loaded in you will be able to view the entities used in the map. Sometimes logic entities are somewhat scattered in valve maps so you can use the entity report to find anything you need if you're looking.
Extracting Assets
bspsrc does not extract any packed assets, so if a map uses custom assets like sounds, models, or textures, they will show up as missing.
To extract assets, we can instead use VPKEdit. VPKEdit has both a windows and linux binary you can use, as well as a linux command line tool (though for regular extraction the GUI is easier)
The program is fairly robust. You can open up a map file and get access to all the information packed inside it, including materials and sounds. You can open up map files, as well as TF2 .vpk files which are found in your tf/ directory. Examples include tf2_misk_dir.vpk, and tf2_textures_dir.vpk.
Here I opened up cp_hadal and we can see the full tree of files packed inside of it.

Once you get the output, do not extract it to your tf/ directory! This is a big organization mishap you will regret doing in the future.
If you recall from earlier, since each folder within the tf/custom/ directory acts like a miniature version of your main tf/ directory, you can instead put the extracted folder within your tf/custom/ directory. For instance, if you were extracting assets from cp_dustbowl (which you don't have to do, as it only uses assets in the game) the final directory would look like tf/custom/cp_dustbowl/materials. If you then want to remove all these assets you can just delete the cp_dustbowl folder so they don't show up anymore.
Having a lot of folders in tf/custom slows your game down. Occasionally clean your directory!
Additional resources
There are a handful of useful resources for you to know about and that you will use in your career mapping.
Valve Developer Wiki
The Valve Developer Wiki is as close as you can get to comprehensive documentation of the source engine. There are countless useful pages for all aspects of mapping.
While there are too many pages to list individually, one good page is this one on TF2 Level Creation. It is a glossary of all sorts of tutorials and documentation regarding creating TF2 levels. There are guides on making individual gamemodes and what logic is needed, individual pages for entities, as well as general design theory.






