7

Physics Collisions and Health System

As games try to simulate real-world behaviors, one important aspect to simulate is physics, which dictates how objects move and how they collide with each other, such as in the collision of players and walls, or bullets and enemies. Physics can be difficult to control due to the myriad of reactions that can happen after a collision, so we will learn how to properly configure our game to create physics as accurately as we can. This will generate the desired arcade movement feeling but get realistic collisions working—after all, sometimes, real life is not as interesting as video games!

In this chapter, we will examine the following collision concepts:

  • Configuring physics
  • Detecting collisions
  • Moving with physics

First, we will learn how to properly configure physics, a step needed for the collisions between objects to be detected by our scripts, using new Unity events that we are also going to learn. All of this is needed in order to detect when our bullets touch our enemies and damage them. Then, we are going to discuss the difference between moving with Transform, as we have done so far, as well as moving with Rigidbody and the pros and cons of each version. This will be used to experiment with different ways of moving our player and let you decide which one you will want to use. Let’s start by discussing physics settings.

Configuring physics

Unity’s physics system is prepared to cover a great range of possible gameplay applications, so properly configuring it is important to get the desired result.

In this section, we will examine the following physics settings concepts:

  • Setting shapes
  • Physics object types
  • Filtering collisions

We are going to start by learning about the different kinds of colliders that Unity offers, and then learn about different ways to configure those to detect different kinds of physics reactions (collisions and triggers). Finally, we will discuss how to ignore collisions between specific objects to prevent situations such as the player’s bullets damaging the player.

Setting shapes

At the beginning of this book, we learned that objects usually have two shapes, the visual shape—which is basically the 3D mesh—and the physical one, the collider—the one that the physics system will use to calculate collisions. Remember that the idea of this is to allow you to have a highly detailed visual model while having a simplified physics shape to increase the performance.

Unity has several types of colliders, so here we will recap the common ones, starting with the primitive types, that is, Box, Sphere, and Capsule. These shapes are the cheapest ones (in terms of performance) to detect collisions due to the fact that the collisions between them are done via mathematical formulae, unlike other colliders such as the Mesh Collider, which allows you to use any mesh as the physics body of the object, but with a higher performance cost and some limitations. The idea is that you should use a primitive type to represent your objects or a combination of them, for example, an airplane could be done with two Box colliders, one for the body and the other one for the wings. You can see an example of this in the following screenshot, where you can see a weapons collider made from primitives:

Figure 7.1: Compound colliders

Anyway, this is not always necessary; if we want the weapon to just fall to the ground, maybe a Box collider covering the entire weapon can be enough, considering those kinds of collisions don’t need to be accurate, thereby increasing performance. Also, some shapes cannot be represented even with a combination of primitive shapes, such as ramps or pyramids, where your only solution is to use a Mesh collider, which asks for a 3D mesh to use for collisions, but we won’t use them in this book given their high-performance impact; we will solve all of our physics colliders with primitives.

Now, let’s add the necessary colliders to our scene to prepare it to calculate collisions properly. Consider that if you used an Asset Store environment package other than mine, you may already have the scene modules with colliders; I will be showing the work I needed to do in my case, but try to extrapolate the main ideas here to your scene. To add the colliders, follow these steps:

  1. Select a wall in the base and check the object and possible child objects for collider components; in my case, I have no colliders. If you detect any Mesh collider, you can leave it if you want, but I would suggest you remove it and replace it with another option in the next step. The idea is to add the collider to it, but the problem I detected here is that, due to the fact my wall is not an instance of a Prefab, I need to add a collider to every wall in the scene.
  2. One option is to create a Prefab and replace all of the walls with instances of the Prefab (the recommended solution) or to just select all walls in the Hierarchy (by clicking them while pressing Ctrl or Cmd on Mac) and, with them selected, use the Add Component button to add a collider to all of them. In my case, I will use the Box Collider component, which will adapt the size of the collider to the mesh. If it doesn’t adapt, you can just change the Size and Center properties of the Box Collider to cover the entire wall:

Figure 7.2: A Box Collider added to a wall

  1. Repeat steps 1 and 2 for the corners, floor tiles, and any other obstacle that will block player and enemy movement.

Now that we have added the needed colliders to the walls and floor, we can continue with the player and enemy. We will be adding the Capsule Collider to them, the usual collider to use in movable characters due to the fact that the rounded bottom will allow the object to smoothly climb ramps. Being horizontally rounded allows the object to easily rotate in corners without getting stuck, along with other conveniences of that shape. You might want to create an enemy Prefab based on one of the characters we downloaded before, so you can add the collider to that Prefab. Our player is a simple GameObject in the scene, so you will need to add the collider to that one, but consider also creating a Prefab for the player for convenience.

You may be tempted to add several Box colliders to the bones of the character to create a realistic shape of the object, and while we can do that to apply different damage according to the part of the body where the enemies were shot, we are just creating movement colliders; the capsule is enough. In advanced damage systems, both capsule and Bone colliders will coexist, one for the movement and the other for damage detection; but we will simplify this in our game.

Also, sometimes the collider won’t adapt well to the visual shape of the object, and in my case, the Capsule collider didn’t fit the character very well. I needed to fix its shape to match the character by setting its values as shown in the following screenshot: Center to 0,1,0, Radius to 0.5, and Height to 2:

Figure 7.3: Character collider

The bullet we created with the Sphere already had a Sphere collider, but if you replaced the mesh of the bullet with another one, you might want to change the collider. For now, we don’t need other objects in our game, so now that everything has its proper collider, let’s see how to set the different physics settings to each object to enable proper collision detection.

If you check the Terrain’s components, you will see that it has its own kind of collider, the Terrain Collider. For Terrains, that’s the only collider to use.

Physics object types

Now that we have added colliders to every object by making the objects have a presence in the physics simulation, it is time to configure them to have the exact physics behavior we want. We have a myriad of possible combinations of settings, but we will discuss a set of common profiles that cover most situations. Remember, besides colliders, we saw the Rigidbody component at the beginning of this book, which is the one that applies physics to the object. The following profiles are done with a combination of colliders and Rigidbody settings:

  • Static Collider: As the name suggests, this kind of collider is not supposed to move, aside from some specific exceptions. Most of the environment objects fall into this category, such as walls, floors, obstacles, and the terrain. These kinds of colliders are just colliders with no Rigidbody component, so they have a presence in the physics simulation but don’t have any physics applied to them; they cannot be moved by other objects’ collisions, they won’t have physics, and they will be fixed in their position no matter what. Take into account that this has nothing to do with the Static checkbox at the top-right part of the editor; those are for systems we will explore later in several chapters (such as Chapter 12, Lighting Using the Universal Render Pipeline), so you can have a Static Collider with that checkbox unchecked if needed.
  • Physics Collider: These are colliders with a Rigidbody component, like the example of the falling ball we did in the first part of this book. These are fully physics-driven objects that have gravity and can be moved through forces; other objects can push them and they perform every other physics reaction you can expect. You can use this for the player, grenade movement, falling crates, or in all objects in heavily physics-based games such as The Incredible Machine.
  • Kinematic Collider: These are colliders that have a Rigidbody component but have the Is Kinematic checkbox checked. These don’t have physics reactions to collisions and forces like Static Colliders, but they are expected to move, allowing Physics Colliders to properly handle collisions against them when moving. These can be used in objects that need to move using animations or custom scripting movements such as moving platforms.
  • Trigger Static Collider: This is a regular Static Collider but with the Is Trigger checkbox of the collider checked. The difference is that kinematic and physics objects pass through it but by generating a Trigger event, an event that can be captured via scripting, which tells us that something is inside the collider.

This can be used to create buttons or trigger objects, in areas of the game when the player passes through something happening, such as a wave of enemies being spawned, a door being opened, or winning the game in case that area is the goal of the player. Note that regular Static Colliders won’t generate a trigger event when passing through this type because those aren’t supposed to move.

  • Trigger Kinematic Collider: Kinematic Colliders don’t generate collisions, so they will pass through any other object, but they will generate Trigger events, so we can react via scripting. This can be used to create moveable power-ups that, when touched, disappear and give us points, or bullets that move with custom scripting movements and no physics, just straight like our bullets, but damage other objects when they touch them.

Of course, other profiles can exist aside from the specified ones to use in some games with specific gameplay requirements, but it’s down to you to experiment with all possible combinations of physics settings to see whether they are useful for your case; the described profiles will cover 99% of cases.

To recap the previous scenarios, I leave you with the following table showing the reaction of contact between all of the types of colliders. You will find a row per each profile that can move; remember that static profiles aren’t supposed to move. Each column represents the reaction when they collide with the other types, Nothing meaning the object will pass through with no effect, Trigger meaning the object will pass through but raise Trigger events, and Collision meaning that the object won’t be able to pass through the object:

Collides with Static

Collides with Dynamic

Collides with Kinematic

Collides with Trigger Static

Collides with Trigger Kinematic

Dynamic

Collision

Collision

Collision

Trigger

Trigger

Kinematic

Nothing

Collision

Nothing

Trigger

Trigger

Trigger Kinematic

Trigger

Trigger

Trigger

Trigger

Trigger

Figure 7.4: Collision Reaction Matrix

Considering this, let’s start configuring the physics of our scene’s objects.

The walls, corners, floor tiles, and obstacles should use the Static Collider profile, so no Rigidbody component on them, and their colliders will have the Is Trigger checkbox unchecked:

Figure 7.5: Configuration for floor tiles; remember the Static checkbox is for lighting only

The player should move and generate collisions against objects, so we need it to have a Dynamic profile. This profile will generate a funny behavior with our current movement script (which I encourage you to test), especially when colliding against walls, so it won’t behave as you expected. We will deal with this later in this chapter:

Figure 7.6: Dynamic settings on the player

The Enemy Prefab we suggested you create previously will be using the Kinematic profile because we will be moving this object with Unity’s AI systems later, so we don’t need physics here, and as we want the player to collide against them, we need a collision reaction there, so there’s no Trigger here:

Figure 7.7: Kinematic setting for the enemy

For the Bullet Prefab, it moves with simplistic movement via scripting (it just moves forward), and not physics. We don’t need collisions; we will code the bullet to destroy itself as soon as it touches something and will damage the collided object (if possible), so a Kinematic Trigger profile is enough for this one. We will use the Trigger event to script the contact reactions:

Figure 7.8: The Kinematic Trigger setting for our bullet; Is Trigger and Is Kinematic are checked

Now that we have properly configured the objects, let’s check how to filter undesired collisions between certain object types.

Filtering collisions

Sometimes we want certain objects to ignore each other, like the bullets shot by the player, which shouldn’t collide with the player itself. We can always filter that with an if statement in the C# script, checking whether the hit object is from the opposite team or whatever filtering logic you want, but by then, it is too late; the physics system wasted resources by checking a collision between objects that were never meant to collide. Here is where the Layer Collision Matrix can help us.

The Layer Collision Matrix sounds scary, but it is a simple setting of the physics system that allows us to specify which groups of objects should collide with other groups. For example, the player’s bullets should collide with enemies, and enemy bullets should collide with the player. In this case the enemies’ bullets will pass through enemies, but this is desired in our case. The idea is to create those groups and put our objects inside them, and in Unity, those groups are called layers. We can create layers and set the layer property of the GameObject (the top part of the Inspector) to assign the object to that group or layer. Note that you have a limited number of layers, so try to use them wisely.

We can achieve this by doing the following:

  1. Go to Edit | Project Settings and, inside it, look for the Tags and Layers option from the left pane:

Figure 7.9: The Tags and Layers settings

  1. From the Layers section, fill the empty spaces to create layers. We will use this for the bullet scenario, so we need four layers: Player, Enemy, PlayerBullet, and EnemyBullet:

Figure 7.10: Creating layers

  1. Select the Player GameObject in the Hierarchy and, from the top part of the Inspector, change the Layer property to Player. Also, change the Enemy Prefab to have the Enemy layer. A window will show, asking you whether you want to also change the child objects; select Yes:

Figure 7.11: Changing the layers of the player and the enemy Prefab

  1. In the case of the bullet, we have a problem; we have one Prefab but two layers, and a Prefab can only have one layer. We have two options: changing the layer according to the shooter via scripting, or having two bullet Prefabs with different layers. For simplicity, I will choose the latter, also taking the chance to apply another material to the enemy bullet to make it look different.
  2. We will be creating a Prefab Variant of the player bullet. Remember that a Variant is a Prefab that is based on an original one like class inheritance. When the original Prefab changes, the Variant will change, but the Variant can have differences, which will make it unique.
  3. Drop a bullet Prefab into the scene to create an instance.
  4. Drag the instance again to the Prefabs folder, this time selecting the Prefab Variant option in the window that will appear.
  5. Rename it Enemy Bullet.
  6. Destroy the Prefab instance in the scene.
  7. Create a second material similar to the player bullet with a different color and put it on the enemy bullet Prefab Variant.
  8. Select the enemy bullet Prefab, set its layer to EnemyBullet, and do the same for the original Prefab (PlayerBullet). Even if you changed the original Prefab layer, as the Variant modified it, the modified version (or override) will prevail, allowing each Prefab to have its own layer.

Now that we have configured the layers, let’s configure the physics system to use them:

  1. Go to Edit | Project Settings and look for the Physics settings (not Physics 2D).
  2. Scroll down until you see the Layer Collision Matrix, a half grid of checkboxes. You will notice that each column and row is labeled with the names of the layers, so each checkbox in the cross of a row and column will allow us to specify whether these two should collide. In our case, we configured it as shown in the following screenshot so that player bullets do not hit the player or other player bullets, and enemy bullets do not hit enemies or other enemy bullets:

Figure 7.12: Making player bullets collide with enemies and enemy bullets with the player

It is worth noticing that sometimes filtering logic won’t be that fixed or predictable, for example, only hit objects that have a certain amount of life, objects that don’t have an invisibility temporal buff, or conditions that can change during the game and are difficult to generate for all possible layers for all possible groups. So, in these cases, we should rely on manual filtering after the Trigger or Collision event.

Now that we have filtered collisions, let’s check whether our settings are working properly by reacting to collisions in the next section.

Detecting collisions

As you can see, proper physics settings can be complicated and very important, but now that we have tackled that, let’s do something with those settings by reacting to the contact in different ways and creating a health system in the process.

In this section, we will examine the following collision concepts:

  • Detecting Trigger events
  • Modifying the other object

First, we are going to explore the different collision and trigger events Unity offers to react to contact between two objects through the Unity collision events. This allows us to execute any reaction code we want to place, but we are going to explore how to modify the contacted object components using the GetComponent function.

Detecting Trigger events

If objects are properly configured, as previously discussed, we can get two reactions: collisions or triggers. The Collision reaction has a default effect that blocks the movement of the objects, but we can add custom behavior on top of that using scripting; but with a Trigger, unless we add custom behavior, it won’t produce any noticeable effect. Either way, we can script reactions to both possible scenarios such as adding a score, reducing health, and losing the game. To do so, we can use the suite of Physics events.

These events are split into two groups, Collision events and Trigger events, so according to your object setting, you will need to pick the proper group. Both groups have three main events, Enter, Stay, and Exit, telling us when a collision or trigger began (Enter), whether they are still happening or are still in contact (Stay), and when they stopped contacting (Exit). For example, we can script a behavior such as playing a sound when two objects first make contact in the Enter event, such as a friction sound, and stop it when the contact ends, in the Exit event.

Let’s test this by creating our first contact behavior: the bullet being destroyed when coming into contact with something. Remember that the bullets are configured to be triggers, so they will generate Trigger events on contact with anything. You can do this with the following steps:

  1. Create and add a script called ContactDestroyer on the Player Bullet Prefab; as the Enemy Bullet Prefab is a Variant of it, it will also have the same script.
  2. To detect when a trigger happens, such as with Start and Update, create an event function named OnTriggerEnter.
  3. Inside the event, use the Destroy(gameObject); line to make the bullet destroy itself when touching something:

Figure 7.13: Auto-destroying on contact with something

  1. Save the script and shoot the bullets against the walls to see how they disappear instead of passing through them. Here we don’t have a collision, but a trigger that destroys the bullet on contact. So, this way, we are sure that the bullet will never pass through anything, but we are still not using physics movement.

For now, we won’t need the other Collision events, but if you need them, they will work similarly; just create a function called OnCollisionEnter instead.

Now, let’s explore another version of the same function. It not only tells us that we hit something but also what we came into contact with. We will use this to make our Contact Destroyer also destroy the other object. To do this, follow these steps:

  1. Replace the OnTriggerEnter method signature with the one in the following screenshot. This one receives a parameter of the Collider type, indicating the exact collider that hit us:

Figure 7.14: Version of the trigger event that tells us which object we collided with

  1. We can access the GameObject of that collider using the gameObject property. We can use this to destroy the other one as well, as shown in the following screenshot. If we just use the Destroy function by passing the other variable, it will only destroy the Collider component:

Figure 7.15: Destroying both objects

  1. Save and test the script. You will notice that the bullet will destroy everything it touches. Remember to verify that your enemy has a Capsule collider for the bullet to detect collisions against it.

The equivalent version in Visual Scripting would be like the following figure:

Figure 7.16: Destroying both objects with Visual Scripting

As you can see, we created an On Trigger Enter node and chained it to two Destroy nodes. To specify which object each Destroy node will destroy, we used the Component: Get GameObject node twice. The right one was created with no node connected to its left input pin, which means it will return the GameObject that is currently executing this script (hence, the This label in the node left pin), in this case, the bullet. For the second one we needed to connect the Collider output pin at the right of the OnTriggerEnter node to the Get GameObject node; this way we specify we want to obtain the GameObject that contains the collider our bullet collided with.

Now, in our game we don’t want the bullet to destroy everything on contact; instead, we will make the enemies and the player have a life amount; the bullets will reduce that life amount until it reaches 0, so let’s check how to do that.

Modifying the other object

For the bullet to damage the collided object, we will need to access a Life component to change its amount, so we will need to create this Life component to hold a float field with the amount of life. Every object with this component will be considered a damageable object. To access the Life component from our bullet scripts we will need the GetComponent function to help us.

If you have a reference to a GameObject or component, you can use GetComponent to access a specific component if the object contains it (if not, it will return null). Let’s see how to use that function to make the bullet lower the amount of life of the other object:

  1. Create and add a Life component with a public float field called amount to both the player and enemy Prefabs. Remember to set the value 100 (or whatever life amount you want to give them) in the Amount field for both in the Inspector:

Figure 7.17: The Life component

  1. Remove the ContactDestroyer component from the player bullet, which will also remove it from the Enemy Bullet Variant.
  2. Add a new script called ContactDamager to both the enemy and player.
  3. Add an OnTriggerEnter event that receives the other collider as a parameter and just add the Destroy function call that auto-destroys itself, not the one that destroys the other object; our script won’t be responsible for destroying it, just reducing its life.
  4. Add a float field called damage, so we can configure the amount of damage to inflict on the other object. Remember to save the file and set a value before continuing.
  5. Use GetComponent on the reference to the other collider to get a reference to its Life component and save it in a variable:

Figure 7.18: Accessing the collided object’s Life component

  1. Before reducing the life of the object, we must check that the Life reference isn’t null, which would happen if the other object doesn’t have the Life component, as in the case of walls and obstacles. The idea is that the bullet will destroy itself when anything collides with it and reduce the life of the other object if it is a damageable object that contains the Life component.

In the following screenshot, you will find the full script:

Figure 7.19: Reducing the life of the collided object

  1. Place an enemy in the scene and set its speed to 0 to prevent it from moving.
  2. Select it in the Hierarchy before hitting Play and start shooting at it.

You can see how the life value reduces in the Inspector. You can also press the Esc key to regain control of the mouse and select the object while in Play mode to see the life field change during the runtime in the editor.

Now, you will notice that life is decreasing, but it will become negative; we want the object to destroy itself when life is below 0 instead. We can do this in two ways: one is to add an Update to the Life component, which will check all of the frames to see whether life is below 0, destroying itself when that happens. The second way is by encapsulating the life field and checking that inside the setter to prevent all frames from being checked. I would prefer the second way, but we will implement the first one to make our scripts as simple as possible for beginners.

To do this, follow these steps:

  1. Add Update to the Life component.
  2. Add If to check whether the amount field is below or equals 0.
  3. Add Destroy in case the if condition is true.
  4. The full Life script will look like the following screenshot:

Figure 7.20: The Life component

  1. Save and see how the object is destroyed once Life becomes 0.

The Visual Scripting version for the Life component would look like this:

Figure 7.21: The Life component in Visual Scripting

The script is pretty straightforward—we check if our Life variable is less than 0 and then destroy ourselves as we did previously. Now, let’s check the Damager script:

Figure 7.22: The Damager component in Visual Scripting

This version is a little bit different from our C# counterpart. At first glance it looks the same: we use Get Variable as before to read the life, then we use the Subtract node to subtract damage from life, and the result of that calculation becomes the new value of life, with the Set Variable node used to alter the current value of that variable.

The first difference we can see here is the absence of any GetComponent node. In C# we used that instruction to get the Life component on the collided object in order to read and alter its amount variable, reducing the remaining life. But as in Visual Scripting our node graphs don’t have variables, so we don’t need to access the component to read them. Instead, knowing that the enemy has a Life variable in its Variables component, we use the Get Variable node, connecting it to the collider we hit (the Collider output pin of On Trigger Enter), so essentially we are reading the value of the Life variable of the collided object.

The same goes for changing its value: we use the Set Value node, connecting it to the collider, specifying we want to alter the value of the Life variable of the collider object, not ours (as bullets, we even don’t have a Life variable). Note that this can raise an error if the collided object doesn’t have the Life variable, and that is why we added the Object Has Variable node, which checks if the object has a variable called Life. If it doesn’t, we just do nothing, which is useful when we collide with walls or other non-destructible objects. Finally, we make the Damager (the bullet in this case) auto-destroy itself.

Optionally, you can instantiate an object when this happens such as a sound, a particle, or maybe a power-up. I will leave this as a challenge for you. By using a similar script, you can make a life power-up that increases the life value or a speed power-up that accesses the PlayerMovement script and increases the Speed field; from now on, use your imagination to create exciting behaviors using this.

Now that we have explored how to detect collisions and react to them, let’s explore how to fix the player falling when hitting a wall.

Moving with physics

So far, the player, the only object that moves with the Dynamic Collider Profile and the one that will move with physics, is actually moving through custom scripting using the Transform API. Every dynamic object should instead move using the Rigidbody API functions in a way the physics system understands better. As such, here we will explore how to move objects, this time through the Rigidbody component.

In this section, we will examine the following physics movement concepts:

  • Applying forces
  • Tweaking physics

We will start by seeing how to move objects the correct physical way, through forces, and we will apply this concept to the movement of our player. Then, we will explore why real physics is not always fun, and how we can tweak the physics properties of our objects to have a more responsive and appealing behavior.

Applying forces

The physically accurate way of moving an object is through forces, which affect the object’s velocity. To apply force, we need to access Rigidbody instead of Transform and use the AddForce and AddTorque functions to move and rotate respectively. These are functions where you can specify the amount of force to apply to each axis of position and rotation. This method of movement will have full physics reactions; the forces will accumulate on the velocity to start moving and will suffer drag effects that will make the speed slowly decrease, and the most important aspect here is that they will collide against walls, blocking the object’s way.

To get this kind of movement, we can do the following:

  1. Create a Rigidbody field in the PlayerMovement script, but this time, make it private, meaning, do not write the public keyword in the field, which will make it disappear in the editor; we will get the reference another way:

Figure 7.23: The private Rigidbody reference field

  1. Note that we named this variable rb just to prevent our scripts from being too wide, making the screenshots of the code in the book too small. It’s recommended to call the variable properly in your scripts—in this case, it would be named rigidbody.
  2. Using GetComponent in the Start event function, get our Rigidbody and save it in the field. We will use this field to cache the result of the GetComponent function; calling that function every frame to access the Rigidbody is not performant. Also, you can notice here that the GetComponent function can be used to retrieve not only components from other objects (like the collision example) but also your own:

Figure 7.24: Caching the Rigidbody reference for future usage

  1. Replace the transform.Translate calls with rb.AddRelativeForce. This will call the add force functions of the Rigidbody, specifically the relative ones, which will consider the current rotation of the object. For example, if you specify a force in the z-axis (the third parameter), the object will apply its force along with its forward vector.
  2. Replace the transform.Rotate calls with rb.AddRelativeTorque, which will apply rotation forces:

Figure 7.25: Using the Rigidbody forces API

  1. Check that the player GameObject capsule collider is not intersecting with the floor, but just a little bit over it. If the player is intersecting, the movement won’t work properly. If this is the case, move it upward.

In the Visual Scripting version, the change is the same; replace the Transform and Rotate nodes with Add Relative Force and Add Relative Torque nodes. An example of Add Relative Force would be the following one:

Figure 7.26: Using the Rigidbody forces API

And for rotation like this:

Figure 7.27: Using the Rigidbody torque API

You can see that we don’t need to use GetComponent nodes here either, given that just using the Add Relative Force or Torque nodes makes Visual Scripting understand that we want to apply those actions on our own Rigidbody component (explaining again the This label). If in any other case we need to call those functions on a Rigidbody other than ours, we would need the GetComponent node there, but let’s explore that later.

Now, if you save and test the results, you will probably find the player falling and that’s because now we are using real physics, which contains floor friction, and due to the force being applied at the center of gravity, it will make the object fall. Remember that, in terms of physics, you are a capsule; you don’t have legs to move, and here is where standard physics is not suitable for our game. The solution is to tweak physics to emulate the kind of behavior we need.

Tweaking physics

To make our player move like in a regular platformer game, we will need to freeze certain axes to prevent the object from falling. Remove the friction to the ground and increase the air friction (drag) to make the player reduce its speed automatically when releasing the keys.

To do this, follow these steps:

  1. In the Rigidbody component, look at the Constraints section at the bottom and check the X and Z axes of the Freeze Rotation property:

Figure 7.28: Freezing rotation axes

  1. This will prevent the object from falling sideways but will allow the object to rotate horizontally. You might also freeze the y-axis of the Freeze Position property if you don’t want the player to jump, preventing some undesired vertical movement on collisions.
  2. You will probably need to change the speed values because you changed from a meters-per-second value to newtons-per-second, the expected value of the Add Force and Add Torque functions. Using 1,000 in speed and 160 in rotation speed was enough for me.
  3. Now, you will probably notice that the speed will increase a lot over time, as will the rotation. Remember that you are using forces, which affects your velocity. When you stop applying forces, the velocity is preserved, and that’s why the player kill keeps rotating even if you are not moving the mouse. The fix to this is to increase the Drag and Angular Drag, which emulates air friction, and will reduce the movement and rotation respectively when no force is applied. Experiment with values that you see suitable; in my case, I used 2 for Drag and 10 for Angular Drag, needing to increase Rotation Speed to 150 to compensate for the drag increase:

Figure 7.29: Setting air friction for rotation and movement

  1. Now, if you move while touching the wall, instead of sliding, like in most games, your Player will stick to the obstacles due to contact friction. We can remove this by creating a Physics Material, an asset that can be assigned to the colliders to control how they react in those scenarios.
  2. Start creating one by clicking on the + button from the Project window and selecting Physics Material (not the 2D version). Call it Player and remember to put it in a folder for those kinds of assets.
  3. Select it and set Static Friction and Dynamic Friction to 0, and Friction Combine to Minimum, which will make the Physics system pick the minimum friction of the two colliding objects, which is always the minimum—in our case, zero:

Figure 7.30: Creating a physics material

  1. Select the player and drag this asset to the Material property of the Capsule Collider:

Figure 7.31: Setting the physics material of the player

  1. If you play the game now, you may notice that the player will move faster than before because now we don’t have any kind of friction on the floor, so you may need to reduce the movement force.

As you can see, we needed to bend the physics rules to allow a responsive player movement. You can get more responsiveness by increasing drags and forces, so the speeds are applied faster and reduced faster, but that depends, again, on the experience you want your game to have.

Some games want an immediate response with no velocity interpolation, going from 0 to full speed and vice versa from one frame to the other, and in these cases, you can override the velocity and rotation vectors of the player directly at your will or even use other systems instead of physics, such as the Character Controller component, which have special physics for platformer characters; but let’s keep things simple for now.

Summary

Every game has physics in some way or another, for movement, collision detection, or both. In this chapter, we learned how to use the physics system for both, being aware of proper settings to make the system work properly, reacting to collisions to generate gameplay systems, and moving the player in such a way that it collides with obstacles, keeping its physically inaccurate movement. We used these concepts to create our player and bullet movement and make our bullets damage the enemies, but we can reuse the knowledge to create a myriad of other possible gameplay requirements, so I suggest you play a little bit with the physics concepts seen here; you can discover a lot of interesting use cases.

In the next chapter, we will be discussing how to program the visual aspects of the game, such as effects, and make the UI react to the input.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset