<!doctype html>

Time.deltaTime - Yasthil’s Blog

Time.deltaTime

1 minute read

In any 3D game you’re likely to encounter a situation where you want to do a time-based feature. For example, moving an object or character X units per second. Using Time.deltaTime is going to help avoid frame-rate dependence.

What is Time.deltaTime?

  • The time in seconds between the last frame and the current frame

How often is Update() called?

  • Update is called every frame

Why use Time.deltaTime?

  • In a nutshell, it allows us to go from:
    • “Do [Some-time-based-action] per frame” to
    • “Do [Some-time-based-action] per second”

Example: Move an object 2 meters per second

  • Note: it is 2 meters per second
  • It it NOT 2 meters per frame

Frame-rate Dependent movement (incorrect)

private float m_MetersPerFrame = 2;
void Update() 
{
	// move forward metersPerFrame meters PER FRAME
	transform.Translate(Vector3.forward * metersPerFrame); 
}

  • Since the above is frame-rate dependent, the following will occur at the respective FPS:
    • Moving 2 meters/frame:
      • 60FPS => 2 * 60 = 120 meters covered in 1 second
      • 30FPS => 2 * 30 = 60 meters covered in 1 second
  • For the same time (1 second) the object’s distance covered will be different - this is bad and will produce unexpected behaviour.

Frame-rate Independence (correct)

  • Time.deltaTime helps us prevent frame-rate dependence on any time-based actions
private float m_MetersPerSecond = 2;
void Update() 
{
	// move forward m_MetersPerSecond meters PER SECOND
	transform.Translate(Vector3.forward * m_MetersPerSecond * Time.deltaTime);
}

  • Since we’re multiplying by the time taken since the last frame (a variable value), this allows us to scale our movement steps to ensure the same distance is moved per second
  • The following will occur at the respective FPS:
    • Moving 2 meters/second:
      • 60FPS => 2 * 60 * 1/60 = 2 meters covered in 1 second
      • 30FPS => 2 * 30 * 1/30 = 2 meters covered in 1 second

Therefore, whenever you’re developing a time-based feature, ensure you’re using Time.deltaTime.

<!doctype html>

Collisions in Unity - Yasthil’s Blog

Collisions in Unity

1 minute read

The aim of this post is to summarize:

  • What’s required for collisions in Unity
  • How to setup some basic collision detection
    • When OnTrigger and OnCollision are called and when they aren’t

What’s required for collision to occur?

For collisions to be detected by Unity, there are 2 components that are of particular importance:

1. Rigidbody

  • Allows an object to react to physics
  • A key property to note is: isKinematic
  • If you do not require the object to have physics-based interactions with other objects (i.e. isKinematic is true), a Rigidbody is still required (on at least 1 object) for any collision to occur

2. Collider

  • Used to define the shape of the object (or rather an approximation of it) which will be used for collisions
  • There are various types of colliders, namely; Box, Sphere, Capsule, Mesh, etc
  • A key property to note is: isTrigger

How to setup basic collision detection?

1. When you need two solid objects to interact with each other:

  • Object 1:

  • Object 2:

Notes:

  • At least one Rigidbody needs to be non-kinematic (i.e. isKinematic = false)

Callbacks that will be called:

  • OnCollisionEnter
  • OnCollisionExit

2. When you want to do something when a collision occurs (i.e. a trigger) on a non-physical object.

  • Object 1:

  • Object 2:

Notes:

  • At least one object requires a Rigidbody
    • isKinematic can be either true or false
  • At least 1 Collider needs to have isTrigger = true
    • Both cannot be set to isTrigger = true

Callbacks that will be called:

  • OnTriggerEnter
  • OnTriggerExit

Tags:

Updated:

<!doctype html>

Coroutines - Yasthil’s Blog

Coroutines

1 minute read

Coroutines

When working in Unity and you find yourself asking the following questions:

  • How can I make sure task A finishes before I can do [task B]?
  • How do I delay a method by X seconds?
  • How can I wait for a condition to be met before continuing?
  • How can I wait for a response from a web request?
  • How to I prevent locking up the main thread whilst performing a computationally-intensive operation?

This is where Coroutines can help!

What are Coroutines?

Unlike normal C# functions which are executed to completion within a single frame, Coroutines can:

  • Pause execution (yield) and return control to Unity
  • Continue execution in the next frame

Syntax

  • A Coroutine must return: IEnumerator
  • Within the body, you must have: a yield statement
  • To start a Coroutine: StartCoroutine(COROUTINE_FUNCTION()) OR StartCoroutine("NAME_OF_COROUTINE")_

How do you Use Coroutines?

How to wait for X seconds:

    private void Start()
    {
        StartCoroutine(TaskA(3));
    }

    private IEnumerator TaskA(int delay)
    {
        Debug.Log("Task A started!");
        yield return new WaitForSeconds(delay);
        Debug.Log("Task A finished after " + delay + "seconds");
    }

    private void TaskB()
    {
        Debug.Log("Task B!");
    }

Result:

How to run a computationally-intensive operation without locking up the main thread?

    private int _counter = 0;
    private void Start()
    {
        StartCoroutine(LongRunningTask());
    }

    void Update()
    {
        if(_counter < 20)
        {
            Debug.Log("_counter: " + _counter);
        }
        
    }

    private IEnumerator LongRunningTask()
    {
        // simulate a long running task
        while(_counter < 20)
        {
            _counter++;
            // return execution to main thread and 
			// continue from here next frame
            yield return null;
        }
    }

Result:

  • You’ll notice that 1 is printed twice. That’s because:
    • Start is called just before the first time Update is called
    • When Update is called, any previously yielded Coroutines will be processed afterwards

Source

Wait until a condition is met

    private IEnumerator WaitUntilCounterIs(int value)
    {
        yield return new WaitUntil(() => _counter == value);
        Debug.Log("Counter is now at: "+ value);
    }

Result:

  • The Debug.Log will only be called once _counter equals value
  • The above would be the same performance as running while(_counter != value)

Considerations

  • Coroutines do not run on separate threads, they run on the main thread
  • You can stop a Coroutine using StopCoroutine(name Of Coroutine/reference)
  • Where possible, you can use C# Tasks for asynchronous functionality

<!doctype html>

2 Ways to Host Unity WebGL Builds Locally - Yasthil’s Blog

2 Ways to Host Unity WebGL Builds Locally

less than 1 minute read

Introduction

Working with WebGL can be tricky and a simple thing that’s overlooked is hosting a WebGL app locally for debugging. I’ll share 2 easy ways to host a WebGL build locally below.

1. Unity’s Build-and-Run

  • This one is the easiest.
  • All you have to do is go to Build Settings -> WebGL -> Build and Run
    • Unity will host a temporary local sever for your app once it builds!

2. Using Python

  • Install python
  • Change directory to where your Unity WebGL Build resides
  • Run the following command: python3 -m http.server 9090
    • This will host a local http server on port 9090

P.S I tried getting this working using Docker, but it wasn’t as easy as copying the files into an nginx container.

Tags: ,

Updated:

<!doctype html>

Tracking Multiple Images - AR Foundation - Yasthil’s Blog

Tracking Multiple Images - AR Foundation

1 minute read

Background

Augmented Reality is a technology that has really taken the world by storm. I was fortunate enough to have worked on some AR apps early in my career and it has been amazing watching how Unity has evolved into one of the defacto engines for building MR content. Back in 2014, I used Unity and Vuforia to do basic image tracking and displaying a 3D model in AR. Nowadays, we are spoilt with a host of amazing free tools that’s built right into Unity!

AR Foundation

AR Foundation is Unity’s framework that provides the high-level AR functionalty. Instead of working directly with ARKit (Apple) and ARCore (Android) we can use a single framwork, whilst being able to deploy to both Android and Apple. More info here

Goal

I wanted to replicate what I did using Vuforia in the past, but using AR Foundation instead.

  • Have a bunch of QR codes as reference images (‘markers’)
  • Display a 3D model once the marker is scanned

Result

  • The scene is made up of:

    • Here, the ARTrackedImageManager does all the heavy lifting
    • You provide it with a ReferenceImageLibrary - which contains your images you want to use as markers (QR codes in my case)
    • Provide it with a Prefab that will be spawned once the marker is tracked

    • Example: Map the GUID for each image in the XRReferenceImageLibrary to a GameObject - this specific example, order mattters.
      • Then once the trackedImagesChanged event is raised on the ARTrackedImageManager:
            // activate an image that was added
            foreach(ARTrackedImage image in obj.added)
            {
                GameObject characterPrefab;
                if(TryGetCharacterPrefab(image.referenceImage.guid, out characterPrefab))
                {
                    GameObject spawndedCharacter = Instantiate(characterPrefab, 
                    image.transform.position, 
                    image.transform.rotation);
                    this.m_SpawnedCharacters.Add(image.referenceImage.guid, spawndedCharacter);
                    spawndedCharacter.SetActive(true);
                }
            }
      
            // tracking has updated
            foreach (ARTrackedImage image in obj.updated)
            {
                if(image.trackingState == TrackingState.Tracking)
                {                
                    // update tracked image's position and rotation
                    if (TryGetSpwanedCharacter(image.referenceImage.guid, out spwanedCharacterGO))
                    {
                        spwanedCharacterGO.transform.SetPositionAndRotation(
                            image.transform.position, image.transform.rotation);
                    }
                }
                // image is no longer tracked, disable
                else
                {
                    if (TryGetCharacterPrefab(image.referenceImage.guid, out spwanedCharacterGO))
                    {
                        spwanedCharacterGO.SetActive(false);
                    }
                }
            }
      

Future

There are some improvements that I could make to this simple project:

  • Better handling of when the image looses tracking
  • Add some interaction perhaps

Resources