Travail: Synchro. : Image/Musique
12.08.2010Logiciels: Adobe® Flash CSx
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;
}
}
//...
Résumé
La fonctionnalité principale du jeu était la synchronisation entre musique et affichage.
Responsabilité : Développement de cette fonctionnalité.