Les palettes d'images sont utilisées en infographie depuis le début et, même si elles sont rares dans les jeux modernes, une certaine catégorie de problèmes serait pratiquement impossible sans eux. Dans ce tutoriel, nous allons créer un concepteur de personnages MMO pour un jeu en 2D à l'aide de palettes, de textures multiples et de shaders..
Remarque: Bien que ce tutoriel ait été écrit avec AS3 et Flash, vous devriez pouvoir utiliser les mêmes techniques et concepts dans presque tous les environnements de développement de jeux..
Le modèle de personnage utilisé pour la démo a été créé par Dennis Ricardo Díaz Samaniego et peut être trouvé ici: Leviathan. L'appareil a été fabriqué par Daren Davoux et peut être trouvé ici: Leviathan Rigged.
Cliquez sur une section du modèle de personnage, puis cliquez n'importe où dans le sélecteur de couleurs. Vous ne pouvez pas voir le fichier SWF ci-dessus? Regardez cette vidéo à la place:
Les fichiers source complets sont disponibles dans le téléchargement source.
L'implémentation de démonstration utilise AS3 et Flash, avec la bibliothèque Starling pour le rendu accéléré par GPU et la bibliothèque Feathers pour l'interface utilisateur. Notre scène initiale contient une image du personnage et le sélecteur de couleurs, qui seront utilisés pour changer les couleurs de la palette de caractères..
La représentation des couleurs à l'aide de palettes était courante dans les premiers jeux en raison de la configuration matérielle requise. Cette technique mapperait une valeur d'une image à une autre valeur dans une palette. Habituellement, l’image aurait un ensemble de valeurs plus petit pour économiser la mémoire et être utilisée pour rechercher la valeur réelle dans la palette..
L’image-objet Mario ci-dessous ne se compose pas de rouge, d’orange et de brun - elle est composée de 1, 2 et 3. Lorsque Mario prend une fleur de feu, la palette change de sorte que 1 représente le blanc et 3 le rouge. Mario a l'air différent, mais son esprit ne change pas vraiment.
Étant donné que l'utilisation de la représentation d'images en couleurs vraies 24 bits est courante depuis plus d'une décennie, vous vous demandez peut-être en quoi cette technique pourrait être utile aujourd'hui. Le premier cas d'utilisation évident est la création de jeux rétro, mais il est rarement nécessaire d'utiliser des palettes. Un artiste peut se limiter à un certain ensemble de couleurs, mais utiliser tout le spectre 24 bits, car il constitue un moyen simple de gérer les textures avec du matériel moderne..
Une technique utilisée à l'époque des images de palette était l'échange de palette: elle était utilisée pour modifier l'image existante en un jeu de couleurs différent. Et beaucoup d’entre vous se souviennent peut-être de la façon dont les couches de monstres dans les vieux RPG étaient colorées différemment; Cela permettait aux artistes de gagner du temps et d'utiliser moins de mémoire, ce qui permettait une plus grande variété de conceptions de monstres. (On pourrait soutenir que cela pourrait donner l'impression que le jeu est répétitif.)
Cela nous amène au but de ce tutoriel, qui est de vous permettre d’avoir plus de variété dans les actifs de votre jeu. Nous allons simuler un créateur de personnage MMO, avec des couleurs personnalisables pour les parties de personnage, en utilisant une palette..
Faire des images avec des palettes est un peu plus difficile que de faire des images ordinaires. Il y a quelques limitations et détails techniques à surveiller.
Tout d'abord: les palettes ont une portée limitée; dans ce tutoriel, chaque caractère de l'image-objet a 8 bits - soit 256 valeurs possibles - dont la valeur "255" sera utilisée pour indiquer "transparent"..
Pour le pixel art, ce n’est pas un problème car elle est généralement basée sur une palette limitée choisie par un artiste. Pendant que vous dessinez, les couleurs de la palette sont définies et appliquées à l'image..
Dans l'exemple, j'utilise un modèle 3D. J'ai rendu ceci dans des calques séparés, puis mappé chaque calque sur le spectre d'une palette. Cela peut être fait manuellement en modifiant les niveaux de l'image pour les adapter à la partie souhaitée de la palette. Les parties de la palette que nous représentons sous forme de petits dégradés, pour permettre le mappage des ombres aux rehauts.
Cela réduira la profondeur globale de l'image. Si vous jouez à un jeu de cartoony (cel shaded), cela peut aller, mais il se peut qu’il manque un style plus réaliste. Nous pouvons y remédier quelque peu en sacrifiant la mémoire tout en augmentant la profondeur d'une image à 16 bits - la palette pourrait conserver la même taille et nous utiliserions l'interpolation pour nous offrir plus de variété..
Notre implémentation utilise une seule texture de canal 8 bits pour le personnage. Pour des raisons de simplicité, nous allons utiliser une image PNG standard, dont les canaux vert et bleu sont réglés sur 0, et tout est stocké dans le canal rouge (c’est pourquoi l’image exemple est en rouge). En fonction de votre plate-forme, vous pouvez l'enregistrer dans un format de canal unique approprié. Une palette initiale est enregistrée en tant qu’image 1D, avec une largeur de 256 pour représenter toutes les valeurs que nous allons mapper..
La partie principale n'est pas si compliquée. Nous allons rechercher certaines valeurs dans une texture basée sur une autre texture. Pour cela, nous allons utiliser plusieurs textures et un simple fragment shader..
(Multi-texturer signifie essentiellement utiliser plusieurs textures tout en dessinant un seul morceau de géométrie, et cela est supporté par les GPU.)
// définissant plusieurs textures, context est un contexte Stage3D context.setTextureAt (0, _texture.base); // fs0 dans le shader context.setTextureAt (1, _palette.base); // fs1 dans le shader
Une autre chose à laquelle nous devons prêter attention est la nécessité de trouver un moyen de rendre certaines parties des images transparentes. J'ai mentionné plus tôt que cela se ferait à l'aide d'une valeur spéciale (255), ce qui signifie transparent. Les shaders de fragments ont une commande qui éliminera le fragment, le rendant invisible. Nous ferons cela en détectant quand la valeur est 255.
Cet exemple utilise le langage de shader AGAL. Cela peut être un peu difficile à comprendre car c'est un langage qui ressemble à un assemblage. Vous pouvez en apprendre plus à ce sujet sur Adobe Developer Connection..
// lit la valeur de la texture normale (en ft1) tex ft1, v1, fs0 <2d, linear, mipnone, repeat> // soustrait la valeur de texture (ft1.x) du // seuil alpha (fc0.x défini à 0,999 dans le code principal) sub ft2.x, fc0.x, ft1.x // ignore le fragment s'il représente le masque, // 'kil' fait cela si la valeur est inférieure à 0 kil ft2.x // lit la couleur de la palette en utilisant la valeur de la texture normale (ft1) tex ft2, ft1, fs1 <2d, nearest, mipnone, repeat> // multiplie la couleur du sommet par la couleur de la palette et la stocke dans le résultat mul oc, ft2, v0
Cela peut maintenant être encapsulé dans un Starling DisplayObject personnalisé qui contient la texture et l'image de la palette - c'est ainsi qu'il est implémenté dans l'exemple de code source.
Pour changer le réel couleurs du personnage, nous devrons changer la palette. Si nous voulions changer la couleur d'une partie particulière du caractère, nous prendrions la palette d'origine en niveaux de gris et changerions la coloration du segment qui correspond à cette partie..
Le code suivant parcourt la palette et applique la couleur appropriée à chaque pièce:
// passe par la palette pour (var i: int = 0; i < _paletteVector.length; i++) // 42 is the length of a segment in the palette var color:uint = _baseColors[int(i / 42)]; // extract the RGB values from the segment color value and // multiply original grayscale palette var r:uint = Color.getRed(color) * _basePaletteVector[i]; var g:uint = Color.getGreen(color) * _basePaletteVector[i]; var b:uint = Color.getBlue(color) * _basePaletteVector[i]; // create a new palette color by joining color components _paletteVector[i] = Color.rgb(r, g, b);
Nos couleurs sont enregistrées en tant qu'entiers non signés comprenant 8 bits de valeur rouge, verte et bleue. Pour les faire sortir, il faudrait faire quelques opérations binaires. Heureusement, Starling propose des méthodes d’aide pour cela dans le Couleur
classe.
Pour permettre la sélection des parties de caractères, nous gardons les données d'image en mémoire. Nous pouvons ensuite déterminer la section du caractère à laquelle un clic de souris correspond en lisant la valeur de pixel à cet endroit..
var characterColor: uint = _chracterBitmapData.getPixel (touchPoint.x, touchPoint.y); // prend la valeur rouge var characterValue: uint = Color.getRed (characterColor); // 255 signifie transparent, nous allons donc utiliser cette désélection si (characterValue == 255) _sectionSelection = SECTION_NONE; else // calcule la section, chaque section prend 42 pixels de la palette _sectionSelection = int (characterValue / 42) + 1;
Cette implémentation a quelques limitations, qui peuvent être résolues avec un peu plus de travail en fonction de vos besoins. La démo ne montre pas l'animation de la feuille de sprite, mais cela peut être ajouté sans modification à la classe de palette principale.
Vous avez peut-être remarqué que la transparence est gérée comme visible et invisible. Cela provoque des aspérités qui peuvent ne pas convenir à tous les jeux. Cela peut être résolu en utilisant un masque - une image en niveaux de gris qui représente la valeur de transparence, du noir (totalement opaque) au blanc (totalement transparent). Cela augmentera un peu les besoins en mémoire, cependant.
Une autre technique permettant de modifier la couleur des parties de l’objet consiste à utiliser une texture supplémentaire ou un canal. Ils sont utilisés en tant que masques ou tables de consultation (qui s'apparentent à des palettes) pour rechercher des valeurs de couleur qui seront multipliées par la texture d'origine. Un exemple de ceci peut être vu dans cette vidéo:
Des effets vraiment intéressants peuvent être obtenus en animant la palette. Dans l'exemple de démonstration, il est utilisé pour représenter la partie du personnage où la couleur est modifiée. Nous pouvons le faire en déplaçant le segment de la palette qui représente une section du caractère, créant ainsi un mouvement circulaire:
// enregistre la valeur de départ de la section palette var tmp: int = _paletteVector [start]; // passe par le segment de section de la palette et décale les valeurs à gauche pour (var i: int = start; i < end; i++) _paletteVector[i] = _paletteVector[i + 1]; // use saved staring value, wrapping around _paletteVector[end] = tmp;
Un exemple assez étonnant de ceci peut être vu ici: Cycle de toile.
Un mot d'avertissement: Si vous comptez beaucoup sur cette technique pour l'animation, il peut être préférable de le faire sur le processeur, car le téléchargement de la texture sur le GPU à chaque changement de palette peut coûter cher..Pour gagner en performance, nous pouvons regrouper plusieurs palettes de caractères (1D) en une seule image (2D). Cela nous permettrait d'ajouter une variété de caractères avec un minimum de modifications de l'état de rendu. Le cas d'utilisation parfait pour cela est un jeu MMO.
La technique décrite ici peut être très efficace dans les environnements MMO 2D, en particulier pour les jeux Web où la taille du téléchargement compte beaucoup. J'espère que j'ai réussi à vous donner quelques idées sur ce que vous pouvez faire si vous pensez à vos textures différemment. Merci d'avoir lu!