Travail: Pixel Art: Pirates

15.06.2011
Réalisé pour le projet: Divers en 2011 à titre personnel - Compétence: Pixel Art
Logiciels: Character Maker

Résumé

Avec un ami, nous avons travaillé ensemble sur un projet commun. J'étais responsable du graphisme du jeu.
Travail : Nous avons fait le Game Design ensemble. J'ai créé l'ensemble des décors, des personnages avec leurs animations, et autres objets en pixel art.

Pendant quelques mois j'ai eu l'occasion de travailler avec un ami sur un projet commun. Malheureusement, nous avons arrêté en cours de production. Le jeu est un platformer 2D sur le thème des pirates. Je ne pourrais pas expliquer le Game Design sur lequel nous avons travaillé ensemble, mais je vais profiter de cet article pour montrer quelques éléments que j'ai réalisé en graphisme.

Le héros du jeu

Le personnage principal a nécessité un grand nombre d'animations : à peu près 25 (soit 150 images).Il est capable d'exécuter des acrobaties variées. Quelques exemples dans cette vidéo :


Les décors

Je ne pourrais évidemment pas montrer grand chose à part cet exemple d'ambiance. Évidemment, le décor permet le défilement parallaxe. A la manière des jeux 16-bits, les éléments bougent de façon cohérente pour créer un effet de profondeur : les éléments proches défilent plus vite que les éléments lointains.

Des lutins

Quelques animations amusantes en bonus. Des lutins plutôt vulgaires font leur apparition dans le jeu.

Quelques images

landscape.png

Travail: Synchro. : Image/Musique

12.08.2010
Réalisé pour le projet: Pump it! en 2010 à MXP4 - Compétence: ActionScript
Logiciels: Adobe® Flash CSx

Résumé

La fonctionnalité principale du jeu était la synchronisation entre musique et affichage.
Responsabilité : Développement de cette fonctionnalité.

Sur Pump it! j'ai eu l'occasion de programmer la gestion de la synchronisation entre le jeu et la musique.

Le deltaTime

Habituellement, les jeux en temps réel fonctionnent sur le principe du deltaTime : c'est la différence de temps écoulé entre deux images. A priori, ce temps devrait être stable :

Pour une application en 30 images par seconde, le temps entre deux images est de 1/30 de secondes : 0.03333.. s.

Néanmoins, en fonction de la machine, de l'occupation du processeur, ce temps varie au cours de l'exécution. Le connaître permet de placer les éléments à l'écran de manière à combler les ralentissements ou les hausses de vitesse. Le jeu est ainsi fluide et même avec une perte sensible du nombre d'images par seconde, le temps s'écoule de manière constante.

Synchronisation avec la musique

Pour synchroniser le jeu avec la musique, il suffit de calculer ce deltaTime à partir de la piste audio. C'est à dire que l'on calcule la différence entre la position en millisecondes de la tête de lecture à l'image en cours et celle qu'elle avait à l'image précédente. Ainsi, si la piste audio subit des ralentissements ou des hausses de vitesse, les éléments de l'écran sont ajustés et parfaitement synchrones avec le son.

Côté code

On calcule le deltaTime lié à la piste son avec une fonction liée à un évènement onEnterFrame.

import flash.display.MovieClip;
import flash.events.Event;
import flash.media.SoundChannel;

public final class SoundTimer
{
private var _channel:SoundChannel;//Une référence à la musique

private var _lastTime:Number;//Contient la position de la tête de lecture à l'image précédente
private var _deltaTime:Number = 0;//Contient le deltaTime

public static function get deltaTime() {//Permet de récupérer le deltaTime de n'importe où
return _deltaTime;
}

public function SoundTimer(soundChannel:SoundChannel)
{
//On assigne OnEnterFrame à updateDeltaTime
//updateDeltaTime est appelé à chaque nouvelle image
var mc:MovieClip = new MovieClip();
mc.addEventListener(Event.ENTER_FRAME, updateDeltaTime);

_lastTime = 0;//Au départ le temps à l'image précédente est de 0
assignSound(soundChannel);//assigne la référence au son
}

public function assignSound(soundChannel:SoundChannel) {
channel = soundChannel;
}

private function updateDeltaTime(ev:Event) {
var currentTime:Number = _channel.position;//On récupère la position à l'image actuelle
_deltaTime = (currentTime-_lastTime) / 1000;//on fait la différence avec la position à l'image prédédente
//La position de l'image précédente est écrasée par l'actuelle
//Ainsi quand la fonction est appelée à l'image suivante, son image précédente est celle juste avant
_lastTime = currentTime;
}
}

On utilise ensuite ce deltaTime lors du calcul des déplacements des notes, par exemple.

//! Ce code n'est pas du tout optimisé mais sert à illustrer l'exemple.
//...
private var tones:Vector.<Sprite>//Contient les notes
private tempo:Number = 120;//Le tempo en pulsations/minutes est ici régulier
private laneWidth:Number = 200;//La longueur en pixels parcourue par la note à chaque pulsation
//...
//Update est appelée à chaque nouvelle image
public function update() {
for each(var tone:Sprite in tones) {
//On traduit le tempo en secondes
//On multiplie par la longueur que la note doit parcourir à l'écran en une pulsation
//On multiplie par le temps qui s'est écoulé entre l'image précédente et celle-ci pour être synchronisé avec la musique
tone.x += tempo / 60 * laneWidth * SoundTimer.deltaTime;
}
}
//...


Travail: Interface de PUMP IT! V1.0

15.07.2010
Réalisé pour le projet: Pump it! en 2010 à MXP4 - Compétence: Graphisme vectoriel
Logiciels: Adobe® Flash CSx

Résumé

C'est la première interface du jeu. Je l'ai dessinée au début du projet.
Responsabilité : Design de l'écran de jeu.

Alors que le Game Design était en cours, j'ai réalisé une maquette de l'écran principal de Pump It!.

Pour cette interface, je me suis inspiré des jeux vidéo de rythme les plus joués du moment : Guitar Hero, Rock Band.

Le jeu que nous devions faire était à l'origine déstiné à la page des fans de David Guetta, je me suis donc attardé sur un design en accord avec le style musical de l'artiste et son public.

Les informations à afficher étaient :

  • L'aire de jeu avec les quatre quart de cercles, le bouton pump it, les notes ;
  • Les bonus multiplicateur X2 pour chaque quart ;
  • Le score, le nombre de fans : les fans viennent s'ajouter au score en fin de partie ;
  • Le volume : la jauge représentant le nombre de notes collectées. Une fois au maximum, le joueur clique sur le bouton pump it au milieu, au moment où il est rouge.

Au final, l'interface représente un haut-parleur sur lequel les notes défilent. Le centre représente le point de récupération des notes et correspond au bouton à cliquer quand le maximum des notes à collecter est atteint. Dans chaque coin, les valeurs des bonus sont clairement identifiables par leur design lumineux.

D'un point de vue défilement des notes, on pourrait penser que les faire glisser de l'intérieur du haut-parleur à l'extérieur représente mieux la propagation du son mais côté gameplay les notes sont plus difficiles à suivre car le regard doit se poser sur les quatre coins de l'écran en même temps. Il était plus judicieux d'avoir un mouvement vers l'intérieur.

Une version 2

Entre la V1 et la suivante, nous avons remis en question quelques principes de Gameplay et réajusté l'interface. Je suis passé en programmation à ce moment là et le graphiste de notre équipe a modifié le design graphique.

  • La jauge de volume a été conservée pour l'esthétique mais il fallait un signe au centre, où le regard se porte constamment, nous avons entouré une jauge autour du bouton pump it ;
  • Le design du bouton est passé en bleu ;
  • Les multiplicateurs sont plus visibles grâce à un filigrane qui apparaît sur chaque quart ;
  • Le format 4/3 est devenu un format carré, il a donc fallu adapter ;
  • Nous avons ajouté une feature de pause que l'on déclenche à l'aide du bouton en haut à gauche ;
  • Le graphisme a été épuré.

Quelques images

pumpit_interface_v1.png

Travail: Game Design sur Pump it!

02.07.2010
Réalisé pour le projet: Pump it! en 2010 à MXP4 - Compétence: Game Design
Logiciels: Microsoft Word

Résumé

En préproduction, nous avons travaillé ensemble à la rédaction du Game Design document de Pump it!
Travail : Rédaction du Game Design document permettant de tirer les 'user stories' dans le cadre de la méthode Scrum.

Le Game Design était la première tâche que nous avions sur Pump it. Nous avons commencé avec le game concept, un petit prototype et nous avons écrit le game design document. Tous le design a été fait par notre équipe.

Le game concept

Appelé Simon, comme le jeu de mémoire de Ralph Baer, nous avons changé son nom pour Pump it au cours de la rédaction du GD.

Le prototype

Pour moi, quand les idées peuvent être implémentées rapidement, il est important de développer un prototype. Très tôt, nous avons décidé d'en créer un pour montrer le principe à la direction. Développé en Actionscript2 par un membre de l'équipe, il était aussi très utile pour communiquer sur le projet.

Le Game Design document (GDD)

Le game design document servait exclusivement à remplir le product backlog. Nous étions en scrum donc nous avons rédigé un document orienté sur les besoins du client (la direction de MXP4) . Malheureusement, je ne peux pas divulguer le document sur ce site.


Travail: La sauvegarde

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

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.

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
}
}