8 Jan, 2014

Mon incroyable banquet : les avatars

Développement d'un système de création d'avatars dans un projet CakePHP
Attention, cet article a désormais quelques années… Il se peut que toutes les informations qu’il contient ne soient plus pertinentes.

Récemment chez Web And Cow, nous avons réalisé une application web sous forme de jeu concours pour l’asssociation Bleu-Blanc-Coeur. Le projet s’appellait « Mon Incroyable Banquet » et était d’envergure nationale. Chaque participant devait réussir à réunir 100 personnes à son banquet (amis facebook, etc) pour participer à un tirage au sort final. Le gagnant se faisait offrir son Incroyable Banquet par l’association Bleu-Blanc-Coeur et cuisiné par le chef Emmanuel Picard.

Dans le cadre de ce grand projet, nous avons été amené à réaliser quelques fonctionnalités techniques originales. Cet article fait donc partie d’une série de plusieurs explications des mécanismes du jeu.

Les Avatars

A son inscription, chaque participant, qu’il soit créateur d’un banquet ou invité, devait créer son avatar. Il avait à sa disposition une palette de combinaisons pour différentes parties du corps. Du visage au buste en passant par les lunettes, l’utilisateur pouvait configurer son personnage avec un choix de couleurs pour chaque élément. Dès que son image était prête et validée, elle était générée.

Fig. 1 : Génération d’un avatar

Nous avons donc commencé par récupérer toutes les différentes parties du corps de l’avatar en images séparées. Cette étape était un peu longue et répétitive mais nécessaire. Afin d’avoir le moins de problème possible, nous les avons découpés en images de même largeur et de même hauteur (1). Ensuite nous avons créé un algorithme qui générait ces morceaux d’images dans toutes les couleurs d’une palette définie au préalable (2). Ces images étant au format .png, il fallait donc faire attention à bien conserver la transparence.

Fig. 2 : Exemple de rendu de la couleur des avatars

Voici la fonction que nous avons utilisé pour réaliser ce traitement :

/**
 * @param string $img Chemin de l'image
 * @param string $dst_color Couleur de remplacement en tableau RGB (ex: array(112, 80, 64))
 * @param string $dst Chemin de destination
 * @return void
 */
  public function changeColor($img, $dst_color, $dst) {
	// Création de l'image source
	$img_src = imagecreatefrompng($img);
	// Récupération de la largeur
	$width = imagesx($img_src);
	// Récupération de la hauteur
	$height = imagesy($img_src);
	// Creation de l'image finale
	$img_dst = imagecreatefrompng($img);
 
	// On dessine un rectangle blanc sur l'image finale
	imagefilledrectangle($img_dst, 0, 0, $width, $height, 0xFFFFFF);
 
	// On enlève l'alphablending de l'image finale
	imagealphablending($img_dst, false);
 
	// On parcours chaque pixel de l'image 
	// Par la largeur
	for($x=0; $x<$width; $x++) {
		// Par la hauteur
	    for($y=0; $y<$height; $y++) {
 
			// Récupération de la couleur du pixel
	        $alpha = (imagecolorat($img_src, $x, $y) >> 24 & 0xFF);
 
			// On alloue la nouvelle couleur à l'image finale
	        $col = imagecolorallocatealpha( $img_dst,
	            $dst_color[0] - (int) ( 1.0 / 255.0  * $alpha * (double) $dst_color[0]),
	            $dst_color[1] - (int) ( 1.0 / 255.0  * $alpha * (double) $dst_color[1]),
	            $dst_color[2] - (int) ( 1.0 / 255.0  * $alpha * (double) $dst_color[2]),
	            $alpha
	            );
 
	        if ($col === false) {
	            die("La couleur n'a pas pu être remplacée.");
	        }
 
	        // On redessine le pixel avec la nouvelle couleur
	        imagesetpixel($img_dst, $x, $y, $col);
	    }
	}
 
	// Sauvegarde de la transparence de l'image
	imagesavealpha($img_dst, true);
	// Sauvegarde de l'image finale
	imagepng($img_dst, $dst, 0);
	// Destruction de l'image une fois le traitement terminé
	imagedestroy($img_dst);
  }

La génération de l’avatar

Pour la gestion de l’avatar, nous avons donc développé un système permettant d’afficher, de façon superposée, l’ensemble de ces images. La propriété CSS z-index a donc forcément été beaucoup utilisée. Ensuite, lorsque l’utilisateur modifiait une partie de son avatar, un peu de javascript était utilisé pour venir modifier l’attribut href de l’image concernée.

Une fois l’avatar validé par l’utilisateur, il fallait réaliser la « fusion » de toutes ces parties de l’avatar pour créer l’image finale. Nous avions préalablement convenu d’un nommage strict des fichiers images des avatars, de la façon suivante :
avatar _ visage1 _ cheveux3brun _ lunettes13blanc _ barbe4brun _ buste11blanc.png

Et voici ci-dessous le code de la « fusion » :

// Si l'image n'existe pas, alors on la créée (pour éviter les doublons)
if (!file_exists($cheminDeLImage)) {
	// Creation d'un objet image
	$imageProfil = new ImageComponent();
	// Ajout du visage de l'avatar dans le tableau d'images
	$imageProfil->addImage('visage' . $donnees['Avatar']['visage']['numero'] . '.png', $largeurDeLImage, $hauteurDeLImage, 0, 0, 0, 0);
	// On enlève le visage des données transmises 
	// pour éviter de le remettre dans le tableau plus tard
	unset($donnees['Avatar']['visage']);
	// Ajout des éléments du visage de l'avatar 
	// dans le tableau d'images (bouche, yeux, etc)
	$imageProfil->addImage('elements_visage.png', $largeurDeLImage, $hauteurDeLImage, 0, 0, 0, 0);
 
	// On parcours chaque partie du corps de l'avatar
	foreach ($donnees['Avatar'] as $key => $elementAvatar) {
		$chemin = $key . $elementAvatar['numero'] . $elementAvatar['couleur'] . '.png';
		$imageProfil->addImage($chemin, $largeurDeLImage, $hauteurDeLImage, 0, 0, 0, 0);
	}
 
	// Création de l'image finale grâce au tableau d'images
	$imageProfil->pngFusion($largeurDeLImage, $hauteurDeLImage, $cheminDeLImage);
}

Nous avons donc créé un composant Image pour CakePHP qui nous facilite cette fonctionnalité. L’image générée pouvait servir pour un ou plusieurs profil puisqu’elle n’était pas créée une nouvelle fois si elle existait déjà.

Fig. 3 : Exemple d’avatar généré

Il fallait aussi faire attention à l’ordre d’ajout des images dans le tableau. Comme les images se superposent, une partie du corps de l’avatar pouvait passer par dessus une autre si l’ordre était mauvais (ex: La barbe en dessous du visage). Les avatars étaient générés au format .png, il était donc nécessaire de garder la transparence malgré cette association d’images.

C’est la fin de ce deuxième article de la série sur l’Incroyable Banquet de Bleu-Blanc-Coeur.

#avatars #Bleu-Blanc-Coeur #CakePHP