Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Utilisation des cookies en AS3

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Par Nataly, le 23 février 2013

Bonjour :)

Qu'il s'agisse d'enregistrer un score, des préférences claviers, des préférences tout court, le recours aux cookies est souvent la solution.

En AS3 c'est très simple comme vous allez le voir et, pour suivre ce tuto, vous n'aurez besoin que des connaissances de base, entre autres savoir manipuler les objets de type Objet (justement).


En revanche, l'exemple proposé pour mémoriser les préférences clavier, considère une pratique courante de l'écriture des classes. Pour sa mise en œuvre vous pourrez consulter le tuto dont il prend la suite (gestion des touches du clavier), mis en lien dans le paragraphe considéré.

Un cookie, qu'est-ce que c'est ?

Un cookie, vous le savez, c'est un fichier enregistré sur l'ordinateur de l'utilisateur par et pour une application, programme, site…

Du point de vue qui nous intéresse : AS3, nous le manipulerons via la classe SharedObjects (objets partagés) comme nous le confirme la doc :

La classe SharedObject est utilisée pour lire et stocker des quantités limitées de données sur l'ordinateur d'un utilisateur […]
lire la doc




Physiquement il s'agit d'un fichier texte d'extension “sol”, enregistré sur le disque local.

Démarrage rapide

Créer et accéder à un cookie

La seule chose un peu surprenante dans l'utilisation des cookies c'est qu'il n'y a pas de méthode dédiée à la création, hé non ! Quand on tente d'accéder à un cookie, s'il n'existe pas, il est créé.
La méthode (statique) qui fait tout, c'est getLocal. Vérifions :

Soit un fichier .fla nommé premierTest avec ces deux lignes :

// j'essaie d'accéder à un cookie nommé : unCookiePourTester
var _cookie:SharedObject=SharedObject.getLocal("unCookiePourTester");
trace(_cookie);
[object SharedObject]

A ce stade le cookie n'est que “potentiel” il n'est pas (encore) enregistré sur votre disque, et ne le sera que si vous y écrivez quelque chose. Pour l'instant donc, une fois le swf refermé, il n'y a rien sur votre disque.

Ecrire des données

On accède aux données via la propriété data, qui renvoie un Objet, qui se manipule comme n'importe quel Objet.
Par exemple pour y ajouter un attribut nommé uneVariable, on s'y prend comme suit :

// j'essaie d'accéder à un cookie nommé : unCookiePourTester
var _cookie:SharedObject=SharedObject.getLocal("unCookiePourTester");
trace(_cookie);
// ----> ici
_cookie.data.uneVariable="truc";
// vous pouvez préférer la syntaxe à crochets :
_cookie.data["uneAutreVariable"]=18;



Aussitôt le swf fermé, le fichier unCookiePourTester.sol est enregistré sur le disque.

Forcer l'enregistrement des données : flush

Si vous souhaitez que les données soient enregistrées immédiatement, sans attendre la fermeture du .swf considéré, vous utiliserez la méthode flush.

_cookie.flush()

Où trouver le cookie ?

Avant d'aller plus loin, sans doute serez vous curieux de vérifier la présence du-dit cookie sur votre disque.
Selon que vous utilisez un PC ou un Mac la localisation est un peu différente mais dans les deux cas un ensemble de répertoires et de sous répertoires calqués sur l'arborescence (le chemin) du swf l'ayant généré, sera créé.
Par exemple :

OSX

Depuis un Mac une recherche standard ne trouvera pas le cookie.
Pour une recherche approfondie vous pouvez utiliser EasyFind (libre)

Pour un .swf exécuté en local, on trouve donc le cookie dans le répertoire localhost.
Qu'en est-il des .swf distants ?
Ils sont tout bonnement dans un répertoire portant le nom du domaine :

Windows

Depuis windows, en attendant que quelqu'un ait le courage de produire une copie d'écran1), voici l'adresse telle que Lilive l'a constatée depuis sa machine.

C:\Users\nom-utilisateur\AppData\Roaming\Macromedia\Flash Player\#SharedObjects\XQC7G9N9\localhost\[puis arborescence du swf, comme expliqué plus haut].

Sous Chrome

Le navigateur Chrome fait quant à lui sa soupe, vous pouvez remercier Galacta qui a levé le lièvre et Goa qui l'a tiré ;)

Voici les chemins selon que vous soyez OSX ou Windows.

     %LOCALAPPDATA%\Local\Google\Chrome\User Data\Default\Pepper Data\Shockwave Flash\WritableRoot\#SharedObjects\ (Windows)
    ~/Library/Application Support/Google/Chrome/Default/Pepper Data/Shockwave Flash/WritableRoot/#SharedObjects/ (OS X)


conversation à ce sujet ici et lien sur l'article kivabien (en anglais)

Lire les données via la propriété data

Vous manipulerez l'Objet renvoyé par la propriété data comme vous en avez l'habitude, par exemple :

trace(_cookie.data.uneVariable);

Syntaxe alternative

Bien sûr vous pouvez avoir recours à la syntaxe à crochets :

_cookie.data["uneAutreVariable"]=50
trace(_cookie.data["uneAutreVariable"])

Manipuler l'objet (renvoyé par) data

Type de variables

Chaque attribut peut être un objet d’un quelconque type ActionScript ou JavaScript : tableau, nombre, valeur booléenne, ByteArray, XML, etc. la doc

Je la trouve un peu gonflée la doc ;) Quand on lit “un type quelconque”, il faut comprendre - j'imagine - un type primitif et le xml…

En tous cas ne pensez pas pouvoir enregistrer un clip (instance) et le récupérer.
Il faudra enregistrer une à une les valeurs le caractérisant.

Pour le reste, un coup de Saint Thomas nous permet de vérifier qu'on récupère bien les types enregistrés :

var _cookie:SharedObject=SharedObject.getLocal("unCookiePourTester");
 
trace(_cookie)
_cookie.data["uneString"]="10"
_cookie.data["unNumber"]=10
_cookie.data.unTableau=new Array(10,20,30)
_cookie.flush()
trace("index 0 du tableau "+_cookie.data.unTableau[0])
trace("syntaxe à crochets "+_cookie.data["unTableau"][0])
trace("type de uneString : "+typeof(_cookie.data["uneString"]))
trace("type de unNumber : "+typeof(_cookie.data["unNumber"]))
[object SharedObject]
index 0 du tableau 10
syntaxe à crochets 10
type de uneString : string
type de unNumber : number

Parcourir les attributs de l'objet data

C'est donc bien via l'objet (renvoyé par la propriété) data qu'on accède, en lecture et en écriture, aux données d'un cookie. Il faut impérativement passer par les attributs du-dit objet.

Remarque : n’affectez pas directement de valeurs à la propriété data d’un objet partagé, tel que dans so.data = someValue car Flash Player ignore ces affectations
la doc


for (var prop in _cookie.data){
	trace(prop)
}
unTableau
uneString
unNumber
uneVariable

• Enregistrer l'état d'un clip

Rafraichissez la page pour constater que l'objet conserve ses caractéristiques de taille, disposition et visuel :)
L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Pour la démo, j'imagine un clip (instance) dont on peut modifier la taille, la position et le visuel via la tête de lecture.
On le déplace par glissé-lâché
Maj-Clic augmente en largeur
Alt_Clic réduit la largeur
Le bouton modifie le visuel (déplace la tête de lecture)

C'est du tout bête pour s'entrainer à enregistrer les caractéristiques d'un clip (à défaut de l'enregistrer lui ;))

leClip.stop();
bt.addEventListener(MouseEvent.CLICK,qdClic);
leClip.addEventListener(MouseEvent.MOUSE_DOWN,qdEnfonce);
leClip.addEventListener(MouseEvent.MOUSE_UP,qdLache);
 
function qdEnfonce(me:MouseEvent) {
	leClip.startDrag(true);// Déplacer
}
function qdLache(me:MouseEvent) {
	stopDrag();
	if (me.shiftKey) {
		leClip.scaleX+=0.2;// grossir
	}
	if (me.altKey) {
		leClip.scaleX-=0.2;// réduire
	}
}
 
function qdClic(me:MouseEvent) {
	var max:int= leClip.totalFrames;
	leClip.gotoAndStop(leClip.currentFrame%max+1);// image suivante et on boucle
 
}

Pas plus pour ce qu'on veut en faire ;)
Il y a différentes techniques, c'est à vous de choisir celle qui vous convient. On peut décider un attribut par “info” à mémoriser - c'est ce que j'ai fait ; on peut décider d'un attribut de type Objet ou tableau (Array) qui stockera tout ce qui concerne le clip considéré - ce qu'on ferait sans doute dans un contexte plus élaboré.

 
leClip.stop();
bt.addEventListener(MouseEvent.CLICK,qdClic);
leClip.addEventListener(MouseEvent.MOUSE_DOWN,qdEnfonce);
leClip.addEventListener(MouseEvent.MOUSE_UP,qdLache);
 
var _cookie:SharedObject=SharedObject.getLocal("cookieMemoriseClip");
 
if (_cookie.data.taille) {
	// je considère que si la taille est enregistrée, le reste aussi
	leClip.scaleX=_cookie.data.taille;
	leClip.x=_cookie.data.x;
	leClip.y=_cookie.data.y;
	leClip.gotoAndStop(_cookie.data.img_c);
}
 
 
function qdEnfonce(me:MouseEvent) {
	leClip.startDrag(true);
}
function qdLache(me:MouseEvent) {
	stopDrag();
	if (me.shiftKey) {
		leClip.scaleX+=0.2;
	}
	if (me.altKey) {
		leClip.scaleX-=0.2;
	}
// mémoriser les infos qui seront enregistrées à la fermeture du swf
	_cookie.data.taille=leClip.scaleX;
	_cookie.data.x=leClip.x;
	_cookie.data.y=leClip.y;
}
 
function qdClic(me:MouseEvent) {
	var max:int= leClip.totalFrames;
	leClip.gotoAndStop(leClip.currentFrame%max+1);
// mémoriser l'image courante
	_cookie.data.img_c=leClip.currentFrame;
}

sources en fin du tuto

• Enregistrer des préférences clavier

Pour citer BillyBen depuis son excellent tuto sur la gestion des touches du clavier :

Il est parfois de bon ton de laisser l’utilisateur définir ses touches. D’ailleurs je regrette que ce ne soit pas fait plus souvent sur les appli/jeux que l’on rencontre, alors que c’est pourtant si facile…



Au point où on est de lui permettre, à notre utilisateur préféré, de définir ses préférences clavier, autant aller au bout et les mémoriser dans un cookie.

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.
Ici, le clavier permet de déplacer la pastille.

Et, puisque on va le faire à chaque fois, autant se faire la classe qui va bien.

Comme toujours, il y a autant de façons de s'y prendre que de développeur

J'ai choisi l'option d'une classe à associer en tant que classe de base à un symbole de bibli afin de rester libre de son aspect.
Le nombre de touches à mémoriser variant selon le contexte, j'ai décidé de contraintes :
• les champs de saisie devront être préfixés de 'txt'
• on assumera, pour le cookie, un objet dont les attributs seront les noms des champs de saisie - hors préfixe -

Enfin, le clip “palette” devra porter un bouton nommé btFerme.

Le .fla

 
var palettePrefs:Mv_PalettePreferences=new Mv_PalettePreferences();
//Mv_PalettePreférence étend PrefClavier qui est la classe qui s'occupe du cookie (voire propriétés du symbole)
palettePrefs.touchesDefaut = {Droit:39,Gauche:37,Haut:38,Bas:40};// par défaut si pas de cookie
// Lit préférences clavier via cookie. si première fois, il sera crée et initialisé des valeurs par défaut
var _prefTouches:Object = palettePrefs.touchesCourantes;
 
 
btPrefs.addEventListener(MouseEvent.CLICK,affichePrefsClavier);// un bouton pour afficher la palette de préférences
stage.addEventListener(PrefClavier.FERME_PREFS, qdFermePalette);// écouter la fermeture de la palette
 
function affichePrefsClavier(me:MouseEvent) {
	// intérompre l'écoute du clavier pour le jeu
	stage.removeEventListener(KeyboardEvent.KEY_DOWN, qdToucheBas);
	stage.removeEventListener(KeyboardEvent.KEY_UP, qdToucheHaut);
	// afficher la palette;
	addChild(palettePrefs);
}
 
function qdFermePalette(e:Event) {
	trace("palette fermée, mise à jour du jeu de touches écoutées");
	_prefTouches = palettePrefs.touchesCourantes;
	// reprendre l'écoute du clavier pour le jeu
	stage.addEventListener(KeyboardEvent.KEY_DOWN, qdToucheBas);
	stage.addEventListener(KeyboardEvent.KEY_UP, qdToucheHaut);
}
 
 
// ============================================================================
// comme expliqué sur tuto Billy
 
// écoute clavier pour jeu
var touches:Object=new Object();// stockera les touches sollicitées
stage.addEventListener(KeyboardEvent.KEY_DOWN, qdToucheBas);
stage.addEventListener(KeyboardEvent.KEY_UP, qdToucheHaut);
 
 
 
function qdToucheBas(ev:KeyboardEvent):void {
	touches[ev.keyCode] = true;
}
 
function qdToucheHaut(ev:KeyboardEvent):void {
	touches[ev.keyCode] = false;
}
stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
function enterFrameHandler(ev:Event) {
	if (touches[_prefTouches.Droit]) {
		demo.x +=  5;
	}
	if (touches[_prefTouches.Gauche]) {
		demo.x -=  5;
	}
	if (touches[_prefTouches.Haut]) {
		demo.y -=  5;
	}
	if (touches[_prefTouches.Bas]) {
		demo.y +=  5;
	}
}



la classe PrefClavier.as

package {
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.display.Stage;
 
	import flash.display.SimpleButton;
	import flash.display.MovieClip;
	import flash.events.KeyboardEvent;
	import flash.text.TextField;
	import flash.net.SharedObject;
 
	// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 
	//  *** *** *** ***                      REQUIERT                             *** *** *** ***
	// un bouton - voire clip -  de fermeture, nommé btFerme
 
	//  *** *** *** ***                      ASSUME                             *** *** *** ***
	// des champs TextField nommés, avec préfixe txt
	//          les caractères suivants sont les attributs de l'objet _prefTouches
 
	//  *** *** *** ***                      EXPOSE                             *** *** *** ***
	//  touchesDefaut [écriture] : Object
	//  touchesCourantes [lecture] : Object
	//                                                      les deux via _prefTouches 
 
	// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 
	public class PrefClavier extends MovieClip {
		private var correspondance:Object = {38:"flèche haut",39:"flèche droit",40:"flèche bas",37:"flèche gauche"};
		private var _prefTouches:Object;//
		private var _cookie:SharedObject;
		public static const FERME_PREFS:String = "ferme_prefs";
 
 
		public function PrefClavier() {
			addEventListener(Event.ADDED_TO_STAGE,qdAffiche);
			btFerme.addEventListener(MouseEvent.CLICK, qdFerme);
		}
		private function qdFerme(e:Event):void {
			trace("ferme ds palette");
			// enregistre le cookie
			_cookie.flush();
 
			stage.removeEventListener(KeyboardEvent.KEY_UP,qdTouche);
			stage.focus = stage;
			stage.dispatchEvent(new Event(FERME_PREFS,true));
			MovieClip(parent).removeChild(this);
		}
 
 
		private function qdAffiche(e:Event):void {
			// init les champs
			_cookie = SharedObject.getLocal("prefClavier");
			for (var prop in _cookie.data["pref"]) {
				trace("-- "+prop+" : "+_cookie.data["pref"][prop]);
				var codeTouche:int = _cookie.data["pref"][prop];
				var special:String = correspondance[codeTouche];// pour affichage
				if (special) {
					// ecrit en toutes lettres le nom de la touche. ASSUME des champs préfixés 'txt'
					TextField(getChildByName("txt"+prop)).text = special;
				} else {
					TextField(getChildByName("txt"+prop)).text= String.fromCharCode(codeTouche);
				}
 
			}
			// écoute du clavier pour gérer le changement de préférences
			stage.addEventListener(KeyboardEvent.KEY_UP,qdTouche);
		}
 
 
		private function qdTouche(ev:KeyboardEvent):void {
			//ecrit dans le champ texte sélectionné et met à jour l'objet _prefTouches
			if (stage.focus) {
				trace(ev.keyCode);
				if (ev.keyCode == 9) {
					trace("tabulation");
					return;
				}
				var special:String = correspondance[ev.keyCode];// pour affichage en toutes lettres
				var nomChamp:String = stage.focus.name.substr(3);
				trace(("nom champ " + nomChamp));
				// ecrit
				if (special) {
					TextField(stage.focus).text = special;
				} else {
					TextField(stage.focus).text = String.fromCharCode(ev.charCode).toUpperCase()
				}
				//met à jour
				_prefTouches[nomChamp] = ev.keyCode;
			}
 
		}
 
		public function get touchesCourantes():Object {
			// Crée le cookie  si absent et renvoie le jeu de touches en cours
			_cookie = SharedObject.getLocal("prefClavier");
			trace("_cookie "+_cookie.data["pref"]);
			if (! _cookie.data["pref"]) {
				// écrit le cookie avec les valeurspa défaut
				trace("----init par défaut");
				_cookie.data["pref"] = _prefTouches;
				_cookie.flush();
			} else {
				_prefTouches = _cookie.data["pref"];
				trace("-----utilisation des préférences utilisateur");
			}
 
			return _prefTouches;
		}
 
		public function set touchesDefaut(o:Object):void {
			// au cas ou pas/plus de cookie
			_prefTouches = o;
		}
 
	}
 
}

sources en fin du tuto

Un même cookie pour plusieurs swf

On l'a vu plus haut, par défaut les cookies sont créés au fin fond d'une arborescence reproduisant le chemin (adresse complète) du swf qui fait appel à la méthode getLocal. En conséquence même si deux swf sont au même endroit et utilisent un cookie du même nom, il n'y aura pas de conflit, chaque cookie sera bien au chaud isolé dans son répertoire.

Imaginons :

www.leDomaine.fr/jeux/aventure/laVieDeToto.html  (et le swf chargé :laVieDeToto.swf)\\
www.leDomaine.fr/jeux/aventure/chasseAuTresor.html  (et le swf chargé :chasseAuTresor.swf)\\


Chacun de ces jeux pourra utiliser un cookie sauvegarde.sol sans qu'il y ait conflit (voici leur adresse en local):

[…]/#sharedobjects/LDMNE228/www.leDomaine.fr/jeux/aventure/laVieDeToto.swf/sauvegarde.sol\\
[…]/#sharedobjects/LDMNE228/www.leDomaine.fr/jeux/aventure/chasseAuTresor.swf/sauvegarde.sol\\


Si vous souhaitez partager un cookie entre plusieurs swf vous devrez avoir recours au deuxième paramètre de la méthode getLocal. Sur ce coup là la doc est très claire, je ne vais pas paraphraser :-P

Le pareil du différent selon version de lecteur

Pour finir et à titre de conclusion, je ne peux que vous inviter à lire avec attention le chapitre Eléments importants relatifs à l’espace disque local de la toujours même doc.

Sachez aussi que selon lecteur pour lequel vous publiez (préférences de publication), l'utilisateur aura ou non un avertissement (détail), et que l'adresse par défaut du cokie peut changer (depuis mon CS5.5 par exemple, le cookie est créé par défaut à la racine du répertoire, comme si j'avais passé ”/” à getLocal).

Et enfin, parce qu'on peut passer à côté et chercher longtemps (en tous cas j'en garde un vieux mais cuisant souvenir), n'oubliez pas :

Assurez-vous que le fichier SWF fait au moins 215 pixels de large et 138 pixels de haut (ce qui constitue les dimensions minimales d’affichage de la boîte de dialogue qui suggère à l’utilisateur d’augmenter sa limite locale de stockage des objets partagés locaux)

Les sources

1) pourquoi pas toi ? ;)