User Tools

Site Tools


notes:unity3d:engine

Editor

Hierarchy View

The Hierarchy view organizes elements using GameObjects. Some GameObjects may serve as containers for other GameObjects.

Project View

The Project view organizes elements using folders.

GameObject

Bring the GameObject to the full view (three ways):

  • Select the object in the Hierarchy view > Place the cursor over the Scene view > Press F
  • Select the object in the Hierarchy view > Menu: Edit > Frame Selected
  • Double-click the object in the Hierarchy view

Duplicate the GameObject (three ways):

  • Select the object in the Hierarchy view > Menu: Edit > Duplicate
  • Select the object in the Hierarchy view > Ctrl + D
  • Right-click the object in the Hierarchy view > Duplicate

Activate or deactivate the GameObject by clicking the Active checkbox in the Inspector view. When the object is deactivated, it is not visible in the scene:

Editor Layout

You can update or create a new layout of the editor's views by using the Layout button:

Global and Local modes

Switch between Global and Local modes:

  • Use Global mode when you want to position an object relative to the scene coordinate system.
  • Use Local mode when you want to position an object relative to its local coordinate system.

Scripts

Create a C# script (three ways):

  • Menu: Assets > Create > C# Script
  • Project View: Create > C# Script
  • Select a GameObject > Inspector > Add Component > New Script (this creates an attaches the script to the GameObject in one step)

Open a script (three ways):

  • Project View: double-click the script
  • Project View: select the script > Inspector > [Open]
  • Context-sensitive gear menu > Edit Script

Tutorial: Roll-a-ball

This tutorial is based on the Unity's Roll-a-ball Tutorial.

Gameboard (playfield)

Add a 3D GameObject (a plane) to the scene (two methods):

  • Menu: GameObject > 3D Object > Plane
  • Hierarchy: Create > 3D Object > Plane

Similarily, you can create other 3D objects: cube, sphere, cylinder, etc. All standard 3D objects have dimensions either 1x1x1 or 1x2x1.

Reset the GameObject's Transform using the context-sensitive Gear menu of the Transform component:

Change the scale of the playfield object (two ways):

  • drag the red, green, or blue handles in the Scene view
  • type the values or drag the X, Y, Z labels

Note that the plane is a single-sided object and it does not have volume. That's why you can scale the plane only on the X and Z axes but not on the Y axis.

Player

  • Add a sphere to the scene. It will serve as our player.
  • Rename the sphere to “Player” and reset its transform to ensure that it is at the origin.
  • Move the sphere up by setting it's position Y value to 0.5. This way the sphere will rest on the plane.

Material

Use a material to add a color or a texture to an object.

  • Create a material: Project View: Create > Material
  • Change the material's color:

Apply the material to the object by dragging the material from the Project view onto the object in the Scene view.

Lighting

  • Select the Directional Light in the Hierarchy view.
  • Change the rotation of the light on the Y axis to 60. This gives a better shade to our player.

Player Movement

To use physics, add the Rigidbody component to the Player GameObject (two ways):

  • Menu: Component > Physics > Rigidbody
  • Inspector: [Add Component] > Physics > Rigidbody

Create a script and attach it to the Player GameObject. Name the script “PlayerController”.

using UnityEngine;
 
// The PlayerController script allows the player to move an associated GameObject using the keyboard.
public class PlayerController : MonoBehaviour
{
    // Controls the GameObject's speed. The public variable
    // shows up as an editable field in the Inspector.
    public float speed;
 
    // Holds the GameObject's Rigidbody component.
    private Rigidbody rb;
 
    // Start is called on the first frame the script is active.
    // Usually, it is the first frame of the game.
    private void Start()
    {
        // Keep the reference to the GameObject's Rigidbody (if any).
        rb = GetComponent<Rigidbody>();
    }
 
    // FixedUpdate is used to apply physics.
    private void FixedUpdate()
    {
        // Accept input from the keyboard.
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");
 
        // Create the force vector. The force has components
        // in the xz-plane only. We don't want the y-component.
        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
 
        // Apply the force vector to the Rigidbody. 
        // As a result, the GameObject associated 
        // with the Rigidbody will start moving. 
        rb.AddForce(movement * speed);
    }
}

Follow Camera

First, set the position of the camera by lifting it up by 10 units and tilting it down by 45 degrees:

Next, make the camera follow the Player by attaching the CameraConroller script to the camera:

using UnityEngine;
 
public class CameraConroller : MonoBehaviour
{
    // The player object. Provide it in the camera's Inspector view.
    public GameObject player;
 
    // A difference between the current position of the camera and the position of the player.
    private Vector3 offset;
 
    void Start ()
    {
        // Calculate the offset by subtracting the position of the player from the position of the camera.
        // The offset is constant.
        offset = transform.position - player.transform.position;
    }
 
    // LateUpdate is used to update the follow cameras, procedural animations, and to gather last known state.
    // LateUpdate is guaranteed to be called after all items in the frame are processed.
    void LateUpdate ()
    {
        // Move the camera to a position aligned with the player object. 
        // We execute this code in LateUpdate because we know that the player 
        // has already moved.
        transform.position = player.transform.position + offset;
    }
}

Collectibles

  • Create a cube and rename it “PickUp”.
  • Reset the PickUp's transform to the origin.
  • Make the PickUp smaller by scaling it along all axes: X=0.5, Y=0.5, Z=0.5
  • Tilt the Pickup by setting its rotation: X=45, Y=45, Z=45

Spin the PickUp by attaching the Rotator script to it:

using UnityEngine;
 
public class Rotator : MonoBehaviour
{
    // Update is called once per frame. We can use Update instead of FixedUpdate
    // because we are not using forces in this script.
    void Update()
    {
        // Multiply the rotation angles by Time.deltaTime to make the rotation smooth and frame rate independent.
        transform.Rotate(new Vector3(15, 30, 45) * Time.deltaTime);
    }
}

Make the PickUp GameObject dynamic by adding the Rigidbody component to it. We need to make it dynamic as we rotate it. Static objects such as a floor or walls do not move.

  • A GameObject that has a collider and the Rigidbody component is considered dynamic.
  • A GameObject that has a collider but no Rigidbody component is expected to be static.

If you don't add Rigidbody to a moving object, it will remain static and Unity will recalculate its static cache with every movement.

Also, make the PickUp GameObject's Rigidbody kinematic. The kinematic Rigidbody does not respond to physics forces and it can be animated and moved using its transforms:

Turn the PickUp object into a prefab. A prefab is a blueprint for a GameObject or a GameObject family. A prefab instance is a copy of a prefab placed in a scene.

  • Create the prefab by dragging a GameObject from the Hierarchy view and dropping it in the Project view.
  • Apply a material to the prefab by dropping the material on the prefab in the Project view.

Collisions

We want to pick up our collectibles when the Player collides with them. To do this we need to detect a collision between the collectible GameObject and the Player GameObject.

Detect and test collisions by adding the following code to your PlayerController script:

// OnTriggerEnter is invoked when the GameObject first touches
// a trigger collider. This collider is passed as the 'other' parameter.
private void OnTriggerEnter(Collider other)
{
    // Deactivate the GameObject only if it has the 'PickUp' tag.
    // Use the CompareTag method rather than an equality operator 
    // for better efficiency.
    if (other.gameObject.CompareTag("PickUp"))
    {
        // Deactivate the GameObject associated with the other collider.
        other.gameObject.SetActive(false);
    }
}
  • Add the 'PickUp' tag using the Tag Manager:

  • Assign the 'PickUp' tag to our PickUp prefab by selecting it from the Tag drop-down list.
  • Mark the PickUp GameObject as a trigger volume. Thanks to that, we can detect the contact with the trigger in the OnTrigger events.

Tutorial: Space Shooter

This tutorial is based on the Unity tutorial Space Shooter

Player

  • Add the player ship model to the hierarchy and name it Player.
  • Reset the Player's transform component to place it at the origin.

The Player GameObject has the number of components:

  • Mesh Filter - holds our mesh model.
  • Mesh Renderer - renders the mesh in the scene; it also references materials.
  • Transform - defines the position, the direction, and the scale of the GameObject.

As we will be moving the ship using physics:

  • Add the Rigidbody component to the Player GameObject.
  • Deselect the Use Gravity checkbox.

Add the Capsule Collider to the Player GameObject. The collider defines the volume of the object.

  • Change the Direction of the Capsule Collider to Z-Axis to better fit the ship.
  • Increase the Capsule Collider's Radius and decrease the Height.
  • For the top-down view, you may want to click the y-arm in the Scene View gizmo.

You may also consider using a Mesh Collider instead of the Capsule Collider. In this case, you need to supply the mesh collider yourselves.

  • To get a better view of the Mesh Collider, turn off the Mesh Renderer. It will reveal the green cage of the collider.
  • Provide your own collider by assigning it to the Mesh Collider's Mesh slot. Otherwise, Unity will use a mesh provided in the mesh filter.

On the collider component:

  • Select the Is Trigger checkbox to make the collider a trigger.

Another example of the PlayerController script:

using UnityEngine;
 
// The Boundary values shows up as a group in the GameObject's Inspector view.
[System.Serializable]
public class Boundary
{
    public float xMin, xMax, zMin, zMax;
}
 
public class PlayerController : MonoBehaviour
{
    // Controls the GameObject's speed.
    public float speed;
 
    // Controls the GameObject's tilt along the z-axis.
    public float tilt;
 
    // Restricts the boundary of the GameObject's position.
    public Boundary boundary;
 
    // Holds the GameObject's Rigidbody component.
    private Rigidbody rb;
 
    private void Start()
    {
        rb = GetComponent<Rigidbody>();
    }
 
    private void FixedUpdate()
    {
        // Accept input from the keyboard.
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");
 
        // Create the movement vector.
        Vector3 movement = new Vector3 (moveHoriz, 0.0f, moveVert);
 
        // Set the GameObject's velocity.
        rb.velocity = speed * movement;
 
        // Restrict the GameObject's position to the boundary.
        rb.position = new Vector3 
        (
            Mathf.Clamp(rb.position.x, boundary.xMin, boundary.xMax),
            0.0f,
            Mathf.Clamp(rb.position.z, boundary.zMin, boundary.zMax)
        );
 
        // Tilt/bank the GameObject along the z-axis. The negative value makes the tilt in 
        // the opposite direction to velocity. We are using rb.velocity.x to tilt the object 
        // in the left/right direction.
        rb.rotation = Quaternion.Euler (0.0f, 0.0f, rb.velocity.x * -tilt);
    }
}

A missile is a GameObject that moves across the screen and collides with other GameObjects possibly destroying them.

Create GameObjects in hierachy

  • Create an empty GameObject and name it Missile.
  • Reset the Missile's transform.
  • Create a quad to hold a missile image. Add it as a child of the Missile object.
  • Rename the quad to VFX and reset its transform.
  • The Missle object will hold our logic such as scripts and the child VFX object will hold the visual effects such as textures or shaders.

Set up visual effects

  • Change the VFX's rotation to 90 to face the upward camera.
  • Create a new material: Project view > Create > Material
  • Add a texture to the new material: in the material's Inspector view, click [Select] in the main texture field. This brings the Asset Picker.

  • From the Asset Picker, select a texture you want.
  • If you want to remove an existing texture, you can select None in the Asset Picker.
  • Instead of using the Asset Picker, you can also drag a texture onto the main texture field in the material's Inspector.
  • Associate the material with the VFX quad by dragging the material onto the quad in the Scene view. This adds the material to the VFX quad's Mesh Renderer's Materials array.

  • Select the Mobile/Particles/Additive shader for your material. This causes to the bright parts to add to the scene and the black parts vanish.

Set up logic

  • We want to move the Missile object using physics. Add the Rigidbody component to the Missile.
  • Deselect Use Gravity in the Rigidbody component.
  • We need to add a collider to the Missile object. However, the VFX quad already has a default collider.
  • Remove the existing collider from the VFX object.
  • Add the Capsule Collider component to the Missile object.
  • Adjust the Capsule Collider's properties:
    • Radius: 0.03
    • Height: 0.5
    • Direction: Z-Axis
    • Is Trigger: True to make the collider a trigger collider
  • You can also modify the collider by dragging its control points that can be accessed by pressing the Shift key.
  • Create a Mover script and attach it to the Missile. It moves the GameObject forward along the z-axis.
using UnityEngine;
 
public class Mover : MonoBehaviour 
{
    public float speed;
 
    private Rigidbody rb;
 
    // Start is invoked on the first frame the GameObject is instantiated.
    void Start()
    {
        rb = GetComponent<Rigidbody> ();
 
        // Move the GameObject forward. The transform.forward property
        // is the local z-axis of the GameObject.
        rb.velocity = transform.forward * speed;
    }
}
  • Turn the Missle GameObject into a prefab by dragging it from the Hierarchy view to the Project view.
  • Set the speed for the Missile in the Missile's Mover script component to 20.

Shoot the missiles

  • Create an empty GameObject and name it ShotSpawn. It will serve as the origin of the missiles.
  • Make the ShotSpawn a child of your Player.

Add the following code to your player GameObject:

using UnityEngine;
 
public class PlayerController : MonoBehaviour 
{
    // The missle prefab.
    public GameObject shot;
 
    // Specify the type of the shotSpawn as a Transform.
    public Transform shotSpawn;
 
    // A delay when the next shot is fired. For example, 
    // fireRate 0.25 fires four shots per second.
    public float fireRate;
 
    // Indicates when we can fire the next shot.
    private float nextFire;
 
    void Update()
    {
        // We can put detection of the fire button in Update because we are not using physics.
        if (Input.GetButton("Fire1") && Time.time > nextFire)
        {
            // Limit the fire rate.
            nextFire = Time.time + fireRate;
 
            // shotSpawn is located at the front of the Player.
            Instantiate(
                shot,               // an object to instantiate
                shotSpawn.position, // the shot's initial position
                shotSpawn.rotation  // the shot's initial rotation
            ); 
 
        }
    }
}

Remove the missiles

We need to remove missiles that did not collide with any objects. Otherwise, they will live forever hogging the game's resources.

  • Create a box and name it BoundingBox.
  • Resize the box to include the boundaries of your game field.
  • Attach the following script to the BoundingBox:
using UnityEngine;
 
public class DestroyByBoundary : MonoBehaviour 
{
    // Use the OnTriggerExit event as we want to destroy the missiles when they leave the BoundingBox.
    void OnTriggerExit(Collider other)
    {
        Destroy (other.gameObject);
    }
}
  • Remove the Mesh Renderer and Mesh Filter component from the BoundingBox.

Hazards

  • Create a GameObject and add a Rigidbody to it.
  • Make it tumble by assigning the following script to it:
using UnityEngine;
 
public class RandomRotator : MonoBehaviour 
{
    // Determines the maximum tumble value.
    public float tumble;
 
    private Rigidbody rb;
 
    void Start()
    {
        rb = GetComponent<Rigidbody> ();
 
        // angularVelocity determines how fast a GameObject is rotating.
        // Random.insideUnitSphere gives a random Vector3 value.
        rb.angularVelocity = Random.insideUnitSphere * tumble;
    }
}
  • Because we use the angular velocity in code, set the Rigidbody's Angular Drag property to 0. This will prevent the hazards from slowing down.

Add the following script to the hazard GameObject (or rather a prefab) in order to destroy it upon a collision with a missile of the player:

using UnityEngine;
 
public class DestroyByContact : MonoBehaviour 
{
    // Explosion is rendered when the hazard is destroyed.
    public GameObject explosion;
 
    // PlayerExplosion is rendered when the player is destroyed.
    public GameObject playerExplosion;
 
    // ScoreValue keeps the current number of destroyed hazards.
    public int scoreValue;
 
    // GameController manages the entire game. For example, it spawns the waves of hazards.
    private GameController controller;
 
    void Start()
    {
        // Find the instance of the GameController. The FindWithTag method finds the first instance of
        // a given GameObject.
        GameObject controllerObject = GameObject.FindWithTag("GameController");
        if (controllerObject != null) 
        {
            // Get the GameController component associated with the GameController object.
            controller = controllerObject.GetComponent<GameController>();
        }
 
        if (controller == null) 
        {
            Debug.Log("Cannot find 'GameController' script.");
        }
    }
 
    void OnTriggerEnter(Collider other)
    {
        // Show an object this GameObject collided with in the Console window.
        Debug.Log(other.name);
 
        // Make sure that this GameObject cannot be destroyed by a collision with the BoundingBox or 
        // a hazard.
        if (other.CompareTag("BoundingBox") || other.CompareTag("Hazard"))
            return;
 
        // Check if the explosion GameObject is assigned to this GameObject.
        if (explosion != null)
        {
            // Instantiate an explosion in the same position as the hazard.
            Instantiate (explosion, transform.position, transform.rotation);
        }
 
        // Check if this GameObject (which is a hazard) collided with the Player.
        if (other.CompareTag("Player"))
        {
            // Instantiate a player explosion in the same position as the player.
            Instantiate(playerExplosion, other.transform.position, other.transform.rotation);
 
            // The Player died - game over.
            controller.GameOver();
        }
 
        controller.AddScore(scoreValue);
 
        // Destory the missile when it touches the hazard.
        // The Destroy method marks the object to be destroyed. All of the marked objects
        // are destroyed at the end of the frame.
        Destroy(other.gameObject);
 
        // Destroy the GameObject this script is attached to, and all of its children and attached components.
        Destroy(gameObject);
    }
}

GameController

Below, there is an example of the GameController which manages the entire game.

using UnityEngine;
using System.Collections;
 
public class GameController : MonoBehaviour 
{
    public GameObject[] hazards;
    public Vector3 spawnRange;
    public int hazardCount; // the number of hazards
    public float spawnWait;
    public float startWait;
    public float waveWait;
 
    private bool gameOver;
    private int score;
 
    void Start()
    {
        gameOver = false;
        score = 0;
 
        StartCoroutine(SpawnWaves ());
    }
 
    void Update()
    {
	// Spawns waves of hazards. SpawnWaves is a co-routine. That's why it returns IEnumerator.
	IEnumerator SpawnWaves()
	{
            while(true) // allows for continued waves of hazards
            {
                // Initial delay.
                yield return new WaitForSeconds (startWait);
 
                for (var i = 0; i < hazardCount; ++i) 
                {
                    // Pick a random hazard.
                    GameObject hazard = hazards[Random.Range(0,hazards.Length)];
 
                    // Spawns the hazard only if the game is not over.
                    if (!gameOver) 
                    {
                        Vector3 spawnPosition = new Vector3(
                            Random.Range(-spawnRange.x, spawnRange.x), 
                            spawnRange.y, 
                            spawnRange.z); // y=0, z=16
 
                        Quaternion spawnRotation = Quaternion.identity; // no rotation
 
                        Instantiate(hazard, spawnPosition, spawnRotation);
                    }
 
                    // Wait before spawning the next hazard.
                    yield return new WaitForSeconds(spawnWait);
                }
 
                // Wait before spawning the next wave.
                yield return new WaitForSeconds(waveWait);
 
                if (gameOver) 
                {
                    break;
                }
            }
        }
 
        public void AddScore(int newScore)
	{
            score += newScore;
        }
 
	public void GameOver()
	{
            gameOver = true;
        }
    }
}

Gizmos

Show or hide the grid from the Gizmos menu in the upper-right corner of the Scene view:

Move the Scene view to the top-down view by clicking Y in the gizmo:

Materials

Use a material to add colour or a texture to a model.

What How
Create a material Project view: Create > Material
-or-
Menu: Assets > Create > Material
Set the color of a material Inspector view: Material preview window > Main Maps section > Albedo > Click on the Albedo's colour field to open the colour picker
Preview a material Make sure that the Material preview window is open:
Apply a material to an object Drag the material from the Project view on the object in the Scene view or the Hierarchy view.
-or-
Drag a material from Project view onto a slot in the object's Mesh Renderer component.
Inspect a mesh material Materials are associated with the Mesh Renderer component of a game object. Expand the Mesh Renderer to see the materials applied:
View properties of a material Select the material in the Project tab.
-or-
Select an object in the Hierarchy tab. In the Inspector, at the bottom of the game object's components, there are properties of associated materials. This part of inspector describes the actual material rather than an instance associated with the GameObject.
Change the shininess of a material Adjust the Smoothness property (smoother surfaces are more shiny)

Audio

There are three main audio components in Unity:

  • audio clips - hold audio data (sound files)
  • audio sources - play an audio clip in the scene
  • audio listener

Associate an audio clip with a GameObject, using an Audio Source component (two ways):

  • Attach an Audio Source component to a GameObject and assign an audio clip to it.
  • Drag an audio clip on to a GameObject. Unity will create a new Audio Source component on the GameObject and automatically assign the audio clip.

  • If you want the audio to play automatically when the GameObject is created, set the Play On Awake property.
  • Change the strength of the sound by adjusting the Audio Source's Volume property.

Play a sound when the player clicks the fire button:

private AudioSource audioSource;
...
 
void Start()
{
    audioSource = GetComponent<AudioSource>();
}
 
void Update()
{
    if (Input.GetButton("Fire1"))
    {
        audioSource.Play();
    }
}

UI

GameObjects and components related to Unity UI:

  • Canvas: Every UI element has to be a child of the Canvas element. There can be multiple Canvas GameObjects in a scene and several controls can be in the same Canvas.
  • EventSystem: A GameObject for managing the UI control events.
  • Panel: A UI element used to group other UI objects.
  • Rect Transform component: Describes how an element should be positioned and sized relatively to its parent. It provides pivot points which are reference points for scaling, resizing, and rotations as well as anchor points.
  • Sibling Depth: The bottom-to-top display order.
  • Rich Text: You can style substrings in the UI Text component by using simple HTML tags such as <b>, <i>, or <color=#FF0000>

UI controls can be grouped in two categories:

  • Visual UI controls: Visible controls such as Button, Image, Text, Toggle, etc.
  • Interaction UI controls: Non-visible components added to GameObjcts such as InputField and ToggleGroup.

There are three Canvas render modes:

  • Screen Space - Overlay: The UI elements are displayed independently of a camera. The elements overlay the scene contents.
  • Screen Space - Camera: The Canvas is a flat plane facing the camera.
  • World Space: The Canvas is a flat plane but does not always faces the camera. The Canvas appears as any other object in the scene, relative to where it is in the camera's frustrum.

Add a UI Text element to the scene:

  • Hierarchy View: Create > UI > Text

When we add the first UI element to the scene, two other elements are added automatically:

  • Canvas
  • EventSystem

UI elements have the Rect Transform tool:

You can use the Anchor Presets tool from the Rect Transform to anchor a UI element to a specific place in Canvas, for example, a left-top corner.

By default, UI elements are anchored to the middle of the screen.

Access a UI Text element from a script. The GameObject that owns the script has to have the 'scoreText' slot associated with a UI Text element.

using UnityEngine;
using UnityEngine.UI;
 
public class PlayerController : MonoBehaviour
{
    // Holds the reference to the UI Text element that displays the score.
    public Text scoreText;
 
    private void Start()
    {
        scoreText.text = "Score: 0";
    }
 
    // ... update the score, etc.
}

Deployment

Access the Build Settings:

  • Menu: File > Build Settings

If you change the platform, you need to press the [Switch Platform] button:

Add the scene to the Build Settings window (two ways):

  • Use the [Add Open Scenes] button
  • Drag-and-drop a scene from the Project view to the Build Settings window.

If you don't add a scene to Build Settings, Unity will build the project with the currently opened scene.

Links

notes/unity3d/engine.txt · Last modified: 2020/08/26 (external edit)