II — Manipulations par scripts
Mettons la position de la caméra à l'origine et faisons-la pivoter pour qu'elle pointe dans la direction de la flèche bleue (l'axe Z) en modifiant son composant de transformation en conséquence : définir la position avec X=0, Y=0, Z=0
et la rotation avec X=0, Y=90, Z=0
.
Créer un premier objet
Ajoutons un cube dans la scène.
Bien que vous puissiez aller dans le menu principal sous GameObject et sélectionner ce que vous souhaitez créer, une méthode plus précise consiste à faire un clic droit sur une partie vide de la hiérarchie et à sélectionner 3D Object → Cube. Cela créera un GameObject cube de base à l'intérieur de notre scène, avec une transformation par défaut. Si nous avions fait un clic droit sur un objet existant dans la scène au lieu d'un espace vide, nous aurions créé un objet enfant qui serait lié à son parent, et ce n'est pas ce que nous voulons dans ce cas.
Si vous double-cliquez sur le nouvel objet Cube dans la hiérarchie, la vue de la scène se rapprochera de celui-c. Si vous sélectionnez à nouveau notre objet caméra, sa vue (ou la vue de jeu) ne montrera pas le cube car la caméra est maintenant à l'intérieur du cube — de la manière dont la plupart des moteurs 3D fonctionnent, les objets sont transparents de l'intérieur.
Déplaçons la boîte à une position où la caméra peut la voir, et essayons également de la rendre plus petite en définissant son composant de transformation à la position X=2, Y=0, Z=0
et échelle X=0.2, Y=0.2, Z=0.2
.
En sélectionnant à nouveau notre caméra, nous pouvons maintenant voir une petite boîte apparaître au centre de sa vue. Si le cube a disparu de notre vue de scène, il suffit de dézoomer un peu ou de double-cliquer à nouveau dessus dans la hiérarchie.
Vous pouvez continuer à jouer avec le composant de transformation du cube pour le déplacer et le redimensionner, ou utiliser les poignées dans la vue de scène : faire glisser l'une des trois flèches ou les carrés au center changera également la transformation en conséquence.
Sur le positionnement... (clicquez pour dérouler)
Il existe de nombreuses autres façons de modifier la transformation avec la souris. Vous pouvez changer les modes de transformation en appuyant sur W, E, R, ou en sélectionnant les différentes icônes dans la petite fenêtre flottante dans la vue de scène pour déplacer, faire pivoter ou redimensionner un objet, respectivement. Consultez la Documentation d'Unity pour plus de détails à ce sujet.
Première animation
Tout ce que nous avons fait jusqu'à présent n'est pas très différent de l'utilisation d'un logiciel de création 3D basique. Là où un moteur de jeu comme Unity se distingue, c'est par sa capacité à scripter les objets que nous plaçons dans les scènes, en utilisant un langage de programmation (C#).
Créer un premier fichier de script
Essayons cela en créant un script qui fera constamment tourner notre cube. La façon la plus simple de le faire est de cliquer sur le bouton Add Component en bas de l'Inspector et de commencer à taper le nom du script que nous voulons créer dans le champ de recherche, appelons-le Rotating
. Il comprendra automatiquement que nous voulons créer un nouveau script, alors sélectionnez cette option et confirmez en appuyant sur Create and Add.
Après un peu d'attente, l'éditeur Unity aura créé un nouveau fichier dans son dossier Assets, que vous pouvez voir dans la vue Projet en cliquant sur le nouveau composant de script dans l'inspecteur du cube.
Nouveau script — C#!
Un double-clic sur le champ Script dans le composant Script du cube, ou sur l'icône du script dans la vue Projet ouvrira un éditeur de texte (Visual Studio probablement) avec le fichier de script, nommé Rotating.cs
, déjà ouvert. Avec l'éditeur, vous pouvez maintenant apporter des modifications au script ! L'éditeur Unity prendra en compte toutes les modifications chaque fois que vous enregistrerez vos changements dans l'éditeur de texte et que vous reviendrez à l'éditeur Unity.
Chaque script que vous créez de cette manière sera un script C#, ou C Sharp, comme on peut le voir àson extension ".cs". Unity créera toujours un nouveau fichier de script avec une structure de base pré-écrite, ressemblant à ceci :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotating : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Les trois premières lignes (commençant par using
) indiquent à Unity quelles bibliothèques logicielles charger avant d'exécuter le code, ce qui lui permet d'accéder, par exemple, aux fonctionnalités et types de données intégrés de Unity, ou à tout autre élément que vous pourriez vouloir inclure.
Le bloc suivant, entouré par les accolades { }
de public class Rotating : MonoBehaviour
, englobe toute la fonctionnalité de ce script, dans le cas le plus simple. Il indique à Unity que ce bloc définit un objet MonoBehavior
, qui est le type d'objet script que l'on peut attacher aux GameObjects dans la hiérarchie de Unity.
À l'intérieur des accolades, il y a déjà les contours de deux fonctions standard que de nombreux scripts utilisent : void Start()
et void Update()
. Comme déjà expliqué par les commentaires au-dessus d'elles (toutes les lignes qui commencent par //
sont ignorées par Unity et sont uniquement là pour que le programmeur puisse les lire), le code à l'intérieur des accolades de Start()
sera exécuté une seule fois, au tout début de chaque exécution du "jeu", tandis que le contenu de Update()
sera appelé chaque fois que l'écran se rafraîchit. Les parenthèses vides ( )
suivant les noms de ces fonctions signifient qu'elles n'acceptent aucune variable d'entrée, et le mot clé void
avant leurs noms signifie qu'elles ne retournent rien non plus.
Exécution du script
Comme tout ce code est encore fondamentalement vide, l'exécuter ne changera rien — vous pouvez essayer cela en appuyant sur le bouton Play en haut au milieu de l'éditeur Unity, ce qui compilera tous les scripts que vous avez pu modifier, passera à la vue de jeu (la vue de notre caméra principale) et exécutera tous les scripts qui sont attachés aux GameObjects dans la hiérarchie.
Comme prévu, la vue de jeu montre la perspective de la caméra principale sur un cube statique. Vous pouvez toujours accéder à l'inspecteur du cube en cliquant dessus dans la hiérarchie, où vous pouvez apporter des modifications à sa transformation en cliquant et en faisant glisser les coordonnées souhaitées, par exemple.
Quitter le mode Play en appuyant à nouveau sur le bouton Play réinitialisera toutes les modifications que nous avons apportées dans la hiérarchie et l'inspecteur à leur état d'origine, et affichera à nouveau la vue de scène par défaut.
Modifier le script — rotation constante autour d'un axe fixe
Modifions le script Rotating.cs
afin qu'il fasse réellement quelque chose lorsque le jeu s'exécute.
Ce que nous voulons, c'est changer quelque chose concernant la transformation du cube chaque fois que l'écran se rafraîchit. Cela peut être accompli en ajoutant une ligne de code à l'intérieur de la fonction void Update()
donnée, de sorte qu'elle ressemble à ceci :
Update()
appelle maintenant la fonction Rotate() du composant transform
du GameObject avec les paramètres Vector3.up
pour l'axe de rotation, et 30.0f * Time.deltaTime
pour l'angle de rotation — pour chaque image que le jeu exécute. Si vous tapez cela vous-même dans l'éditeur de code au lieu de copier et coller, vous pourriez voir la complétion de code se déclencher, ce qui offre des suggestions et des explications sur chacun de ces champs.
Vector3.up
est une abréviation pour un vecteur tridimensionnel qui pointe directement vers le haut, et Time.deltaTime
est le temps qui s'est écoulé depuis la dernière image avant celle en cours. 30.0f
est simplement une convention d'écriture des nombres à virgule f
lottante, par opposition aux entiers. Si nous enregistrons ce fichier de script, retournons dans Unity et appuyons sur le bouton Play, nous verrons à nouveau la vue de la caméra dans la vue de jeu, avec le cube tournant régulièrement autour de son axe vertical, à 30 degrés par seconde.
Rendre le script interactif
Pour l'instant, cette rotation est fixe — bien que vous puissiez toujours modifier manuellement la transformation dans la vue de jeu comme ci-dessus, le cube continue de tourner. Vous pouvez essayer de le déplacer ou de le faire pivoter autour des axes X ou Z pour voir sa rotation automatique changer de direction par rapport au monde, mais rester constante par rapport au cube.
Pour rendre la rotation automatique plus interactive, nous pouvons modifier notre script pour inclure des variables, qui doivent être placées en dehors de la méthode Update()
, mais toujours à l'intérieur des accolades de la classe Rotating
. Modifions ces parties du script pour qu'elles ressemblent à ceci :
public class Rotating : MonoBehaviour
{
[Tooltip("Rotation angle per second")] public float speed = 30.0f;
[Tooltip("Vector of the rotation axis")] public Vector3 axis = Vector3.up;
// Update is called once per frame
void Update() {
transform.Rotate(axis, speed * Time.deltaTime);
}
}
Les variables sont introduites en indiquant d'abord leur type, puis leur nom, comme float speed
— un nombre à virgule flottante que nous référencions par le nom "speed". Si nous voulons rendre une variable accessible à des scripts en dehors de son scope (le fichier MonoBehaviour "Rotating" dans ce cas), nous devons les préfixer avec public
. De cette manière, nous pouvons également voir directement leur valeur actuelle dans l'inspecteur de Unity, en tant que paramètres du composant de script du cube.
Il est souvent nécessaire, et généralement une bonne pratique, d'initialiser de nouvelles variables avec une certaine valeur par défaut, qui est assignée avec le signe égal =
et une valeur qui suit. Ici, nous pouvons leur donner exactement ce que nous avions précédemment mis dans les paramètres de la fonction Rotate()
: 30.0f
pour speed
, et Vector3.up
pour axis
.
Enfin, nous devons remplacer les valeurs fixes dans l'appel à transform.Rotate()
par nos nouvelles variables : transform.Rotate(axis, speed * Time.deltaTime);
le fait. En option, nous pouvons rendre ces paramètres, qui sont maintenant exposés dans l'inspecteur, encore plus lisibles en ajoutant des infobulles dans le format montré ci-dessus, avant les définitions public
des variables.
Essayez-le : enregistrez le script, retournez dans l'éditeur Unity, appuyez sur Play. Vous voyez la même rotation se produire, mais vous pouvez maintenant modifier directement les paramètres de rotation de la même manière que vous pouvez interagir avec la transformation du cube !
Édition en ligne/hors ligne des valeurs
Comme auparavant, les modifications dans l'inspecteur qui se produisent pendant que le jeu est en cours d'exécution seront réinitialisées à leurs valeurs par défaut lorsque vous arrêtez, mais resteront si vous les effectuez lorsque le jeu n'est pas en cours. Si vous changez l'axe ou la vitesse de rotation lorsque le jeu est arrêté, les nouvelles valeurs remplaceront celles que vous avez comme valeurs initiales dans le code. Si vous revenez au code et que vous mettez de nouvelles valeurs initiales et enregistrez, celles-ci écraseront tout ce qui est actuellement défini dans l'inspecteur.
Avertissement
Assurez-vous toujours que le jeu n'est pas en cours d'exécution lorsque vous apportez des modifications que vous souhaitez conserver, en particulier lors de l'ajout ou de la suppression d'objets et de composants !
Combiner des animations
Un deuxième script pour le déplacer
Pour pratiquer davantage l'animation d'un objet, créons un nouveau script pour le cube comme nous l'avons fait précédemment, en ajoutant un nouveau composant de script et en le nommant Moving
. Comme auparavant, nous pouvons conserver la structure pré-écrite et simplement y ajouter des éléments.
Avec le nouveau script, nous voulons déplacer le cube entre deux points spécifiés dans l'espace, à une certaine vitesse. Ajoutons ces variables au MonoBehaviour Moving
, de manière similaire à ce que nous avons fait ci-dessus :
public class Moving : MonoBehaviour
{
[Tooltip("Units per second")] public float speed = 1.0f;
[Tooltip("Starting position in 3D space")] public Vector3 startPoint = new Vector3(2, 0, -1);
[Tooltip("End position in 3D space")] public Vector3 endPoint = new Vector3(2, 0, 1);
// Update is called once per frame
void Update() {
}
}
La principale différence réside dans la façon dont nous définissons maintenant de nouveaux vecteurs pour startPoint
et endPoing
: nous les initialisons avec leurs coordonnées. Enregistrez ce code et assurez-vous que ce nouveau composant apparaît dans l'inspecteur du cube avec les valeurs correctes que nous lui avons données dans le code. Sinon, vous pouvez changer la vitesse et les points pour leurs valeurs directement là.
Interpolation
Pour se déplacer entre ces deux points, nous devons interpoler entre eux au fur et à mesure que le temps passe. Pour cela, ajoutons une autre variable qui suivra la position intermédiaire entre les deux points où le cube devrait se trouver. Cette fois, nous la garderons private
— elle ne sera pas accessible par des scripts externes, car nous voulons uniquement la modifier en interne. Nous pouvons cependant afficher sa valeur dans l'inspecteur en ajoutant une ligne au-dessus de sa déclaration pour en faire un "serialized field:"
public class Moving : MonoBehaviour
{
[Tooltip("Units per second")] public float speed = 1.0f;
[Tooltip("Starting position in 3D space")] public Vector3 startPoint = new Vector3(2, 0, -1);
[Tooltip("End position in 3D space")] public Vector3 endPoint = new Vector3(2, 0, 1);
[SerializeField]
private float interpolator = -1.0f;
// Update is called once per frame
void Update() {
}
}
Nous le définissons initialement à -1
et voulons l'augmenter constamment. Cela est accompli par l'opération +=
dans la boucle Update()
: nous prenons sa valeur actuelle et y ajoutons le temps écoulé depuis la dernière image multiplié par la vitesse, comme auparavant.
Lorsqu'il atteint ou dépasse 1
, nous le réinitialiserons à -1
, afin qu'il boucle indéfiniment entre ces valeurs. Cela se fait en le testant à l'intérieur d'une instruction if()
: si le contenu de ses parenthèses ()
est vrai (l'interpolateur est-il supérieur ou égal à un ?), alors le contenu de ses accolades {}
est exécuté : (réinitialiser l'interpolateur à -1.
public class Moving : MonoBehaviour
{
[Tooltip("Units per second")] public float speed = 1.0f;
[Tooltip("Starting position in 3D space")] public Vector3 startPoint = new Vector3(2, 0, -1);
[Tooltip("End position in 3D space")] public Vector3 endPoint = new Vector3(2, 0, 1);
[SerializeField]
private float interpolator = -1.0f;
// Update is called once per frame
void Update() {
interpolator += speed * Time.deltaTime;
if (interpolator >= 1)
{
interpolator = -1;
}
}
}
Pour traduire cela en un mouvement pour le cube, nous avons besoin que cet interpolateur affecte la partie position
de sa transformation. Pour cela, nous pouvons utiliser la fonction Lerp() de Vector3
: elle prend un point de départ et un point d'arrivée, ainsi qu'une valeur entre zéro et un qui lui permet de calculer un nouveau point entre les deux.
Puisque notre interpolateur va de -1 à 1, nous devons le modifier légèrement avant de le donner à cette fonction Lerp()
, ce que nous pouvons faire directement à l'intérieur de son champ de paramètres :
public class Moving : MonoBehaviour
{
[Tooltip("Units per second")] public float speed = 1.0f;
[Tooltip("Starting position in 3D space")] public Vector3 startPoint = new Vector3(2, 0, -1);
[Tooltip("End position in 3D space")] public Vector3 endPoint = new Vector3(2, 0, 1);
[SerializeField]
private float interpolator = -1.0f;
// Update is called once per frame
void Update() {
interpolator += speed * Time.deltaTime;
if (interpolator >= 1)
{
interpolator = -1;
}
transform.position = Vector3.Lerp(startPoint, endPoint, interpolator < 0 ? -interpolator : interpolator);
}
?: ou Opérateur conditionnel ternaire
La structure sous la forme x < y ? a : b
est un raccourcipratique : nous testons si x
est inférieur à y
(ou toute autre comparaison dont nous avons besoin ici), et retournons a
si c'est vrai et b
si c'est faux.
Ici, nous donnons à la fonction Lerp()
le négatif de interpolator
s'il est inférieur à zéro, le rendant ainsi à nouveau positif. Si interpolator
est déjà positif, il est directement donné à Lerp()
. Entrez ce code, enregistrez-le et exécutez le jeu :
Interplay
Vous pouvez maintenant voir le cube se déplacer d'un côté à l'autre, tout en continuant à tourner grâce à son script Rotating
! Comme auparavant, vous pouvez jouer avec les paramètres pour changer la vitesse (à la fois de la rotation et de la translation) ainsi qu'avec les points de départ et d'arrivée. Vous pouvez également désactiver l'un des deux scripts à tout moment pendant le jeu en cliquant sur leur case à cocher, ce qui figera leur exécution et les empêchera d'influencer le cube.
Connecter des objets
Au lieu de cette rotation folle, faisons en sorte que ce cube soit toujours orienté vers la caméra.
Regarder vers
Tout d'abord, désactivez le composant Rotating
tout en étant en dehors du mode de jeu, afin que ce changement soit pris en compte. Ensuite, ajoutons deux lignes à notre script Moving
:
public class Moving : MonoBehaviour
{
[Tooltip("Units per second")] public float speed = 1.0f;
[Tooltip("Starting position in 3D space")] public Vector3 startPoint = new Vector3(2, 0, -1);
[Tooltip("End position in 3D space")] public Vector3 endPoint = new Vector3(2, 0, 1);
[Tooltip("Object to face")] public Transform targetObject;
[SerializeField]
private float interpolator = -1.0f;
// Update is called once per frame
void Update() {
interpolator += speed * Time.deltaTime;
if (interpolator >= 1)
{
interpolator = -1;
}
transform.position = Vector3.Lerp(startPoint, endPoint, interpolator < 0 ? -interpolator : interpolator);
transform.LookAt(targetObject);
}
}
Nous lui avons maintenant donné une nouvelle variable (targetObject
), cette fois un Transform
. Cela nous permet de référencer le transform de tout autre GameObject, et donc, par exemple, sa position. La nouvelle dernière ligne dans la boucle Update()
utilise la méthode LookAt() de la classe transform
, qui orientera le transform de l'objet de jeu actuel pour faire face à la direction d'un autre transform que nous lui donnons (regarder dans sa direction...).
Référencer des objets
Notez que nous n'avons pas initialisé cette variable dans le code ; nous allons plutôt l'assigner depuis l'inspecteur. Une façon de le faire est de cliquer sur l'icône de cible qui se trouve maintenant dans le nouveau champ de variable dans l'inspecteur du cube et de sélectionner l'objet Main Camera dans la liste qui apparaît, ou simplement de faire glisser la Main Camera depuis la hiérarchie dans le champ :
Nous pouvons voir le cube se déplacer d'un côté à l'autre et toujours faire face à la caméra si nous lançons le jeu, mais pour afficher ce nouveau comportement plus clairement, nous pouvons organiser notre éditeur Unity pour montrer les vues de scène et de jeu en même temps afin d'avoir une vue externe. Changer les points de départ et d'arrivée, ou la position de la caméra n'empêchera pas la méthode lookAt
d'ajuster correctement la position du cube :
Si tout fonctionne et que vous avez compris chaque partie, vous devriez maintenant maîtriser les bases de Unity, félicitations !
N'oubliez pas, si quelque chose n'est pas clair ou ne fonctionne pas : n'hésitez pas à nous demander de l'aide.