Mise en situation

B.B.B. se prépare à développer leur premier jeu 3D avec Unity. Par contre, aucun prototype n’existe, à ce point. Ayant saisie les éléments essentiels du moteur Unity, votre équipe se proponse pour développer un prototype qui démontre les transformations 3D.

Vous avez la tâche de tester les fonctionnalités de transformation dans l’espace, afin de générer des animations et des effets. Vous devez créer trois composantes, chacune touchant une sorte de transformation (rotation, homothétie, translation) selon le FPS de la simulation. Une deuxième version de chaque composante devra aussi être conçue, mais ces versions devront transformer selon le temps réel.

À propos des Vector3

Vous pouvez multiplier un Vector3 par un scalaire à l’aide de l’opération *. Ceci retourne un nouveau vecteur. Vous pouvez aussi soustraire et additionner des Vector3 ensemble avec les opérateur - et + respectivement. Ces opérations retourne également un nouveau vecteur.

L’objet GameManager

Créez une objet de jeu dans la scène avec le nom GameManager. Cet objet possède la tâche de gérer le jeu dans son ensemble. Cette tâche inclut contrôller la valeur de FPS cible.

Attachez le script suivant à cet objet :

using UnityEngine;

//Avertissement : il est insensé d'avoir plus qu'un GO avec ce script
public class FrameRateControllerComponent : MonoBehaviour
{
    public int targetFrameRate = 60;
    
    void Awake()
    {
         QualitySettings.vSyncCount = 0;
         Application.targetFrameRate = targetFrameRate;
    }
}

Dans l’inspecteur, il est possible de changer la valeur de targetFrameRate. Par défaut, la valeur est de 60, ce qui implique une simulation qui cible rendre 60 FPS.

SpinComponent

Cette composante doit faire tourner un GameObject sur lui-même en fonction du FPS.

Attributs modifiables dans l’inspecteur

  • Vector3 rotationPerFrame : vecteur qui représente la rotation à faire par frame. Chaque composante du vecteur exprime une rotation en degré sur son axe respectif;
  • Space space : indique si la rotation doit se faire selon les axes de l’espace locale de l’objet ou selon les axes de l’espace du monde. La valeur peut être Self ou World.

Comportements

En utilsant la composante Transform du GameObject possédant ce script, utilisez la méthode Rotate(Vector3, Space) accessible aux instances de Transform pour faire tourner le GameObject. Le GameObject doit tourner à chaque frame de la quantité donnée par rotationPerFrame.

Tests

  1. Fonctionalité
    • targetFrameRate : 60
    • rotation : \((0,0,0)\)
    • rotationPerFrame : \((1,0,0)\)
    • space : Self
  2. Deux fois moins vite
    • targetFrameRate : 30
    • rotation : \((0,0,0)\)
    • rotationPerFrame : \((1,0,0)\)
    • space : Self
  3. World
    • targetFrameRate : 60
    • rotation : \((0,0,90)\)
    • rotationPerFrame : \((1,0,0)\)
    • space : World
  4. Self
    • targetFrameRate : 60
    • rotation : \((0,0,90)\)
    • rotationPerFrame : \((1,0,0)\)
    • space : Self

SpinComponentV2

Cette composante doit faire tourner un GameObject sur lui-même en fonction du temps réel.

Attributs à changer

  • Vector3 rotationPerFrameVector3 rotationPerSecond : vecteur qui représente la rotation à faire par seconde. Chaque composante du vecteur exprime une rotation en degré sur son axe respectif;

Tests

  1. Fonctionalité
    • targetFrameRate : 60
    • rotation : \((0,0,0)\)
    • rotationPerSecond : \((60,60,60)\)
    • space : Self
  2. Autant de rotation, mais deux fois moins de frames
    • targetFrameRate : 30
    • rotation : \((0,0,0)\)
    • rotationPerSecond : \((60,60,60)\)
    • space : Self

ScalePulsateComponent

Cette composante doit faire varier l’échelle local du GameObject en fonction du FPS.

Attributs modifiables dans l’inspecteur

  • float maxDeltaScale : indique le delta maximum entre l’échelle originale et celui résultant des transformations. Par exemple, si le GameObject a un scale de \((2,2,2)\) et que maxDeltaScale est de 3, la composante doit faire varier le _scale entre \((2,2,2)\) et \((5,5,5)\);
  • float stepPerFrame : indique la valeur à ajouter ou enlever à l’échelle du GameObject par frame.

Comportements

Utilisez la propriété localScale accessible aux instances de Transform pour varier la taille du GameObject. Le GameObject doit agrandir ou rappetisser sur chaque axe à chaque frame de la quantité donnée par stepPerFrame. Une fois que la taille maximale est atteinte, le GameObject doit revenir à sa taille originale. Une fois rendu à ça taille originale, le GameObject doit recommencer à prendre de l’expansion. Par contre, il ne faut pas que la taille maximale soit dépassée, peu importe le stepPerFrame donné.

Par exemple, supossons que maxDeltaScale est de 2, que le scale original du GameObject est de \((1,1,1)\) et que stepPerFrame est de 1,5. Au premier Update(), l’échelle du GameObject augmente à \((2.5,2.5,2.5)\). Au prochain Update(), il ne faut pas que l’échelle deviennent \((4,4,4)\) mais plutôt \((3,3,3)\). L’échelle du GameObject ne doit pas baisser en dessous de sa valeur originale, semblablement. En autre mot, les valeurs d’échelle sont « serrées » (clamped).

Aussi, tenez pour acquis que l’utilisateur ne donne jamais une valeur négative pour maxDeltaScale et stepPerFrame.

Tests

  1. Fonctionalité simple
    • targetFrameRate : 1
    • maxDeltaScale : 2
    • stepPerFrame : 1
  2. Fonctionalité « serrer »
    • targetFrameRate : 1
    • maxDeltaScale : 2
    • stepPerFrame : 1.5

Nombre de lignes de code

La solution de l’enseignant a 9 lignes de code dans Update et aucun raccourci mystérieux hors de vos connaissances. Ce code contient qu’un seul if. Trouvez une solution et ensuite optimisez là pour viser qu’un seul if.

ScalePulsateComponentV2

Cette composante doit faire varier l’échelle local du GameObject en fonction du temps réel.

Attributs à changer

  • float stepPerFramefloat stepPerSecond : indique la valeur à ajouter ou enlever à l’échelle du GameObject par seconde.

Tests

  1. Fonctionalité simple
    • targetFrameRate : 60
    • maxDeltaScale : 3
    • stepPerSecond : 1
  2. Fonctionalité « serrer »
    • targetFrameRate : 60
    • maxDeltaScale : 3
    • stepPerSecond : 1.5
  3. Plus grand FPS, même vitesse
    • targetFrameRate : 120
    • maxDeltaScale : 3
    • stepPerSecond : 1.5

WaveMotionComponent

Cette composante doit faire une translation sur le GameObject avec un mouvement « en vague » en fonction du FPS. La formule \(\y = amplitude * sin(valeurRadians)\) risque d’être pertinente (utilisez Mathf.Sin(...)).

Attributs modifiables dans l’inspecteur

  • float amplitude : indique l’amplitude de la vague;
  • float radStepPerFrame : indique la quantité de radians à augmenter par frame pour valeur utilisée dans le sin;
  • Space space : indique si la translation doit utiliser l’orientation locale ou non.

Comportements

Les objets avec un WaveMotionComponent doivent se déplacer sur l’axe \(y\) (local ou monde). Car le mouvement est déterminé par la fonction sin, l’objet commence au centre de son déplacement et monte d’amplitude nombre d’unités. Ensuite, il descend d’amplitude * 2 d’unités. Finalement, il remonte et le cycle continue.

Par exemple, si un cube est placé à une position locale \((0,1,0\)) est donné un WaveMotionComponent avec valeurs d’amplitude : 3 et space : world, les positions possibles pour le cube sont entre \((0,-2,0)\) et \((0,4,0)\) inclusivement.

Si radStepPerFrame est de \(\frac{\pi}{2}\), l’objet se déplacera de 2 unités par frame :

Frame :

  1. \(sin(0) = 0 → \Delta y = 3sin(0) = 0 → position locale = (0,1,0)\)
  2. \(sin(\frac{\pi}{2}) = 1 → \Delta y = 3sin(\frac{\pi}{2})) = 3 → position locale = (0,4,0)\)
  3. \(sin(\pi) = 0 → position locale = (0,1,0)\)
  4. \(sin(\frac{3\pi}{2}) = -1 → position locale = (0,-2,0)\)

Le cercle trigonométrique suivant peut vous servir de référence :

Utiliser la méthode Translate(Vector3, Space) disponible aux instances de Transform.

Tests

  1. Fonctionalité
    • targetFrameRate : 1
    • amplitude : 3
    • radStepPerFrame : 3.14159/2
    • space : World
  2. Self
    • targetFrameRate : 60
    • rotation : \((45,45,45)\)
    • amplitude : 3
    • radStepPerFrame : 0.1
    • space : Self

Nombre de lignes de code

La solution de l’enseignant n’a que 3 lignes dans Update. Visez pour un nombre semblable.

WaveMotionComponentV2

Cette composante doit faire une translation sur le GameObject avec un mouvement « en vague » en fonction du temps réel.

Attributs à changer

  • float radStepPerFramefloat radStepPerSecond : indique la quantité de radians à augmenter par seconde pour la valeur utilisée dans le sin.

Tests

  1. Fonctionalité
    • targetFrameRate : 60
    • amplitude : 3
    • radStepPerSecond : 3.14159/2
    • space : World
  2. Fonctionalité combo
    • targetFrameRate : 60
    • amplitude : 3
    • radStepPerSecond : 3.14159/2
    • space : Self
    • Activer les 2 autres composantes de transformations en même temps

Démos