Blog
Personnalisation du personnage
Pour ce jeu vidéo, j'ai attaché une assez grande importance à la personnalisation des personnages, c'est-à-dire du ou de la sale gosse que vous contrôlez… ainsi que des bots, évidemment. C'est également ce que vous pouvez retrouver dans le générateur d'avatar « Sales Gosses ! ».
Je vous propose de découvrir comment cela se passe techniquement avec le moteur de jeu utilisé, Godot.
La création d'un sale gosse type
Le jeu est vu en fausse-3D isométrique avec un style cartoon 2D. J'ai pris le parti de représenter les personnages dans huits directions : haut, droite, bas, gauche, ainsi que les huits directions intermédiaires haut-droite, bas-droite, bas-gauche, haut-gauche.
Pour chacune de ces huit directions, on a également deux animations possibles : au repos ou en mouvement. Ce qui nous fait donc 16 animations différentes en tout. En tirant avantage de la symmétrie, on peut réduire ce nombre à 2 fois 5 (les positions haut-gauche, gauche et bas-gauche sont obtenues en faisant un simple miroir horizontal sur les positions haut-droite, droite et bas-droite).
Au niveau dessin, on a besoin de 12 sprites (morceaux d'image) pour chaque personnage : 2 pieds, 2 jambes, 1 bassin, 1 torse, 2 bras, un visage, des cheveux, une bouche et des yeux. Dans certaines positions, on ne voit pas forcément l'intégralité des sprites, et certains sprites sont réutilisés d'une position sur l'autre (par exemple, les sprites de la tête, du torse et du bassin sont les mêmes dans les positions au repos et en mouvement). À cela s'ajoutent d'autres variantes : différentes humeurs pour les yeux et la bouche, des mouvements comme la baffe, etc.
En pratique, on a aussi d'autres animations lorsque le personnage utilise un objet (corde à sauter, yo-yo, balançoire, etc.), mais elles réutilisent pas mal des sprites déjà existants.
Tout cela, c'est très bien, mais maintenant, comment personnalise-t-on ce personnage ?
Les couleurs
Vous l'avez sans doute remarqué : soit mon personnage est albinos avec un goût prononcé pour les vêtements blancs… soit il manque tout simplement de la couleur.
Évidemment, c'est à dessein que j'ai colorié l'intégralité des sprites
en blanc, parce que ça me permet d'utiliser une chouette
fonctionnalité de Godot : la propriété self_modulate
.
C'est une propriété de l'objet
CanvasItem
(dont Sprite2D hérite) qui permet de… eh bien moduler la couleur
de l'objet. Elle est similaire à modulate
, à ceci près que
modulate
s'applique à ce nœud et à tous les nœuds enfants, alors
que self_modulate
ne s'applique qu'au nœud considéré : les bras, par
exemple, sont des enfants du nœud du torse, mais on ne veut clairement
pas que la couleur du t-shirt soit appliqué aux bras.
En réglant cette propriété, on peut donc donner la teinte que l'on veut à chacun des sprites de notre personnage :
Évidemment, en pratique, les couleurs sont restreintes à un sous-ensemble pré-déterminé, pour éviter de se retrouver avec des personnages qui ont une peau d'alien par exemple…
Dans le menu de création du personnage, ce sont des barres de réglages qui permettent de sélectionner la couleur de chaque élément : peau, cheveux, t-shirt, pantalon et chaussures. Chaque barre est connectée à une rampe de couleur avec, encore une fois, des couleurs prédéfinies. Pour enregistrer la couleur de chaque personnage, il suffit donc d'enregistrer la position de chacune de ces barres !
Les variantes de sprites
Changer les couleurs, c'est bien chouette, mais on voudrait aussi pouvoir changer de coiffure, de forme de visage, de lunettes, de t-shirt, etc.
Pour faire cela, on commence par bien organiser notre feuille de sprite : chaque élément se trouve sur une ligne séparée. Ensuite, lorsque l'on veut ajouter une nouvelle variante, il suffit de créer une nouvelle ligne, et en décalant la position du rectangle choisi à l'affichage, on peut tout simplement switcher sur une nouvelle ligne de sprites.
Il est même possible d'utiliser plusieurs feuilles de sprites pour éviter de se retrouver avec une immense image unique. Voici par exemple les quatre feuilles utilisées dans la v1 du jeu :
Au niveau du script, on fait une fonction qui applique le changement de ligne sur l'ensemble des sprites choisis, ce qui nous donne :
func set_y_for_all(sprites: Array, y: int) -> void:
for sprite: Sprite2D in sprites:
sprite.region_rect.position.y = y
Comme je veux pouvoir ajouter des variantes plus tard, provenant parfois d'autres feuilles de sprite, j'ai un fichier de config dans lequel je peux indiquer quelles lignes utiliser pour quelle partie du corps :
const BRAT_BASE: Texture2D = preload("res://assets/characters/brat_base.png")
const BRAT_VARIANT: Texture2D = preload("res://assets/characters/brat_variant.png")
const BRAT_VARIANT2: Texture2D = preload("res://assets/characters/brat_variant2.png")
const BRAT_VARIANT3: Texture2D = preload("res://assets/characters/brat_variant3.png")
const POSSIBLE_HAIR_SKINS: Array = [
[BRAT_BASE, 2, PRNG.Proba.COMMON], # Straight
[BRAT_VARIANT, 2, PRNG.Proba.COMMON], # Curly
[BRAT_VARIANT2, 4, PRNG.Proba.COMMON], # Ponytail
[BRAT_VARIANT3, 8, PRNG.Proba.COMMON], # Curly ponytail
[BRAT_VARIANT, 10, PRNG.Proba.COMMON], # Long
[BRAT_VARIANT2, 10, PRNG.Proba.COMMON], # Thick
[BRAT_VARIANT3, 10, PRNG.Proba.COMMON], # Wavy
[BRAT_VARIANT, 8, PRNG.Proba.COMMON], # Nerdy
[BRAT_VARIANT2, 0, PRNG.Proba.COMMON], # Short
[BRAT_VARIANT2, 2, PRNG.Proba.COMMON], # Messy
[BRAT_VARIANT3, 12, PRNG.Proba.COMMON], # Bushy
[BRAT_VARIANT, 4, PRNG.Proba.RARE], # Pigtails
[BRAT_VARIANT2, 12, PRNG.Proba.RARE], # Dreadlocks
[BRAT_VARIANT, 6, PRNG.Proba.RARE], # Punk
[BRAT_VARIANT2, 6, PRNG.Proba.RARE], # Palmtree
[BRAT_VARIANT2, 8, PRNG.Proba.RARE], # Mohawk
[BRAT_BASE, 0, PRNG.Proba.VERY_RARE], # Bald
]
const POSSIBLE_HEAD_SKINS: Array = [
[BRAT_BASE, 4, PRNG.Proba.COMMON],
[BRAT_VARIANT, 0, PRNG.Proba.COMMON],
[BRAT_VARIANT3, 0, PRNG.Proba.RARE],
[BRAT_VARIANT3, 2, PRNG.Proba.RARE],
[BRAT_VARIANT3, 4, PRNG.Proba.RARE],
[BRAT_VARIANT3, 6, PRNG.Proba.RARE],
]
const POSSIBLE_EYES_SKINS: Array = [
[BRAT_BASE, 7, PRNG.Proba.COMMON], # No glasses
[BRAT_VARIANT, 12, PRNG.Proba.RARE], # Round glasses
[BRAT_VARIANT, 13, PRNG.Proba.RARE], # Square glasses
[BRAT_VARIANT2, 14, PRNG.Proba.VERY_RARE], # Sun glasses
[BRAT_VARIANT2, 15, PRNG.Proba.VERY_RARE], # Sun glasses
[BRAT_VARIANT2, 16, PRNG.Proba.VERY_RARE], # Sun glasses
]
const POSSIBLE_TSHIRT_SKINS: Array = [
[BRAT_BASE, 11],
[BRAT_VARIANT, 14],
[BRAT_VARIANT, 15],
[BRAT_VARIANT, 16],
[BRAT_VARIANT, 17],
[BRAT_VARIANT, 18],
[BRAT_VARIANT3, 14],
[BRAT_VARIANT3, 15],
[BRAT_VARIANT3, 16],
]
const POSSIBLE_PANTS_SKINS: Array = [
[BRAT_BASE, 12],
[BRAT_VARIANT2, 17],
[BRAT_VARIANT2, 18],
]
Sur chaque ligne, on a donc le fichier dans lequel trouver la ligne desprite, et en dessous l'indice de la ligne. Comme vous pouvez le deviner avec le code, on a aussi des notions de rareté pour certains skins, ce qui permet notamment d'éviter de se retrouver avec 10 gamins qui ont une coupe punk ou chauve, ce qui serait quand même assez peu réaliste…
Avec cette méthode, je peux facilement ajouter des skins par la suite, les ranger dans différentes spritesheets, etc.
Dans la v1 du jeu, on a donc 17 coiffures, 6 formes de visage, 6 types de lunettes, 9 t-shirts et 3 pantalons, soit 16524 combinaisons possibles ! Et encore, c'est sans compter sur les variantes de couleur. Bref, une bonne façon d'apporter de la diversité et de s'assurer de ne pas avoir de « clones » dans la cour de récré !
Conclusion
En utilisant deux propriétés simples de Godot (la modulation de couleur et la position du rectangle de sprite), on peut ainsi donner la possibilité de personnaliser le personnage, de générer des bots avec des skins aléatoires variés… tout en conservant les animations et interactions développées pour l'ensemble des personnages. L'enregistrement du skin est une simple liste d'entiers qui correspondent à l'index de chaque sprite et, pour les couleurs, à la position sur la rampe de couleur.
Évidemment, d'autres skins viendront s'ajouter à la liste existante dans les prochaines mises à jour du jeu, alors restez à l'écoute !