Accueil > Code > Calcul du framerate réel d’une animation

Calcul du framerate réel d’une animation

Parfois, avec Flash, il faut savoir réinventer la roue. Par exemple, lorsque l’on veut coder un moteur d’animation qui s’intègre au sein d’un système de rendu isométrique complexe, on aimerait à la foi pouvoir disposer d’un framerate le plus élevé possible, tout en jouant les animations au framerate dans lequel elles ont été réalisées.

Pour se faire, il faut obligatoirement passer par l’indispensable case du calcul du framerate en cours. Il existe peut-être des mondes merveilleux ou la valeur que l’on a entrée dans le champ « Nombre d’images par secondes » de l’IDE Flash est tout le temps vraie, mais il ne s’agit pas du notre.

Voyons différentes approches pour calculer un framerate.

La méthode couplée : Timer et ENTER_FRAME

package net.tynambule.exp {
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	import flash.events.Event;
	import flash.text.TextField;
	import flash.display.Sprite;
	/**
	 * Calcul du framerate :
	 * Méthode du timer
	 */
	[SWF(backgroundColor="#ffffff", frameRate="100", width="100", height="100")]
	public class FramePerSecondCounter extends Sprite
	{
		private var _fpsDisplayer : TextField;
		private var _fpsUpdater : Timer;
		private var _framesCount : uint;
		public function FramePerSecondCounter()
		{
			// On crée et on affiche un textfield pour afficher nos FPS
			_fpsDisplayer = new TextField();
			this.addChild(_fpsDisplayer);
			// On initialise un compteur et un timer
			_fpsUpdater = new Timer(1000);
			_fpsUpdater.addEventListener(TimerEvent.TIMER, onTimer);
			stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
			_fpsUpdater.start();
		}
		private function onEnterFrame(e : Event) : void
		{
			_framesCount ++;
		}
		private function onTimer(e : TimerEvent) : void
		{
			_fpsDisplayer.text = _framesCount + "fps";
			_framesCount = 0;
		}
	}
}

L’avantage de cette méthode, c’est qu’elle donne une valeur aussi réaliste que possible. Le désavantage, c’est qu’elle est couteuse en performances (puisque l’on utilise un ENTER_FRAME, qui est toujours un évènement qu’il vaut mieux éviter quand c’est possible – même si c’est rarement possible, surtout dans ce cas), et en plus, notre valeur est mise à jour une fois par seconde seulement et n’est pas disponible durant la première seconde d’écoulement de l’application.

Méthode de l’estimation en fonction de l’écart

package net.tynambule.exp {
	import flash.utils.getTimer;
	import flash.events.Event;
	import flash.text.TextField;
	import flash.display.Sprite;
	/**
	 * Calcul du framerate :
	 * Méthode de l'estimation en fonction de l'écart
	 */
	[SWF(backgroundColor="#ffffff", frameRate="100", width="100", height="100")]
	public class FramePerSecondCounter extends Sprite
	{
		private var _fpsDisplayer : TextField;
		private var _lastFrame : int = -1;
		public function FramePerSecondCounter()
		{
			// On crée et on affiche un textfield pour afficher nos FPS
			_fpsDisplayer = new TextField();
			this.addChild(_fpsDisplayer);
			// On lance l'évènement ENTER_FRAME
			stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		private function onEnterFrame(e : Event) : void
		{
			var now : uint = getTimer();
			if(_lastFrame != -1)
				_fpsDisplayer.text = (1000 / (now - _lastFrame)) + "fps";
			_lastFrame = now;
		}
	}
}

Cette méthode permet d’avoir un FPS mis à jour image par image, disponible dès la deuxième image de l’animation. Désavantage : on fait une opération relativement complexe sur un évènement ENTER_FRAME.

Méthode du calcul du FPS global depuis le début de l’animation

package net.tynambule.exp {
	import flash.utils.getTimer;
	import flash.events.Event;
	import flash.text.TextField;
	import flash.display.Sprite;
	/**
	 * Calcul du framerate :
	 * Méthode du calcul du FPS global depuis le début de l'animation
	 */
	[SWF(backgroundColor="#ffffff", frameRate="100", width="100", height="100")]
	public class FramePerSecondCounter extends Sprite
	{
		private var _fpsDisplayer : TextField;
		private var _framesCount : uint;
		public function FramePerSecondCounter()
		{
			// On crée et on affiche un textfield pour afficher nos FPS
			_fpsDisplayer = new TextField();
			this.addChild(_fpsDisplayer);
			// On lance l'évènement ENTER_FRAME
			stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		private function onEnterFrame(e : Event) : void
		{
			_framesCount ++;
			// On ne met à jour qu'une fois chaque 50 frames pour prendre moins de temps
			if(_framesCount % 50 == 0) {
				_fpsDisplayer.text = (_framesCount / (getTimer() / 1000)) + "fps";
			}
		}
	}
}

Cette méthode est relativement peu couteuse (quoique dépendante du taux de rafraichissement désiré pour la valeur de FPS), mais n’a une précision que très limitée, puisque son temps d’adaptation est proportionnel au nombre d’images déjà jouées. Elle peut être intéressante pour des benchmarks très courts, tant que l’on garde cette méthode d’évaluation pour toutes les mesures, et que l’on fasse chaque mesure dans un temps prédéterminé égal.

La méthode de la mesure sur une période sans timer

package net.tynambule.exp {
	import flash.utils.getTimer;
	import flash.events.Event;
	import flash.text.TextField;
	import flash.display.Sprite;
	/**
	 * Calcul du framerate :
	 * Méthode du calcul du FPS sur une durée sans timer
	 */
	[SWF(backgroundColor="#ffffff", frameRate="100", width="100", height="100")]
	public class FramePerSecondCounter extends Sprite
	{
		private var _fpsDisplayer : TextField;
		// Temps en miliseconde entre deux mises à jour du compteur
		private const REFRESH_DELAY : uint = 5000;
		private var _lastThreshold : uint = 0;
		private var _framesCount : uint;
		public function FramePerSecondCounter()
		{
			// On crée et on affiche un textfield pour afficher nos FPS
			_fpsDisplayer = new TextField();
			this.addChild(_fpsDisplayer);
			// On lance l'évènement ENTER_FRAME
			stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		private function onEnterFrame(e : Event) : void
		{
			_framesCount ++;
			var now : uint = getTimer();
			var delay : uint = now - _lastThreshold;
			if(delay > REFRESH_DELAY) {
				_fpsDisplayer.text = (_framesCount / (delay / 1000)) + "fps";
				_framesCount = 0;
				_lastThreshold = now;
			}
		}
	}
}

Cette méthode me parait être la moins gourmande, et garde une bonne précision. En plus, elle a l’indéniable avantage d’être personnalisable : plus le délai de rafraichissement est long, moins souvent l’on fait un calcul « complexe » (la relativité est une chose merveilleuse). C’est la méthode pour laquelle j’ai finalement opté.

Allez, salut !

Categories: Code Tags: , ,
  1. Amano69
    05/01/2008 à 15:00 | #1

    Cool ton petit FramePerSecondCounter! Si ça ne te dérange pas je vais l’utiliser pour tester les perfs de mes scènes papervision3D. Tu passeras le bonjour à Blaggy de ma part si tu le croises dans les bureaux d’Ankama ;)

  2. 07/01/2008 à 09:15 | #2

    C’est fait pour, n’hésite pas. :)

    Je lui transmet ton bonjour ! :p

  3. 06/04/2008 à 07:06 | #3

    Merci beaucoup de partager, ca m’a été utile :)

  4. 08/03/2010 à 15:39 | #4

    merci !

  1. Pas encore de trackbacks