Académique Documents
Professionnel Documents
Culture Documents
DES JEUX
VIDEOS EN
C# AVEC UNITY 3D
MARS 2020
1
A propos
Les jeux attirent un public de plus en plus large, et séduisent les
développeurs. Cependant les jeux vidéo demandent beaucoup plus
d’investissement en termes de connaissances théoriques et pratiques
accès avancer que la plupart des codeurs n’ont pas.
Toutefois, pour ceux qui sont bien conscients de tout cela, coder un
jeu vidéo ne se révèle tout de même pas une partie de plaisir, et beaucoup
se perdent dans la jungle du développement de jeux.
2
PLAN DU COURS
A propos ....................................................................................................................................2
PLAN DU COURS.......................................................................................................................3
0. INTRODUCTION ................................................................................................................5
0.1. Organigramme des métiers du jeu vidéo .......................................................................6
0.2. Le processus de développement d’un jeu vidéo .............................................................6
0.3. Les différentes étapes du développement d’un jeu vidéo ...............................................6
0.3.1.La validation du projet ..............................................................................................7
0.3.2. L’élaboration de la bible de production du jeu .......................................................7
0.3.3.La réalisation d’une maquette ...................................................................................7
0.3.4. La production du jeu ..............................................................................................7
0.3.5.La phase de tests et de debug ....................................................................................7
0.3.6. La localisation du jeu ..............................................................................................8
0.3.7. La préparation de la version Gold ..........................................................................8
0.3.8. La fabrication du jeu ...............................................................................................8
0.3.9. La distribution .........................................................................................................8
0.4. Choix de la modèle économique ...................................................................................8
CHAPITRE 1 : NOTION DE BASES SUR LE LANGAGE C# ........................................................ 10
1.1. Présentation de langage de programmation c# ................................................................ 10
1.2. Avantages de .NET par rapport aux autres plateformes .................................................... 11
1.3. Programmation en console de type élémentaire .............................................................. 12
1.4. Programmation orientée objet (C#) .................................................................................24
1.4.1. Classes et objets ...........................................................................................................24
Membres de classe .......................................................................................................25
1.4.2. Méthodes ....................................................................................................................26
1.4.3. Constructeurs ..............................................................................................................26
1.4.4. Finaliseurs ................................................................................................................27
1.4.5. Événements .................................................................................................................27
1.4.6. Classes imbriquées ....................................................................................................27
1.4.7. Modificateurs d’accès et niveaux d’accès .....................................................................28
1.4.8. Instanciation de classes .............................................................................................28
1.4.9. Classes et membres statiques ........................................................................................29
1.4.10. Types anonymes ......................................................................................................29
1.4.11. Héritage .................................................................................................................. 30
1.4.12. Interfaces.................................................................................................................. 31
1.4.13. Génériques ............................................................................................................... 31
1.4.14. Délégués ..................................................................................................................32
1.4.15. Afficher votre programme avec la méthode principale .............................................33
Les énumérations..............................................................................................................43
3
Sauf que ce n'est pas une classe (mot clé "class") que nous voulons, mais une énumération
(mot clé "enum"), nous enlevons donc "class" que nous remplaçons par "enum", ce qui nous
donnera l'extrait de code suivant : .......................................................................................44
1.5. Premier Exercice ...............................................................................................................45
1.6. Deuxième Exercice ........................................................................................................... 51
1.7. Travail Pratique ...........................................................................................................53
CHAPITRE 2 : PRESENTATION DE L’UNITY............................................................................54
2.1. Présentation .......................................................................................................................54
2.2. Historique .........................................................................................................................55
2.3. Plateformes supportées......................................................................................................55
2.3. Fonctionnement ................................................................................................................57
2.4. Installation de Unity ..........................................................................................................57
2.5. Exemple sur le 2D ............................................................................................................58
Etape 6 : Le script pour contrôler le robot .......................................................................72
1.8. Travaux Pratique ............................................................................................................. 118
CHAPITRE 3 : INTRODUCTION A LA TROSIEME DIMENSION (3D) .................................... 119
3.1. Les concepts de base ....................................................................................................... 119
Norme d'un vecteur ........................................................................................................... 125
3.2. Travaux Pratique ....................................................................................................... 128
CHAPITRE 4 : EN ROUTE VERS LA BASE D’UNITY 3D .......................................................... 130
4.1. Lancer Unity 3D et créer un nouveau projet ................................................................ 130
Sauvegarder votre travail : projet et scenes. ....................................................................... 130
4.2. L’éditeur d’Unity 3D ................................................................................................... 130
4.3. Créer une scène simple ................................................................................................ 132
4.4. Animer un objet de la scène ........................................................................................ 133
4.5. Gestion des interactions .............................................................................................. 134
4.6. Programmation d’interaction simple avec la souris ...................................................... 135
6.7. Navigation en ré-utilisant des scripts d’interaction clavier/souris ................................. 136
4.8. Création dynamique d’objets ...................................................................................... 136
4.9. Charger une géométrie ................................................................................................ 137
4.10. Créer un exécutable pour la scène ............................................................................. 138
4.11. TP................................................................................................................................... 139
Conclusion ............................................................................................................................. 139
Bibliographie .......................................................................................................................... 140
4
0. INTRODUCTION
La création d’un jeu vidéo exige plusieurs tâches et plusieurs disciplines, Que
ça soit sur console de salon, PC, console portable, ou téléphone, de nos jours, tout
le monde joue à des jeux vidéo et beaucoup ont déjà rêvé de créer le leur. Nous
allons voir comment réaliser notre jeu étape par étape. Sans paraître pessimiste,
les jeux ne sont pas conçus par une seule personne, car il faut définir les rôles de
chaque personne à savoir :
Cette liste évolue avec le temps, mais dicte les actions de l’équipe en charge
du projet sans risquer de fausses routes.
5
0.1. Organigramme des métiers du jeu vidéo
Game Level
Designer Designer Animateur Prgm Prgm Prgm
moteur GamePlay Spécialisé
Il faut du temps, de l’argent. Lorsqu’un nouveau jeu vidéo fait son apparition en
boutique, il faut savoir que pour être arrivé jusque-là, il est passé par un certain nombre
d’étapes dont voici le détail ci-dessous.
6
Le développement d’un jeu est parfois long et fastidieux, voici les étapes de
développement, de la naissance de l’idée à la vente en magasin :
Cette étape consiste à définir si le projet est réalisable. Il faut prendre en compte
le budget financier, les ressources humaines, les points forts et les points faibles du
projet, les contraintes et les limites technologiques… Toute l’équipe travaille main dans
la main durant cette étape. Tout le monde donne son avis et lorsqu’un accord est trouvé,
l’étape suivante débute.
Cette étape consiste à vérifier le projet est réellement réalisable. Une petite
équipe réalise donc une partie du jeu (un circuit, un niveau…) et vérifie les mécaniques
de jeu, la cohérence de l’univers… Cette étape est décisive car elle permet de savoir s’il
faut poursuivre le projet ou non, de plus, grâce à cette maquette il sera possible de
chercher des moyens de financements et des éditeurs.
Une fois le financement débloqué et un éditeur près à vous faire confiance, vous
passez à la phase de production qui consiste à produire le jeu dans ses moindres détails.
Plus la bible de production est détaillée et plus le travail en sera facilité. Il faut qu’à ce
stade la bible de production soit parfaite car l’équipe en charge du projet n’a plus le
temps de la corriger ou la modifier. Le moindre écart et les conséquences peuvent être
lourds surtout au niveau du budget financier. Il faut également savoir que s’il y a des
financeurs au projet, ils voudront très certainement surveiller la progression du projet.
Il faut beaucoup communiquer avec l’éditeur dans cette étape car sans sa validation, le
projet peut finir mort-né.
Voici l’une des phases les plus difficiles de la production. Une fois le projet terminé, il
faut effectuer différents tests et debug afin de corriger les bugs et de peaufiner au
maximum le jeu. Ces bêtas tests sont réalisés chez les développeurs, chez les éditeurs
et parfois même par le public.
7
0.3.6. La localisation du jeu
Un jeu congolais qui ne sortira que sur le sol congolais n’a pas besoin de cette
étape mais si le jeu sort à l’international, alors il faut le traduire dans les différentes
langues visées. Dans cette étape il faut également prendre en compte les restrictions au
niveau des pays. Par exemple la violence doit être censurée en Australie ou en
Allemagne dans certains cas.
Lorsqu’un jeu passe « Gold », cela veut tout simplement dire qu’il est
entièrement fini et qu’il peut passer à la phase suivante.
Durant cette étape le jeu est fabriqué, c’est-à-dire qu’il est réalisé pour être vendu
en magasin. Il faut donc graver les DVD (ou Blu-Ray), imprimer les jaquettes,
fabriquer les boîtes…
0.3.9. La distribution
L’éditeur va distribuer le jeu dans les chaînes de magasins afin qu’il soit mis en
vente. Quand le jeu est prêt, il faut le dévoiler au public en s’appuyant sur la
communication pour qu’un maximum de personnes soit au courant de l’existence et de
la sortie du jeu vidéo.
Il y a différents canaux de distribution possibles. Je ne parlerais ici que de la
distribution dématérialisée qui est de plus en plus utilisée par un choix collectif des
joueurs. Des plateformes dédiées existent afin de faire connaitre le jeu aux utilisateurs
de celle-ci mais, distribuer son propre produit par le biais de son propre site est aussi un
bon moyen.
8
Abonnement.
Publicité : placement de produits par le biais d’objets en jeu (voiture,
consommables, ...), panneaux publicitaires intégrés dans le décor ou dans le
menu du jeu, mais aussi des pubs visibles sur le site web du jeu.
Cette liste est non exhaustive puisque ce n’est pas le but de ce cours, mais il en
reste (le financement participatif, la vente de produits dérivés, démo de jeu payant à
partir d’un certain niveau).
9
CHAPITRE 1 : NOTION DE BASES SUR LE LANGAGE C#
10
1.2. Avantages de .NET par rapport aux autres plateformes
Avant d'utiliser les possibilités offertes par les classes et les objets en C#,
apprenons à utiliser et exécuter des applications simples C# ne nécessitant pas la
construction de nouveaux objets. Comme C# est un langage entièrement orienté objet,
un programme C# est composé de une ou plusieurs classes, nous nous limiterons ici à
une seule classe mais la notion des classes sera exploités dans le point 1.4 ci-dessous.
Nom Description
Bool Un type qui peut prendre deux valeurs: true ou false (vrai ou faux)
decimal Un type codé sur 16 octets qui peut contenir des nombres décimaux
Deux fois plus petit que decimal (8 octets). Peut contenir des nombres
Double
décimaux.
Deux fois plus petite que double (4 octets). Peut contenir des nombres
Float
décimaux mais avec une précision moindre que celle d'un double
13
Nom Description
Deux fois plus grand que int (8 octets). Contient des nombres entiers
Long
positifs ainsi que des entiers négatifs. Est équivalant à Int64.
Un byte signé. Cela veut dire que le valeur de la variable peut être
Sbyte
positive ou négative.
Un int non-signée. Cela signifie que la valeur ne peut être que positive.
Uint
En revanche, des nombres deux fois plus grand sont disponibles.
Les noms énoncés au-dessus sont en fait les alias vers des types ayant des noms
plus complexes, par exemple "int" va être la même chose que "Int32", short va être la
même chose que "Int16", l'utilisation du "vrai" type ou de son alias revient au même,
mais il faut savoir que ça existe car il arrive qu'on croise le "vrai" type au lieu de l'alias.
J'y venais justement. Tout d'abord pour taper du code, il faut bien entendu
entrer dans un fichier de code. Dans l'immédiat nous allons écrire dans le fichier
"Program.cs" créé automatiquement par Visual Studio lors de la création de notre
projet. Chaque fichier de notre solution (un genre de conteneur pour rassembler un ou
plusieurs projets), se trouve dans ... l'explorateur de solution (Solution Explorer pour
les versions anglophones).
Celui se trouve généralement sur la partie droite de Visual Studio (il peut bien
sur être déplacé où vous voulez, même en dehors de la fenêtre de Visual Studio), si
vous ne le voyez pas, allez dans le menu "Affichage" ("View") puis "Explorateur de
solution" ("Solution explorer") ou faites les combinaisons de touches "Ctrl+W,
S". Une fois que vous voyez bien votre explorateur de solution, double-cliquez sur
"Program.cs" pour ouvrir le fichier dans la partie centrale de Visual Studio. A ce
niveau-là, vous devriez vous trouver en face de ce code si vous avez gardé le même
fichier que tout à l'heure :
14
using System;
namespace PremierProgramme
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello Word");
}
}
}
Ou de celui-là si vous avez créé un nouveau projet :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace PremierProgramme
{
class Program
{
static void Main(string[] args)
{
}
}
}
Nous pouvons faire ainsi avec n'importe-quel type. La seule différences résidera
dans les caractères (char) dont la valeur sera placée entre simple quotes ( ' ) et les
chaines de caractères (string) dont la valeur sera placée entre doubles quotes ( " )
comme dans l'exemple suivant :
char caractere = 'c';//une variable de type caractère (char) dont on affecte
la valeur grâce à un caractère placé entre simples quotes
string chaine = "une chaine"; //une chaine de caractère délimitée par des do
ubles quotes
17
string chaine = "salut";
string entierEnChaine = entier.ToString();//1 devient "1"
string flottantEnChaine = flottant.ToString();// 3.5 devient "3.5"
string boolEnChaine = booleen.ToString();// true devient "True"
string caractereEnChaine = caractere.ToString(); // 'c' devient "c"
string chaineEnChaine = chaine.ToString(); // "salut" devient... "salut"
Pour un long Vous voyez que pour un même nombre, la place occupée peut aller
du simple au double (et même du simple à l'octuple). Donc gardez bien à l'esprit que
même si aujourd'hui ces tailles de variables sont insignifiantes par rapport aux tailles
de mémoires vives disponibles (en comparaison, un ordinateur neuf aujourd'hui
"milieu/haut de gamme" avec 4 Go de RAM peut théoriquement contenir à peu près
500 millions de "long" simultanément), il est important de rester conscient de la taille
prise pas nos variables en mémoire et de pourvoir l'optimiser.
En effet, des tas de développeurs utilisent systématiquement des int (4 octets)
pour des entiers et des doubles (8 octets) pour des flottants, alors qu'honnêtement on
n’a pas souvent besoin d'avoir une variable pouvant aller de -2 147 483 648 à 2 147 483
647, vous pouvez généralement très bien vous en sortir avec un short (2 octets) et même
un byte (1 octet) parfois.
L'optimisation maximale de votre mémoire vive, car en effet, l'optimisation ne
concerne pas seulement la mémoire vive ! Il faut que vous sachiez que, globalement,
un int se modifie en mémoire plus rapidement que les autres types d'entiers, donc pour
18
un entier qui doit changer souvent de valeur, nous préférons un int et nous nous
attachons à l'optimisation de nos types seulement lorsqu'un très grand nombre de
variable est utilisé (par exemple dans le cas de la génération d'image ).
D'où l'intérêt d'effectuer des conversions entre certains types numériques. Si la
conversion se fait sans perte de donnée, c'est à dire vers un type "petit" vers un grand
type, tout ce fait automatiquement comme pour le code suivant:
byte unByte = 4;
short unShort = unByte;
Nous voyons ici que nous assignons un short à partir d'un byte, les types ne
sont pas les mêmes et pourtant cet extrait de code fonctionne parfaitement. La raison
est simple, du côté binaire il n'y a qu'à "compléter" avec des 0 pour passer d'un byte en
short. Par contre, le code suivant ne fonctionnera pas (enfin, pas directement):
short unShort = 4;
byte unByte = unShort;
En effet, même si, dans notre cas, il n'y a pas de perte de donnée lors de la
conversion (un byte a une taille suffisante pour stocker 4), il pourrait y en avoir une
avec un short ayant une valeur supérieur à 255, donc Visual Studio nous signale qu'on
ne peut pas implicitement convertir un short en byte.
Si on analyse un peu ce message d'erreur, on peut deviner que, puisqu'on ne peut
pas le faire implicitement, on doit pouvoir le faire explicitement (logique :p ). En C#,
pour convertir explicitement une variable d'un type en un autre type, il nous suffit de
préciser le type entre parenthèses juste devant notre variable, le code précédant corrigé
donnera donc :
short unShort = 4;
Ici, il n'y aura pas de problème particulier, par contre, si votre short était plus
grand que 255, notre byte aurait valu à la sortie la valeur du short modulo 256 (par
exemple si le short valait 260, le byte aurait valu 4) car le compilateur vas juste se
contenter de prendre les bits de poids faibles jusqu'à remplir l'espace alloué à un byte
puis "oubliera" le reste (les bits de poids fort).
Gardez donc bien à l'esprit qu'il peut y avoir de la perte d'information lorsque
vous effectuez des conversions d'un type vers un type "plus petit". Conversions de
string vers autres types de données : méthodes Parse() et TryParse()
Parfois, nous allons avoir besoin de convertir des chaînes de caractères en
d'autres types de données. Par exemple convertir "123" (chaine de caractère) en 123
(entier), ou convertir "false" en un booléen valant false. Pour chacune de ces
conversions, nous allons avoir deux manières de procéder, l'une étant légèrement plus
19
complexe que l'autre, mais permet de voir plus facilement s'il y a eu une erreur de
conversion.
Mais tout d'abord voyons la plus simple : Parse. "Parser" va signifier parcourir
tout les éléments d'une chaine de caractère pour un obtenir autre chose, dans notre cas
on va parcourir chaque caractère de la chaine, voir si c'est un chiffre, et si on n'a vu que
des chiffres, on retourne le nombre correspondant. Cela peut paraitre bien compliqué
mais, ne vous en faites pas, la méthode Parse le fait pour nous. Voyons son utilisation:
string nombreEnString = "123";//On a un nombre en chaine de caractère
int nombreEntier = int.Parse(nombreEnString);//Grace à int.Parse(...) on en
fait un entier
La syntaxe de int.Parse(..) nous importe peu pour l'instant. Retenez juste que
int.Parse(...) va permettre de convertir en entier une chaine de caractères passée en
paramètres (entre les parenthèses de "Parse(...)"). Dans notre
exemple, nombreEntier vaudra 123 comme vous pouvez le deviner.
Pour convertir une chaine de caractère en nombre décimal ou en booléen, cela
va être identique, on remplacera juste "int" par le type que l'on veut. Par exemple :
bool booleen = bool.Parse("true");//Booleen vaudra true
double unDecimal = double.Parse("123.4");//unDecimal vaudra 123,4
Heureusement, nous avons aussi une méthode qui ne fait pas tout crasher : la
méthode TryParse ! Pour les non-anglophones, "try" signifie "essayer" ; cette méthode
va donc essayer de parser ce que nous lui donnons en paramètre et renverra un booléen
si le parsage n'a pas réussi. La syntaxe va être légèrement différente, par exemple avec
le même exemple que tout à l'heure:
string nombreEnString = "123";
int unEntier;
int.TryParse(nombreEnString, out unEntier);
L'entier ne va pas être renvoyé directement, il nous faudra créer une variable de
type int au préalable et la passer en second paramètre à la méthode. Vous pouvez
remarquer le mot-clé "out", celui-ci est obligatoire pour l'utilisation de cette méthode,
nous expliquerons l'utilité de mot clé dans un chapitre suivant, pour l'instant retenez
juste qu'il nous faut absolument le mettre (le programme ne compile pas sinon).
Pour l'instant, rien de fabuleux par rapport au simple "Parse", ceci est dû au fait
qu'ici j'utilise TryParse de la manière dont j'utiliserais Parse. Et oui, ces deux méthodes
20
ne s'utilisent pas vraiment pareil, pour bien utiliser TryParse, je préfèrerais le code
suivant:
string nombreEnString = "123";
int unEntier;
bool aReussi = int.TryParse(nombreEnString, out unEntier);//TryParse renvoie
un booléen valant vrai en cas de succès, sinon il renvoie faux
21
On peut aussi afficher en une seule fois plusieurs chaînes de caractères
concaténées, c'est à dire mises les unes à la suite des autres. Pour concaténer plusieurs
chaînes, nous allons simplement utiliser l'opérateur "+". Nous pourrons donc écrire
l'instruction suivante:
Console.WriteLine("Je suis une chaine. " + "Moi aussi!! " + "Et moi alors!!"
);
Si vous n'avez vraiment pas le choix et que vous devez absolument concaténer des
nombres, convertissez simplement chacun des nombres en chaine de caractères.
Console.WriteLine((3.5).ToString() + (2).ToString());
Un grand intérêt qui réside est que vous n'êtes pas obligés de taper directement la
chaîne à afficher, vous pouvez passer par des variables intermédiaires :
string chaine1 = "Bonjour ";
string chaine2 = "les ";
string chaine3 = "zéros ";
Console.WriteLine(chaine1 + chaine2 + chaine3);
uneChaineDeCaractere = Console.ReadLine();
22
Lorsque le programme arrivera à cette instruction dans le code, l'utilisateur devra
écrire une ligne de texte dans la console et finir en appuyant sur la touche 'entrée'.
Toute la ligne écrite sera stockée dans la variable uneChaineDeCaractere.
Si nous combinons ReadLine() avec les conversions vues dans un chapitre précédent,
pour récupérer un entier entré par l'utilisateur nous ferrons:
int unEntier;
Console.WriteLine("Entrez un nombre");
bool aReussi = int.TryParse(Console.ReadLine(), out unEntier);
Console.WriteLine("aReussi vaut " + aReussi.ToString() + " et l'entier vaut
" + unEntier.ToString());
Ainsi, si nous entrons un nombre, cela affichera:
Entrez un nombre
3
aReussi vaut True et l'entier vaut 3
Et en cas de faute de frappe, cela affichera:
Entrez un nombre
"
aReussi vaut False et l'entier vaut 0
Nous verrons plus tard comment faire pour demander à l'utilisateur d'entrer un
nombre tant qu'il n'a effectivement pas tapé un nombre correct (sans aucun autre
caractère qu'un chiffre)
Ceux qui auront commencé à explorer les propositions de l'auto-complétion
(appelée "intellisense" sous Visual Studio) auront remarqué une méthode "Read()" et
auront fait le rapprochement avec la méthode "Write()", c'est à dire une méthode
"capable de lire une chaine de caractère entrée à l'écran sans sauter de ligne".
Or ce n'est pas le cas !! La méthode "Read()" va seulement récupérer le dernier caractère
du flux d'entrée standard, son utilisation ne nous intéresse pas encore, nous utiliserons
donc pour le moment exclusivement la méthode "ReadLine()".
A cela, s'ajoutera aussi Console.ReadKey() qui se contentera elle de lire un seul
caractère, nous ne verrons pas tout de suite comment l'utiliser pour stocker un seul
caractère, nous l'utilisons généralement en fin de programme pour éviter que la console
ne se ferme toute seule, de la manière suivante:
//Contenu du Main()...
Console.WriteLine("Appuyez sur une touche pour continuer...");
Console.ReadKey();
23
Grâce à ce code, vous pouvez lancer votre programme en mode debeug sans que
la console se ferme toute seule à la fin
Il peut être intéressant de noter que, par défaut, Console.ReadKey() affiche le
caractère tapé. Pour ne pas l'afficher, préférez le code suivant:
//Contenu du Main()...
Console.WriteLine("Appuyez sur une touche pour continuer...");
Console.ReadKey(true);
Sachez qu'en programmation, si on n'avait que des types primitifs, on n'irait pas très
loin. Néanmoins, ils sont à la base de tout.
class SampleClass
{
}
C# fournit également des types appelés structures qui sont utiles lorsque vous
n’avez pas besoin de prise en charge de l’héritage ou du polymorphisme.
struct SampleStruct
{
}
24
Membres de classe
Chaque classe peut avoir différents membres de classe : des propriétés qui
décrivent les données de classe, des méthodes qui définissent le comportement de classe
et des événements qui permettent la communication entre les différents objets et
classes.
Propriétés et champs
Les propriétés et les champs sont des informations contenues dans un objet. Les
champs sont similaires aux variables, car ils peuvent être lus ou définis directement,
sous réserve de modificateurs d’accès applicables. Pour définir un champ accessible à
partir d’instances de la classe :
Les propriétés get ont set des accesseurs et, qui fournissent davantage de
contrôle sur la façon dont les valeurs sont définies ou retournées.
class SampleClass
{
public int SampleProperty { get; set; }
}
class SampleClass
{
private int _sample;
public int Sample
{
// retourne une valeur après la lecture
get => _sample;
// Lu une valeur pour la retourne
set => _sample = value;
}
}
25
La plupart des propriétés disposent de méthodes ou de procédures destinées à la
fois à définir et à obtenir la valeur de propriété. Toutefois, vous pouvez créer des
propriétés en lecture seule ou en écriture seule pour empêcher qu'elles soient modifiées
ou lues. En C#, vous pouvez omettre la méthode de propriété get ou set. Toutefois, les
propriétés implémentées automatiquement ne peuvent pas être en écriture seule. Les
propriétés implémentées automatiquement en lecture seule peuvent être définies dans
les constructeurs de la classe conteneur.
1.4.2. Méthodes
Une méthode est une action que peut effectuer un objet. Pour définir une
méthode de classe :
class SampleClass
{
public int sampleMethod(string sampleParam)
{
// insérer les codes ici
}
}
Dans la plupart des cas, vous déclarez une méthode dans une définition de
classe. Toutefois, C# prend également en charge des méthodes d’extension, qui vous
permettent d’ajouter des méthodes à une classe existante hors de la définition réelle de
la classe.
1.4.3. Constructeurs
26
{
public SampleClass()
{
// Add code here
}
}
1.4.4. Finaliseurs
Un finaliseur est utilisé pour détruire des instances de classes. Dans le .NET
Framework, le garbage collector gère automatiquement l'allocation et la libération de
mémoire pour les objets managés figurant dans votre application. Toutefois, vous
pouvez avoir besoin de finaliseurs pour nettoyer toutes les ressources non managées
créées par votre application. Il ne peut y avoir qu’un seul finaliseur pour une classe.
Pour déclarer un finaliseur :
1.4.5. Événements
Une classe définie à l’intérieur d’une autre classe est dite imbriquée. Par défaut,
une classe imbriquée est privée.
class Container
27
{
class Nested
{
// ajouter tes codes ici.
}
}
Pour créer une instance de la classe imbriquée, utilisez le nom de la classe de
conteneur suivi d'un point, puis du nom de la classe imbriquée :
Toutes les classes et tous les membres de classe peuvent spécifier le niveau
d’accès qu’ils fournissent aux autres classes à l’aide des modificateurs d’accès. Les
modificateurs d’accès suivants sont disponibles :
Modificateur C# Définition
publique Tout autre code du même assembly ou d'un autre assembly qui y fait
référence peut accéder au type ou au membre.
priv Seul le code de la même classe peut accéder au type ou au membre.
Protect Seul le code de la même classe ou d'une classe dérivée peut accéder au
type ou au membre.
internal Tout code du même assembly, mais pas d'un autre assembly, peut
accéder au type ou au membre.
protected internal Tout code du même assembly ou toute classe dérivée dans un autre
assembly peut accéder au type ou au membre.
protégé privé Le code de la même classe ou d'une classe dérivée peut accéder au type
ou au membre dans l’assembly de la classe de base.
Pour créer un objet, vous devez instancier une classe ou créer une instance de
classe.
SampleClass sampleObject = new SampleClass();
28
Après avoir instancié une classe, vous pouvez assigner des valeurs aux
propriétés et champs de l'instance et appeler des méthodes de classe.
sampleObject.sampleMethod();
Pour accéder au membre statique, utilisez le nom de la classe sans créer d’objet
de cette classe :
Console.WriteLine(SampleClass.SampleString);
Les types anonymes vous permettent de créer des objets sans écrire de définition
de classe pour le type de données. À la place, le compilateur se charge de générer une
classe. La classe ne possède pas de nom utilisable et contient les propriétés que vous
spécifiez lors de la déclaration de l'objet.
Pour créer une instance de type anonyme :
1.4.11. Héritage
Il vous permet de créer une nouvelle classe qui réutilise, étend et modifie le
comportement défini dans une autre classe. La classe dont les membres sont hérités
porte le nom de classe de base et la classe qui hérite de ces membres porte le nom de classe
dérivée.
Toutefois, toutes les classes dans C# héritent implicitement de la
classe Object qui prend en charge la hiérarchie de classes .NET et fournit des services
de bas niveau à toutes les classes.
Remarque
C# ne prend pas en charge l’héritage multiple. Vous pouvez donc spécifier une
seule classe de base pour une classe dérivée. Pour hériter d'une classe de base :
class DerivedClass:BaseClass {}
Par défaut, toutes les classes peuvent être héritées. Toutefois, vous pouvez
spécifier si une classe ne doit pas être utilisée comme classe de base ou créer une classe
qui peut être utilisée uniquement comme classe de base. Pour spécifier qu'une classe ne
peut pas être utilisée comme classe de base :
Pour spécifier qu'une classe peut être utilisée uniquement comme classe de base
et ne peut pas être instanciée :
Remplacement de membres
Par défaut, une classe dérivée hérite de tous les membres de sa classe de base. Si
vous souhaitez modifier le comportement du membre hérité, vous devez le
substituer. Autrement dit, vous pouvez définir une nouvelle implémentation de la
méthode, de la propriété ou de l'événement dans la classe dérivée. Les modificateurs
suivants sont utilisés pour contrôler la façon dont les propriétés et les méthodes sont
substituées :
30
Modificateur C# Définition
virtual Autorise la substitution d'un membre de classe dans une classe dérivée.
override Substitue un membre virtuel (substituable) défini dans la classe de base.
abstraction Requiert qu'un membre de classe soit substitué dans la classe dérivée.
Modificateur new Masque un membre hérité d'une classe de base.
1.4.12. Interfaces
interface ISampleInterface
{
void doSomething();
}
1.4.13. Génériques
31
Les classes, les structs, les interfaces et les méthodes dans le .NET Framework
peuvent inclure des paramètres de type, qui définissent les types d’objets qu’ils peuvent
stocker ou utiliser. L’exemple le plus commun de génériques est une collection dans
laquelle vous pouvez spécifier le type d’objets à stocker dans une collection. Pour définir
une classe générique :
1.4.14. Délégués
Un délégué est un type qui définit une signature de méthode et peut fournir une
référence à toute méthode avec une signature compatible. Vous pouvez appeler la
méthode par le biais du délégué.Les délégués sont utilisés pour passer des méthodes
comme arguments à d'autres méthodes.
Remarque
Les gestionnaires d'événements sont tout simplement des méthodes appelées
par le biais de délégués. Pour plus d’informations sur l’utilisation de délégués dans la
gestion des événements.
Pour créer un délégué :
Pour créer une référence à une méthode qui correspond à la signature spécifiée
par le délégué :
class SampleClass
{
// Method that matches the SampleDelegate signature.
public static void sampleMethod(string message)
{
// Add code here.
}
// Method that instantiates the delegate.
void SampleDelegate()
{
SampleDelegate sd = sampleMethod;
32
sd("Sample string");
}
}
class Exemple1 { }
Cette classe ne fait rien et ne produit rien. En fait, une classe quelconque peut
s'exécuter toute seule à condition qu'elle possède dans ces déclarations internes la
méthode Main qui sert à lancer l'exécution de la classe (fonctionnement semblable au
lancement d'un programme principal).
class Exemple2
{
static void Main(string[ ] args)
{ // c'est ici que vous écrivez votre programme principal
}
}
class Exemple3
{
static void Main(string[ ] args)
{
System.Console.WriteLine("Bonjour !");
}
}
class Application1
{
static void Main(string[ ] args)
{ /* inverse d'une suite de caractère dans un tableau par
permutation des deux extrêmes */
char [ ] Tablecar ={'a','b','c','d','e','f'} ;
int i, j ;
System.Console.WriteLine("tableau avant : "
+ new string(Tablecar));
for ( i = 0 , j = 5 ; i<j ; i++ , j-- )
33
{ char car ;
car = Tablecar[i];
Tablecar[i ]= Tablecar[j];
Tablecar[j] = car;
}
System.Console.WriteLine("tableau après : "
+ new string(Tablecar));
}
}
Exemple2
class Application2
{
static void Main(string[ ] args)
{ // recherche séquentielle dans un tableau
int [ ] table= {12,-5,7,8,-6,6,4,78,2};
int elt = 4, i ;
for ( i = 0 ; i<8 ; i++ )
if (elt= =table[i]) break ;
if (i = = 8) System.Console.WriteLine("valeur : "+elt+" pas
trouvée.");
else System.Console.WriteLine("valeur : "+elt+" trouvée au rang :"+i);
}
}
34
valeur : 4 trouvée au
rang : 6
Syntaxe :
Corps de fonction :
36
L'appel de méthode en C# s'effectue très classiquement avec des paramètres
effectifs dont le nombre doit obligatoirement être le même que celui des
paramètres formels et le type doit être soit le même, soit un type compatible ne
nécessitant pas de transtypage.
class Application4
{ static void Main(string[ ] args)
{ // recherche séquentielle dans un
tableau
int [ ] table= {12,-5,7,8,-
6,6,4,78,2}; Appel de la méthode afficher
long elt = 4; afficher(i,elt);
int i ; Les deux paramètres
for ( i = 0 ; i<=8 ; i++ )
effectifs "i" et "elt" sont du
if (elt= =table[i]) break ;
afficher(i,elt); même type que le paramètre
} formel associé.
static void afficher
(int rang , long val) - Le paramètre effectif "i" est
associé au paramètre formel rang.
{ if (rang == 8)
System.Console.WriteLine("valeur - Le paramètre effectif "elt" est
: "+val+" pas trouvée."); associé au paramètre formel val.
else
System.Console.WriteLine("valeur
: "+val+" trouvée au rang :"+ rang);
}
}
37
Rappelons tout d'abord quelques principes de base : Dans tous les langages
possédant la notion de sous-programme (ou fonction ou procédure), il se pose une
question à savoir : à quoi servent les paramètres formels ? Les
paramètres formels décrits lors de la déclaration d'un sous-programme ne sont que des
variables muettes servant à expliquer le fonctionnement du sous-programme sur des
futures variables lorsque le sous-programme s'exécutera effectivement.
38
Les réels :
class Application5
{ static void Main(string[ ] args) Appel de la méthode
{ // recherche séquentielle dans un afficher
tableau afficher(i,elt);
int [ ] table= {12,-5,7,8,- Les deux paramètres
6,6,4,78,2};
effectifs "i" et "elt" sont
sbyte elt = 4;
short i ; d'un type compatible avec
for ( i = 0 ; i<8 ; i++ ) celui du paramètre formel
if (elt= =table[i]) break ; associé.
afficher(i,elt);
} - Le paramètre effectif "i" est
static void afficher associé au paramètre
formel rang.(short = entier
(int rang , long val)
signé sur 16 bits et int = entier
{ if (rang == 8) signé sur 32 bits)
System.Console.WriteLine("valeur
: "+val+" pas trouvée."); - Le paramètre effectif "elt" est
else associé au paramètre
System.Console.WriteLine("valeur formel val.(sbyte = entier signé
: "+val+" trouvée au rang :"+ rang); sur 8 bits et long = entier signé
sur 64 bits)
}
}
39
Un paramètre effectif transmis au sous-programme appelé est en fait un moyen
d’utiliser ou d’accéder à une information appartenant au bloc appelant (le bloc appelé
peut être le même que le bloc appelant, il s’agit alors de récursivité).
En C#, ces trois modes de transmission (ou de passage) des paramètres (très
semblables à Delphi) sont implantés.
40
a) Les paramètres C# passés par valeur
Le passage par valeur est valable pour tous les types élémentaires
(int, byte, short, long, boolean, double, float, char) et les objets.
En C# tous les paramètres sont passés par défaut par valeur (lorsque le
paramètre est un objet, c'est en fait la référence de l'objet qui est passée par valeur). Pour ce
qui est de la vision algorithmique de C#, le passage par valeur permet à une variable
d'être passée comme paramètre d'entrée .
Le passage par référence est valable pour tous les types de C#.
41
static int methode1(int a , ref char b) {
//.......
return a+b;
}
Le passage par résultat est valable pour tous les types de C#.
Remarque :
42
exemple dans un passage par valeur, car nous verrons plus loin que les tableaux en
C# sont des objets et que leur structure est passée par référence.
Les énumérations
Une énumération va être une variable qui ne pourra avoir que certaines valeurs
prédéfinies. Imaginons par exemple un doigt, un doigt ne pourrait avoir que cinq
valeurs différentes (pouce, index, majeur, annulaire et auriculaire).
Bien entendu, ce sera à nous de créer nos énumérations, ceux qui ont créé .net
ne se sont pas amusés à fabriquer toutes les énumérations possibles et inimaginables .
- Création
La manière de créer une énumération va être simple : généralement, pour plus
de clarté, nous créons un fichier pour chaque énumération, classe et structure, nous
allons donc ajouter un nouveau fichier à notre projet. Pour cela, faite un clic droit sur
votre projet "Ajouter" ("Add") puis "Classe" ("Class"), là une fenêtre avec différents
types de fichier proposés va s'ouvrir, le fichier "classe" va être présélectionnée, nous
n'aurons donc qu'à lui donner un nom dans le champ de texte en bas (nous l'appelons
"Doigt" dans notre cas) et valider. Un code de base sera généré:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ExempleConsole
{
class Doigt
{
}
}
43
Sauf que ce n'est pas une classe (mot clé "class") que nous voulons, mais une
énumération (mot clé "enum"), nous enlevons donc "class" que nous remplaçons par
"enum", ce qui nous donnera l'extrait de code suivant :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ExempleConsole
{
enum Doigt
{
}
}
Voilà notre énumération créée, il ne nous reste plus qu'a lui donner les
différents valeurs qu'elle peut avoir, nous plaçons ces valeurs entre les accolades ( " { "
et "" } ") juste en dessous de "enum Doigt", chacune séparée par une virgule et,
optionnellement, un espace. Attention les valeurs ne peuvent contenir que des lettres
minuscules, majuscules et des underscores ( " _ " ).
Dans notre cas nous aurons cinq valeurs :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MenuConsole
{
enum Doigt
{
Pouce,
Index,
Majeur,
Annulaire,
Auriculaire
}
}
Notre énumération est prête à être utilisée
- Déclaration
Pour cela, nous faisons comme pour un entier ou une chaine de caractère : (code
à taper entre les accolades de Main comme dans la partie sur les types primitifs)
static void Main(string[] args)
{
Doigt monDoigt;
44
}
- Assignation
Pour assigner un enum, la syntaxe sera la suivante: monEnum =
TypeEnum.Valeur;.
Si nous voulons par exemple que "monDoigt" soit un index nous ferrons:
static void Main(string[] args)
{
Doigt monDoigt;
monDoigt = Doigt.Index;
}
- Conversion enum vers entier
Un avantage de l'enum est que nous pouvons lui donner une valeur entière et,
vice versa, nous pouvons à partir d'un entier obtenir un type énumératif. Par exemple:
Doigt monDoigt;
monDoigt = Doigt.Index;
int valeurDeMonDoigt = (int)monDoigt;//valeurDeMonDoigt vaudra 1 (les énumér
ation commencent à 0)
Doigt autreDoigt = (Doigt)3;//autreDoigt vaudra Doigt.Annulaire
45
Le modèle de données (ou "Model" en anglais) en architecture 3-tiers va être la
partie structure de données citée un peu plus haut, c'est ce que nous allons créer dès à
présent ;)
Énoncé
Pour votre premier modèle, on va faire bien, mais pas trop complexe. Il nous
faudra créer deux types de personnages:
Un héros qui aura
Un nom
Des points de vie
Une position
Une épée
Celui-ci pourra
Attaquer un adversaire
Recevoir des dégâts
Faire une bonne action
Se déplacer
Nous créerons aussi un monstre qui aura
Un nom
Des points de vie
Une position
Un gourdin
Et celui-ci pourra
Attaquer un adversaire
Recevoir des dégâts
Se nourrir
Se déplacer
Le gourdin aura
Un nom
Des dégâts
Une masse
A la fin nous aurons besoin d'une classe Jeu qui fera par la suite combattre nos
deux joueurs. Elle devra donc avoir un héros et un ennemi.
46
1.5.2. Résolution
La classe Arme
Nous pouvons commencer par représenter l'épée et le gourdin, elles ont toute
deux des champs communs : le nom et les dégâts. Nous allons donc commencer à créer
une classe abstraite que nous nommerons par exemple "arme" avec un constructeur
initialisant les deux champs :
#region Constructeur
public Arme(string nom, int degats)
{
this.Nom = nom;
this.Degats = degats;
}
#endregion
}
Ensuite nous faisons hériter cette classe dans deux autres classes : Epee et Gourdin :
La classe Epee
public class Epee : Arme
{
#region Propriétés
public double Longueur { get; private set; }
#endregion
#region Constructeur
public Epee(string nom, int degats, double longueur)
: base(nom, degats)
{
this.Longueur = longueur;
}
#endregion
}
La classe Gourdin
47
public class Gourdin : Arme
{
#region Propriétés
public double Poids { get; private set; }
#endregion
#region Constructeur
public Gourdin(string nom, int degats, double poids):base(nom, degats)
{
this.Poids = poids;
}
#endregion
}
Remarquez ici que nous réutilisons même le constructeur de base que nous
enrichissons en lui faisait prendre un paramètre de plus (longueur pour Epee et poids
pour Gourdin).
Les personnages
Ici ce n'est pas moins de quatre champs et trois méthodes qui peuvent être
abstraites entre le héros et le monstre. Pour la démonstration, une méthode est
"normale", une méthode est virtuelle et une méthode est abstraite :
abstract class EtreVivant
{
#region Propriétés
public int PositionY { get; private set; }
#region Constructeur
public EtreVivant(string nom, int pointsDeVie)
{
this.Nom = nom;
this.PointsDeVie = pointsDeVie;
}
#endregion
#region Methodes
public void SePositionner(int positionX, int positionY)
{
this.PositionX = positionX;
this.PositionY = positionY;
}
public virtual void Attaquer(EtreVivant cible)
{
48
cible.RecevoirDegats(this);
}
public abstract void RecevoirDegats(EtreVivant source);
#endregion
}
Vous allez remarquer que ici, nous n’avons pas mis de corps à la méthode
"RecevoirDegats" étant donné que nous serons obligé de lui fournir dans les classes
héritières. Pour une méthodes sans corps, nous sommes obligés de remplacer les
accolades par un point-virgule pour que le fichier de code passe la compilation.
Concernant la méthode "Attaquer", nous lui passons ici en paramètre un objet de type
EtreVivant duquel nous appelons la méthode "RecevoirDegats", si vous n'avez pas fait
pareil ce n'est pas grave ce TP est fait pour s'entrainer à créer nos classes, les divers
traitement que nous faisons ont pour l'instant peu d'importance ;)
Heros
Les seuls ajouts à faire ici sont l'Epee et la méthode "FaireUneBonneAction".
Pour les autres méthodes, nous utilisons le mot-clé override pour les ré-implémenter.
class Heros : EtreVivant
{
#region Proprietes
public Epee Epee { get; private set; }
#endregion
#region Constructeur
public Heros(string nom, int pointsDevie, Epee epee)
: base(nom, pointsDevie)
{
Epee = epee;
}
#endregion
#region Methodes
public override void Attaquer(EtreVivant cible)
{
base.Attaquer(cible);
Console.WriteLine("Le héros " + Nom + " attaque " + cible.Nom);
}
public override void RecevoirDegats(EtreVivant source)
{
Console.WriteLine(Nom + " s'est fait attaqué par " + source.Nom);
}
public void FaireUneBonneAction()
{
Console.WriteLine(Nom + " a fait une bonne action et en récompense,
sa vie remonte ! ");
}
#endregion
}
49
Comme les implémentations de Arme, nous enrichissons le constructeur de
base, mais cette fois-ci avec trois paramètres supplémentaires (nomArme, degatsArme
et longueurEpee) et ensuite nous instancions la propriété "Epee" grâce aux paramètres
précédemment récupérés.
Monstre
Pour la classe Monstre, il n'y pas de difficultés supplémentaires par rapport à
Heros, on ajoute juste le champ et la méthode spécifique à Monstre :
class Monstre : EtreVivant
{
#region Attributs
public Gourdin Gourdin { get; private set; }
#endregion
#region Constructeur
public Monstre(string nom, int pointDevie, Gourdin gourdin):base(nom, po
intDevie)
{
Gourdin = gourdin;
}
#endregion
#region Methodes
public override void Attaquer(EtreVivant cible)
{
Console.WriteLine(Nom + " le gros monstre a attaqué " + cible.Nom);
}
public override void RecevoirDegats(EtreVivant source)
{
Console.WriteLine(Nom + " a reçu des dégâts de " + source.Nom);
}
public void SeNourrir()
{
Console.WriteLine(Nom + " se nourri et regagne de la santé");
}
#endregion
}
Classe Jeu
Pour finir, nous aurions pu éviter de créer cette classe et tout faire dans la classe
Program. Mais en général, nous mettons le moins de code possible dans la classe
Program et nous préférons créer une classe "de démarrage" à part. Cette classe est
surtout utile pour des traitements, mais étant donné que nous ne savons pas encore en
faire, nous créerons juste deux champs et propriétés de type Monstre et Heros :
public class Jeu
{
#region Attributs
50
public Heros Gentil { get; private set; }
public Monstre Ennemi { get; private set; }
#endregion
#region Constructeur
public Jeu(Heros gentil, Monstre ennemi)
{
this.Gentil = gentil;
this.Ennemi = ennemi;
}
#endregion
}
Même si lors de la compilation ce programme ne fait rien, nous avons déjà fait
beaucoup : en effet comme je vous l'ai dit précédemment, dans tout programme vous
aurez un modèle de données (plus ou moins important, certes) et la création de ce
modèle de donnée est primordiale avant de réaliser les traitements.
#region Attributs
public EtreVivantEtat Etat
{
51
get { return _etat; }
set { _etat = value; }
}
public int PositionY
{
get { return _positionY; }
set { _positionY = value; }
}
public int PositionX
{
get { return _positionX; }
set { _positionX = value; }
}
public string Nom
{
get { return _nom; }
set { _nom = value; }
}
public int PointsDeVie
{
get { return _pointDeVie; }
set { _pointDeVie = value; }
}
#endregion
}
Quant à l'état des armes, nous en définissons aussi trois : Neuf, Usee et
Inutilisable:
enum EtatArme
{
Neuf,
Use,
Inutilisable
}
a. Écrivez un programme C # Sharp pour trier une liste d'éléments à l'aide du tri
par bulles. Selon Wikipedia, «le tri à bulles, parfois appelé tri par affaissement,
est un algorithme de tri simple qui parcourt à plusieurs reprises la liste à trier,
compare chaque paire d'éléments adjacents et les échange s'ils sont dans le
mauvais ordre. La liste est répétée jusqu'à ce qu'aucun échange ne soit nécessaire,
ce qui indique que la liste est triée. L'algorithme, qui est un tri par comparaison,
est nommé en fonction de la façon dont les éléments plus petits "remontent" en
haut de la liste. Bien que l'algorithme soit simple, il est trop lent et peu pratique
pour la plupart des problèmes, même par rapport au tri par insertion. Cela peut
être pratique si l'entrée est généralement dans l'ordre de tri, mais peut parfois
avoir des éléments dans le désordre presque en position.
b. Concevoir un jeu qui permet à notre cochon d’inde de chercher sa nourriture
mais dans cette ville où il vit, il y a des dégâts causé par des chats qui ont aussi
faim. Le cochons d’inde n’as pas d’arme seul le déplacement qui lui permet de
fuir son adversaire.
53
CHAPITRE 2 : PRESENTATION DE L’UNITY
2.1. Présentation
54
membres de l’équipe de développement du jeu puissent plus facilement manipuler les
éléments du moteur.
Ces outils facilitent non seulement le processus de création du jeu mais rendent
également le moteur accessible aux acheteurs potentiels et aux équipes de
postproduction. Cela est également vrai pour Unity, et une très importante
communauté d’utilisateurs partage ses outils sous forme de plugins.
2.2. Historique
Unity est un moteur de jeu multiplateforme (smartphone, ordinateur, consoles
de jeux vidéo et Web) développé par Unity Technologies. Il est l'un des plus répandus
dans l'industrie du jeu vidéo, aussi bien pour les grands studios que pour
les indépendants du fait de sa rapidité aux prototypages et qu'il permet de sortir les jeux
sur tous les supports.
Il a la particularité de proposer une licence gratuite dite « Personal » avec
quelques limitations de technologie avancée au niveau de l'éditeur, mais sans limitation
au niveau du moteur.
Le logiciel a la particularité d'utiliser du code (C#) sur la plateforme « .NET »
avec l'implémentation Mono. Son éditeur était auparavant basé sur MonoDevelop, via
MonoDevelop-Unity, mais à partir de la version 2018.1, il est basé sur Visual Studio
Community. Il permet toujours de sortir du code Mono, ayant l'avantage d'être
multiplateforme et multi-architecture, mais permet également, via son backend
« IL2CPP », de générer du code intermédiaire C++ afin de pouvoir générer des binaires
natifs (.exe, APK…). Ce backend permet également de générer des applications
pour iOS et WebGL.
UnityScript (un langage proche du JavaScript et inspiré d'ECMAScript et
arrêté depuis la version 2017.2) et Boo2 (arrêté à la version 5.0) au lieu de Lua très utilisé
dans les jeux vidéo. Son approche est orientée asset, par le biais d'un EDI dédié, à la
différence des moteurs comme le Quake engine dont les éléments centraux sont les
codes sources. Il est l'équivalent du logiciel de création Director pour la 2D qui
utilise Lingo. Il se rapproche plus pour la 3D des logiciels tels que Shiva, Virtools,
Cheetah3D. Parmi les logiciels d'animations, il ne permet pas la modélisation (sauf
depuis la version 2018.1) mais permet de créer des scènes supportant des éclairages,
des terrains, des caméras, des textures, la musique et les vidéos. Il est par ces
fonctionnalités un mélange de VRML et de QuickTime.
2.3. Plateformes supportées
Le logiciel de conception développé d'abord pour la plate-forme Mac a été porté
sous Windows et permet d'obtenir des applications compatibles Windows, Mac OS
X, iOS, Android, TV OS, PlayStation 3, PlayStation Vita, PlayStation 4, Xbox
360, Xbox One, Xbox One X, Windows Phone 8, Windows 10 Mobile , PlayStation
Mobile, Tizen, Oculus Rift, Wii U, Nintendo 3DS, Nintendo
Switch, WebGL, Samsung TV, dans une page web grâce à un plugin, ou depuis la
55
version 3.5 le format Flash d'Adobe, bien que cette dernière option ait été retirée fin
avril 20133.
La version 4.0, sortie en novembre 2012, intègre le développement de jeux
compatibles avec Linux4,5. Les jeux développés pourront ainsi fonctionner sous Linux.
Depuis le 25 août 2015, Unity lui-même est disponible sous Linux avec des restrictions
d'exports (pas d'export Windows par exemple). Le support de jeux compatibles avec
Linux s'appuie dans un premier temps sur la distribution Ubuntu et sur les pilotes
propriétaires fournis par les fabricants de cartes graphiques. Unity 3D collabore
avec Canonical au sein d'une équipe dédiée aux jeux vidéo.
Le support Linux est cependant limité aux systèmes d'exploitation mobiles
(Android et iOS) et aux processeurs x86 sur ordinateur de bureau.
Il est capable d'importer de nombreux formats 3D (Blender, Maya, Cinema
4D, Cheetah3D (en), FBX), des ressources variées : (des
textures Photoshop, PNG, TIFF, audios, vidéos) qu'il optimise par l'utilisation de
filtres.
Unity possède une large palette de déploiement :
il est compatible avecles API graphiques Direct3D4, OpenGL4 , Vulkan11, VR.
les navigateurs web peuvent, grâce au plugin Unity Web Player, afficher les
productions du moteur ;
il est compatible avec QuickTime et utilise en interne le format Ogg Vorbis.
57
Procédure d'activation
Si tout se passe comme prévu, il est maintenant temps d’activer votre logiciel.
Je vous propose ici de choisir la deuxième option, à savoir l’activation d’une license
gratuite d’Unity qui nous sera largement suffisante. Il est toutefois nécessaire de
disposer d’un compte Unity, si vous n’en avez pas, cliquez sur Create Account et
renseignez les informations demandées. Comme souvent maintenant, il vous sera
nécessaire de cliquer sur un lien de confirmation qui vous sera envoyé par mail.
Une fois cela fait, vous n’avez plus qu’à rentrer vos informations de connexion
sur la fenêtre d’activation. Vous pourrez prendre quelques minutes également pour
renseigner quelques informations personnelles à usage statistique pour Unity. À la
suite de cela, deux options sont possibles, soit une fenêtre vous propose d’ouvrir un
projet existant ou de créer un nouveau projet, soit l’éditeur s’ouvre automatiquement
avec le projet de démonstration pré-chargé.
Quel que soit le cas, vous pouvez fermer cette fenêtre, nous en avons
maintenant terminé avec ce premier chapitre, et avant de commencer à nous servir de
ce fantastique outil, il est nécessaire de faire un état des lieux de ce qu’est Unity et à
quoi ce dernier va nous servir.
Tout d'abord, vous aurez besoin du fichier compressé appelé "Sprites". Cliquez sur
l'onglet "Project". Déposez ce fichier compressé dans la zone grise à droite. Tous les
éléments dont vous aurez besoin pour reproduire mon projet sont dans ce fichier .
58
Trouvez l'élément appelé "navbar". Cliquez dessus et déposez-le en bas de la fenêtre
"Scène" comme sur la capture d'écran.
Si vous cliquez ensuite sur "Main Camera" dans la fenêtre "Hierarchy", vous verrez déjà à
quoi cet élément ressemble dans votre jeu.
59
Vous aurez besoin d'ajouter d'autres composants pour rendre votre boîte solide.
Sélectionnez "navbar" dans "Hierarchy" et cliquez sur "Add Component" dans la fenêtre
"Inspector" puis sur "Physics 2D" et enfin sur "Box Collider 2D".
60
Refaites l'opération une seconde fois. Cela évitera au robot de tomber
accidentellement de la barre de navigation dès le début du jeu.
J'ai voulu que notre robot commence le jeu là où il se trouve sur la page du site des
Instructables et c'est pour cette raison que j'ai diminué un peu la taille de la Box
Collider 2D. Comme la seconde Box Collider 2D est en fait dans un espace ouvert,
cela avertira notre robot qu'il faut aller vers la droite lorsque le jeu commence.
Dans l'onglet "Transform", pour le "Scale", indiquez 1,5 pour X et Y. Voyez les
changements que cela produit sur la barre de navigation.
Maintenant que nous avons une plateforme sur laquelle notre personnage peut se
tenir debout, nous avon/s besoin d'un personnage.
Cliquez sur "GameObject" dans la barre de menus supérieurs et sélectionnez "Create
empty".
61
Faites un clic-droit sur votre nouveau "GameObject" puis cliquez sur "Rename" pour changer
son nom en "Player".
62
Dans Project>Assets>Sprites, trouvez le "Idle_Sprite, cliquez sur le petit triangle et déposez
une des images dans le "Sprite value" du "Sprite Renderer Component".
Nous avons encore besoin d'ajouter des éléments pour étoffer notre personnage. Cliquez
"Add component">"Physics2D">"Rigidbody2D".
63
Si vous cliquez alors sur le bouton "Play", notre joueur tombera de la barre de navigation.
Essayez !
Nous allons ajouter quelques éléments pour le consolider : un élément circulaire à ses pieds
et son corps tout entier et un élément en forme de boîte à sa tête.
64
A présent, si vous cliquez sur "Play", le robot roulera et tombera sur le côté. Vous pouvez le
régler en cliquant sur "Edit Collider". Maintenant, le joueur tombe sur la barre de navigation et
il y reste.
Je le trouve un peu gros à mon goût donc je change sa taille de la façon suivante : dans
"Transform", j'indique 0,75 pour X et Y.
Maintenant que nous avons un personnage pour jouer, nous allons lui permettre de faire
quelques actions en créant des animations.
65
66
Ouvrez ensuite la fenêtre "Animator". Pour plus de confort, j'ai mis cette fenêtre à côté de
"Project".
Dans la fenêtre "Animation", cliquez sur les deux petits triangles dont les bases se font face
puis sur "Create New Clip". Nommez-le "Idle".
67
Juste en-dessous, ouvrez le dossier "Sprites". Prenez l'ensemble des "Idle_sprite" et déposez-
les dans la fenêtre "Animation".
68
Pour le "Sample", indiquez 10.
Jetez un coup d'œil à votre fenêtre "Animator". Vous avez normalement deux états : l'un "Any
state" et l'autre "Idle". Sa couleur orange indique qu'il s'agit d'un état dont les paramètres sont
des paramètres par défaut.
Normalement si vous avez suivi ces étapes correctement, votre robot doit s'animer si vous
pressez "Play".
69
Créez une autre animation que vous appellerez "Drive". Glissez-déposez la feuille
"Drive_sheet". Pour "Sample", indiquez 20. Vous pourrez modifier ce paramètre plus tard si
vous le souhaitez.
Vous verrez dans la fenêtre un autre état appelé "Drive". Cliquez sur la croix dans l'onglet
"Parameters" (en bas à gauche) et sélectionnez "Float". Nommez ce paramètre "Speed".
70
Faites un clic-droit sur l'état "Idle" et sélectionnez "Make Transition".
71
"Drive".
73
Cela peut prendre un peu de temps pour qu'il s'ouvre donc soyez patient.
Copiez-collez le code suivant pour commencer :
using UnityEngine; using System.Collections;
public class RobotController : MonoBehaviour { //This will be our maximum speed as
we will always be multiplying by 1 public float maxSpeed = 2f; //a boolean value
to represent whether we are facing left or not bool facingLeft = true; //a value
to represent our Animator Animator anim; // Use this for initialization void Start
() {
//set anim to our animator
anim = GetComponent<Animator>();
}
// Update is called once per frame void FixedUpdate () {
float move = Input.GetAxis ("Horizontal");//Gives us of one if we are moving via
the arrow keys
//move our Players rigidbody
rigidbody2D.velocity = new Vector3 (move * maxSpeed, rigidbody2D.velocity.y);
//set our speed
anim.SetFloat ("Speed",Mathf.Abs (move));
//if we are moving left but not facing left flip, and vice versa
if (move < 0 && !facingLeft) {
Flip ();
} else if (move > 0 && facingLeft) {
Flip ();
}
}
//flip if needed void Flip(){
74
facingLeft = !facingLeft;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
} }
Sauvegardez le script et retournez dans Unity. Déposez ce nouveau script dans "Player" dans
la fenêtre "Hierarchy".
75
Cliquez sur le bouton "Play". Vous pouvez à présent contrôler le robot avec les flèches de
gauche et de droite de votre pavé numérique. Si vous ouvrez la fenêtre "Animator", vous le
verrez changer d'état (de "idle" à "drive") selon qu'il bouge ou non.
Nous allons donner à notre personnage la capacité de sauter. Nous aurons besoin pour cela
d'un état d'animation pour sauter et tomber et quelques lignes de code en plus pour mieux
contrôler ses mouvements.
Commençons par ajouter deux autres paramètres à notre "Player Animator". Ajoutez un
"Float" que vous appellerez "vSpeed" et un "Bool" que vous appellerez "Ground". Nous
utiliserons vSpeed pour contrôler la vitesse verticale et "Ground" permettra de détecter quand
le personnage a touché le sol. Il réalisera ainsi des sauts plus réalistes car sauter sans
toucher le sol c'est en fait voler.
Dans la fenêtre "Scene", créez un nouveau "Game Object".
76
Glissez-déposez le nouveau "Collider" dans "Player" (fenêtre "Hierarchy").
Dans le radius du groundcheck collider indiquez 0,1 en valeur. Cela doit changer la position
en -1. Vous devez voir apparaître un petit cercle autour des pieds de votre robot. Indiquez
ensuite pour "Tag" et "Layer" la mention "Player".
77
Ajoutez ces lignes de code à notre script afin que le robot saute lorsque vous appuierez sur la
touche "Espace".
void FixedUpdate () {
//set our vSpeed
anim.SetFloat ("vSpeed", rigidbody2D.velocity.y);
//set our grounded bool
grounded = Physics2D.OverlapCircle (groundCheck.position, groundRadius,
whatIsGround); //set ground in our Animator to match grounded anim.SetBool
("Ground", grounded);
}
//flip if needed void Flip(){ facingLeft = !facingLeft; Vector3 theScale =
transform.localScale; theScale.x *= -1; transform.localScale = theScale; } }
Pour régler le paramètre "Ground Check", cliquez sur le cercle qui a un point au centre et
sélectionnez "groundcheck" dans la fenêtre qui apparaîtra. Fermez ensuite cette fenêtre.
79
Pour le paramètre "What Is Ground", cliquez pour vérifier que "Player" n'est pas
sélectionné.
Créons un "blend tree" pour avoir nos différents états (tomber et sauter). Nous devons réaliser
deux animations différentes car cela sera plus efficace et requière moins de mémoire pour
notre petit jeu.
Retournez dans la fenêtre "Animation" avec "Player" sélectionné et créez cinq nouvelles
animations que vous appellerez "jump1", "jump2", etc.
80
Dans "Project", ouvrez le dossier "Sprites". Pour chaque "jump", glissez-déposez le modèle de
la série "jumpsheet" en faisant attention de déposer le premier dans "jump1", le second dans
"jump2", etc.
81
Double-cliquez dessus pour l'ouvrir dans la fenêtre "Inspector".
Cliquez sur le signe + en-dessous de "List is Empty" et cliquez sur "Add Motion Field".
Répétez l'opération cinq fois.
82
Si vous n'êtes pas satisfait de l'animation, vous pouvez apporter une petite modification en
décochant "Automate Threshold".
83
Retournez dans la fenêtre "Animator", vous verrez le "blend tree" ouvert. Cliquez sur "Base
Layer" dans le coin supérieur gauche pour retrouver toutes vos animations.
Vous devez alors supprimer tous les états "jump" qui ont été générés automatiquement.
Créez une transition entre "Any state" et "JumpFall", puis entre "JumpFall" et "Idle".
Cliquez sur la transition entre "Any state" et "JumpFall" et dans "Conditions", sélectionnez
"Ground" et "false".
84
Cliquez sur la transition entre "JumpFall" et "Idle" et dans "Conditions", sélectionnez "Ground"
et "true".
Cliquez à nouveau sur "Play" et vous verrez que vous pouvez faire faire un saut au robot.
Problèmes avec les animations !
Dans cette étape, nous allons créer des objets qui vont donner naissance à d'autres objets
dont il faudra se débarrasser si l'on ne veut pas que notre jeu soit alourdi.
85
Commencez par créer un rectangle. Cliquez sur "GameObject">"3D Object">"Quad".
Dans "Transform", pour "Position", inscrivez -25 pour X et 0 pour Y. Pour "Scale", indiquez 1
pour X et 25 pour Y.
Nommez-le "Destroyer".
86
Enlevez l'élément "Mesh Collider" et ajoutez une "box collider 2D". Vérifiez que "Is Trigger" est
coché.
87
Ajoutez l'élément "Rigidbody 2D" et vérifiez que, dans la fenêtre Inspector, la valeur de la
gravité soit bien 0.
Dans la fenêtre "Project", ouvrez le dossier Scripts et ajoutez un nouveau script en C#.
Appelez-le "Destroyer". Double-cliquez dessus pour ouvrir Monodevelop.
88
Copiez-collez dans Monodevelop les lignes de codes suivantes :
using UnityEngine; using System.Collections;
public class Destroyer : MonoBehaviour {
// Use this for initialization void Start () {
}
void OnTriggerEnter2D(Collider2D other){ //if the object that triggered the event
is tagged player if (other.tag == "Player") {
Debug.Break (); return; } if (other.gameObject.transform.parent) { Destroy
(other.gameObject.transform.parent.gameObject); } else { Destroy
(other.gameObject); } }
}
89
Paramétrez le nouveau Destroyer de la façon suivante :
Si vous cliquez sur "Play" et que votre joueur tombe de la barre de navigation, vous verrez
"Player" disparaît de la fenêtre "Hierarchy". Cela arrêtera le jeu et vous ramènera à l'éditeur.
90
Nous réglerons ce problème plus tard.
Dans la fenêtre "Hierarchy", sélectionnez "Main Camera". Dans "Project", ouvrez le dossier
"Sprites" puis glissez-déposez la main sur la scène.
91
Pour "Scale" (dans "Transform"), paramétrez de la façon suivante :
x=2
y=2
92
Taguez-le en cliquant sur le cube en haut à gauche de la fenêtre "Inspector". Cela servira à le
retrouver plus facile.
Ensuite, glissez-déposez le Penciltip dans la main afin qu'ils soient liés l'un à l'autre et
fonctionnent ainsi ensemble.
93
Un prefab est comme un enregistrement d'objet. Si vous créez un objet dans un préfabriqué,
vous pouvez l'utiliser dans votre jeu. Nous allons en créer beaucoup par la suite.
Commencez par créer un nouveau dossier dans "Assets" et nommez-le
"Prefabs".
94
Ouvrez le dossier, faites un clic-droit et créez un nouveau prefab appelé
"pencilline".
95
Ouvrez le dossier "Sprites" et glissez-déposez l'élément appelé "pencilline".
Ajoutez-lui un élément "Box Collider 2D" et indiquez pour "Size" les paramètres suivants :
x=0,03
y = 0,05
96
Ouvrez le dossier "Prefab" puis glissez-déposez le GameObject dans le fichier "pencilline".
Vous verrez l'icône du fichier changer.
97
Une fois glissé-déposé dans le prefab, vous pouvez supprimer le game object. Nous n'en
aurons plus besoin.
99
Notre caméra n'a toujours pas de script mais si vous cliquez sur "Play", vous verrez un point
blanc au bout du
crayon.
100
La caméra est déjà créée et nous avons juste besoin d'un script pour la contrôler.
Juste avant d'écrire ce script, nous aurons besoin de changer la couleur du fonds qui doit être
blanche car sinon nous ne verrons pas la ligne que nous avons créée à l'étape précédente.
Sélectionnez Main Camera dans la fenêtre "Hierarchy" et dans la fenêtre "Inspector", cliquez
sur l'échantillon de couleur face à "Background". Sélectionnez le blanc dans la fenêtre qui
vient d'apparaître.
101
Ouvrez le dossier "Scripts". Créez un nouveau fichier C# et nommez-le "pancam". Ouvrez
Monodevelop et copiez-collez les lignes de code suivantes :
using UnityEngine; using System.Collections;
public class pancam : MonoBehaviour {
float ydir = 0f; public GameObject player;
// Use this for initialization
// Update is called once per frame void Update () { //check that player
exists and then proceed. otherwise we get an error when player dies if
(player) { //if player has passed the x position of -1 then start moving
camera forward with a randomish Y position if (player.transform.position.x >
-1) {
float randy = 0f; randy = Random.Range (0f, 100f); if (randy < 20) { ydir =
ydir + .005f; } else if (randy > 20 && randy < 40) { ydir = ydir - .005f; }
else if (randy > 80) { ydir = 0f; } transform.position = new Vector3
(transform.position.x + 0.03f, transform.position.y + ydir, -10); } } } }
Glissez-déposez ce script dans "Main Camera" (dans la fenêtre "Hierarchy") puis glissez-
déposer le Player de la fenêtre "Hierarchy" dans "Player" du script "Pancam".
102
Si vous cliquez à nouveau sur "Play", vous verrez que la caméra avance de manière continue.
103
La main est un petit peu trop haute à mon goût. J'ai juste besoin de l'abaisser un peu dans la
fenêtre "Scene".
104
Paramétrez la position de ce mot comme vous le désirez, par exemple, en mettant dans "Rect
Transform" 0,03 pour Pos X et 0,97 pour Pos Y.
Nous avons maintenant besoin d'ajouter un peu de code au script de notre caméra. Copiez-
collez les lignes suivantes dans le code :
using UnityEngine; using System.Collections;
public class pancam : MonoBehaviour {
float ydir = 0f; public GameObject player; //for our GUIText object and our
score public GUIElement gui; float playerScore = 0;
105
//this function updates our guitext object void OnGUI(){ gui.guiText.text =
"Score: " + ((int)(playerScore * 10)).ToString (); } //this is generic
function we can call to increase the score by an amount public void
increaseScore(int amount){ playerScore += amount; }
// Update is called once per frame void Update () { //check that player
exists and then proceed. otherwise we get an error when player dies if
(player) {
//if player has passed the x position of -1 then start moving camera forward
with a randomish Y position if (player.transform.position.x > -1) {
//update our score every tick of the clock playerScore += Time.deltaTime;
float randy = 0f; randy = Random.Range (0f, 100f); if (randy < 20) { ydir =
ydir + .005f; } else if (randy > 20 && randy < 40) { ydir = ydir - .005f; }
else if (randy > 80) { ydir = 0f; } transform.position = new Vector3
(transform.position.x + 0.03f, transform.position.y + ydir, -10); } } } }
Cliquez à nouveau sur "Play" et vous verrez que le score augmente à mesure que le
personnage se déplace.
106
Pas de réel danger pour notre héros jusqu'à présent mais nous allons y remédier. Faisons
quelques bombes, comme celles que l'on voit dans les dessins animés qui tombent du ciel et
brisent la ligne.
Créez un nouveau Game Object que vous appellerez "Bomb".
x=0,5
107
y=0,5
Ajoutez un Circle Collider 2D et paramétrez la bombe comme vous le souhaitez. Pour qu'elle
tombe, nous avons besoin d'ajouter un Rigidbody 2D.
108
Nous avons aussi besoin d'un Animator pour contrôler l'explosion de la bombe. Dans la
fenêtre "Inspector", décochez la case "Apply Root Motion" et pour "Update Mode",
sélectionnez "Animate Physics".
109
Ouvrez la fenêtre "Animation" et assurez-vous que Bomb_0 est toujours sélectionné. Ajoutez
une nouvelle animation et nommez-la "bombidle".
Cela créera dans la fenêtre "Animator" une nouvelle animation dont les paramètres sont les
110
paramètres par défaut.
111
Créez une nouvelle animation que vous appellerez "bombexplode". Glissez-déposez le reste
des bombes et réduisez le "Sample" à 20.
112
Dans "Conditions", indiquez 5.00 en valeur pour "Exit Time". Cela signifie que vous avez 5
secondes avant que la bombe n'explose.
Créez une troisième animation dans la fenêtre "Animation" que vous appellerez "bombdead".
Laissez-la vide. Dans la fenêtre "Animator", créez une transition de "bombexplode" à
"bombdead".
113
Dans votre dossier "Scripts", créez un nouveau fichier C# que vous nommerez "bomb" et
copiez-collez dans les lignes de code suivantes :
using UnityEngine; using System.Collections;
public class bomb : MonoBehaviour { //a holder for our Animator Animator
anim; //a public float for the explosion radius public float explodeRadius =
1f;
// Use this for initialization void Start () { anim = GetComponent
<Animator>(); }
// Update is called once per frame void Update () { //if we are done
exploding if (anim.GetCurrentAnimatorStateInfo (0).IsName ("bombdead")) {
//destroy all the objects in a radius unless they are tagged Player or hand
Collider2D[] colliders = Physics2D.OverlapCircleAll
(transform.position,explodeRadius); foreach(Collider2D col in colliders){ if
(col.tag != "Player" && col.tag != "hand"){
Destroy(col.collider2D.gameObject);
} } Destroy(this.gameObject);
} } }
114
Enfin, glissez-déposer le script dans bomb_0.
Une dernière chose pour terminer cette étape. J'ai oublié de vous informer de taguer la main
avec "hand". Pour cela, sélectionnez "hand" dans la fenêtre "Hierarchy" et pour "Tag",
sélectionnez "Add Tag".
115
En face de "element_0", inscrivez "hand". Fichier:Step.1420.jpg
Ensuite sélectionnez "hand" pour "Tag". Sélectionnez aussi le tag "hand" pour "penciltip".
116
Si vous cliquez à nouveau sur "Play", la bombe doit tomber, exploser et supprimer la barre de
navigation.
117
1.8. Travaux Pratique
a. Partant de notre jeu de cochon d’inde, faites un jeu en 2D qui aura comme 3
niveau :
b. Créer un jeu de gestion de parc d'attraction pour parler de ce style de jeu et de ses
contraintes. En premier lieu, il faut énormément d'éléments de gameplay (les
attractions, les décors, les files d'attente, les ventes de produits annexes, etc ...)
beaucoup de sprites en perspective, il y a aussi de l'IA, les personnes qui viennent
dans le parc, qui utilisent les attractions et qui consomment des produits.
L'interface va être un point très important avec tous les menus de gestion et de
construction, de plus la caméra est primordiale dans le confort du gameplay. Tous
ces points sont techniques et certains comme les éléments de gameplay nécessaires sont très
chronophages. Si vous développez seul, je vous déconseille de vous lancer dans une telle
aventure, il y a peu de chance qu'elle aboutisse et même si vous y arrivez ça va être très dur de
tenir la comparaison avec les jeux du même style faits en studio, autant en terme de quantité
d'élément et de fonctionnalité qu'en terme de qualité graphique.
118
CHAPITRE 3 : INTRODUCTION A LA TROSIEME DIMENSION (3D)
Dans ce chapitre, nous essaierons aborder cela sous forme théorique pour
éclairer les apprenants dans le monde de la modélisation à trois dimensions et comment
utiliser les éléments de base d’Unity pour vous donner un élan ‘apprentissage.
3.1. Les concepts de base
119
Tout demi-cercle passant par les pôles est un méridien. Pour repérer les
méridiens, on choisit une méridienne origine qui le méridien de Greenwich
On utilise les coordonnées pour repérer une position. Nous allons en étudier
deux : les coordonnées géographiques et les coordonnées cartésiennes.
120
3.1.2. Les vecteurs
Un vecteur est représenté par un segment orienté (une flèche), ayant pour
extrémités un point de départ et un point d'arrivée. L’emplacement dans le plan ou
l'espace n’a pas d’importance, deux déplacements de deux points d'origine distincts
peuvent correspondre au même vecteur, seuls comptent sa longueur, sa direction et son
sens. Il est donc possible de le faire glisser librement dans le plan, parallèlement à lui-
même. Si A et B sont deux points distincts, le vecteur AB possède trois éléments
caractéristiques :
121
Si l’on a choisi une unité de longueur dans le plan, un vecteur est caractérisé
par :
Exemple :
Egalité de vecteurs
Deux vecteurs sont égaux s’ils ont la même direction, le même sens et la
même norme ou longueur.
Exemple :
(AB) // (CD)
AB = CD
Remarque 2 :
123
Règle du parallélogramme n°2.
Relation de Chasles.
Ce qui est important pour cette relation de Chasles, c’est que le deuxième point du
premier vecteur (ici B) soit le même que le premier point du second vecteur.
124
Translation.
L’image d’une droite (d) par une translation est une droite (d’) qui est parallèle à (d).
La norme du vecteur est la longueur AB. On la note et on lit "norme du vecteur AB" Un
vecteur unitaire est un vecteur de norme égale à 1.
La notion de vecteur peut être définie en dimension deux (le plan) ou trois
(l'espace euclidien usuel). Elle se généralise à des espaces de dimension quelconque.
Cette notion, devenue abstraite et introduite par un système d'axiomes, est le
fondement de la branche des mathématiques appelée algèbre linéaire. Le vecteur
permet, en physique, de modéliser des grandeurs qui ne peuvent être complètement
définies par un nombre ou une fonction numérique seuls. Par exemple, pour préciser
un déplacement, une vitesse, une force ou un champ électrique, la direction et le
sens sont indispensables. Les vecteurs s'opposent aux grandeurs scalaires décrites par
un simple nombre, comme la masse, la température, etc.
Par exemple, les trois vecteurs de la figure ci-dessous sont égaux, même s'ils ont
des points initiaux et terminaux différents. Ces trois flèches représentent donc le
même vecteur. Un vecteur n'a pas de « point d'attache ».
125
Le vecteur qui a une longueur de 0 est appelé vecteur nul et est noté ⃗𝟎.
Le vecteur nul n'a évidemment pas de direction, donc pas de sens.
Les trois vecteurs ci-contre sont les représentants d'un même vecteur car ils ont même
sens, même direction et même norme. On peut donc désigner ce vecteur par un nom
unique, par exemple :
⃗⃗ , 𝒗
b. L'addition de vecteurs est aussi associative. Cela veut dire que, si 𝒖 ⃗ et
⃗⃗⃗ sont des vecteurs, alors
𝒘
126
⃗ −𝒘
La différence 𝒗 ⃗⃗⃗ de deux vecteurs est définie comme
Il est tout à fait possible de prendre deux autres vecteurs pour former une base, pourvu
qu'ils ne soient pas multiples. Il est à noter que l'ordre des vecteurs a de l'importance.
En deux dimensions, les deux vecteurs 𝒊 et 𝒋 forment ce que l'on appelle la base
canonique. Elle est orthonormée : les deux vecteurs sont orthogonaux et ont une
longueur de 1.
⃗ est un vecteur ayant son point initial à l'origine O et son point terminal en P(a,b),
Si 𝒗
⃗ comme combinaison des vecteurs 𝒊 et 𝒋:
alors on peut représenter 𝒗
127
⃗ dans la base ( 𝒊 ; 𝒋 ), a étant
Les scalaires a et b sont appelés les composantes du vecteur 𝒗
la composante dans la direction 𝒊 et b la composante dans la direction 𝒋. En n
dimensions, les vecteurs ont n composantes.
Supposons qu'un vecteur 𝒗⃗ a pour point initial P1(x1; y1) et comme point terminal
P2(x2; y2). On a alors :
128
03. Soit G le centre de gravité d’un quadrilatère quelconque ABCD, c.-à-d. G est l’unique
point vérifiant l’égalité :
129
CHAPITRE 4 : EN ROUTE VERS LA BASE D’UNITY 3D
• Enregistrer la scène courante (extension .unity) en lui donnant un nom (File >
Save Scenes).
Lorsque vous créez un projet, vous obtenez par défaut dans la scène une caméra
(Main Camera) et une lumière pour éclairer la scène 3D (Directional Light). La
fenêtre de l’éditeur se présente alors de la façon suivante :
130
Cette fenêtre de l’éditeur est composée des éléments suivants :
1. L’éditeur graphique de la scène 3D permettant de manipuler directement des
objets de la scène.
2. L’arborescence de la scène permettant de visualiser la hiérarchie des objets
présents dans la scène.
3. L’inspecteur permettant de voir les différents scripts rattachés à l’objet
sélectionné (par 1 ou 2) et de modifier leurs paramètres.
4. L’explorateur de projet permettant de voir les différents composants du projet
et de les ajouter à la scène par un simple glisser-déposer. Pour l’instant, il y a
seulement la scène dans le projet.
5. Les boutons permettant de lancer l’exécution de la scène 3D dans l’éditeur ce
qui est très utile pour tester et debugger la scène (nous verrons plus tard
comment créer un exécutable pour la scène).
6. La visualisation de la scène 3D à partir du point de vue de la caméra virtuelle.
L’éditeur bascule automatiquement sur cette visualisation lorsque l’exécution
de la scène est lancée.
131
7. La console permettant de voir les messages d’erreurs ou les logs lors de
Pour commencer, nous allons créer une application toute simple qui affiche
plusieurs cubes de couleur bleue à des positions différentes :
• Insérer un plan dans la scène pour créer un sol (GameObject > 3D Object >
Plane).
Au moyen de l’inspecteur (3), placer ce plan aux cordonnées (0, 0, 0) et vérifier
qu’il est bien visible par la caméra maintenant. Agrandir ce plan en jouant sur
l’échelle pour qu'il est une longueur et une largeur de 100 mètres et vérifier qu’il
est bien visible à l’infini lorsque l’on prend la vue de la caméra.
• Ajouter un cube dans la scène (GameObject > 3D Object > Cube) et placer le
pour qu’il soit visible par la caméra.
• Créer un material pour donner une couleur au cube (Assets > Create > Material).
Un nouveau composant est créé dans le projet (4), donner lui un nom. Puis
modifier sa couleur dans l’inspecteur pour obtenir le bleu de l’image suivante.
Ensuite glisser-déposer le material à partir de l’explorateur de projet (4) sur le
cube dans l’arborescence de la scène (2). Le cube devrait prendre la couleur bleue
dans l’éditeur graphique (1).
• Ajouter une texture sur le plan : télécharger la texture grid_grey.png sur le site du
cours, aller dans (Assets > Import New Asset…) et choisir la texture. Glisser-
déposer la texture à partir de l’explorateur de projet (4) sur le plan dans
l’arborescence de la scène (2). Puis sélectionner le plan dans l’arborescence de la
scène (2) et modifier les paramètres du material dans l’inspecteur (3) afin
d’obtenir la même texture du sol que dans l’image ci-dessous. Il faudra en
particulier jouer sur le nombre de fois que la texture est répétée (« tiling »).
• Déplacer le cube et la caméra afin d’obtenir une image similaire lorsque vous
exécutez la scène :
132
• Ajouter un comportement physique au cube : sélectionner le cube et lui ajouter
un Rigidbody (Component > Physics > Rigidbody). Observer ce qui se passe
lorsque vous exécutez la scène.
• Exécuter la scène.
133
• La fonction Update(), qui est héritée de la classe MonoBehaviour, est appelée à
chaque pas de temps lors de l’exécution de la scène. Dans cette fonction Update(),
effectuer une rotation de 3° selon l’axe vertical (Y). Pour cela, vous pourrez
accéder à la position de l’objet auquel le script sera associé au travers de l’attribut
transform (déjà défini dans la classe mère MonoBehaviour). Vous pourrez utiliser
l’auto-complétion de Mono pour voir les fonctions que l’on peut appliquer à cet
attribut transform et trouver la fonction qui permette d’effectuer la bonne
rotation.
• Exécuter la scène.
• Pour une plus généricité du script, ajouter un attribut public dans la classe
RotateCube avant la fonction Start() : public float speed = 3.0f;
• Vous remarquerez l’apparition de cet attribut dans l’inspecteur (3), ce qui permet
de modifier la valeur de la vitesse de rotation sans éditer le script à chaque fois.
Noter que la valeur 3.0f est la valeur par défaut lorsque l’on ajoute le script à un
nouvel objet.
Unity 3D permet de capter les entrées de base comme les appuies sur les touches
du clavier ou les actions de la souris. Cependant, afin de permettre une plus grand
généricité et de ne pas coder en dur toutes les commandes dans les scripts, Unity
3D propose un système de gestionnaire d’Input.
Nous voulons donc maintenant modifier le script qui fait tourner les cubes pour
que l’on puisse activer et désactiver la rotation. Par défaut, la rotation sera
désactivée. Lorsque l’utilisateur appuiera sur la touche « r » (et la relâchera ensuite),
les cubes se mettront à tourner. Puis lorsqu’il appuiera à nouveau sur la touche « r
» (et la relâchera ensuite), les cubes s’arrêteront de tourner, et ainsi de suite : dès
134
que la touche « r » est appuyée, les cubes changeront leur comportement. Pour cela
:
• Aller dans le menu (Edit > Project Setting > Input), ajouter un nouvel axe en
modifiant la taille Size. Changer son nom en « ActivateRotation » par exemple et
définir le Positive Button comme la touche « r ».
• Pour vérifier le bon fonctionnement de votre script, vous pouvez afficher dans la
console de l’éditeur (7) un message chaque fois que la touche « r » est appuyée.
Pour cela, vous pourrez utiliser la syntaxe suivante : Debug.Log ("votre message");
Nous souhaitons créer un nouveau script permettant de pousser les cubes lorsque
l’on clique dessus et de changer la couleur du cube change lorsque l’on passe la
souris sur ce cube. Pour cela, nous allons utiliser les fonctions OnMouse… de la
classe mère MonoBehaviour :
-void OnMouseDown ()
-void OnMouseDrag ()
-void OnMouseEnter ()
-void OnMouseExit ()
-void OnMouseOver ()
-void OnMouseUp ()
• Créer deux attributs privés de type Rigidbody et Renderer afin de pouvoir stocker
des liens vers le RigidBody (pour la physique) et le Renderer de l’objet (pour
pouvoir accéder au material et changer la couleur).
• Dans la fonction Start(), initialiser vos deux attributs en utilisant les fonctions :
GetComponent<Rigidbody>() et GetComponent<Renderer>()
• Pour pousser le cube, vous pourrez appliquer une force sur son Rigidbody en
utilisant la fonction AddForce(Vector3D val_dir_force). L’attribut Camera.main
permet d’accéder à la caméra principale et Camera.main.transform.forward
135
donne le vecteur pointant vers l’avant de la caméra. Cependant, il faudra le
multiplier pour obtenir une force suffisante.
• Exécuter la scène, tester les commandes (flèches, barre d’espace, souris) et écouter
aussi le son produit.
Nous voulons maintenant créer dynamiquement des objets dans la scène 3D. Pour
cela, Unity 3D propose une fonction Instantiate(GameObject obj) qui permet
d’instancier dynamiquement dans la scène une copie de l’objet passer en paramètre.
L’objet passé en paramètre peut être un objet déjà présent dans la scène ou bien
un prefab si l’on ne veut pas que l’objet soit déjà dans la scène.
Nous souhaitons faire en sorte que des cubes jaunes soit lancés dans la direction de
la caméra lorsque l’utilisateur appuie sur le bouton droit de la souris :
• Créer un nouveau script C# que vous nommerez CreateOnClick.
136
• Créer un attribut public GameObject aCopier qui vous permettra de stocker une
référence sur l’objet à instancier. Il faudra initialiser cet attribut en faisant un
glisser-déposer du CubePrefab dans le champs de l’attribut dans l’inspecteur (3)
lorsque l’on a sélectionné le FirstPersonCharacter.
• Par défaut dans les Input du projet, le clique droit génère l’événement « Fire2 »
que vous pourrez capter avec les fonctions getButton… comme pour l’évènement
« ActivateRotation ».
Noter que l’on peut aussi supprimer des objets de la scène grâce à la fonction
Destroy(GameObject obj).
Nous souhaitons charger le modèle 3D d’une voiture avec des textures plaquées
dessus :
• Décompresser cette archive. Il contient un .obj décrivant le mesh, un .mtl
décrivant les couleurs et les placements des textures, ainsi que les images de
texture .tga.
137
• Essayer de lancer des cubes sur la voiture ou de marcher au travers. Qu’observez-
vous ? Pourquoi ?
• Trouver une solution pour régler ce problème. Vous pourrez utiliser des Box
Colliders que vous placerez vous même ou des Mesh Colliders que vous ajouterez
aux parties adéquates de la voiture (cf. images sur la page suivante). La 2nd
solution est plus précise, mais plus couteuse en temps de calcul.
Enfin, nous voulons créer un exécutable pour cette scène afin de pouvoir visualiser
la scène en plein écran et éventuellement de la distribuer :
• Aller dans File > Build Settings…
• Sélectionner la cible PC, Mac & Linux Standalone. Observez qu’il y a d’autres
cibles intéressantes comme par exemple WebGL (cf. l’exécutable de la scène du
tutoriel sur le site du cours).
Vous pouvez ensuite cliquer sur Player Settings… en bas de la fenêtre Build
Settings… et explorer les différents paramètres du player (fenêtre de dialogue au
lancement, resolution, etc.).
138
4.11. TP
1. Concevoir une animation en 3d d’un cochon d’inde en utilisant l’objet Game
Play et remplir que l’objet puisse être dynamique et que sa puisse interagir
avec le clavier et la souris.
2. Ajouter l’animation des ennemies (Chat) ainsi que la nourriture de notre
cochon d’inde.
Conclusion
139
Bibliographie
1. Livre
Développez des jeux 3D avec Unity traduit par Philippe Beaudran, avec la
contribution technique de Nicolas Colliot, responsable développement chez
Yamago
Tutoriel Complet Unity Défi 3 et 2 par microsoft et magic
INTRODUCTION TO UNITY, University of Queensland 2016
3D Game Development with Unity A Case Study: A First-Person Shooter
(FPS) Game
2. Site web
https://www.supinfo.com/articles/single/6880-creer-simple-jeu-unity-3d-
partie-1
https://fr.tuto.com/unity/
https://medium.com/@lycanthrop/d%C3%A9couvrir-les-bases-de-unity-
avec-boris-tome-1-a3a4a24f8b05
http://math.ens-paris-saclay.fr/version-francaise/formations/master-
mva/contenus-/nuages-de-points-et-modelisation-3d-
219052.kjsp?RH=1242430202531
https://www.sciencedirect.com/science/article/abs/pii/S1286011505833715
140