Unity Cheat Sheet

I started learning Unity for fun, and I've decided to keep useful snippets here for future reference.

Free Assets

  • Create with code series: low-poly vehicles, animals, foods, props, sky dome, fonts... After importing, you'll have to update Package Manager to avoid some compilation error.
  • Standard Assets: terrain, trees, models...
  • SpeedTree: once imported, I had this error where my trees would appear all pink. In the console it also said "SpeedTree materials need to be generated.". To do so, click on "Apply & Generate Materials".

C#

Access modifiers

In C#, not setting an access modifier defaults to the most restrictive one. Hence no access modifier in a Class will make its declaration private.

To make a variable visible in the Unity Inspector, but private in code, use the SerializeField annotation.

[SerializeField] float speed = 5;

Formatting text

string.Format("Time left: {0}s", secondsRemaining)

## format it as a number with 0 decimal.
string.Format("Speed: {0:N0} km/s", speed)

Read more at https://docs.microsoft.com/en-us/dotnet/api/system.string.format and https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings.

GameObjects

  • Showing/Hiding: gameObject.setActive(true/false).

Vectors

new Vector3(x, y, z)

// Shorthands
Vector3.down, up, left, right, back, forward

// Normalized
(enemy - player).normalized

// Getting the current object's position
transform.position

Finding things

1) Make a public variable (GameObject) for example. In the Inspector, drag the element into the field.

2) Finding a GameObject by name: GameObject player =
GameObject.Find("Player");
.

2.1) Finding a GameObject by tag: GameObject player = GameObject.FindGameObjectWithTag("Player");.

3) Finding a GameObject's own Component: Rigidbody rigidbody = GetComponent<Rigidbody>();.

4) Finding a Component (scripts included) in another GameObject: GameManager gameManager =
GameObject.Find("Game Manager").GetComponent<GameManager>();
.

Moving things

No force

# GameObject
transform.position = ...;
transform.position.Translate(...);
transform.position.Rotate(axis, angle);

# Rigidbody
rigidbody.transform.position = some Vector3;

Rigidbody

Apply forces or torques. It can be an impulse or over time.

rigidbody = GetComponent<Rigidbody>();
rigidbody.AddForce(RandomForce(), ForceMode.Impulse);
rigidbody.AddTorque(RandomTorque(), ForceMode.Impulse);

Preventing camera jitter

Apply forces in FixedUpdate and apply camera movements in LateUpdate, instead of putting both in the Update method.

Handling a vehicle using physics

Making it maneuverable

  • Preventing the vehicle from randomly tipping over: Replace the MeshCollider with WheelColliders to the wheels.
  • Preventing the vehicle from rolling: Place a CenterOfMass child, and set the vehicle's center of mass using that child's position.
  • Lastly, the instructor says the force should be applied to the wheels instead of the vehicle itself.
  • Also check that the whole Vehicle doesn't have other Colliders. My tank's turret for some reason had two rectangular colliders at the bottom that prevented it from moving.
Lesson 6.2 - Research and Troubleshooting - Unity Learn
Overview: In this lesson, you will attempt to add a speedometer and RPM display for your vehicle in Prototype 1. In doing so, you will learn the process of doing online research when trying to implement new features and troubleshoot bugs in your projects. As you will find out, adding a new feature …

Checking that it is grounded

Check its WheelColliders.

Creating things

  • Make a prefab.
  • Declare a public variable to get a reference to it.
  • In the Hierarchy window, drag the prefab to the public field you just created. By the way, it does not have to be a GameObject. It can be a ParticleSystem, a Rigidbody, etc.
  • Use it in yout script.
public GameObject enemyPrefab;
public ParticleSystem explosionParticle;

// Instantiate an enemy at some position.
Instantiate(enemyPrefab, some position, enemyPrefab.transform.rotation);

// Show a particle effect at the gameObject's current position.
Instantiate(explosionParticle, transform.position, explosionParticle.transform.rotation);

Destroying things

// Destroy itself.
Destroy(gameObject);

Random

Random.Range(min, max). Works with ints and floats. For float, max is inclusive. For int, it's exclusive.

Detecting a collision (by collider box)

Let's say you want to detect when your Fruit has dropped down and touched a Sensor. Make sure the Sensor has a Box Collider with Is Trigger set to true, and that your Fruit also has a Box Collider. Then in the Fruit's script:

void OnTriggerEnter(Collider other)
{
  // Destroy the fruit if it touches the bottom sensor.
  Destroy(gameObject);
}

To do something as long as the collision occurs, use OnTriggerStay.

Detecting a Rigidbody collision

void OnCollisionEnter(Collision collision)
{
  // Do something.
}

Detecting a click

Unity computes that for you, so you can directly override:

void OnMouseDown()
{
  ...
}

Timers and Delays

The preferred way is to use Coroutines.

// Start the coroutine in a method.
StartCoroutine(SpawnEnemy());

IEnumerator SpawnEnemy() {
  while (isGameActive)
  {
    yield return new WaitForSeconds(spawnRate);
    Instantiate(enemyPrefab);
  }
}

You can also use Invoke or InvokeRepeating. It uses reflection, so it is supposedly much more resource intensive and discouraged.

# Spawn enemies once after 3 seconds.
Invoke("SpawnEnemies", 3);

# Spawn enemies repeatedly. The first wave happens after 2 seconds, then every 3 seconds, it will spawn another one.
InvokeRepeating("SpawnEnemies", 2, 3);

void SpawnEnemies() { ... }

Animations

Creating animations

Source:

Setting up key frames

To make a simple animation, like a chest opening:

  • Select the Chest in the Hierarchy.
  • Open the Animation window.
  • In the Animation window, click the Create button.
  • Select the animation's desired fps. 30 is a good value.
  • Add the property you want to animate, in this case, Transform, Rotation. This will add the first key frame.
  • Click Record.
  • Select at what time you want to record the next key frame.
  • Change the rotation of the upper part of the chest to simulate opening. This new rotation setting will be recorded in a new key frame at the selected time.

Nice tweaks:

  • Look at the curves.
  • Select the tangents, move them around.
  • If you want the left and right tangent to be separate, set them as Broken.
  • You can select points and scale or move them.

Re-timing animations:

  • In the Animation window's Dopesheet, select the points to re-time.
  • Drag the selected points horizontally.

Triggering events

But it can get messy quick. Instead, in your script, you can use the animation's length.

  • Select the time at which the animation should trigger an event.
  • Click Add event.
  • In the inspector, select which script it should call.

Preventing an animation from looping

  • In the Project window, select the animation that's currently looping.
  • In the Inspector, disable Loop Time.

Programmatically trigger animations

It is possible to access parameters by name, but it is more efficient to use hashes.

public class EthanScript : MonoBehaviour 
{
  Animator anim;
  // Find a parameter's hash in the default Animator layer.
  int jumpHash = Animator.StringToHash("Jump");
  // Find a parameter's hash in a nested Animator layer.
  int runStateHash = Animator.StringToHash("Base Layer.Run");

  void Start ()
  {
    anim = GetComponent<Animator>();
  }

  void Update ()
  {
    float move = Input.GetAxis("Vertical");
    // Set a float by name.
    anim.SetFloat("Speed", move);

    AnimatorStateInfo stateInfo = anim.GetCurrentAnimatorStateInfo(0);
    if(Input.GetKeyDown(KeyCode.Space) && stateInfo.nameHash == runStateHash)
    {
      // Trigger a trigger by hash.
      anim.SetTrigger(jumpHash);
    }
  }
}

Layers

If your animation doesn't show, even if it triggers, check that its weight is not 0.

Override will override what's in the upper layers, while Additive will add to the animation.

Mask certain body parts

Let's say your attack animation overrides the legs from the running animation.

  • In the Project window, create a new Avatar Mask, name it Dwarf_lower_body for example.
  • Disable what you want to mask. In the screenshot, the instructor masked the root body too, since they want to use the running animation's movement.

Once done, assign it to the Mask field of the Attack Layer.

Note: this particular layer is included in the Create with Code assets, but the name is inverted, you want to use mask_upper.

Rigged animations

Setting up bones

  • Click Configure to assign bones, and check muscles (range of motion).

Usually, Mapping, Automap works best.

Tweaking animations

Some animations like walking will move the character, but some may also have some undesired rotation or drift along the X or Y axis. You can offset these by changing a few settings settings.

Adding an extra bone to a humanoid animation

If your character model has extra bones, like a piece of shoulder armor, you can add it to your animation by adding the joint in the Transform list. Once added to the mask, it'll be applied to the other animations too:

UI

Creating Text

In the Hierarchy, Create Object, UI, Text - TextMeshPro.

Dynamically set its content

using TMPro;

public class GameManager : MonoBehaviour
{
  public TextMeshProUGUI scoreUI;

  void UpdateScore() {
    scoreUI.text = "Score: " + score;
  }
  ...

Creating a Button

In the Hierarchy, Create Object, UI, Button - TextMeshPro.

Assigning a listener

1) In the Inspector, select a class, then a public method.

2) Programmatically

Create a Script and assign it to the Button. Then in the Script, add a click listener.

using UnityEngine.UI;

public class DifficultyButton : MonoBehaviour
{
  Button button;

  void Start() {
    button = GetComponent<Button>();
    button.onClick.AddListener(SetDifficulty);
  }

  void SetDifficulty() { ... }
  ...

Showing/Hiding a Text or Button

Get the UI element's GameObject and make it active or inactive.

// For TextMeshProUGUI
using TMPro;
// For Button
using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
  public TextMeshProUGUI gameOverUI;
  public Button restartButtonUI;
  
  void SetVisible(bool visible) {
    gameOverUI.gameObject.setActive(visible);
    restartButtonUI.gameObject.setActive(visible);
  }
  ...

Alternatively, you can group several UI components into a GameObject, and show/hide it like a regular GameObject.

Restarting a game

using UnityEngine.SceneManagement;

...

public void RestartGame()
{
  SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}

Mouse (Raycasting & Set cursor)

Create a Layer, call it "Clickable" for example. In the Hierarchy view, assign any object that you want to be clickable to that "Clickable" layer.

Tag the objects so you can decide which cursor to show.

Import cursor textures, then in the inspector, set the texture type to Cursor, to handle alpha values properly.

public class MouseManager : MonoBehaviour {
  //Know what objects are clickable
  public LayerMask clickableLayer;
  
  //swap cursors per object
  //normal cursor
  public Texture2D pointer;
  //for clickable world objects
  public Texture2D target;
  public Texture2D doorway;
  
  void Update() {
    RaycastHit hit;
    if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 50, clickableLayer.value))
    {
      bool door = false;
      if (hit.collider.gameObject.tag == "Doorway") {
        Cursor.SetCursor(doorway, new Vector2(16, 16), CursorMode.Auto);
        door = true;
      }
      else
      {
        Cursor.SetCursor(target, new Vector2(16, 16), CursorMode.Auto);
      }
    }
    else
    {
      Cursor.SetCursor(pointer, Vector2.zero, CursorMode.Auto);
    }
  }
}

Source: https://learn.unity.com/tutorial/prototyping-in-unity-part-1.

Raycasting (continued)

Casting a ray in front of the player:

Ray ray = new Ray(transform.position, transform.forward);
// Draw the line for debugging in the Scene window.
Debug.DrawRay(ray, ray.direction * rayDistance, Color.red);

if (Physics.Raycast(ray, out hit))
{
  Debug.Log(hit.distance);
  Debug.Log(hit.collider.gameObject);
}

Navmesh

For navigating characters.

Sources:

Setting up the Navmesh

  • Window, AI, Navigation.
  • Make your walkable and non-walkable objects Static.
  • Click Bake.

Excluding the Player

  • Make a Player layer.
  • Move the Player to that layer.
  • In the Navmesh surface, remove the Player layer from Included surfaces.

Excluding a doorway

  • Add a Navmesh obstacle Component to the door. Disable Carve.
  • Add a Navmesh modifier. Check Ignore from the build.

If I want to make the door shut, and prevent the player from going through, enable Carve. But if the door goes up, it'll no longer carve it, and the player can go through.

Drive the Player

  • Add a Navmesh agent component to the Player object.
  • In MouseManager, create a serializable UnityEvent<Vector3>.
  • Set up a public listener of that type.
  • In Update, check that the left button is pressed. If it is, Invoke that listener with the hit position.
  • Back to the Hierarchy, Set the Vector3 event listener to Player's Navmesh agent's destination setter.
public class MouseManager : MonoBehaviour
{
  public EventVector3 OnClickEnvironment;
  public LayerMask clickableLayer;

  void Update()
  {
    RaycastHit hit;
    if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 50, clickableLayer.value))
    {
      // Selectively show cursor.

      // Fire an event.
      if (Input.GetMouseButtonDown(0))
      {
        OnClickEnvironment.Invoke(hit.point);
      }
    }
  }
}

[System.Serializable]
public class EventVector3 : UnityEvent<Vector3> { }
Pick the Walkable layer and set the On Click to Player.destination

To prevent the player from going back and forth between the destination, increase the angular speed, to 800 for example.

Add a stopping distance to make it stop before the target. Good for enemies or chests or doors.

More on UnityEvent

Note: the following actually does not work. Gotta find a proper way to do it.

If you want to use UnityEvents to also enable/disable the navMeshAgent, ie the equivalent of setting navMeshAgent.isStopped to true or false, here's how to do it:

  • In the code, set up a public UnityEvent IsStopped;. Then use it like this: IsStopped.Invoke(true/false);
  • In the editor, set the IsStopped event to NavMeshAgent.isStopped (dynamic). Make sure you pick the one at the top in the "Dynamic bool" category, not the one in the "Static Parameters" category.
Pick the Dynamic bool version of isStopped.

When the player jumps down, they cross a "nav mesh link". To make it look like they jump, you have to programmatically disable the agent's autoTraverseOffMeshLink, listen to events when it will traverse an off mesh link, and programmatically set the agent's position over time to look like it's jumping. See snippet of code at https://learn.unity.com/tutorial/the-navmesh-agent-part-1.

Make some areas harder to cross

Source: https://learn.unity.com/tutorial/navmesh-obstacles-and-areas.

  • Create a new area.
  • Give it a larger cost.
  • Select your Grass objects.
  • Change its Navigation Area to your new area.
Create a new area with a higher cost
Assign your objects to the new Navigation Area

Make a consumable

Make it clickable with the right cursor

Just like the door, but without baking it into the Navmesh.

  • Make a GameObject (a Cube for example).
  • Assign it to the Clickable layer.
  • Create a tag and tag it appropriately.
  • In the MouseManager, assign the appropriate cursor based on its tag.

Make it Disappear and have side effects

Use the OnTriggerEnter method on box collider collision.

NPC behavior

Patrol

  • Make an NPC Controller script, assign it to the NPC GameObject.
  • In the NPC Controller script, make a public GameObject[] waypoints.
  • Set up empty GameObjects as waypoints in your scene.
  • Assign the objects to your NPC's waypoints.
  • In the script, make a function that automatically sets the next waypoint as the target. Give it an interval, let's say 10s. Then make another repeating function that sets its NavMeshAgent's destination to that waypoint.
  • Another way is to use agent's pathPending and remainingDistance properties to more intelligently decide when to assign the next waypoint. Script below.

Script source: https://learn.unity.com/tutorial/the-navmesh-agent-part-2.

void Update()
{
  // If the agent is still moving, do nothing.
  if (agent.pathPending)
  {
    return;
  }
  if (patrolling) {
    if (agent.remainingDistance < agent.stoppingDistance)
    {
      StartCoroutine(GoToNextPoint());
    }
  }
  else
  {
    StartCoroutine(GoToNextPoint());
  }
}

Attack when spotting the player

One basic way is to compute the distance between the NPC and the player. See below for the second, more advanced way using ray casting.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class NPCController : MonoBehaviour
{
  // Every 10s, go to the next patrol point.
  public float patrolTime = 10;
  public float aggroRange = 10;
  public Transform[] waypoints;

  int index;
  float maxAgentSpeed;
  Transform player;

  NavMeshAgent agent;

  void Awake()
  {
    agent = GetComponent<NavMeshAgent>();
    maxAgentSpeed = agent.speed;
    player = GameObject.FindGameObjectWithTag("Player").transform;

    InvokeRepeating("Tick", 0, 0.5f);

    if(waypoints.Length > 0)
    {
      InvokeRepeating("AssignNextPatrolPoint", 0, patrolTime);
    }
  }

  void AssignNextPatrolPoint()
  {
    index++;
    if (index == waypoints.Length)
    {
      index = 0;
    }
  }

  void Tick()
  {
    agent.destination = waypoints[index].position;
    // Walk.
    agent.speed = maxAgentSpeed / 2;

    if (player != null && Vector3.Distance(transform.position, player.position) < aggroRange)
    {
      agent.destination = player.position;
      // Run.
      agent.speed = maxAgentSpeed;
    }
  }
}

Using ray casting:

// The position of the eye of the NPC.
public Transform eye;

bool CanSee(GameObject target) {
  var ray = Ray(eye.position, target.transform.position - eye.position);
  RaycastHit hit;
  return Physics.Raycast(ray, out hit);
}

Now as the NPC sees the character, you can save it in lastKnownPosition to go to that spot. Later, if the NPC cannot see the character anymore, it will return to patrolling.

Source: https://learn.unity.com/tutorial/navmesh-building-components.

  • Download the NavMesh Surface component from GitHub, and import it into your project.
  • Add a NavMesh Surface component to your surface. Your surface doesn't have to be static.
  • Click Bake.
  • Now at runtime, if you modify your surface, the NavMesh will still work.
Nav Mesh Surface for non static objects.

Then to make some areas more costly to cross:

  • In the Nav Mesh Surface, set the Default Area to Grass.
  • Select your roads, add a Nav Mesh Modifier component, and select the Area Type to Walkable.
  • Bake.
Nav Mesh Modifier on the road.

Another option is to use a Nav Mesh Volume. It's easier to set up for areas like non-linear rivers.

The green area is set up by a Nav Mesh Volume.

Make some areas jumpable

Source: https://learn.unity.com/tutorial/navmesh-building-components.

  • Add a NavMesh Link.
  • Choose the two points. Cmd+Shift drag to position the points.
  • Make the directional or not.
  • Select the Area type to choose its cost.

Camera movements

You could programmatically move the camera in UpdateAfter. But a simpler way is to use Cinemachine.

Source:

Steps:

  1. Install the Cinemachine package.
  2. Open the Cinemachine menu, click Create Virtual Camera.
  3. Select the Virtual Camera in the Hierarchy.
  4. Drag the Player into the Virtual Camera's Follow and Look At fields.
  5. Set Body to Framing Transpose.
  6. Set Aim to Do Nothing.
  7. Reset its view with Cmd+Shift+F like a regular camera.
  8. Adjust the camera distance in Body, Camera distance.
  • Now adjust the rectangles to change the following behavior to your liking.

Designing levels

Better just watch the video as it's very visual. https://learn.unity.com/tutorial/level-design-part-1. The process is called grayboxing.

  • In Package Manager, import ProBuilder and ProGrids.
  • Snap all to grid.
  • Optional: Disable lighting recomputation to make it smoother. Window, Rendering, Light Settings, disable Auto Generate.
  • New Shape, Cube, Build Cube.
Face mode. To extend a volume along one of its surfaces.

Making a center line edge. Select all the edges of the door, click "Select edge ring". Or set the mode to "Select hidden: on" and drag to select all. Then click "Connect edges".

Holding Shift and drag a surface to extrude it.

Environment assets

  • Import models (FBX files), associated textures, and materials.
  • Open a material.
  • Lock the inspector.
  • Browse your textures, and assign each of the 4-5 textures to their corresponding slot in the inspector.
  • There might be a shared, detailed normal map for use as secondary normal map, for all similar materials (all stone for example), drag that in too.
  • An emission map can make parts of the model glow.
  • Now for each model, set its corresponding remapped material, and click Apply.

Lighting

Sources:

Add lights

  • Open Window, Lighting.
  • Set Skybox material to black if it's indoors.
  • Pick your directional light color. Dark blue for a dungeon.
  • Add point lights with orange color for torches. Set shadows to Hard shadows to cast shadows.

Bake light maps

Into what won't move (the environment). This will make objects reflect light onto one another, eg a red wall will emit red light to the statue nearby.

  • Set the environment to Static.
  • In the lighting tab, enable Global Illumination and Baked Global Illumination.
  • Enable Auto Generate or click Generate.

More adjustments to lighting: they also changed the source to Gradient and colors

Make an emissive material

  • Add a primitive object.
  • Make a Material, called my glow for example.
  • Assign the material to the primitive.
  • Enable "Emissive".
  • Pick a color other than black, and an intensity over 1.
  • Make the object static.
  • Notice how it will illuminate things around it.

Light probe group

A light probe group will compute global illumination so that dynamic objects receive some approximate light as well. The rule of thumb is to add more probes close to where there is interesting light.

Reflection probes

Useful for scenes with reflective surfaces likes mirrors, metal, rocks and woods.

Add them to the hierarchy and draw the volume inside which it'll reflect. It's ok for probes to overlap. The whole geometry should be covered.

Once done, bake again, this time with Reflection probes.

Without reflection probes

Without reflection probes baked in
With reflection probes baked in
Without reflection probe
With reflection probe

Materials

Source: https://learn.unity.com/tutorial/exploring-maps-and-map-channels

Albedo/Diffuse

Contains the information about the color. It can sometimes also contain an alpha channel. Then in the rendering mode, you can choose how to use that alpha information. For example if a flag is torn around the edges, you want to choose another rendering mode than Opaque. For a flag, Cutout or Fade is appropriate. For something like glass, you want Transparent. This will make it transparent, but still make it cast shadows and reflect light.

Metallic

Makes it more or less metallic. 0 is dull, 1 is like a mirror. You can apply a texture to set the metallic and smoothness values, if you use Source: Metallic Alpha instead of Albedo Alpha. The texture's red channel defines how metallic it is, and its alpha channel defines how smooth it is. That's why those images are usually grayscale.

If you're developing on mobile, you may want to disable forward rendering options for better performance..

With Forward Rendering
Without Forward Rendering

Normal maps

A normal map tells the engine in which direction light should bounce off when it hits your surface. This can make your surface look more bumpy without changing your geometry. If you look at it from the side, it'll still appear flat, since you didn't change the geometry.

When importing a normal map, set its texture type to Normal, or hit the "Fix" button.

Example of Normal map

Height map

That changes your geometry, so that you'll notice some etching on your door even if you see it from the side.

Ambient occlusion

This determines how much shadow an object casts on itself.

For better performance like on mobile, you can use an Albedo texture that has Occlusion info baked in. But then you have less options.

Emission maps

They determine which part of your geometry emits light. Then you can configure what color it emits and at what intensity.

The Emission map texture itself can contain some color information.

To finish, make your object Static so that its emission is taken into account when computing Global Illumination.

Here we're emitting a gray light

Secondary maps

Secondary maps contain noisy textures to make your objet look less flat. They are specifically designed to be tiled, so feel free to change its tiling values.

Tiled 10 by 10
Without secondary textures
With secondary textures

It's particularly noticeable up close.

With a normal and height map, but without secondary textures
With secondary textures

Metallic vs Specular

First, check out the charts for realistic albedo, metallic and smoothness values: https://docs.unity3d.com/Manual/StandardShaderMaterialCharts.html.

The Metallic value defines how metallic a component is, so most of its color comes from the Albedo. In Specular, Albedo is mostly ignored, so the color comes from the Specular settings.

They are mostly similar, and it's up to you which to use.

Making maps yourself

Cool tutorial on how the instructor did it in Photoshop.

How to Make Different Map Types - Unity Learn
In this tutorial, you will learn how to create the different map types to use with your Materials. This will include learning how to create alpha maps, normal maps, occlusion maps and emission maps.

Shaders

Source: https://learn.unity.com/tutorial/standard-metallic-and-standard-specular

Post-processing

Sources:

Brings nice visual effects for polish.

  • Install the package.
  • Two methods. Either create an empty object, add a Post-processing Volume component to it. Or in Project, create a Post-processing profile, then drag it to the camera.

Effects:

  • Ambient occlusion is good to improve shadows.
  • Bloom is good for making fire particle effects look more lively.
  • Anti-aliasing for softer edges.
  • Grain to add some texture.
  • Vignette darkens the corners. Good to add some feel of mystery.

Sound

Source: https://learn.unity.com/project/unity-audio-fundamentals.

Music

Add an Audio Source. Give it an Audio Clip. Adjust volume.

Sound effect

Upon entering water for example. Create a new script onto your Player Character. Add two variables: an Audio Clip and an Audio Source. OnTriggerEnter, play the sound.

void OnTriggerEnter(Collider other)
{
  if (other.CompareTag("water"))
  {
    audioSource.PlayOneShot(audioClip);
  }
}

Create an Audio Source on your Player, and assign it to the script's Audio Source.

For the Audio Source, make sure you set Spatial Blend to 3D, so that it adjusts to where the player is.

Set Spatial Blend to 3D

Terrain

Source: https://learn.unity.com/project/introduction-to-terrain-editor.

Basics

  • Create a terrain.
  • Elevate terrain.
  • Create layers of texture.
  • Paint the layers onto the terrain.
  • Import free SpeedTrees.
  • Paint trees.
  • Paint grass.

Next steps

Use the advanced package "Terrain Toolbox".

Ragdolls

Source: https://learn.unity.com/tutorial/ragdoll-physics.

  • Right click in the Hierarchy, create a Ragdoll.
  • Assign the bones from your character model to the Ragdoll set up dialog. Look for joints ("Jnt").
  • "Shoulder joint" == "arm", "Chest" == "Middle spine"
  • Click Create.
  • Upon creation, it'll add colliders and Character joint components to the character.
  • Disable isKinematic on the character's main Rigidbody. "is Kinematic" means "is controlled by Animation, disable physics".

If the ragdoll looks broken, you'll have to adjust box colliders and joints a bit more yourself.

Disabling Ragdoll mode while the player is alive

  • Programmatically disable all child colliders.
  • Programmatically enable all child rigidbodies' kinematic.

Enabling Ragdoll mode when they die

  • Disable the character's main collider.
  • Enable the character's main rigidbody kinematic field.
  • Disable the Animator.
  • Disable the navmesh agent so they can't move anymore.
  • Enable all child colliders.
  • Disable all child rigidbodies' kinematic.

Little time savers

Assigning several objects to an array or List in the Hierarchy

Here's how to do it faster than setting the array size to K, then dragging each of the K elements one by one.

  • Select the GameObject which array you want to set.
  • Lock the inspector.
  • Select the K objects you want to assign to the array.
  • Drag them to the array field.