Applications Flash avec Facebook Graph API

En cours de modification, Facebook a changé la méthode de passage du token.

Dernièrement, enfin ca fait un peu de temps maintenant, Facebook à mis en place sa nouvelle API : Graph. Donc aujourd'hui vous avez deux possibilités pour créer une application Facebook en Flash :

  • avec l'ancien SDK, et donc là vous pouvez continuer à utiliser l'API AS3 Facebook crée par Adobe.
  • avec Graph, d'où le titre de ce billet.

Facebook graph api

J'utilise la méthode de rendu "Iframe".

La première chose à faire est de tester si l'utilisateur qui visite l'application l'a déjà installée ou non. Pour cela, on utilise un bout de code FBML :






Donc là, facebook fait tout pour vous. En gros, si l'utilisateur n'a pas déjà installé l'application, facebook le redirige vers une page où il doit accepter l'application avec différentes permissions supplémentaires (le paramètre scope). Pour la liste des permissions, je vous laisse parcourir la doc.

Je ne conseille pas d'ajouter toutes les permissions nécessaires à votre application sur ce premier écran. Vous pourrez demander plus tard des autorisations supplémentaires. Vous risquez de le faire fuire dans le cas contraire (on y reviendra après)...

Une fois cliquer sur "autoriser", facebook redirige vers votre superbe application.

Grâce aux balises FBML, on ajoute le shockwave dans la page :


Facebook utilise dorénavant le protocole OAuth 2.0, comme Youtube par exemple, pour l'authentification. Si l'utilisateur a bien autorisé l'application (en fait il n'a pas le choix), le flash reçoit alors une FlashVar nommée "session" encodée en JSON. Cet objet contient un "access token" qui va vous permettre d'interroger Facebook via Graph afin de récupérer les données "publiques" de l'utisateur (sinon, il faut demander l'autorisation). Le token est valable 24h.

var o:Object = JSON.decode(loaderInfo.parameters.session);
_token = o.access_token;

Facebook retourne les résultats au format JSON. J'utilise l'as3CoreLib pour le décoder.
On implémente une petite méthode raccourcie qu'on utilisera tout le temps où on passe ce fameux token en paramètre dans l'url :

private function _callGraph(method:String):URLLoader
{
	var urlLoader:URLLoader = new URLLoader();
	urlLoader.load(new URLRequest("https://graph.facebook.com/" + method + "?access_token=" + _token));
	return urlLoader;
}

Puis dès lors qu'on souhaite, par exemple récupérer les données de l'utilisateur "loggué", on utilise la methode "me" (me étant l'utilisateur actuel :

var urlLoader:URLLoader = _callGraph("me");
urlLoader.addEventListener(Event.COMPLETE, _getFriendsComplete);

_callGraph("/me/friends"); // les amis de l'utilisateur actuel
_callgraph("gwenn.guihal"); // mes infos (gwenn.guihal ou 567172079)
_callgraph("gwenn.guihal/friends"); // mes amis

Voici la liste des actions possibles : http://developers.facebook.com/docs/api

L'utilisateur peut restreindre l'accès à son profil, et par exemple empêcher de lister les "likes". Si on en a besoin, on lui demande d'autoriser. Pour cela, on utilise Javascript :
En plus de l'objet session, Facebook nous donne un identifiant LocalConnection.

_fb_local_connection = _loaderInfo.parameters.fb_local_connection;

Grâce à ce dernier, on va pouvoir appeller du JS sur la page Facebook (nos fonctions JS et celles de Facebook). Voici une méthode raccourci :

private function _callJS(method : String,arg:Array = null) : void
{
	if (!_localConnection) // private variable
	{
		_localConnection = new LocalConnection();
	}
	
	_localConnection.send(_fb_local_connection, "callFBJS", method, arg);
}

Donc pour afficher une popup de demande de permissions :

_callJS("Facebook.showPermissionDialog", ['likes,email,publish_stream,...']);

La liste des permissions

Après, tout se trouve dans la doc http://developers.facebook.com/docs/ ou sur Internet.

Encore un truc pas mal, si vous ne l'avez pas déjà deviné : une fois que vous avez un token valide, vous pouvez interroger facebook pendant 24h, via par exemple le navigateur :

https://graph.facebook.com/gwenn.guihal?access_token=_TOKEN_

Et enfin, si vous compilez en Flash 9 et si vous avez des erreurs de flux, compilez en Flash 10.

Préchargement avec le compilateur Flex en pure AS3 sur un seul SWF (2 frames)

Dans l'IDE de Flash, la manière de preloader le site avec un joli loading est d'exporter les assets sur la deuxième frame et donc sur la première frame implémenter le chargement :

public var myLoading:MovieClip;
		
public function Main()
{
	loaderInfo.addEventListener(ProgressEvent.PROGRESS, _progressHandler);
	loaderInfo.addEventListener(Event.COMPLETE, _completeHandler);
}

private function _progressHandler(event:ProgressEvent):void
{	
	var percent:Number = event.bytesLoaded / event.bytesTotal;
	myLoading.percent_txt.text = int(percent *100) + "%";
}

private function _completeHandler(event:Event):void
{
	gotoAndStop(2); // lance le site
}

Lorsqu'on compile un projet AS3 avec le compilateur de Flex, cette méthode semble compromise (projet en pure AS3, sans flex). Une solution consite à créer un premier SWF qui va charger votre application. Cependant, pour causes de performances je suis pas fan (j'évite au maximum ajouter des swf dans des swf).
Après quelques recherches, ici et puis surtout , voici une autre solution : Le compilateur flex propose un argument "-frame" qui permet de rajouter des frames sur la timeline avec un label et une classe (il faut que votre main hérite de MovieClip, c'est mieux).

Dans Main.as, qui est la classe de base de l'application/site, on ajoute, via la méthode "addFrameScript", du code sur les frames comme si on était dans la timeline de l'IDE : Sur la première frame le loading, puis sur la deuxième le "go" de l'application. Pour cela, on part du principe que la base du site/application est implémentée dans une classe à part, par exemple Site.as.

Le but de la manœuvre consiste à exporter la classe Site, et donc toutes les classes associées dans la deuxième frame. On ajoute
" -frame START Site"
aux arguments de compilation. Cet argument ajoute une frame sur la timeline de Main, dont le label est "START" mais surtout va exporter la classe Site sur cette même frame, donc sur la frame 2 !

Donc Main.as va ressembler à ça :

public function Main()
{
	addFrameScript(0,frame1); // ajoute du code sur la frame 1
	addFrameScript(1,frame2); // ajoute du code sur la frame 2
}

private function frame1():void
{
	loadingClip = new LoadingClip(); // loadingClip est un clip contenant un champ texte
	addChild(loadingClip);
	// listeners de chargement
	loaderInfo.addEventListener(ProgressEvent.PROGRESS, _progressHandler);
	loaderInfo.addEventListener(Event.COMPLETE, _completeHandler);
}


private function _progressHandler(event:ProgressEvent):void
{	
	var percent:Number = event.bytesLoaded / event.bytesTotal;
	loadingClip.text_txt.text = int(percent *100) + "%";
}

private function _completeHandler(event:Event):void
{
	loaderInfo.removeEventListener(ProgressEvent.PROGRESS, _progressHandler);
	loaderInfo.removeEventListener(Event.COMPLETE, _completeHandler);
	
	removeChild(loadingClip);
	gotoAndStop(2); // on va sur la frame 2 (la méthode frame2 est appellée)
}

private function frame2():void
{
	// création manuelle d'une intance de Site
	var SiteClass:Class = getDefinitionByName("Site") as Class;
	addChild(new SiteClass());
}
Si lors de l'appel de la méthode frame2, on avait écrit :
private function frame2():void
{
	var site:Site = new Site();
	addChild(site);
}
On aurait importé la classes Site + ses associations dans Main, ce qui aurait rendu obselète notre chargement. On retrouve donc la classe Site et on l'instancie manuellement au runtime.

Je mets un exemple ici, j'utilise FDT mais la méthode est similaire sur FlashDevelop ou bien Flash Builder. Mes assets (clip graphiques) sont crées dans l'IDE Flash, puis exporter dans un SWC que j'inclue à la compilation (ajouter au buildPath). De cette manière, je peux compiler avec le compilateur Flex (beaucoup plus rapide) et en plus avoir l'autocomplétion sur mes assets !
Le beurre et l'argent du beurre !

Nommer les boucles en AS3

Hola,

Imaginons 2 boucles imbriquées :

var searchedVo:MyVo;

for each (var voList:Array in superVoList) // firstloop
{
	for each (var vo:MyVo in voList) // secondloop
	{
		if (vo.name == "raimond")
		{
			searchedVo = vo;
			break;
		}
	}
}
Une fois le résultat escompté, la seconde boucle sera détruite mais la première continuera... Pas terrible.
Heureusement, il est possible d'associer un label sur chacune des boucle, et spécifier lors du "break", qu'elle boucle faut-il stopper.
label : for (...)

En reprenant notre code précédent, ça donne ça :

firstLoop : for each (var voList:Array in superVoList) // firstloop
{
	secondLoop : for each (var vo:MyVo in voList) // secondloop
	{
		if (vo.name == "raimond")
		{
			break firstLoop; // kill firstloop
		}
	}
}
Et là, on passe illico à la suite, les DEUX boucles sont stoppées.

Coordonnées globales

Hello,

Erratum

Après quelques recherches au fond de moi-même, j'ai, enfin je crois avoir compris comment utiliser la méthode localToGlobal dans mon cas :
J'ai un clip Albert sur ma scène, il contient un clip Bertand qui contient lui un clip Clément. Je veux, dans Bertrand, connaitre la position de Clément par rapport à la scène (stage). Il suffit de faire :

// je suis dans Bertand.as, classe liée au clip Bertrand
var pt:Point = clement_mc.localToGlobal(new Point(0,0)));

Le point 0,0 représente le point 0,0 dans clément_mc. Et donc, Flash me retourne ce point mais relativement à la scène. Bien entendu, il s'agit du même résultat qu'obtenu avec la classe Coord.as. Cette dernière n'est donc plus trop utile... Mais bon, je laisse tout de même la suite...

Je ne sais pas vous, mais moi, j'ai pas encore tout compris aux fonctions globalToLocal et localToGlobal... En tout cas elles ne fonctionnent pas toujours comme j'aimerais l'entendre...

Par exemple, dans un projet Flash, vous avez besoin de tooltips. Le tooltip doit toujours se trouver au dessus de tous les autres clips. Donc, logiquement, il est placé sur la scène, tout au dessus. Maintenant, au rollover de votre bouton, le tooltip doit venir se placer sur votre bouton ou pas loin. Le problème, c'est que votre bouton, est placé dans un clip, qui est lui même placé dans un clip, [...], qui est enfin placé sur la scène. On doit donc donner au tooltip les coordonnées du bouton relatives à la scène.
Voici une simple classe, amenée à être complétée avec d'autres fonctionnalités qui va faire le travail pour vous :

package fr.myrddin.utils 
{
	import flash.display.DisplayObject;
	import flash.geom.Point;		


	public class Coord 
	{
		/**
		 * Renvoie les coordonnées d'un clip par rapport à la scène
		 * @param $mc Le clip
		 * @return Les coordonées du clip par rapport à stage
		 */
		public static function getCoordGlobal($mc:DisplayObject):Point
		{
			var pt:Point = new Point();
			
			return incrementeCoord($mc,pt);
		}
		
		private static function incrementeCoord($mc:DisplayObject,$pt:Point):Point
		{
			$pt.x += $mc.x;
			$pt.y += $mc.y;
			
			if ($mc.parent)
			{
				return incrementeCoord($mc.parent,$pt);
			}
			else
			{
				return $pt;
			}
			
		}
	}
}

Et quand vous en avez besoin :

var btn:MovieClip = $e.currentTarget as MovieClip;
var pt:Point = Coord.getCoordGlobal(btn);

La classe est téléchargeable ici

Expérimentation sur Five3D

Bonjour

APrès Papervision, j'ai voulu tester un peu Five3D, le moteur 3D vectoriel de Mathieu Badimon via un mini (vraiment mini) jeu.

J'ai trouvé Five3D nettement moins gourmand en ressources que Papervision, en tout cas en ce qui concerne les graphiques vectoriels. Papervision décline le graphisme vectoriel en bitmap alors que Five3D le conserve. (Je n'ai pas encore testé VectorVision...).
Cependant, on arrive au même problèmes de latence dès lors qu'on travaille avec les bitmaps. En effet dans sa nouvelle version, Five3D comprend une classe Bitmap3D.

A l'utilisation, j'ai trouvé five3D très simple d'utilisation, son fonctionnement est très proche de l'implémentation de l'AS3 :

body.graphics3D.beginFill(0x68bb21);
body.graphics3D.drawRoundRect(-20, 0, 40, 40, 4, 4);
body.graphics3D.endFill();
Par contre, ses fonctionnalités sont plus limitées, pour le moment en tout cas. Il est impossible de créer des dégradés ou d'ajouter dans la displayList d'un Sprite3D un élément vectoriel 2D (dessiné précédemment dans l'API de Flash par exemple). La classe Sprite2D le permet, mais les possibilités 3D sont limités (rotation uniquement sur l'axe Z).
Le Z-sorting est bien géré ainsi que le Flat-shading. Je n'ai malheureusement pas testé la gestion de la typo...

container.childrenSorted = true; // Z-sorting
me.flatShaded = true; // Flat shading
Five3D example and sources

Déplacez-vous avec les flèches du clavier et allez toucher les items clignotants...

Pour conclure ce petit test, j'ai trouvé Five3D très agréable à utiliser et le rendu est superbe comparé à un Papervision. Vivement les prochaines versions et bravo à son créateur !

Visualiser l'exemple. Télécharger les sources.

Boules rebondissantes (Papervision3D + Shadows + WOW-Engine)

Après avoir vu l'article de Andy Zupko à propos des ombres dans papervision et le moteur physique WOW-Engine de Seraf, je me suis dit, j'ai bien envie d'essayer un truc...

Il ne s'agit pas d'une expérimentation bien compliquée, mais simplement d'un essai afin d'amener des possibilités futures (jeu de pétanque par exemple...).
Des boules rebondissent sur un plan avec ombre et tout. Cliquez pour faire apparaître une nouvelle boule (attention, ça lague rapidement...).

balls papervision3D wow shadows

Pour les sources, clic droit sur l'exemple.

A bientôt

Télécharger les anciennes versions de Flash Player

Hello hello,

J'ai un peu galérer à les trouver, donc si vous en avez besoin, voici comment télécharger les anciennes versions de Flash Player :

Anciennes versions de Flash Player

Ca peut toujours être utile pour quelques tests...

A bientôt.

Expressions régulières (RegEx)

Bonjour,
J'ai trouvé un lien intéressant en feuilletant le web. Il s'agit d'un résumé, tenant, sur une page, des "formules" utilisées par les expressions régulières.
Imprimez-le, accrochez le dans vos WC puis ça finira par rentrer... ou pas....
Et hop le lien !

Serveur Java & Flash

Bonjour !

Dans le cadre du projet Balade Champêtre, j'ai réalisé une version alpha d'un serveur de jeu multi-joueurs en java, couplé avec un client en Flash en AS3.
L'exemple qui se trouve encore en phase de test, permet de se connecter au serveur, de créer ou rejoindre une partie (4 joueurs max par partie), puis d'envoyer à tous les joueurs d'une partie (multicast) la position de sa souris lors d'un clique. (Il se peut que l'exemple ne fonctionne pas de temps en temps du à l'arrêt du serveur...)

Voyons voir comment tout cela fonctionne avec un exemple simple - connexion et envoi d'un message à tous les utilisateurs connectés.

Lire la suite

Lecteur de Flux RSS en Flash AS3

Bonjour,

Voici un lecteur de flux RSS en Flash, qui peut-être, pourrait aider certains. Je l'ai réalisé rapidement ce matin pour un exercice.
Pas très compliqué à réaliser, même plutôt simple, surtout que la classe RSSParser n'est pas de moi, mais du livre "Programming ActionScript 3.0 book"...

Ce script utilise principalement des composants : comboBox, TextArea, ..., donc fichier FLA relativement lourd...

Le truc un peu relou avec la "sandBox" de flash, c'est l'accès à d'autres domaines...
En effet, pour accéder à des médias d'un autre domaine que le vôtre, il faut que sur le domaine en question se trouve un fichier ""cross-domain policy"...
Pour le flux RSS venant de blog.waytomorrow.fr, j'ai du créer "crossdomain.xml" posé à la racine du blog "waytomorrow" (c'est à dire à la racine de blog.waytomorrow.fr".
Et voila ce qu'il faut mettre dans ce fichier (les domaines qui veulent accéder à vos médias, en l'occurence myrddin.fr ici) :

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy 
  SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
  <allow-access-from domain="www.myrddin.fr" />
  <allow-access-from domain="myrddin.fr" />
</cross-domain-policy>

Vous voici donc paré à lire des flux RSS à partir d'une application Flash...

Télécharger les sources.

A bientôt !

SWFAddress

SWFAddress est un petit script qui permet d'avoir une URL pour chaque partie de votre application Flash. Il s'ajoute à SWFObject. En d'autres termes, il permet d'utiliser les boutons "Précédent" et "Suivant" de votre navigateur.

Prenons l'exemple d'un portfolio. Pour chaque travaux, l'URL dans la barre d'adresse ainsi que le titre vont changer :

http://www.monsiteweb.com/portfolio/#/travail01/
Ainsi, si vous souhaitez montrer un travail spécifique sans faire 10 clics, vous entrez l'url et puis voila !

Pour exemple, vous pouvez visionner mon portfolio qui fonctionne avec ce script. J'ai eu du mal à l'implémenter dans la mesure où j'avais déjà réalisé une grosse partie de l'application avant de trouver SWFAddress. Ainsi pour la partie portfolio, j'ai du bidouiller pas mal pour que cela fonctionne...

Comment ça marche :

Premièrement, dans votre fichier HTML, insérez l'appel au script Javascript :

<script type="text/javascript" src="swfobject/swfobject.js"></script>  
<script type="text/javascript" src="swfaddress/swfaddress.js"></script>

Ensuite, dans votre fichier Flash (.fla ou .as), après avoir importer la classe SWFAddress, au lieu de réaliser une action lorque l'utilisateur clique sur un bouton, vous changez seulement la valeur de SWFAddress (classe statique) :

private function onButtonClic(event:Event) {
	SWFAddress.setValue('/la-partie-demandee/');
}

La méthode définie par onChange va alors détecter que l'url change, et selon celle-ci, vous allez faire en sorte que la la partie demandée s'affiche... :

private var value:String;
private var currentState:String;
SWFAddress.onChange = handleSWFAddress;
private function handleSWFAddress():void {
	value = SWFAddress.getValue();
	value = value.replace(/\//g, '');
	// permet de vérifier si l'on ne se trouve pas déjà dans la partie demandée
	if (currentState != value) { 
		currentState = value;
		var title:String = 'Titre de mon site';
		if (currentState != '') {
			title += ' » ' + currentState;	
			// Votre code pour afficher la partie demandée	
		}else {	
			title += ' » ' + "Accueil";
		}
		// On change le titre de la page (balise <title>)
		SWFAddress.setTitle(title);
	}
}

Ensuite, si vous tapez l'url complète, comme :
http://www.monsiteweb.com/portfolio/#/travail01/
Au chargement du SWF, la méthode handleSWFAddress() va être appelée et donc la partie demandée va s'afficher...

Voila, cela est ma première explication, j'espère avoir été relativement clair, sinon posez vos questions, j'essaierais d'y répondre...Pour télécharger le script, c'est ici. A bientôt.

Edit : j'ai oublié de mentionner que pour pouvoir tester cela, il vous faut bien entendu un serveur Apache...(WampServer par exemple en localhost.)
Afin de voir le bugs, je vous conseille aussi FlashTracer qui va vous permettre de lire les trace à partir de Firefox...