Supposons le code suivant :

public class MoveComponent: MonoBehaviour
{
    void Update()
    {
        //La méthode Translate à un paramètre fait une translation 
        //dans l'espace local avec le vecteur donné
        transform.Translate(new Vector3(1,0,0), Space.Self); //translation de 1 unité sur l`axe x local
    }
}

Le vecteur de vitesse est \((1,0,0)\). En une seconde, il est attendu qu’un GameObject avec un MoveComponent se déplace de 60m sur son axe de \(x\) local, si le jeu vise 60 FPS. Par contre, si la cible est 30 FPS, l’objet est attendu de se déplacer de 30m. Avec ce code, l’état du jeu dépend de la mesure des FPS. Le temps se mesure en frame et non en seconde.

\(\Delta x = V_{x_{i}} \Delta t\)

Supposons \(t = 60 frames → \Delta x = \frac{1m}{f} \times 60f = 60m\)

Compter des frames

En comptant des frames, il est possible de faire des opérations après un lapse de temps (toujours mesuré en frames).

public class MoveComponent: MonoBehaviour
{
    private int elapsedTime;
    
    void Update()
    {
        //faire la translation à chaque 60 frames
        if (elapsedTime == 60)
        {
            transform.Translate(new Vector3(1,0,0), Space.Self);
            elapsedTime = 0;
        }   
        else
            elapsedTime++;
    }
}

Problèmes avec mesurer le temps en frames

Utiliser les frames comme unité de temps, peut causer des problèmes. Un utilisateur peut forcer un jeu à cibler un FPS plus élevé qu’attendu pour que le temps s’écoule plus vite, par exemple, et « briser » un jeu multijoueur.

De plus, il est commun que la mesure des FPS fluctue pendant le jeu. Il est très improbable que deux CPU/GPU — malgré qu’ils soient du même modèle — produisent la même performance. Ceci s’applique sur tous les autres composants d’un ordinateur. Dans la plupart des cas, il est déconseillé que les vitesses, accélérations et autres taux soient en fonction du FPS. Si un développeur détermine que le joueur devrait se déplacer de 60m par seconde, ceci devrait être le cas même si les FPS varient entre 30 et 120.

Logique selon le temps réel

Supposons qu’un objet doit se déplacer de 60m de longueur par seconde et que le temps entre chaque frame est de 1 seconde. À chaque frame, l’objet doit se déplacer de 60m.

\(\frac{x}{1s} = \frac{60m}{1s} → x = 60m\)

Maintenant, supposons que le temps entre chaque frame est 0.5 seconde ; l’objet doit se déplacer de 30m par frames.

\(\frac{x}{0.5s} = \frac{60m}{1s} → x = 30m\)

Pour déterminer le nombre d’unité dont l’objet doit se déplacer à un frame donné, tout ce qu’il faut savoir est le temps entre chaque frame.

Plus que les FPS est haut, moins de temps qu’il y a entre chaque frame et plus que le déplacement paraît fluide, mais l’objet arrivera au même endroit après une seconde peut importe les FPS. Si le temps entre deux frames est plus long, le déplacement à faire est plus grand pour compenser.

Time.deltaTime

L’API Unity donne une propriété calculée qui sonde le moteur pour savoir combien de secondes le dernier frame a prit pour compléter. Cette propriété est deltaTime et se trouve dans la classe Time. La propriété est static et public, alors elle est accessible en tout temps, tant que l’API est importé avec using UnityEngine.

Pour un déplacement de 60 unités par seconde, invariable des FPS :

public class MoveComponent : MonoBehaviour
{
    void Update()
    {
        transform.Translate(new Vector3(60,0,0) * Time.deltaTime);
    }
}

Compter le temps réel

Avec une variable, il est possible de mesurer un interval de temps ; il n’est qu’une question d’accumuler les valeurs de Time.deltaTime à chaque Update().

private float elapsedTime;

void Update()
{
    elapsedTime += Time.deltaTime;
}

Supposons un script qui vise faire un Debug.Log(...) par seconde, invariable des FPS avec les valeurs de Time.deltaTime suivantes :

Rendu au 5e frame (frame 4), le temps écoulé a dépassé une seconde et un Debug.Log(...) est fait, mais il y a une erreur de 50 ms. Ces erreurs peuvent s’accumuler rapidement, alors il est important de ne pas repartir le chronomètre à 0s. Aussi, un FPS plus élevé réduit la marge d’erreur, car il y a moins de temps entre chaque frame donc moins de temps avant de réaliser que le temps écoulé ait dépassé une seconde.

public class LogComponent : MonoBehaviour
{
    private float tempsÉcoulé;
    
    void Update()
    {
        tempsÉcoulé += Time.deltaTime;
    
        //faire un log approximativement à chaque seconde
        if (tempsÉcoulé > 1)
        {
            Debug.Log("log");
            tempsÉcoulé -= 1;
        }   
    }
}

Exemple 1 : afficheur de FPS simple

using UnityEngine;

public class DisplayFPSComponent : MonoBehaviour
{
    private float elapsedTime;
    private float nFrames;
    private float fPS;

    void Update()
    {
        elapsedTime += Time.deltaTime;
        nFrames++;
        
        if (elapsedTime > 1) //très improbable d'être exactement 1
        {
            fPS = nFrames / elapsedTime;
            Debug.Log(fPS);
            
            nFrames = 0;
            elapsedTime -= 1;
        }
    }
}

Exemple 2 : translation

public class TranslateComponent : MonoBehaviour
{
    public float displacementPerSecond;
    
    void Update()
    {
        float displacementThisFrame = displacementPerSecond * Time.deltaTime;
    
        //Vector3.right retourne un vecteur (1,0,0) 
        //Space.Self indique d'utiliser l'orientation locale
        transform.Translate(Vector3.right * displacementThisFrame, Space.Self); 
    }
}

L’article suivant est un des plus populaires à propos des sujets de ce chapitre : https://tinyurl.com/qtn63ym