Archive for the “Création” Category

On a fait grosso modo le tour de tout ce qui était utilisation normale de Mugen : installations de composants et réglages. J’entre donc dans une partie qui devrait prendre beaucoup plus de temps : la création.

Car si Mugen est déjà attrayant par le fait de pouvoir mélanger des éléments issus de jeux et d’univers différents, son véritable but est de permettre à l’utilisateur de créer ses propres éléments, pour obtenir un jeu original.

Toute création se décompose en 2 parties : d’un côté, la partie « média » (images & son) et de l’autre, la partie code. La partie media fait bien sûr appel à des talents d’artiste. Ce n’est donc pas un domaine où on peut véritablement expliquer comment s’y prendre, et en tout cas, ce n’est pas le but de ce site. En outre, la majeure partie des créateurs reprennent des graphismes et des sons pré-existants en les extrayant notamment des jeux consoles/arcade. C’est ce qu’on appelle le rip, et on aura l’occasion d’en développer les grandes lignes ultérieurement.

Ce qui va nous intéresser dans la partie media, c’est la façon de l’intégrer dans Mugen. Aujourd’hui, nous allons notamment nous intéresser aux traitements des images.

Dans Mugen, les images d’un élément, quel qu’il soit, sont regroupées dans un fichier SFF (et ce même s’il n’y a qu’une seule image). Donc une fois qu’on a réuni les images à utiliser (qu’on les ait crées ou ripées), il faut les « compiler » dans un fichier SFF afin que Mugen puisse les exploiter. Pour cela, nos images doivent respecter certaines petites conditions.

Deux conditions incontournables

Tout d’abord, les images doivent être en 256 couleurs indexées (ou 8-bits). C’est une opération que la plupart des logiciels de dessin, même les plus simples, savent faire. Un exemple avec Irfan View (gratuit) :

La seconde condition, c’est que ces images doivent être au format PCX. Là encore, nombre de logiciels permettent de convertir des images JPG ou BMP (par exemple) au format voulu. A nouveau, Irfan View fait cela très bien :

A savoir : Depuis l’arrivée de la version Windows 1.0 de Mugen, Elecbyte a mis à jour son format SFF. L’ancienne version du SFF est maintenant dénommée « SFFv1″, et la nouvelle « SFFv2″, lorsqu’il y a besoin de les distinguer. Pour des raisons de rétro-compatibilité avec les milliers de créations existantes, la version 1.0 lit aussi bien les SFFv1 que les SFFv2. En revanche, les anciennes versions de Mugen ne liront, bien sûr, que les SFFv2. On peut également penser que dans un futur plus ou moins lointain, lorsque le SFFv2 se sera généralisé, le SFFv1 ne sera plus supporté.

Quelles sont les différences entre les SFFv1 et v2 ? Ce sont surtout beaucoup de points de détails qu’on ne va pas présenter ici. Retenez, pour le sujet du jour, que le SFFv2 supporte également le format PNG en plus du PCX.

Histoire de palettes

A partir du moment où vous vous lancez dans la création, vous allez souvent entendre parler de « palettes ». Qu’est-ce donc ? Les palettes sont en fait un tableau listant les couleurs (avec leurs valeurs RVB) utilisées dans l’image (ou pas, d’ailleurs !). Dès que vous passez à 256 couleurs (ou moins), votre image aura une palette. Là encore, la plupart des logiciels de dessin sont en mesure d’afficher la palette d’une image, voire de l’éditer :

Pour expliquer rapidement les palettes : chaque couleur dans la palette possède un numéro d’index (de 0 à 255), et chaque pixel dans l’image est associé à un numéro d’index. En conséquence, si on modifie la couleur d’un index, ça modifie la couleur de tous les pixels qui utilise cet index. En outre, il est possible d’enregistrer et de charger des palettes. Si on charge une palette qui utilise des couleurs différentes de la palette d’origine de l’image, on change les couleurs dans l’image. C’est sur ce principe que fonctionnent les différentes tenues des personnages dans Mugen.

La couleur « transparente »

Les deux conditions ci-dessus sont le B.A.BA de la création Mugen. Si elles sont respectées, vos images pourront être lues dans un SFF. Mais on peut trouver d’autres subtilités dans l’utilisation des images, et l’une d’entre elles est la notion de « couleur transparente ».

A retenir : La notion de « transparence » est assez floue dans Mugen, car elle désigne abusivement deux notions. La première se rapproche du concept « d’opacité partielle », c’est à dire que l’on voit un objet, mais que l’on peut également voir au travers pour distinguer ce qui se trouve dessous (comme par exemple lorsque vous regardez à travers une vitre colorée). La seconde notion, celle que nous allons voir ici, est celle de « l’invisibilité » (c’est à dire qu’une partie de l’image n’est pas affichée à l’écran). Attention de bien les distinguer selon le contexte…

Une image est toujours un rectangle, faisant X pixels de large et Y pixels de haut. Ceci est fondamentalement vrai, et ne souffre aucune exception. Mais alors, comment se fait-il qu’en jeu, nous ayons l’image de gauche, et non pas celle de droite ?

En fait, on utilise la même image, mais à droite, on a l’image « brute », alors qu’à gauche, la couleur de fond n’est pas affichée, ce qui donne l’illusion que l’image n’est pas un simple rectangle.

Pour pouvoir utiliser cette fonctionnalité dans Mugen, la couleur de fond doit être celle en index 0 (ou pour reprendre l’explication donnée avant, les pixels que l’on veut rendre invisibles doivent être associés à l’index n°0). Si vous regardez l’image précédente de Link, vous verrez que la couleur de fond (rose bonbon) correspond à l’index 0 dans la palette, et donc tous ces pixels roses ne seront pas affichés dans le jeu.

Le rognage : un gain de place, une manipulation plus facile

Le rognage consiste à découper l’image sur l’un de ses côtés. Lorsqu’on travaille sur les images (rip ou création), on part souvent d’une image large, et on enlève en fin d’opération les éléments superflus. Il se peut alors que votre image soit complètement entourée de fond. Dans ce cas, il faut rogner l’image sur les côtés de façon à ce qu’il y ait au moins un pixel « utile » sur chaque bord. L’intérêt, c’est que vos images seront plus petites et prendront moins de place, et qu’en outre, sprmaker rognera de toute façon vos images, ce qui risque alors de vous poser des problèmes pour placer votre image dans le SFF (on verra ça une prochaine fois).

Comments Aucun commentaire »

Elecbyte a sorti un nouveau trigger Cond dans sa RC6, qui semble un peu surprenant à première vue. En effet, il se présente et fonctionne globalement à l’identique du trigger IfElse, et semble donc redondant.

Pour rappel, le IfElse fonctionne ainsi : IfElse(exp_cond, exp_vrai, exp_faux) = exp_vrai si exp_cond est vrai, ou exp_faux sinon (ça ressemble donc assez à un « SI() » dans Excel ou Calc). Donc Cond fonctionne de la même façon. Par exemple :

value = IfElse(var(0)=1,4,-4)

et

value = Cond(var(0)=1,4,-4)

sont rigoureusement équivalent (si var(0) = 1, alors value prendra la valeur 4, et sinon, value prendra la valeur -4.

Il y a pourtant une subtile différence entre IfElse et Cond : avec un IfElse, quelque soit le résultat, toutes les expressions qui composent le IfElse sont lues et évaluées, même si elles ne sont pas utilisées (par exemple, si exp_cond est vrai, on n’a pas besoin de exp_faux, mais exp_faux sera quand même lu). En revanche, avec Cond, seules les expressions nécessaires sont lues et évaluées (donc dans le même cas, cette fois, exp_faux ne sera pas lu).

Quelle différence ? Dans un cas comme celui donné en exemple plus haut, aucun, c’est certain. Mais il existe certains cas où la différence peut être assez flagrante, notamment avec l’assignation de variable.

L’assignation de variable consiste à évaluer une expression ou une partie d’expression tout en attribuant la valeur de cette expression à une variable. Par exemple :

value = var(0):=5+RoundNo

Si RoundNo vaut 1, value vaudra 6, et var(0) aussi. Le signe « := » indique que l’expression située à droite du signe doit être attribuée à var(0) (comme le ferait un VarSet). En fait, si on décompose l’expression, on dit à Mugen que value doit prendre la valeur de var(0), qui doit elle-même prendre la valeur de 5+RoundNo.

Revenons à nos triggers Cond et IfElse, et imaginons le cas suivant :

value = IfElse(RoundNo>1,var(1):=RoundNo,var(1):=-RoundNo)

Ici, var(1) est censé prendre la valeur de value, à nouveau : si RoundNo>1, alors value retourne RoundNo, qui est attribué à var(1), et si RounNo n’est pas >1, alors value retourne -RoundNo, qui est attribué à var(1). Bref, var(1) est censé prendre la même valeur que value.

Du moins, en théorie. En pratique, comme toutes les expressions de IfElse sont lues et évaluées, var(1) vaudra toujours -RoundNo, quelle que soit la valeur de value. Pourquoi ? Parce que Mugen lit la chaîne dans son intégralité, et la dernière expression qu’il lit (de droite à gauche) est l’assignation de -RoundNo à var(1), et ce même si value vaut RoundNo, au final.

C’est là qu’intervient Cond. En remplaçant simplement IfElse par Cond, on change le comportement de Mugen :

value = IfElse(RoundNo>1,var(1):=RoundNo,var(1):=-RoundNo)

Ici, l’expression inutile n’étant pas lue, on est certain que var(1) correspondra bien à value (si value vaut RoundNo, alors var(1) = RoundNo, et si value vaut -RoundNo, alors var(1) = -RoundNo).

Une différence subtile, certes, mais qui peut avoir son importance dans certains cas.

Mais dans ce cas, pourquoi ne pas avoir simplement modifié le fonctionnement de IfElse ? Très probablement parce que certains créateurs ont du exploiter le comportement initial du IfElse, et donc si le IfElse venait à se comporter comme le Cond, les personnages de ces créateurs ne fonctionneraient plus comme prévus. C’est donc à des fins de rétro-compatibilité qu’Elecbyte a choisi d’ajouter un trigger légèrement différent du IfElse plutôt que de le modifier.

Comments Aucun commentaire »

Quand on débute dans la programmation de personnage, il est un terme « barbare » qui revient assez souvent : « helper ». Dès qu’il s’agit de faire des choses un peu spéciales et/ou un peu complexe, il surgit comme un mot magique. Seulement voilà, on en arrive toujours à la même question : c’est quoi, un helper ?

Elecbyte présente ça comme un « sous-personnage ». C’est à la fois correct et maladroit. En fait, quand on a compris ce que c’est, on est fatalement d’accord pour dire que « sous-personnage » est une description assez juste. Mais quand on ne sait pas, ça donne une connotation un peu fausse (et restrictive) de la chose, car quand on parle de personnage, on imagine tout de suite un bonhomme. Dès lors, le terme « sous-personnage » fait penser aux « strikers », ces personnages qui entrent parfois dans l’écran pour filer une baffe à l’adversaire avant de ressortir, selon un système instauré par SNK dans ses jeux.

Mais voilà, s’il est vrai que dans Mugen les strikers sont des helpers, la réciproque n’est pas vrai, loin de là. Alors on en revient encore à la même question : « Papa, c’est quoi, un helper ? ».

La meilleure manière que j’ai trouvée pour présenter l’helper, c’est de dire que c’est un « objet ». Le mot est vague à souhait : un objet, ça peut être n’importe quoi ! Eh ben un helper aussi, figurez-vous ! Déjà, un helper n’a pas forcément « d’existence physique », entendez par là qu’il peut très bien être « invisible », et/ou ne pas être « tangible ». Et s’il est visible, il peut très bien ne pas ressembler du tout à un personnage et/ou ne pas être là pour aider le joueur, etc. Bref, c’est un objet, qui deviendra ce que vous avez besoin qu’il devienne : dans le cas de Link, les nombres de rupees, bombes et flèches que vous voyez à l’écran sont un helper ; le menu au début du premier match qui vous permet de choisir les objets que vous avez au départ, c’est un helper ; l’inventaire qui affiche les objets que vous avez : c’est un helper.

On peut se servir des helpers pour faire tout et n’importe quoi. Par exemple, dans Gouki, je me sers d’un helper pour faire des fondus en blanc ou en noir sur le décor pour effacer les « fonds spéciaux » qui apparaissent quand on gagne avec un super ou un hyper. On peut aussi se servir d’un helper comme d’un projectile (ex : les bombes de Link, ou la flèche à tête chercheuse, ou encore le poison attaché à l’adversaire lorsqu’il se fait toucher par une flèche empoisonnée).

Vous voyez maintenant un peu plus ce qu’est un helper. On peut donc revenir à la description initiale d’Elecbyte : un « sous-personnage ». D’abord, pourquoi « personnage » ? C’est du au fait que les helpers se programment de la même façon que les personnages (à quelques nuances près). Quant au « sous », il induit une hiérarchie, et c’est le cas ; si on considère d’un point de vue technique qu’un « personnage » est une « entité programmable » (donc le personnage principal et les helpers), alors le personnage principal est le personnage « racine » (root en anglais, d’où tout part). Un personnage qui crée un helper est le parent de cet helper (qui est donc « sous » le personnage principal). Pour un helper créé par le personnage principal, ce personnage principal est à la fois le parent (puisque créé par lui) et la racine. Pour les helpers créés par un helper, le parent est l’helper qui les a créés, et le root est – toujours – le personnage principal. Cette notion de hiérarchie est assez importante notamment dans les redirections de triggers. Mais ça, c’est une autre histoire ! ^^

Comments Aucun commentaire »

C’est quoi, ce titre ? C’est ce que vous vous demandez peut-être. La réponse se trouve plus loin dans l’explication qui suit. Aujourd’hui, je vous présente :

  • Une information importante et qui contredit la doc officielle, concernant la façon dont sont lus les states négatifs, et notamment les ChangeState à l’intérieur de ces states négatifs,
  • Un « bug » potentiel de Mugen, qui peut être assez gênant, qui serait extrêmement difficile à repérer (d’après moi en tout cas), et qui ne sera pas corrigé (pour cela, il faudrait revoir la façon dont Mugen agit, et utiliser une méthode plus lourde et pas complètement logique, ce qu’Elecbyte se refuse de faire).

Je vous livre ce qui ressort d’une discussion un tantinet pointue sur la façon dont Mugen lit les states dans les personnages. Tout part d’un constat : la documentation dit que le déclenchement d’un sctrl dans les states négatifs n’interrompt pas le processus de lecture de ces states.

Pour rappel, à chaque tick, Mugen lit d’abord les states négatifs (-3, -2 puis -1) avant de lire le state positif dans lequel se trouve le personnage. Pour rappel encore, les states -1 sont utilisés pour changer le personnages de states en fonction des commandes réalisées par le joueur, les states -2 sont forcément lus à chaque ticks, et les states -3 fonctionnent sur le même principe que les -2, mais ne sont pas lus si le personnage est dans un « custom state » (ex : quand il est projeté).

Donc la doc d’Elecbyte stipule que lorsqu’un sctrl est déclenché en state -3, -2 ou -1, Mugen continue ensuite de lire les sctrl suivant. Or en pratique, c’est faux pour un sctrl : le ChangeState. Tous les créateurs le savent au moins implicitement lorsqu’ils créent leurs states -1 : quand on crée une commande « QCF + a », on doit la placer avant une commande « a », parce que quand le joueur réalise « QCF+a », la commande « a » est aussi (forcément) réalisée. Or Mugen ne déclenche que le 1er state -1 dont les conditions (dont les commandes) sont réalisées, et donc si on veut éviter tout bug, on placera les commandes complexes en premier, et les commandes plus simples en dernier, de sorte que lorsqu’une commande est réalisée, Mugen va éliminer les plus complexes pour aboutir à celle qui est vraiment réalisée.

Or si Mugen ne déclenche que le 1er state -1 dont les conditions sont remplies, ça signifie que le ChangeState déclenché interrompt la lecture du state -1, ce qui contredit bien la documentation.

Partant de là, certains ont fait d’autres tests, et il ressort que tout ChangeState déclenché dans les states négatifs entraîne l’arrêt de la lecture sur le state en question. Donc si on déclenche un ChangeState en state -3, tous les states -3 situés après ne sont pas lus, et Mugen passe directement aux states -2. Idem s’il y a un ChangeState déclenché en -2, Mugen passe direct aux -1 sans lire les autres ChangeState situés après. C’est en tout cas le fonctionnement « idéal théorique » voulu par Elecbyte. Dans les faits, il semble que pour le moment, cela ne soit vrai que pour les states -2 et -1. Pour les  -3, le ChangeState déclenché ne saute pas la fin du state. Ceci sera peut-être corrigé par la suite.

Ca veut dire, concrètement, que dans vos states négatifs, si vous souhaitez déclencher des sctrls (autres qu’un ChangeState) avec les même triggers qu’un ChangeState (dans le même state négatif), vous devrez placer ces sctrls avant le ChangeState en question, sans quoi, ils seront ignorés.

Ca, c’est pour l’information dont je parlais dans le premier point.

Passons au second point, le bug ; il est indirectement lié à la découverte faite dans le premier point. Certains se sont en effet penchés sur ce comportement, et ont découvert un élément important : quand un ChangeState est déclenché, le numéro de state du personnage est mis à jour, mais le StateDef correspondant n’est pas lu. Et alors ? Et alors je vais reprendre l’image qui a été fournie sur le forum d’Elecbyte, et vous comprendrez (enfin !) mon titre…

Imaginons que les states soient des véhicules, et que le StateDef indique le nombre de roues de ce véhicule. En gros, on est dans le State « Vélo », et le statedef a logiquement indiqué qu’on avait 2 roues. Le ChangeState négatif nous fait passer à un State « Voiture », dans lequel le statedef indique, toujours aussi logiquement, qu’on a 4 roues. Mais comme le StateDef n’est pas lu immédiatement après le ChangeState, nous sommes dans une Voiture, avec seulement 2 roues ! Nous ne passerons à 4 roues que lorsque le state « Voiture » sera lu, c’est à dire entre le déclenchement du ChangeState et la fin de la lecture des states négatifs, et du coup, les données n’ayant pas été actualisées, tous les triggers liés au StateDef (MoveType, StateType, anim, vel, etc.) sont possiblement faux après le déclenchement d’un ChangeState en state -3 ou -2, tant que Mugen n’est pas passé à la lecture des states positifs, car ces triggers n’auront pas été actualisés avec le nouveau StateDef.

On va voir avec un exemple. Prenons le code suivant :

[Statedef 1000]
type = S
movetype = A
ctrl = 0
anim = 1000
[...]

[State 1000]
type = StateTypeSet
trigger1 = time = 50
movetype = I

[...]

[Statedef -3]

[State -3]
type = ChangeState
trigger1 = ctrl
trigger1 = StateType = I
trigger1 = StateNo = 0
value = 1000

[Statedef -2]

[State -2]
type = ChangeState
trigger1 = StateNo = 1000
trigger1 = MoveType = I
value = 2000

J’ai volontairement mis les states -3 en premier pour garder l’ordre de lecture de Mugen, et ainsi mieux montrer ce qui se passe. On voit que le Statedef du state 1000 règle le movetype sur A, puis qu’après 50 ticks, le movetype devient I (une fois l’attaque finie, par exemple). Notre State -2, lui, est censé placer notre perso en state 2000 une fois que dans le state 1000, on est passé au movetype = I (donc après 50 ticks). On imaginera qu’il y aura d’autres conditions pour que ce sctrl se déclenche (ex : la valeur d’une variable liée à un mode de jeu, ou autre).

Donc, imaginons que nous sommes en state 0 : notre ctrl vaut 0, et le StateType est bien I. Conclusion, quand Mugen lit notre ChangeState en state -3, les conditions sont vraies, et donc il le déclenche. Il met donc à jour le numéro du state du joueur (0 devient 1000). Il saute le reste des state -3 et lit les states -2. Et là regardez ce qui se passe :

  • Trigger1,1 = vrai puisque le ChangeState du state -3 nous a placé en state 1000, ce qui a mis à jour le trigger StateNo.
  • Trigger1,2 = dans un monde utopique et idéal, il devrait être faux, puisque dès l’entrée en state 1000, le statedef précise que le movetype est A. Sauf que comme le statedef n’a pas été lu, le movetype n’a pas été mis à jour et vaut toujour I !!! Et la conclusion, c’est que ce trigger est donc vrai au moment où il est lu !
  • Les deux triggers étant vrai, le ChangeState est déclenché !

Résultat, on passe en state 2000 sans être réellement passé par le state 1000 !

J’ai pris le movetype comme exemple, mais c’est vrai pour n’importe quel trigger modifié par un ChangeState (vel, anim, statetype, etc.).

Pour éviter cet écueil, il faudrait qu’à chaque fois que Mugen déclenche un ChangeState en state négatif, il arrête la lecture de ce state négatif pour aller lire le StateDef du nouveau state afin d’actualiser ses données, puis reprenne la lecture du state là où elle s’était arrêtée. Elecbyte a fait savoir que cette « correction » ne serait pas faite, et que le comportement global de la lecture des states correspondait à ce qu’ils voulaient, malgré cette « faille ».

En attendant, si un jour vous avez des problèmes de changement de states, pensez à cette éventualité, parce que sinon, pour repérer le problème dans le code, faudra sérieusement s’accrocher ! Et pour l’éviter, le mieux serait peut-être de « renforcer » les triggers des sctrls qui suivent le ChangeState (dans notre cas, si on avait ajouté  « trigger1 = Anim = 1000″ ou « trigger1 = Ctrl = 0″, ceux-ci n’auraient pas été vrais, et donc on aurait évité le déclenchement non désiré du ChangeState en -2).

Comments Aucun commentaire »