C# est un langage de programmation originairement conçu pour tirer parti de l'ensemble des possibilités de la plateforme .NET développé par Microsoft®. C'est un langage objet dont la logique est fortement inspirée de Java et la syntaxe du C/C++.

C# et Unity 3D

Aujoud'hui, il en existe une version libre, Mono, qui a été choisi par Unity pour son moteur. Nommé Unity 3D, il intègre une logique toute particulière permettant une grande flexibilité et le développement très rapide de prototypes et de jeux vidéos. En plus des librairies de C#, il utilise le principe des "composants".

La logique "composants"

Chaque objet créé dans l'IDE de Unity3D contient une liste de composants. Chacun d'eux est destiné à une tâche particulière: (affichage d'un objet polygonal, lecture d'un son, etc.). Mais, il existe également un composant de type script, qui permet de définir le comportement de l'objet via le code. Par exemple, on peut accéder au composant d'affichage et appliquer la couleur de son choix à la texture, en créant un script prévu à cet effet, et en l'ajoutant à la liste des composants de l'objet.

Mes compétences

  • Formateur sur C# appliqué à Unity3D: Suivi technique de projet, enseignement, etc.
  • Mono C# pour Unity3D: Utilisation avancée.
  • C# pour Microsoft® .NET: Bonnes notions.

Travail: La sauvegarde

22.04.2010
Réalisé pour le projet: Contre-Jour en 2010 à Supinfogame - Compétence: C#
Logiciels: Unity 3D

En tant que jeu d'aventure, Contre-Jour propose une histoire. Donc, il était nécessaire de pouvoir sauvegarder la progression du joueur. Aussi, le jeu est organisé en petits niveaux reliés par des entrées/sorties. Quand le héros se déplace dans un autre niveau, la progression est sauvegardée dans un fichier binaire.

La sauvegarde concerne différentes informations :

  • Les infos joueur : les pièces collectées, le niveau actuel, l'entrée actuelle dans le niveau... ;
  • Les informations concernant tous les niveaux : les positions des objets interactifs, les pièces et objets récupérés, les portes ouvertes, la position des interrupteurs,... ;
  • Les paramètres sauvegardables du GSE : Pour jouer un évènements une fois seulement...

Pour faire cela, J'ai utilisé un fichier binaire avec une nomenclature adaptée. Ce type de fichiers est organisé de manière séquentiel. Une tête de lecture lit et écrit les informations puis se déplace vers l'avant.

La nomenclature du fichier de sauvegarde

Dans le flux principal

  • Les infos joueur au début ;
  • Le nombre d'octets puis le flux concernant les niveaux.

Dans le flux des niveaux

Le flux contient le nombre total des niveaux et la liste des informations pour chaque niveau : le nombre d'octets et le flux du niveau correspondant.

Dans chaque flux de niveau

Chacun des flux contient le nombre d'objets sauvegardés et une liste séquentielle des informations pour chacun d'eux. Les informations d'un objet sont formées d'un identifiant et d'un flux spécifique (pour une caisse que l'on peut pousser, on conserve la position, par exemple) . Les boîtes du GSE sont gérées comme les autres objets mais on sauvegarde leurs paramètres à la place.

Côté code

A l'origine, j'ai utilisé deux types de fonctions basiques de C#.

Pour sauvegarder
//Créé un fichier ou ouvre le fichier existant et écraser le contenu
BinaryWriter binWriter = new BinaryWriter(File.Open("monfichier.txt",FileMode.Create));
//Ecrit des informations dans le fichier
binWriter.Write("CJSaveGame");//une chaîne: string
binWriter.Write(10);//un entier: int
binWriter.Write(10.5f);//un nombre à virgule flottante: float
binWriter.Write(true);//un booléen: bool
//etc.
binWriter.Close();//ferme le fichier
Pour charger:
if(File.Exists("monfichier.txt")){//Vérifie l'existence du fichier
//Ouvre le fichier
BinaryReader binReader = new BinaryReader(File.Open("monfichier.txt" , FileMode.Open));
try{
if(binReader.PeekChar() != -1)//Si le fichier n'est pas vide
{
//ReadString: lit les octets et déplace la tête de lecture
string str = binReader.ReadString();//une chaîne: string
int integer = binReader.ReadInt32();//un entier: int
float number = binReader.ReadSingle();//un nombre à virgule flottante: float
bool boolean = binReader.ReadBoolean();//un booléen: bool
//etc.
}
}
finally{
binReader.Close();//ferme le fichier
}
}

Résumé

Comme pour tous les jeux vidéo next-gen, Contre-Jour sauvegarde automatiquement la progression du joueur avec des checkpoints (Ici,le changement de niveau). Ainsi, le joueur peut reprendre au niveau en cours si il quitte.
Travail : J'ai programmé les processus de sauvegarde et de chargement.


Travail: GSE : Editeur Scripts Graphique

20.03.2010
Réalisé pour le projet: Contre-Jour en 2010 à Supinfogame - Compétence: C#
Logiciels: Unity 3D

Contre-Jour est avant tout un jeu d'aventure, avec un univers, une histoire, des personnages. Dans le jeu, Charles, le héros, communique souvent avec la reine des lucioles, croise un vigile plutôt patibulaire à plusieurs reprises... Pour mettre en place ces évènements et autres cinématiques, il fallait un outil. J'ai donc créé un éditeur destiné à l'équipe permettant de scripter ces éléments directement sur Unity3D.

L'idée a germé en début de production alors que nos ambitions en terme de scénarisation étaient grandissantes. Inspiré de l'outil disponible sur UDK, le Unreal Kismet, le GSE est une succession de boîtes reliées par des flèches.


Chaque script commence avec une boîte d'évènement (exemple : un personnage entre dans une zone qui déclenche un évènement) . D'autres boîtes sont connectées à elle et effectuent des actions (jouer une animation, émettre un son...) .


Un exemple plus précis


La mise en place

Mettre en place le GSE n'a pas été une décision simple. Il fallait que je vérifie que le temps de développement serait court et m'assurer que l'outil serait fonctionnel et facile à utiliser par toute l'équipe. J'ai donc pris une semaine pour faire un prototype pour quelques boîtes. Les résultats étaient plus qu'encourageants. En quelques jours, je pouvais faire des petites séquences d'actions simples et à l'utilisation, quelques cliquer/déposer permettaient de faire des enchaînements de boîtes.

L'étape suivante a été de communiquer le fonctionnement du GSE à l'ensemble de l'équipe. La plupart des membres du groupe connaissait le Kismet, ce qui m'a grandement facilité la tâche côté logique de l'éditeur. Au fur et à mesure de l'évolution du projet, j'ai ajouté des types de boîtes supplémentaires en fonction des besoins. A terme, nous en avions 65 en tout.

Résultat

Programmer cet éditeur m'a permis d'économiser beaucoup de temps en scripts d'évènements et de cinématiques. Désormais, l'équipe faisait ce travail. En outre, j'ai eu la possibilité de me concentrer sur d'autres features de code.

Résumé

J'ai créé un outil pour ajouter facilement des évènements et des cinématiques on jeu. Cela consiste en un ensemble de boîtes reliées entre elles par des flèches. Chaque type de boîte fait une action particulière (Emettre un son, jouer une animation, afficher des sous-titres...) . Inspiré de Unreal Kismet, l'outil était très utile car étant le seul programmeur du projet, j'aurais eu à intégrer les évènements moi-même sans cela. J'ai pu ainsi me concentrer sur d'autres features de code grâce à cet éditeur et le reste de l'équipe pouvait mettre en place ce qu'ils souhaitaient de manière très précise en jeu.
Travail : Design et code du GSE ; enseignement de son utilisation à l'équipe.


Travail: Le streaming de niveaux

05.02.2010
Réalisé pour le projet: Contre-Jour en 2010 à Supinfogame - Compétence: C#
Logiciels: Unity 3D

Dans Contre-Jour, les niveaux sont courts et le joueur les parcourt rapidement. Pour gagner du temps dans le chargement des scènes, j'ai créer un gestionnaire de streaming des niveaux.

Au lancement du jeu, il charge le niveau courant où le personnage se situe et lance la partie. Ensuite, le niveau est jouable directement mais pendant que l'on joue, le gestionnaire continue à charger les niveaux adjacents. Par exemple, quand le héros est dans la première carte (le point rouge), le deuxième niveau est préchargé.

Si il a fini son chargement quand le joueur termine le premier niveau alors on change de carte sans aucun écran de chargement. Une fois dans ce nouveau décor, le troisième se met en chargement et ainsi de suite. La première carte reste en mémoire parce qu'elle est adjacente. On peut donc revenir sur ses pas sans attente.

Côté code

Le gestionnaire de streaming des niveaux utilise une liste de niveaux. Chaque élément de la liste contient la scène à charger et une liste de booléens.

  • isLoading : Le niveau est-il en cours de chargement ;
  • isDataLoaded : La carte est-elle en mémoire.

[System.Serializable]
public class levelsList{
public string name;//identifiant du niveau
public string description;

[System.NonSerialized]
public bool isLoading = false;//Le niveau est-il en cours de chargement
[System.NonSerialized]
public bool isDataLoaded = false;//Le niveau est-il chargé
}

public class GameManager : MonoBehaviour {
public LevelsListElement[] levelsList;//Liste des niveaux
//...
}

Unity3D dispose d'une fonction simple de chargement de scène en asynchrone.

private IEnumerator streamLevelCoroutine(int level){
if(!levelsList[level].isLoading && !levelsList[level].isDataLoaded){//On lance le chargement que si nécessaire
//LoadLevelAdditiveAsync: Stream le chargement d'une scène.
yield return Application.LoadLevelAdditiveAsync(levelsList[level].name);
levelsList[level].isDataLoaded = true;//Le niveau est chargé et disponible

levelsList[level].isLoading = false;//Chargement terminé
}
}

Résumé

Pour avoir un jeu de qualité, nous avons décidé de diminuer le temps de l'écran de chargement entre les niveaux.
Travail : J'ai programmé un gestionnaire de streaming des niveaux.