This week we are looking at some advanced Unity features that we can use to create our own “first person shooter” (FPS) style game. We will take a look at tools for quick level generation, dive into Unity’s Character Controller, take a deeper examination of the offerings of the Physics library, and finally dip our toes into some Enemy AI. Starting today, our remaining lessons are no longer required to complete the assignments. From this point on, all lessons are strictly educational, but I strongly recommend trying to follow along as these systems are very useful for developing your own games.
Part 1: Level Prototyping (with ProBuilder)
ProBuilder is a very useful plugin for Unity, extending the editor by offering simple (but significantly improved) modeling and alignment tools. Once its own standalone product in the Asset Store, Unity has purchased it and now offers it free in the package manager.
ProBuilder is helpful if you need to quickly prototype some objects, rough out a level design, or generate a quick mesh for collisions. This provides enhanced mesh generation, and access down to the vertex level, as well as a quick way to create and adjust UV mapping for objects.
Another plugin from the same creators, PolyBrush, lets you sculpt, shape, and paint your meshes. It is also available for free in the Package Manager.
- Open the “Package Manager”, accessed by clicking on Window > Package Manager
- Select the “Unity Registry” option to see all available packages
- Scroll down until you see the “ProBuilder” entry, and then click “Install”
To start ProBuilder, navigate the top menu to Tools > ProBuilder > ProBuilder Window.
This will launch a window with either Text or Icon buttons. You can switch between modes by right-clicking a blank area in the window and selecting the other menu type.
Notice that when you launch ProBuilder, you will also see a small icon menu in your Scene window. This indicates the selection type when interacting with a ProBuilder object.
These are, from left to right:
- OBJECT Selection
- VERTEX Selection
- EDGE Selection
- FACE Selection
The ProBuilder Button menu will change depending on the selection type that is active.
By using these selectors, you can grab just a part of an object and use the various transform tools to deform them into the shapes that you want. The ProBuilder menu contains a number of tools that can split, offset, subdivide, and transform these. Additionally, you can use the “shift” key to generate new geometry, including Shift-Move (face) to Extrude and Shift-Scale (face) to create an inset poly.
Additionally, you can use Vertex Color to set the color tint of surfaces. You can also set materials, and make adjustments to the UVW mapping (the way that texture images are applied to the surfaces.
ProBuilder can also Export a model into common 3D formats, including OBJ and STL. (I recommend using .obj as that tends to be the most common amongst other modeling applications).
ADVICE: ProBuilder is great for quickly generating or editing assets, but is not recommended to be used throughout the entire process. The procedural generation of the mesh at runtime makes it susceptible to small errors, at which point your work may be lost without an option for recovery. Once you are happy with an asset, it is highly recommended that you run the EXPORT process to convert it into a mesh file in your Asset folder.
Part 2: The Character Controller
Part 3: Physics
When we first started using Rigidbody Physics way back in the days of Pong (or BingoBangoBongo, as we called it), we set our ball in motion with a push that we created using AddForce( ). We passed a rather large force vector to make such a small and light object move at the speed we anticipated. This is due to the methods which Unity uses to apply forces to an object, known as the ForceMode.
There are four types of ForceMode:
The default mode, ForceMode.Force is designed to apply a force over time to an object. It takes the mass of the object into consideration, so it will take more force to move heavier objects. It is designed to be used in FixedUpdate( ), as it will calculate the force to apply across that fixed time. The unit of force is calculated as:
(mass * distance) / (time^2)
This explains why our force needed to be so high to push a 1 kg object around the field of play. Our game provided one super quick burst, while this method is designed to be called repeatedly over time. A good use case would be a “thruster” that is called during every fixed update command.
Similar to ForceMode.Force, a force calculated over the time of FixedUpdate( ), except the force is applied equally to objects without regard to mass. Useful for purposes such as gravity effects, or driving games where the mass of the vehicle does not matter. The unit of force is applied as:
distance / (time^2)
This mode takes the application of time out of the equation and delivers the force in one single burst, while taking mass into consideration. This mode is best for explosions, with an instant release of energy, but larger objects will affected less by it. The unit of force here is expressed as:
(mass * distance) / time
Similar to impulse, but mass no longer has an effect. This mode instantly alters the velocity of the affected object by the value applied. (Remember, velocity is expressed as units per 1s). The “force” unit here is expressed as:
distance / time
For our experiment, we created a ball prefab with the following script. We set up two balls side by side, one with a weight of “1” and the other a weight of “50”. We created a similar pairing for each ForceMode, and set the applied force value to be equal across all eight balls.
ForceMode.Force required the most effort, rapidly clicking the mouse to barely move the heavy ball, while making some progress getting the light ball up the hill. ForceMode.Acceleration made things a little easier, as the heavy and light ball responded equally to each click. ForceMode.Impulse had a much greater effect, moving the heavy ball a greater distance than Acceleration had, and launching the light ball off of the board. ForceMode.VelocityChange launched both light and heavy balls far into the distance.
Part 4: The Navmesh
While it is fun to wander around blowing things up, I created this level for something more. I want to create enemy objects that have some intelligence and can pursue me through the level. In order to do this, I created a prefab variant of the Barrel that I named “AngryBarrel”. The Angry Barrel is recognizable by it’s greenish hue, it’s white cube on top to help indicate forward direction, and it’s ever present bloodlust.
In order to make this object “intelligent”, or at least smart enough to figure out how to navigate my level, I need to define the areas that it is capable of travelling. This involves generating a surface shape that is comprised of all of the valid positions that one of the intelligent agents could occupy, which we call a Navmesh.
To create a NavMesh, you must first open the Navigation window, which can be reached by going to Window > AI > Navigation. This will open up another panel usually in the same area as the Inspector.
In order to create a Navmesh, you must tell the system which objects to include, and what the rules are about what is and is not accessible terrain. With the Object tab active, I selected all of the mesh objects that I wished to be part of my navigable layer, and checked the Navigation Static property, which indicates that this is an element to be included in the baking process. (You can include any object with either a Mesh Renderer or Terrain component)
Next, I set the conditions for the Agent Size that the bake should consider. This will analyze your navigation objects, and generate a simplified mesh that outlines the valid locations where an agent with these dimensions would fit. Finally I hit the Bake button, and soon I have my navmesh.
The “slope” property defines the maximum sloped angle to be considered traversable. The “step height” indicates how many units high the agent can “step up”. Thanks to the step settings, I was able to create a somewhat safe zone where the enemy cannot follow me up a slope, but must instead go around to use the stairs.
Next we add a Navmesh Agent component to our enemy objects, and then a small script to tell that Agent component where to go. If we feed the agent a position, it will automatically move at the designated speed to to that point considering the shortest path and moving around obstacles and other agents.