Myrne 6312

Un shooter en vue du haut et en coop, développé en 72h dans le cadre du 26e Ludum Dare

Song of the Myrne: What Lies Beneath

Un action-RPG en pixel-art

Affichage des articles dont le libellé est Tutoriaux. Afficher tous les articles
Affichage des articles dont le libellé est Tutoriaux. Afficher tous les articles

vendredi 31 mars 2017

Game design: Donner du punch à vos attaques


Salut les lecteurs (et les autres aussi) !

Aujourd'hui on va parler un peu de game design et si vous savez lire un titre, vous devez déjà savoir qu'on parlera plus précisément d'attaques. Pour une fois, je ne vais pas parler spécifiquement d'Unity, j'estime que ces conseils peuvent s'adapter partout (peut-être même en animation également, pas seulement en game design).

Concrètement, je vais essayer de vous expliquer comment donner de l'impact, du punch, de la juiciness... appelez ça comme vous voulez, aux attaques de corps à corps de vos personnages.

En gros, je vais tenter de vous expliquer ce qui fait la différence entre Dark Souls et Minecraft quand il s'agit des combats. Je suis en train de pas mal bosser sur toutes ces petites choses qui font que le personnage soit "ancré" dans l'univers, qu'il ait un poids quand il marche (au lieu de donner l'impression de flotter au ras du sol) et que lorsqu'il donne un gros coup de hache, qu'on sente le poids et l'inertie de l'arme.

Etape 0: L'observation



Ça c'est un bon conseil pour tout le processus de création de jeux en général: regarder comment les autres font.

Oui, super, merci Sherlock, et continue bien de nous faire croire que tu bosses quand tu est en train de jouer, aussi !

Tss tss tss... L'idée c'est, quand on joue à un bon jeu, d'observer comment les mecs ont fait et pourquoi (précisément) le jeu est si bien. Pas juste en se disant "ah bah leur combat est bien fluide et y'a de l'impact" mais en regardant vraiment pourquoi c'est fluide et impact...ant... En gros, passer d'une attitude passive ("je constate que c'est bien fait") à une attitude active ("quels éléments précis font que c'est bien fait ?").

Bon, voyons comment marche une attaque concrètement.

Etape 1: Diviser pour mieux régner



Une attaque ?! Facile ! Je fais un clic souris, le perso fait un mouvement avec l'arme puis l’ennemi prend le coup !

Faux.

Cette manière de voir l'attaque va vous mener directement à la méthode Minecraft (je précise que j'adore ce jeu bourré de qualités, hein, n'allez pas tirer des conclusions parce que je le prend pour exemple sur un truc qu'il fait moins bien que Dark Souls :D), ou à la méthode que j'utilisais jusqu'ici dans Myrne: The Quest (que je suis donc occupé à améliorer).

A savoir, la méthode du : Une attaque = une animation

Si on veut un truc qui ait un peu de la gueule, on va séparer notre action en plusieurs sous-actions (et au passage on devra sans doute la diviser en plusieurs animations, désolé :D).

Concrètement, une attaque c'est quoi ? Je la vois en 3 ou 4 parties (après ça peut se discuter, ça dépend de la précision et de la complexité que vous souhaitez avoir pour votre attaque). On va leur donner des noms qui claquent ;)

  • La préparation
  • L'exécution
  • La résolution
  • Le retour

Chacune de ses parties va avoir un rôle bien différent dans l'action, et le fait de bien séparer chacune de ces parties dans notre code va nous donner un contrôle énorme sur tout ce qui va se passer avant, pendant et après l'attaque, ce qui serait plus difficile, voir impossible avec une attaque faites en une seule animation/état.

On va passer chacune de ces actions en revue.

La préparation



Peu importe avec quoi on décide de frapper un truc, on va, dans un premier temps, préparer notre coup pour maximiser la force qu'on met dedans. Si on souhaite donner un coup de hache, on va lever cette hache bien haut, pour une attaque à la lance, on va d'abord la reculer, pareil pour un coup de poing, on va tirer notre poing en arrière.



Heu, oui, on s'en fout, non ?

Nope. L'avantage d'avoir séparé ça du reste de l'attaque (et donc éventuellement d'en avoir fait une animation à part) va nous permettre 2-3 trucs sympas.

La première chose c'est d'éventuellement accélérer ou ralentir cette phase en fonction de l'arme. Ça donne du poids à l'objet (c'est super important). Imaginons une attaque à l'arme contondante genre marteau ou massue. Personnellement, pour Myrne: The Quest j'utilise une animation par type d'armes (contondante, lames, perforantes,...) et en principe il n'y a aucun soucis à ça. Mais est-ce que vous ne pensez pas qu'il y a une différence entre manier ça

Petit marteau de guerre



et ça ?

Très gros marteau de guerre

Ah zut, donc il faut deux animations différentes rien que pour la préparation ?

Non. Ce qu'on peut faire c'est utiliser la même animation, mais en variant sa vitesse, donc en rendant la deuxième plus lente à lever au-dessus de notre tête. L'arme aura tout de suite plus de poids, ce sera plus réaliste et en plus ça ralentira la vitesse d'attaque avec cette arme, ce qui a du sens.

Le deuxième truc qu'on peut faire à cette phase, c'est s'arrêter à cette étape, attendre pour charger une attaque plus puissante par exemple. Je m'attarde pas trop sur ce point car on est un peu en dehors de l'ajout de punch/juiciness. Mais voilà, si vous voulez un gameplay où on peut charger les coups pour faire plus de dégâts, le découpage de votre attaque sera nécessaire.

Idem pour la troisième possibilité, c'est qu'on peut par exemple, comme dans Chivalry, annuler l'attaque durant cette phase (après c'est un peu tard vu que l'arme sera emportée par la force du coup et difficilement arrêtable).

L'exécution et la résolution



Voilà, le coup part pour de vrai !

Pas grand chose à dire sur l'exécution, concrètement c'est le moment où l'arme passe d'une vitesse nulle à sa vitesse maximale (le marteau tombe, la lance est propulsée vers l'avant,...). Générallement on va ajouter quelques effets ici pour donner de la présence à l'arme. Je m'étend pas dessus car c'est ce que vous faites rpobablement avec vos attaques en une seule animation: un son "woosh", un petit effet visuel de déformation de l'air,...

Là où les choses deviennent très intéressantes c'est lors de la résolution. C'est ici que le coup prend effet. On a touché quelque chose ? Si oui, quoi ? Et il se passe quoi ?

C'est ici qu'on va vraiment pouvoir ajouter autant de complexité qu'on veut. Si vous visez la qualité Dark Souls, Vermintide ou Chivalry, vous allez vous amusez. Il y a un million de trucs à faire, à voir ce qu'on a le temps (et l'énergie) de faire ou non.

La bonne nouvelle c'est qu'en divisant notre action en différentes parties, on s'est donné la possibilité d'implémenter des trucs petits à petits. Chaque détail que vous ajouterez améliorera la qualité de votre attaque, mais si vous ne faites pas un truc, ce sera facile de revenir l'implémenter par la suite, là où avec une animation unique d'attaque, venir ajouter de la complexité nous forcerait à refaire toute l'animation.

Oui bon, on a compris. On peut faire quoi alors ici ?

On peut déjà commencer par regarder si on a touché ou non un truc avec notre attaque :


  • J'ai raté mon attaque !
Pas de chance... Mais c'est intéressant parce que notre arme ne va pas se comporter de la même manière que si on avait touché un truc. Elle va en fait probablement se comporter comme elle l'aurait fait si on l'avait créée en une seule animation. Elle continue son mouvement jusqu'à la fin de l'animation.
  • Ah non, c'est bon, j'avais touché un truc en fait !
Ah bah super ! Si c'est un ennemi, il émet un cri de douleur (si c'est un rat il couine, un paysan poussera un grognement,...), l'arme fait aussi un bruit (*pof* pour un marteau, bruit de coupure pour une épée,...), du sang gicle,...

Ce qu'on a touché n'était pas vivant ? C'est l'occasion d'ajouter du réalisme, et surtout de montrer que l'arme existe vraiment dans le jeu ! Elle va faire *toc* contre une surface de bois, et à la place de la giclée de sang, on peut utiliser un système de particules pour qu'il y ait des éclats de bois. On peut même ajouter un décal (une texture) sur l'objet pour montrer que notre arme l'a abîmé (trace de coupure, de coups,...).

A noter qu'on peut aussi s'adapter au point touché sur l'ennemi, ok, on l'a touché, mais où ? Dans la tête (Vermintide rend très bien l'effet *sprotch* d'un coup de marteau de guerre sur une tête d'homme-rat) ? Sur l'épaulière en cuir (*pof*) ? Sur son casque métallique (*cling*). Les possibilités sont quasiment infinies.

Et en ce qui concerne le fait d'avoir heurté un truc avec notre arme, c'est important de noter qu'on a... ben... heurté un truc avec notre arme !

Alors je sais pas chez vous, mais perso moi quand je frappe un outil contre une surface, l'outil s'arrête ;)

Et ça c'est super important ! Si votre système d'animations vous le permet (ce qui n'est malheureusement pas le cas du mien, je suis pas super doué en anim^^'), ça peut être super d'arrêter l'animation (enfin, la partie "résolution" de votre anim) ici. Ça va ajouter de l'impact au coup.

Puisque je ne fais pas ça, à la place je vais attendre la fin de mon anim et laisser l'arme figée en position de fin (hache vers le bas, lance vers l'avant,...) quelques instants avant de passer à la phase suivante. Ça marche aussi. L'idée c'est de vraiment montrer que vous avez donné un coup avec une hache, le corps prend un moment pour se ré-équilibrer, la hache reste plantée un moment dans l'objet,...

Je ne sais pas encore si je vais laisser l'arrêt quand on ne touche rien, probablement pas

Et voilà. Je pense que c'est tout pour cette partie.

Le retour


Comme son nom l'indique, cette dernière étape est le retour à l'état initial de l'arme. On a donné notre coup de hache, qui est maintenant tournée vers le sol. On va simplement la remonter pour pouvoir recommencer le cycle lors d'une prochaine attaque ;)

Comme pour "la préparation", on pourrait ralentir ou accélérer l'animation selon le poids de l'arme et le résultat (résolution) de l'attaque. Relever une petite hache à une main qui a raté son coup ira vite, déplanter une lance du corps d'un ennemi beaucoup moins.

Ah mais c'est fini, alors ! Cool !

En fait ça dépend du niveau de complexité voulue et de comment s'est terminé votre phase de résolution. C'est tout à fait possible de juste remonter notre hache et pas trop s'embêter avec des détails.

Mais laissez-moi vous parler de l'équilibre du personnage, juste au cas où vous voulez le top du top niveau qualité.

Ici les choses deviennent un peu plus compliquées/complexes parce qu'on va toucher au gameplay et qu'on va ajouter des animations en plus, je ne vous en voudrai pas si vous évitez de toucher à ça dans un premier temps (n'oubliez pas, on peut toujours repasser dans un an pour mettre le jeu à jour, quand on a un peu plus de temps pour les détails^^).

Il y a certaines actions dans une attaque qui vont déséquilibrer le mec qui porte le coup. Je pense à deux cas en particulier :
  • Frapper un mur (ou un mec en armure de plates avec un bâton en bois, par exemple)
L'arme va rebondir et probablement déséquilibrer le personnage vers l'arrière. C'est ce qu'il se passe dans Dark Souls par exemple et bien adapter son arme à l'endroit où on combat fait partie intégrante du gameplay (agitez une épée dans un couloir étroit c'est pas le top). Concrètement on part sur une animation de déséquilibre et sur un petit temps pendant lequel notre perso ne pourra rien faire le temps de retrouver l'équilibre...
  • Frapper dans le vide
Un peu pareil sauf qu'on va plutôt trébucher vers l'avant. L'animation sera différente et si vous décidez de mettre un temps de "ré-équilibre" pendant lequel le perso ne peut plus faire d'action, c'est une bonne idée de prendre en compte le poids de l'arme (frapper dans le vide avec une claymore ou avec une dague, ça n'a pas le même effet).

Ce genre d'animation est assez complexe à la troisième personne je pense (surtout pour ne pas traverser le mur^^). En vue première personne il s'agit simplement de "pousser" l'arme en arrière/avant et éventuellement relever/baisser la caméra.


Et je pense que c'est à peu près tout pour "le retour". Notez qu'en arrivant à ce stade et en ayant tout fait, on a plus besoin de coder de cooldown (temps d'attente avant de pouvoir attaquer de nouveau) arbitraire à nos attaques, on a eu des temps d'attentes un peu partout en fonction du résultat de l'attaque et au final le cooldown correspondra vraiment à la physique de l'objet et de l'attaque.

On conclusionne ?

Conclusion


Comme je vous ai dis, je pense qu'il n'y a pas vraiment de limites à ce qu'on peut faire, si ce n'est le temps et la complexité voulue.

Mais ce qu'il est important de noter c'est que rien de tout ça n'est compliqué en soi !

On peut vite se laisser impressionné par des jeux comme Dark Souls, Vermintide ou Chivalry (à juste titre, ces jeux ont vraiment un système de melee remarquable) mais ce qu'il ne faut pas faire c'est se heurter au mur du "pfouu, c'est trop dur et les mecs sont 50 pour faire ces jeux, moi je suis juste un petit indé".

Quand on prend le temps de vraiment regarder dans le détail ce qui constitue ces jeux et leur système de combat, on se rend compte que c'est tout un ensemble de petits trucs qu'il est très simple d'implémenter dans nos petits projets indés.

Alors oui, vos animations ne seront peut-être pas aussi fluides que celles de Dark Souls, vos coups de marteau aussi satisfaisants que ceux de Vermintide, mais rappelez-vous qu'entre ces jeux et Minecraft, il y a tout un monde et qu'on est pas soit d'un côté soit de l'autre, il y a tout un milieu. Je ne veux plus voir d'animation d'attaque consistant en un bras qui fait une rotation de 90 degrés :)





jeudi 2 mars 2017

Blender: Trucs et astuces divers


Salut les lecteurs !

Ça faisait longtemps que je n'avais plus posté de tutos sur ce blog... et ça ne va pas changer vu que ce que je vous propose aujourd'hui n'est pas vraiment un tutorial mais plutôt deux trois trucs que j'utilise souvent et qu'on ne retrouve pas vraiment dans les tutos pour débutant (je crois?^^).

Quand je met quelque chose comme ça Ctrl-Tabulation/Faces, ça signifie qu'il faut appuyer sur une combinaison de touche ou une touche puis sélectionner une option dans le menu déroulant qui devrait apparaître.

Allez, c'est part~

Hey, attends ! Ça demande quoi comme connaissances de Blender ?!

Ben, je vais pas vous apprendre la base, je part du principe que vous savez faire un modèle basique, "extruder" des faces, connaitre la différence entre le mode édition et le mode objet, savoir faire un Loop Subdivide (le truc avec ctrl-r), savoir ce qu'est un mesh, un material,... la base.

Cela dit les pros ou les habitués ou les gens qui n'ont pas appris à l'arrache comme moi rouleront sans doute des yeux devant ces conseils qui leur sembleront probablement évidents^^

Cette fois on se lance !

Dupliquer un objet... mais lier son mesh et son material


On commence par un truc tout simple. J'utilise Blender sérieusement depuis environ 2 ans, et j'ai appris ça aujourd'hui, la vie est dure :'(

Lorsqu'on duplique quelque chose en mode objet, plutôt que d'utiliser Shift-D pour une simple copie, on peut utiliser Alt-D et faire une copie liée.

L’intérêt est que lorsque vous modifier le mesh (en mode édition) de l'objet (ou les objets) copié ou bien le mesh de l'objet de base, les deux (3, 4, 5...) objets sont modifiés de la même manière. Un peu comme le font les Prefabs sous Unity3D.

Cela permet par exemple d'éditer les 22 murs constituants votre muraille en un seul coup ;)

Supprimer les faces inutiles


Si vous n'êtes pas au courant, je développe des jeux-vidéos. Une des grosses différences entre la 3D pour jeu-vidéo et la 3D cinéma/image, c'est qu'on est très attentif à ne pas laisser de polygones inutiles dans nos modèles qui utiliseraient des capacités de la carte graphique pour rien.

Un truc qui arrive souvent, c'est que j'ai des faces inutiles qui se retrouvent à l'intérieur d'un modèle. Par exemple la face tout en bas d'un mât que j'aurais planté dans mon bateau pirate, puisque la face est sous le pont, elle ne sert à rien. C'est facile de la sélectionner et la supprimer, mais parfois quand j'ai travaillé comme un porc (genre pour tout ce qui est bâtiment :D), il y en a plein, et je risque de supprimer une face utile (qu'on voit à l'extérieur du modèle) par accident.

Cette face est dans un cube, elle ne sera jamais visible en jeu

Bref, il y a une technique toute bête pour sélectionnez toutes ces faces inutiles assez facilement.

Mettez vous en mode édition et veillez à ce que rien ne soit déjà sélectionné. Passez en mode d'édition de faces (avec Ctrl-Tabulation) puis appuyez sur C pour sélectionner l'outil de sélection. C'est un cercle dont la taille est réglable avec la molette de la souris. Réglez-le pour qu'il soit assez très grand.

Commencez à tout sélectionner (en mode solide, pas en wireframe) sur votre modèle grâce à cet outil. Pas de panique si vous devez (et vous devrez) changer de vue, vous pouvez terminer la sélection en ré-appuyant sur C, bouger la vue puis remettre le mode de sélection sans perdre votre sélection. Passez bien partout, on a vite fait d'oublier une petite face par-ci par-là à cause d'un mauvais angle de vue.

Une fois terminé, désactivez l'outil de sélection C puis faites Ctrl-i pour inverser la sélection. Normalement, vous vous retrouvez avec toutes vos faces internes sélectionnées. Vérifiez bien qu'aucune face sélectionnée n'est visible de l'extérieur (aucun soucis si un des edge l'est) puis faites Delete/Faces.

Quand vous avez fini (ou avant même), c'est souvent une bonne idée quand on "nettoie" un modèle de faire un petit "Ctrl-V/Remove doubles" pour fusionner les vertex qui se chevaucheraient.

Insert face, pour créer une face dans une face


Déjà eu envie de faire quelque chose comme ça ?


Découper un carré dans une face carrée, un triangle dans une face triangle, etc...
C'est un peu de la base aussi mais j'ai utilisé Blender pas mal de temps sans le savoir. Il suffit de sélectionner la face dans laquelle on veut insérer notre nouvelle face et appuyer sur la touche 'i'. Bougez la souris pour régler la taille puis validez par un clic.

Appuyez une seconde fois sur "i" pendant le choix de la taille si vous voulez faire ça sur plusieurs faces à la fois (après avoir sélectionné les faces bien entendu).



Créer des groupes de vertex


En général, dans le jeu-vidéo, on souhaite n'avoir idéalement qu'un seul mesh, un seul objet, par fichier blender. Autrement dit, si je crée un chat, je préfère n'avoir qu'un objet chat plutôt qu'avoir un objet tête, un objet oreille_droite, etc...

Je fusionne donc mes objets mais que se passe-t-il ensuite si en travaillant sur ce chat, je souhaite ne sélectionner que la tête... Je vais m'amuser à chaque fois à sélectionner toutes les faces de la tête, en faisant gaffe de ne pas sélectionner de faces du corps ? Non.

A la place je peux créer des groupes de vertex. Ce qui se passe ici :


L'idée c'est que je vais créer un nouveau groupe, avec l'icone "+". Je peux double-cliquer sur le groupe qui vient d'être créé afin de lui donner un nom explicite, comme "tête".

Ensuite (ou avant, on est pas sectaire), je sélectionne toutes les faces de la tête de mon chat, puis dans Vertex Groups, je fais "Assign". Verrouiller le groupe avec le cadenas est une bonne idée pour éviter de modifier le groupe par accident.

A partir de maintenant je peux grâce au bouton "Select" ajouter la tête de mon chat à ma sélection à tout moment, sans galère^^

Conclusion


Et voilà, c'est tout pour aujourd'hui. C'est pas grand chose mais ça m'aurait fait gagner beaucoup de temps si j'avais su ça dès le début.

J'espère que ces astuces vous aideront et que vous ne venez pas de perdre 10 minutes de votre vie à me lire :)





samedi 4 juillet 2015

Créer un bon tutorial dans votre jeu-vidéo


Salut les gens, il fait chaud et c'est pas facile de travailler. Du coup je vais plutôt vous partager un truc auquel je pensais hier.

Qu'est-ce qui fait un bon tutorial dans un jeu-vidéo ?

Quelques précisions avant de commencer


Je vais principalement me concentrer sur les jeux "d'aventures" (que ce soit des FPS, des RPG, point'n'click,...). Si vous prévoyez de faire un puzzle game ou un clone de Tetris, il y a de forte chance que mes conseils ne vous servent à rien (mais quand-même un peu).

Par tutorial j'entends non seulement l'explication au joueur des concepts du jeu mais aussi la présentation de l'univers, donc on en parlera un peu.

Je vais vous présenter "mes" idées à partir d'un exemple que je vais décortiquer, pour que tout soit bien clair.

Vous êtes prêts ? Alors on y va.

Un bon tuto



Je sais, je vous en parle souvent... Une fois encore je vais vous saouler à propos de... Morrowind ! C'est un RPG open-world dans lequel vous êtes totalement libre.

Ce jeu propose un des meilleurs tutos auxquels il m'ait été donné d'être confronté et j'ai en fait mis pas mal d'années pour me rendre compte que le début était un tutorial.

Et ça, c'est la base d'un bon tuto: le joueur ne dois pas sentir qu'il est dans un tutorial.

Allez, on va suivre ça étape par étape et voir pourquoi il est si bon.

Ne brusquons pas le joueur



Quand le joueur débarque dans votre jeu, il ne connait rien. Et c'est dans la nature humaine de ne pas aimer être en position de faiblesse. Il est donc extrêmement important de ménager le joueur et de ne pas l'écraser sous des informations inutiles dont il n'aura besoin que bien plus tard.

Si vous ne faites pas ça, il risque de se sentir stupide et être frustré ("je ne comprend rien à ce jeu") et quittera simplement le jeu en se disant "bon bah j'y rejouerai plus tard"... Ce qu'il ne fera jamais^^.

Voyons comment les devs de Morrowind on choisi d'introduire leur jeu (ce n'est pas sale).

Vous commencez dans la cale d'un bateau, en face d'un PNJ (Jiub) qui vous parle: "Nous sommes arrivés en Morrowind". Vous ne pouvez pas encore bouger mais vous pouvez regardez autour de vous.

Parfait ! *clap clap, 10/10.



C'est un avis personnel et je ne suis pas certain que tous les game designers le partageront mais je pense qu'il est extrêmement important de placer le joueur directement dans son personnage plutôt que de lancer 40 cinématiques en début du jeu (bon, techniquement, il y a une cinématique de début dans Morrowind mais on nous parle en "tu" et puisque c'est un rêve que notre personnage fait, je considère qu'on est déjà dans la peau du perso même sans être aux commandes).

Je trouve ça important car ça permet aussi de régler les options du jeu (vidéo, sensibilité de la souris, etc...) dès le début et de voir directement s'il y a un soucis technique (bug d'affichage ou quoi).

Cette courte phase est aussi l'occasion de nous donner un peu d'infos sur l'univers dans lequel on va évoluer: "Nous sommes arrivés en Morrowind". C'est toujours mieux de donner l'info au joueur par le biais de personnages plutôt que par une cinématique ou pire, du texte. Le joueur a plus de chance de prêter attention à ce que vous lui dites puisqu'il est dans le perso et c'est plus immersif.

Jiub vous fait rapidement la causette avant de s'interrompre. "Silence, voilà le garde".

Une fois encore on nous donne de manière subtile des infos sur notre personnage et l'univers. On est donc un prisonnier. Je ne vous le dirai plus mais vous remarquerez dans la suite que c'est typique de cet excellent tuto, on vous donne les infos sur le monde petit à petit, pour bien vous laisser le temps de digérer.

Le garde vous demande de le suivre. Tadaaa ! Vous pouvez maintenant marcher ! Un peu plus loin il s'arrête devant une échelle surplombée d'une trappe. "Montez sur le pont et blablabla".

Encore une bonne leçon pour votre tuto. Cette courte phase n'a l'air de rien mais le jeu vient de vous apprendre de manière totalement immersive à vous déplacer et à interagir avec les objets (ici la trappe). Je parle d’immersion car on a choisi de ne pas vous donner l'info directement. Dans un jeu moderne (où les tutos ont tendance à nous prendre pour un con et à nous noyer sous le texte), il y a de fortes chances pour que le jeu se soit mis en pause et qu'une fenêtre soit apparue pour vous dire "Utilisez les touches Z, S, Q et D pour vous déplacer", "Appuyez sur 'E' pour ouvrir la trappe"...

Mais ici non. On vous dit simplement aller, suis mois, puis ouvre cette trappe. Ça peut paraître moins bien que de dire au joueur sur quelle touche appuyer mais ce qu'on y gagne c'est qu'on ne sort pas du personnage. On ne te sors pas de la fiction pour te dire "tu est dans un jeu-vidéo, utilise ton clavier".

Bien contrôler le joueur avant de le livrer à lui-même



L'astuce c'est que tout ça se passe dans un espace confiné avec aucune autre interaction possible. En principe le joueur n'aura aucun mal a effectuer ces actions simples. On s'assure aussi qu'il maîtrisera les concepts de base (marcher, utiliser) dans la prochaine section puisqu'il est impossible d'y accéder sans maîtriser ses concepts (là où un tuto uniquement sous forme de texte échouera lamentablement).

Une fois sur le pont du bateau, vous n'avez pas 36 endroits où aller, un garde sur le quai vous attends (et bloque le passage). Des murs invisibles vous empêchent de sauter du bateau et de partir n'importe où. Je ne suis pas fan du concept de mur invisible mais c'est le seul endroit du jeu où il y en aura, on pardonnera donc ça^^. Vous parlez au garde (apprentissage: la touche action permet de parler) et créez votre personnage.

Rien de spécial à dire ici, c'est juste la création du perso. Ça peut se faire de plusieurs manières pour s'adapter au niveau du joueur (si vous découvrez le jeu, vous pouvez prendre une classe pré-faite).

On vous demande ensuite de vous rendre au bureau de recensement. Pas difficile c'est le seule endroit où vous pouvez vous rendre. Vous entrez dans le bâtiment et...

Vous êtes libre, pour la première fois. Un tout petit peu libre... Ce qui m'amène à un aspect du tuto Morrowind vraiment génial.

Ça vous aura peut-être frappé en me lisant mais je vous ait dit plus haut que Morrowind était un jeu à monde ouvert dans lequel on est très libre. Hors depuis le début du tuto vous êtes sur rails... C'est pourri, non ?

Ben en fait pas vraiment. Déjà si c'est long à lire ici, tout ça se passe en 5 min grand max en jeu (sauf la création de perso si vous êtes perfectionniste comme moi^^). Mais surtout c'est un truc important lorsque l'on traite du monde ouvert. Si vous lâchez votre joueur au milieu du monde vous allez vraiment galérer pour lui apprendre quoi que ce soit. En fait vous seriez probablement obligé de recourir à des fenêtres de texte et vous auriez du malà vérifier que le joueur a bien compris un truc avant de passer au suivant.

Même si Arcanum est un jeu génial, la première fois que j'y ait joué je n'ai pas été capable de jouer plus de 5 minutes parce que le jeu nous lâche littéralement au milieu des bois sans aucune piste (ou presque) d'où aller. C'est bien plus tard (en années) que je l'ai relancé et ait réussi à franchir le cap du "pas de tutorial".



Donc même si ça peut paraître un peu nul, c'est important de confiner le joueur dans des espaces clos et de le forcer à faire ce que vous attendez de lui avant de passer à la zone suivante. Rappelez-vous juste comme je l'ai dit au début de ménager le joueur, n'exigez pas de lui de faire des actions complexes qu'il ne devra maitriser qu'à la fin du jeu... Vous aurez tout le jeu pour lui apprendre ça, concentrez-vous sur la base.

Le but est de lâcher le joueur aussi vite que possible (tout en s'assurant qu'il maîtrise ce que vous lui avez appris). Gardez toujours en tête qu'un tuto, aussi bon soit-il est ennuyant. Un bon tuto doit être court. Quitte à refaire une voir plusieurs petites phases de tutos avancés plus tard dans le jeu.

Dans le bureau de recensement. On vous apprend encore des trucs sur votre situation: vous avez été libéré sur demande de l'empereur. Et là vous commencez à comprendre que Morrowind est en fait inspirée de l'empire romain. Le PNJ à qui vous devez parler achève la création de perso (elle est coupée en deux pour permettre au joueur de souffler, encore un bon point pour le jeu) puis vous apprend à ramasser un objet (un papier reprenant vos infos de perso).

Comme j'ai dit, vous êtes libre dans cette pièce. Et en principe tout joueur de RPG va donc commencer à looter tout ce qu'il voit... et se faire engueuler et confisquer la marchandise par le garde de la pièce.

Le jeu vient encore de vous apprendre un truc, subtilement et sans casser l'immersion. Le vol dans le jeu est puni. Les devs du jeu ont choisi de mettre cette pièce avec ce garde spécial (il ne vous arrête pas, pas d'amende, pas de bain de sang^^) plutôt que de vous laisser apprendre ça à la dure dans "le monde réel du jeu". Je le redis, c'est une très bonne idée d'utiliser des espaces confinés et contrôlés (par vous, le développeur du jeu) pour apprendre des trucs au joueurs.

Étendre le champs d'action



Vous l'aurez peut-être remarqué, mais le tuto de Morrowind, bien que très linéaire, ouvre progressivement la zone de jeu. Il n'y a pas si longtemps, vous ne pouviez pas bouger, puis vous avez pu marcher dans la cale sans interactions possibles... Puis vous êtes montés sur le pont où on vous a montré le monde mais sans vous laissez y gambader... Pour enfin vous vous retrouvez dans une pièce où vous pouvez interagir avec divers objets mais sous le contrôle de PNJs.

Muni de votre ordre de libération, vous passez dans la pièce suivante. La zone de jeu s'élargit encore. Cette zone est constituée de deux pièces, la folie furieuse^^. Ici pas de PNJ, vous êtes libre de prendre tout ce que vous voulez, notamment votre première arme, et le jeu vous apprend quelques nouveaux trucs comme crocheter un coffre verrouillé. Ces actions deviennent plus complexe donc il y a tout de même une pause avec fenêtre de texte mais c'est très concis et simple, on ne vous noie pas sous l'info.

Dans la pièce suivante (après une petite sortie dans une cour extérieure pour varier les environnements) vous donnez l'ordre de libération à un mec, qui vous donne un paquet pour Caius Cossades (et plein d'infos sur le monde), un PNJ important du jeu. Vous sortez et CA Y EST ! Cette fois vous êtes vraiment libre d'aller où vous le souhaitez et de faire ce que vous voulez !

Ah ouf, ce tuto est enfin fini alors !

Heu... Pas vraiment^^

Le tuto après le tuto



C'est ici que le génie du jeu commence. Je vous disais au début que j'ai mis du temps à comprendre que le tuto de Morrowind en était un... Et vous avez du vous dire que j'étais stupide, tout ce qu'on a fait jusqu’ici, bien qu’immersif et non insultant envers l'intelligence du joueur (important^^) sentait quand-même le tuto.

Mais maintenant qu'on est totalement libre, c'est là qu'on ne se rend plus compte de l'aspect tutorial de la suite des événements. C'est reparti^^.

Vous êtes dans le petit village de Seyda Nihyn et il vous reste plein de trucs à apprendre. Le contrôle et l'espace confiné c'est bien pour vous, développeur. Mais pas top pour le joueur. Les mecs qui ont fait Morrowind l'ont bien compris et ils ont décidé de vous lâcher dans la nature.

Mais ça ne veut pas dire qu'ils vous ont abandonné à votre sort. Ce premier village n'en a pas l'air mais il fait toujours partie du tutorial.

Premièrement on doit se poser une question cruciale: est-on vraiment libre ?

Oui... et non !


En fait ce petit village a une situation particulière sur la carte. D'un côté on est sur la côte, ce qui retire pas mal de directions possibles où aller. On est aussi au milieu de marais, ce qui décourage un peu l'exploration sauvage. Il n'y a qu'une route et on se retrouvera rapidement devant un panneau nous parlant de Balmora... Ça tombe bien, c'est là qu'on nous a dit de livrer le paquet.

Mais le village lui-même a encore des trucs à nous apprendre. On y fera notre première quête qui va nous apprendre entre autre à attendre x temps, on y visitera notre premier magasin, on aura des choix de dialogues, des PNJ nous proposent déjà de l'entrainement contre quelques pièces,... Tout ça dans un tout petit village donc le joueur n'est pas perdu.

On part ensuite pour Balmora. On peut considérer que le tuto prend fin lorsque l'on quitte Seyda Nihyn mais je considère qu'il continue jusqu'à Balmora. Arrivé là-bas vous remettez le paquet (ce qui vous demande un peu de recherche pour trouver Caius) et Caius vous parle des guildes (on peut rejoidre plusieurs guildes dans le jeu: guilde des mages, des guerriers...) qui sont un excellent moyen de se lancer dans le jeu.

Caius vous donne aussi votre première vraie quête... Mais on vous a conseillé d'aller rejoindre une guilde, c'est important ! Car lorsque vous faites sa quête. Vous allez rapidement vous faire DE-MO-LIRE par un ennemi qui surveille l'entrée des ruines où il faut aller.

J'estime que le tuto prend fin à ce moment là. Les développeurs vous poussent du nid, assez brutalement pour bien vous faire comprendre que le "je te tiens par la main" c'est fini. On vous a donné quelques pistes mais vous êtes à présent réellement libre et vous avez toutes les clés en main pour vous amuser sans prise de tête.

On conclut ?



C'était long, hein ?^^

Ce qu'il faut retenir d'un bon tuto :


  • L'immersion est importante. Un joueur ne sera pas trop dérangé par un tutorial du moment que vous ne rendez pas évident le fait que c'est un tuto. Placez le aux commandes le plus rapidement possible, évitez les cinématiques ou textes d'intro trop longs.
  • Soyez bref, allez droit au but. Un tutorial trop long va décourager le joueur. Certaines personnes n'ont le temps de jouer qu'une heure ou deux de temps en temps, ne leur volez pas ce temps libre en faisant un tuto d'une heure. Je vous parle de Morrowind et pas d'Oblivion et Skyrim pour une bonne raison. Leurs tutos sont longs et ennuyants...
  • Gardez le joueur sous contrôle. Veillez à ce qu'il maîtrise ce que vous lui avez appris avant de passer au truc suivant. Je vous conseille pour ça d'utiliser des petits espaces où les possibilités d’interactions sont limitées à ce que vous apprenez au joueur.
  • Ménagez le joueur. Faites des pauses au milieu de votre tuto pour le laisser digérer l'info.
  • Donnez-lui de plus en plus de liberté. Être enfermé dans un tuto n'est pas très agréable mais c'est nécessaire, ouvrez l'environnement et les possibilités d'action petit à petit.
  • N'apprenez pas d'actions trop complexes et présentement inutiles au joueur. Surcharger votre tutorial est une très mauvaise idée. Au pire, aménagez une seconde phase de tuto plus loin dans le jeu pour apprendre les actions complexes. Si le joueur se sent écrasé par tout ce qu'il doit apprendre, il ne va pas s'amuser.
Et voilà ! J'espère que ça vous aidera et que plus jamais... PLUS JAMAIS, je n'aurai à subir un tuto comme celui de The Witcher 2 ! J'écrirai peut-être un billet bonus pour vous parler des mauvais tutos et voir ce qu'il faut éviter.

Beaucoup de développeurs ont tendance à laisser le tutorial de côté et à l'ajouter à l'arrache lorsqu’ils ont fini leur jeu. C'est une très mauvaise décision. C'est certain que ce n'est pas la partie du jeu la plus passionnante à créer, mais c'est la première chose que verront les joueurs lorsqu'ils lanceront une partie. Comme pour tout, la première impression est extrêmement importante et ça serait une erreur de ne pas soigner son tutorial.

En attendant, amusez-vous bien et développez de bons jeux :)


mercredi 11 mars 2015

L'UV Unwrap sous Blender - La méthode noob


Hello tout le monde !

Un petit tutorial Blender aujourd'hui pour ceux qui galéreraient à faire de l'UV Unwrap simplement. Si vous ne savez pas ce qu'est de l'UV Unwrap, remerciez les dieux et filez donc d'ici petits chenapans^^ (plus sérieusement si le sujet vous intéresse je vous renvoie vers un tutorial plus détaillé).

Mise en situation



Vous avez un objet relativement simple (cube, prisme, hexagone, sanglion...) a unwrap et les tutos que vous trouvez sur internet on tendance à, soit:

  • Vous embrouiller car trop poussés
  • Être trop simples et ne pas aller plus loin que le cube (du coup c'est pratique si vous êtes là pour un déballage de cube :D)
Et hop, j'arrive à point^^. Je vous propose une méthode simple (mais donc très limitée et un peu sale) pour faire un unwrap vite fait, bien fait.

C'est parti !


  • On va commencer par préparer notre environnement
Le but est d'avoir deux vues, notre mesh à droite, et notre texture UV à gauche. Faites comme sur cette image :

Cliquez pour voir en grand
L'étape 1 coupe la fenêtre en deux vues identiques, l'étape 2 change la vue de gauche en une vue UV

  • Sélectionnez ensuite toutes les faces de votre objet, en mode édition
  • Dans la vue de droite, on utilise le menu "Mesh / UV Unwrap / Smart UV Project"
(Je suis passé sur un cube mais comme la première image vous le montre, ça a marché sur un hexagone)

Vous devriez vous retrouvez nez-à-nez avec cette boite de dialogue :


  • Mettez tout au minimum. En principe il faut juste changer "Angle Limit" et le mettre à 1
Votre vue de gauche va changer. Miracle ! Toutes les faces de notre objet ont étées unwrap !

Nan mais attend, c'est quoi cette arnaque ?! Pourquoi mes carrés sont devenus des rectangles et/ou l'inverse ?! Remboursez !

Du calme, du calme, on va tâcher de corriger ça^^.

Vous vous rappelez de la boite de dialogue précédente, on peut retrouver ses paramètres ici...:

...au point 3

  • Jouez avec le paramètre du milieu, "Island margin". Allez-y molo, il est sensible^^.


Notez que vous pouvez aussi sélectionnez les points que vous souhaitez et les bouger manuellement (en laissant le clic milieu enfoncé en principe), ctrl et shift vous permettront de bouger en restant droit (ctrl) et en étant plus précis (shift). 
C'est utile si vous voulez que toutes les faces de côtés de votre cube aient la même texture par exemple.

  • Il ne vous reste plus qu'a générer une texture et à l'exporter. Sauvez ensuite votre travail.

C'est sur cette texture que vous dessinerez ce que chaque face devra afficher. Vous pouvez utiliser le Paint Tool sous Blender pour déjà peindre les faces et qu'on les voit sur la texture UV que vous venez de créer mais je vous laisse vous renseigner sur ça tout seul vu que j'ai pas du tout le niveau et que ça mériterait un tout autre tuto^^'.

Peindre le truc dans Blender vous facilitera la tâche une fois dans Photoshop/Gimp/... Ne me demandez pas pourquoi Blender à dépassé des bords par contre :/

Une fois sous Unity ou autre, glissez la texture sur votre mesh et elle devrait être appliquée correctement.

Conclusion


Voilà, j'espère que ce petit tuto sans prétentions vous aura aidé. Comme je l'ai dit cette méthode est loin d'être parfaite et elle ne marchera sans doute pas pour des modèles trop élaborés.

Je croise les doigts pour vous^^

dimanche 10 août 2014

Timer.js, un script pour les gouverner tous


Hello tout le monde !

Un billet bien différent de d'habitude aujourd'hui avec un cadeau pour les codeurs qui utilisent Unity.

C'est quoi ?! C'est quoi ?!!!


C'est le script que j'utilise pour gérer tous mes timers, que ce soit pour du fading de trucs divers (musique, transparence d'une image, etc...), pour les cooldowns des sorts, pour les animations de mes personnages, etc...

Ça fait longtemps que je l'utilise et depuis l'horrible première version qui utilisait des numéros d'identification pour désigner les différents timers (genre, timer[0] = le timer de l'animation de marche => bonne chance pour s'y retrouver dans votre code quand vous gérez 30 timers différents^^), ce script a fini par devenir vraiment simple d'utilisation et rudement efficace.

Du coup je me suis dit que ça pourrait en intéresser certains. C'est cadeau !

Les fonctions à connaitre



InitializeTimer(name:String)

Démarre un timer

Cooldown(name:String, timeToWait:float) : boolean

Permet de voir si timeToWait (en secondes) s'est écoulé depuis le lancement du timer

Countdown(name:String, timeToWait:float) : float

Pareil mais renvoie le temps restant (en secondes)

SafeCooldown(name:String, timeToWait:float) : boolean

Idem que Cooldown mais, contrairement à celui-ci, renvoie false si le timer n'a pas été initialisé avant

Utilisation concrète



Vous allez voir que c'est simple comme tout à utiliser, en général je n'utilise que les deux premières fonctions.

La première étape c'est de lier le script à un GameObject et de le déclarer dans le script qui doit l'utiliser. Attention que les timers sont donc partagés dans un même gameObject. Donc si deux scripts présents sur un seul gameObject l'utilisent, attention à ne pas donner deux fois le même nom à deux timers différents.

Ensuite c'est très simple. En fait InitializeTimer() n'est pas vraiment utilisé pour initialiser un timer (en général !) mais plutôt pour le réinitialiser.

On va donc tout de suite partir sur notre fonction Cooldown(). Imaginons qu'on veuille faire une animation de marche ou d'attaque en deux frames.


Rien de plus simple ! On va travailler dans la fonction Update() de notre script de déplacements.

Cliquez sur l'image pour la voir en grand

Et voilà, c'est aussi simple que ça !

Pourquoi on initialise pas le timer avant de l'utiliser ?

En fait, la première fois qu'on va appuyer sur la touche de déplacement, la fonction Cooldown va se rendre compte qu'on a pas initialisé notre timer. Elle va donc renvoyer true, notre animation de marche va se lancer puis on initialise le timer.

Le fait qu'on ne l'ait pas initialisé avant d’appeler Cooldown va simplement faire que dès l'appui sur la touche, l'image du perso va changer.

Parfois, vous voudrez peut-être éviter ça (pour éviter qu'une bombe explose dès que vous la posez par exemple^^). Dans ce cas il suffit d'utiliser SafeCooldown() ou bien d'Initialiser le timer avant d’appeler Cooldown().

Conclusion et download



Voilà voilà, j'espère que j'ai correctement expliqué tout ça. Retenez surtout l'utilisation concrète, c'est ça que j'utilise dans 99% du temps, le reste est assez peu utilisé mais c'est utile de savoir que c'est là pour le jour où vous en avez besoin^^.


Vous pouvez l'utiliser dans tous types de projets (commerciaux ou non), le modifier, le redistribuer, etc... Vous pouvez me citer dans les crédits de votre jeu si vous voulez mais vous n'êtes pas obligés.

(N'hésitez pas à laisser un commentaire si le lien vient à mourir).


mercredi 12 mars 2014

Tutorial: x360ce, l'émulateur de pad xbox



Hello tout le monde !

A la demande générale (de une personne...je suis quelqu'un de très influençable), j'ai décidé de vous écrire un petit tutorial sur comment utiliser x360ce. J'ai utilisé plein de .gif qui pèsent une tonne, désolé :S

x360ce ?! C'est quoi ?


Derrière ce nom totalement barbare se cache un émulateur de pad xbox360... Et là je vous vois déjà ven~

C'est quoi un émulateur de pad xbox ?

Ça permet de simuler un pad xbox à partir d'un pad normal. Donc de faire croire à votre PC que votre pad Thrustmaster est un pad xbox, par exemple.

Heu... ok, c'est complètement débile en fait. Quel est l’intérêt ?

C'est bien ça le drame, c'est qu'aujourd'hui, avoir recours à ce genre de trucs est indispensable si vous n'avez pas de pad xbox (la looze, il utilise un pad deux fois moins cher avec une meilleure prise en main ! Hahah, le noob !).

Le soucis c'est que certains jeux, comme Castle Crashers par exemple, ne supportent que les pad xbox et pas les autres. En général c'est parce que ces jeux viennent tout droit du Xbox Live et ont été portés sur PC avec les pieds.

Si vous jouez à ce genre de jeux (et il y en a pleeeeein, et des bons en plus) avec un pad normal, il se peut que votre pad ne soit pas du tout reconnu, ou qu'il se comporte bizarrement (genre avoir une gâchette comme bouton A).

Mais heureusement x360ce et votre Beldarak préféré sont là pour remédier à ça. Allez, c'est parti !

Se le procurer


Vous avez deux solutions.

La compliquée consiste à se rendre sur le site officiel et à télécharger tout ce qu'il faut. Vous devriez vous retrouver au final avec x360ce.exe, xinput1.3.dll (ou autres versions) et ditool.exe (bien que je ne sois pas sûr qu'il soit utile). Peut-être aussi un ou deux autres fichiers. Mettez tout dans le même dossier.

Ou alors vous choisissez la solution facile et vous téléchargez simplement ceci: x360ce prêt à l'emploi^^. La méthode compliquée a plus de chance d'être à jour mais bon, tant que ça marche moi je m'en fous.

Installation



Commencez par décompresser l'archive (si vous avez suivi la méthode facile). Personnellement je garde toujours le dossier obtenu sur mon bureau pour y avoir accès rapidement (oui il faut faire tout ça pour chaque jeu, mais quand on a l'habitude ça prend 3 secondes).

Ensuite c'est super facile. Vous ouvrez le dossier du jeu pour lequel vous souhaitez utiliser x360ce et vous copiez/collez tout le contenu du dossier x360ce dedans, là où le .exe du jeu se trouve. Si c'est un jeu Steam, ça se trouve en principe dans: C:\Program Files\Steam\SteamApps\common ou un truc du genre selon votre version de Windows et l'endroit où vous avez installé Steam.



Configuration



Une fois que c'est fait, assurez-vous que votre ou vos gamepads sont bien branchés puis cliquez sur x360ce.exe (celui dans le dossier du jeu évidemment).

A peine démarré, x360ce va commencer par vous agresser avec une question en anglais.


Il veut savoir si vous avez un fichier de configuration ou si vous voulez chercher une config toute faite pour ce pad, ici un "Thrustmaster vibrating gamepad" (qui n'a de vibrating que le nom). Vous pouvez aussi cliquer sur "Cancel" pour le configurer à la main (pour les masochistes).

Puisque c'est la première fois qu'on lance le programme, on a pas de fichier de configuration (en fait j'ai jamais trouvé comment en créer un :/), on va donc le laisser chercher sur internet. Cliquez sur "Next" et laissez faire la magie.

Une fois terminé, en principe il vous aura trouvé une config adaptée à votre pad (pas de panique si ce n'est pas le cas). Cliquez sur "Finish".

Si vous avez plusieurs pads, il vous demandera quoi faire pour chaque pad.

Vous devriez ensuite arriver sur ça :


Ici vous allez pouvoir vérifier que tout est bien configuré comme il faut (s'il vous a trouvé une config) ou faire des modifications / faire votre propre config.

Essayez bien tout (axes, boutons, gâchettes) pour vous assurez que tout est bien mappé.

Heu, non, c'est pas bien mappé

Ok, pas de soucis, je vais vous montrer comment faire vos propres modifications. Dans le cas de mon Thrustmaster, les deux champignons ne se règlent jamais comme il faut.

Il y a deux méthodes pour configurer un input. La première c'est avec un "Record" :


Vous sélectionnez l'axe ou le bouton qui pose problème, vous cliquez sur Record puis vous appuyez sur le bouton qui doit correspondre sur votre gamepad. Pour les axes il faut parfois s'y prendre à deux ou trois reprises, genre ici ça ne se voit pas mais je me suis planté de direction et l'axe Y est inversé^^.

La seconde méthode c'est de sélectionner à la main le bouton / axe à associer. Ça demande une assez bonne connaissance de votre pad.


Une fois que tout est à votre goût, cliquez sur "Save" en bas à droite puis fermez le programme.

En principe tout est bon, vous pouvez maintenant lancer le jeu et tout devrait fonctionner comme si vous utilisiez un pad xbox360. Elle est pas belle la vie ?

Bonus: je n'ai pas testé cette fonction mais je viens de voir qu'en allant dans l'onglet "Settings Database", vous pouviez uploader vos configs. Je l'ai fait, on verra bien s'il reconnaîtra directement mes axes la prochaine fois ;)

Conclusion



Et voilà, avec ça vous devriez arriver à vous servir de x360ce sans trop de problèmes. Je n'ai pas été très loin dans le programme mais on peut vraiment configurer plein de trucs. Cependant je n'en vois pas vraiment l'utilité pour une utilisation classique du pad. J'imagine que ça sert si votre pad se comporte vraiment bizarrement ou si vous êtes perfectionniste.

Si vous avez des questions, remarques ou suggestions, les commentaires sont là^^.

Bon jeu !


lundi 11 novembre 2013

Tutorial Unity3D: Construire ses propres outils - Partie 3

Hello tout le monde.

Avec seulement 6 mois de retard (continue de sourire, ça va passer), voici la partie 3 de cette suite de tutos.

Dans cette partie, on va apprendre a modifier Unity en intégrant nos propres outils directement dans son interface (ce n'est pas sale !). L'idée de modifier Unity peut paraître effrayante et/ou difficile. Mais on va voir que ce n'est pas bien différent des scripts que vous êtes habitués a écrire.

Comme d'habitude je préviens que ce tutorial n'a en rien la prétention d'être complet. Son but est surtout de vous aider a vous lancer tout en vous expliquant les bases. Pour le reste la documentation d'Unity vous tend les bras^^.

Allez, c'est parti !




Ce qu'on va réaliser



Pour avoir un cadre de travail, on va coder quelque chose de bien concret, un "éditeur de niveaux" très basique. Ce petit éditeur permet de copier/coller, déplacer et orienter des objets très facilement et de manière précise. Ça permet par exemple de placer des murs à la suite l'un de l'autre très rapidement en étant sûr qu'il n'y aura pas d'overlaps.

Il ressemble à ça, perso je l'utilise tous les jours.



Il est axé 2D mais comme vous le voyez, il y a deux petites rajoutes 3D (Y Up et Y Down) que j'avais faites pour mon prototype de Spacesim.

Vous aurez sans doute aussi besoin de changer les axes utilisés si vous n'utilisez pas les mêmes que moi. J'utilise toujours l'axe Y comme axe vertical. Ce qui fait que les coordonnées de "Song of the Myrne" se font en (X, Z) et non pas en (X, Y) comme certains font.

L'interface



On va commencer par la partie visuelle. Je vais vous apprendre a créer une fenêtre, y placer du texte, des boutons, etc...

Vous allez voir que c'est presque la même chose que de faire une interface normale. La principale différence c'est qu'on va généralement éviter de travailler avec des Rect, on préférera souvent que les éléments se placent à la suite l'un de l'autre dans la fenêtre plutôt qu'en donnant des dimensions et un positionnement fixe à nos éléments.

Notez qu'on peux quand-même le faire, dans ce cas on utilisera les GUI.Button, GUI.Label,... habituels.

Créer une fenêtre

Commençons par créer un script (je travaille en javascript). Mais attention ! Pas n'importe où. Les scripts qui modifient Unity, soit l'éditeur, se placent dans le dossier "Assets/Editor". Si vous ne le mettez pas là, et uniquement là (ou dans un sous-dossier de celui-ci), ça ne fonctionnera pas. On appelle ces scripts des EditorScripts.

Une fois votre script créé, appelons-le "Editor2D.js" par exemple, on va y écrire le code de base pour la création d'une fenêtre:

  • Pour commencer on crée une classe "Editor2D" qui se base sur la classe EditorWindow :

class Editor2D extends EditorWindow {
}


  • Dans cette classe on va ajouter une des fameuses commandes @ vues dans la partie 2 du tuto.
@MenuItem ("Window/Editor2D")

Cela va permettre d’appeler notre fenêtre dans Unity, parce que faire une fenêtre, c'est cool, mais si on ne peux pas y accéder, ça sert un peu à rien^^.

  • Bien, maintenant on peux créer la fenêtre proprement dite (le nom de la fonction n'a pas d'importance):
static function Init ()
    {  
        var window = ScriptableObject.CreateInstance.();
        window.Show();
    }


  • On ajoute une fonction OnGUI() et puis c'est bon !
A ce stade, votre script doit ressembler à ça:


Dans Unity, si vous allez dans le menu Window et que vous cliquez sur Editor2D, il devrait vous ouvrir une fenêtre vide.

On va bientôt ajouter du contenu à cette fenêtre, Unity est un peu capricieux et ne veux pas toujours rafraîchir le contenu de la fenêtre (de mon temps, il ne le faisait pas du tout automatiquement, ne vous plaignez pas^^), voilà donc ce qu'il faut faire pour la rafraîchir:

  • Simplement cliquer ou double-cliquer sur la fenêtre pour prendre le focus, en général ça la rafraîchit et vous pouvez voir l'effet de vos modifications dedans.
  • Si ça ne fonctionne pas, redimensionnez-là, en principe ça marche à tous les coups
  • Si vraiment ça ne fonctionne pas, pensez a ajouter un bouton appelant la fonction Repaint(); ou fermez puis ré-ouvrez la fenêtre.
Bien, elle est bien cool notre fenêtre, mais elle fait un peu vide.

Le contenu de l'interface


C'est ici qu'un bon 90% du travail est fait.

Ce qu'il faut savoir avant de commencer, c'est qu'Unity va remplir notre fenêtre de haut en bas, dans l'ordre de ce qu'il trouvera dans le OnGUI (puisque comme je l'ai dit, on ne va pas donner de positions fixes à nos éléments).

Outils



Voici la liste des outils qu'on va utiliser pour quand-même mettre un peu d'ordre dans notre fenêtre:

GUILayout.BeginHorizontal();
GUILayout.EndHorizontal();

Ces deux commandes vont nous permettre d'ouvrir puis de fermer une zone horizontale. C'est à dire que tout le contenu entre ces deux lignes va s'aligner de gauche à droite et non plus de haut en bas. Comme vous l'avez vu sur le screen de notre éditeur, nos "tableaux de contrôle" (Move, Rotation et Copy) sont placés les uns à côtés des autres, c'est grâce à ces commandes.

GUILayout.Space(tailleEnPixel);

Cette ligne-ci va ajouter un espace vide entre deux éléments. Cet espace sera vertical par défaut, mais horizontal si la ligne se trouve entre un BeginHorizontal et un EndHorizontal.

Au niveau des boutons, labels, etc, on va chaque fois utiliser la classe GUILayout plutôt que la classe GUI. Ce qui comme je l'ai dit, nous permettra de ne pas donner de position fixe à nos éléments.

Ainsi, GUI.Button(Rect(posX, posY, sizeX, sizeY), "Click me") deviendra GUILayout.Button("Click me");

Rien de bien nouveau ici.

Sauf que par défaut, les boutons, les labels,... prendront toute la largeur disponible (la largeur de la fenêtre si le bouton est seul, la moitié s'il y en a deux, etc). Comme ce n'est pas forcément joli, on utilisera GUILayout.Width(tailleEnPixel) pour donner une taille à un élément.

Exemple: GUILayout.Button("Click me", GUILayout.Width(sizeX));

Bien, vous avez tout bien retenu ? Alors c'est parti !

Promis, cette fois on tape du code !

Pour commencer, déclarez la variable var sizeOfObject:int = 10; au tout début de la classe Editor2D. Cette variable va permettre de préciser à notre éditeur la taille des objets qu'on manipule. En gros, si je la laisse par défaut, à 10 (c'est la taille de mes petits carrés dans Song of the Myrne), et que j'appuie sur "Move Right" avec un objet sélectionné, cet objet va être déplacé de 10 unités sur la droite.

A partir d'ici, tout se passe dans la fonction OnGUI si je ne précise rien.

On va donc écrire notre interface en commençant par le haut, On a donc les options (l'option unique en fait^^) de l'éditeur.

// Ces trois variables serviront plus tard
var sizeButtons:int = 130;
var ecartPanneaux:int = 30; // L'écart entre nos panneaux de contrôles
var ecartTitles:int = sizeButtons; // L'écart entre le titre "Move" et le bord gauche de la fenêtre

// Le deuxième argument permet simplement de mettre le texte en gras
GUILayout.Label ("Base Settings", EditorStyles.boldLabel);
     
GUILayout.Space(10); // Cet espace n'est pas dans une zone Horizontale, il sera donc vertical
     
// On commence une zone horizontale pour que la suite s'affiche en ligne et pas en colonne
GUILayout.BeginHorizontal();      
sizeOfObject = EditorGUILayout.IntField("Size of Object", sizeOfObject, GUILayout.Width(250));
if(GUILayout.Button("-", GUILayout.Width(40)))
{
     if(sizeOfObject > 10)
     sizeOfObject -= 10;
}
if(GUILayout.Button("+", GUILayout.Width(40)))
sizeOfObject += 10;
GUILayout.EndHorizontal();

L'étape suivante consiste a afficher les trois panneaux de contrôles. Y'a vraiment rien de nouveau ici donc je vous donne le code complet de la fonction OnGUI. Je pense avoir déjà expliqué tout ce qui s'y trouve.

function OnGUI ()
    {
    var sizeButtons:int = 130;
    var ecartPanneaux:int = 30;
    var ecartTitles:int = sizeButtons;
   
        GUILayout.Label ("Base Settings", EditorStyles.boldLabel);
     
        GUILayout.Space(10);
     
        GUILayout.BeginHorizontal();      
        sizeOfObject = EditorGUILayout.IntField("Size of Object", sizeOfObject, GUILayout.Width(250));
        if(GUILayout.Button("-", GUILayout.Width(40)))
        {
        if(sizeOfObject > 10)
        sizeOfObject -= 10;
        }
if(GUILayout.Button("+", GUILayout.Width(40)))
sizeOfObject += 10;
GUILayout.EndHorizontal();
     
GUILayout.Space(10);
     
        GUILayout.BeginHorizontal();
        var decalTitre:int[] = new int[3];
        decalTitre[0] = -25;
        decalTitre[1] = -decalTitre[0]+ecartPanneaux-20;
        decalTitre[2] = -decalTitre[0]-decalTitre[1]+ecartPanneaux+40;
        GUILayout.Space(ecartTitles+decalTitre[0]);
        GUILayout.Label ("Move", EditorStyles.boldLabel, GUILayout.Width(sizeButtons));
        GUILayout.Space(ecartTitles+decalTitre[1]);
        GUILayout.Label ("Rotation", EditorStyles.boldLabel, GUILayout.Width(sizeButtons));
        GUILayout.Space(ecartTitles+decalTitre[2]);
        GUILayout.Label ("Copy", EditorStyles.boldLabel, GUILayout.Width(sizeButtons));
        GUILayout.EndHorizontal();
     
        GUILayout.Space(10);
     
        GUILayout.BeginHorizontal();
        GUILayout.Space(sizeButtons/2);
        if(GUILayout.Button("Up", GUILayout.Width(sizeButtons))) //Move Up
        Move("Up");
        GUILayout.Space(sizeButtons + ecartPanneaux);
        if(GUILayout.Button("0", GUILayout.Width(sizeButtons))) //Rotation 0
        SetRotation(0);
        GUILayout.Space(sizeButtons + ecartPanneaux);
        if(GUILayout.Button("Up", GUILayout.Width(sizeButtons))) //Copy Up
        Copy("Up");
        GUILayout.EndHorizontal();
     
        GUILayout.BeginHorizontal();
        if(GUILayout.Button("Left", GUILayout.Width(sizeButtons))) //Move Left
        Move("Left");
        if(GUILayout.Button("Right", GUILayout.Width(sizeButtons))) //Move Right
        Move("Right");
       
        GUILayout.Space(ecartPanneaux);
       
        if(GUILayout.Button("-90", GUILayout.Width(sizeButtons))) //Rotation -90
        SetRotation(-90);
        if(GUILayout.Button("90", GUILayout.Width(sizeButtons))) //Rotation 90
        SetRotation(90);
       
        GUILayout.Space(ecartPanneaux);      
        if(GUILayout.Button("Left", GUILayout.Width(sizeButtons))) //Copy Left
        Copy("Left");
        if(GUILayout.Button("Right", GUILayout.Width(sizeButtons))) //Copy Right
        Copy("Right");
        GUILayout.EndHorizontal();
       
GUILayout.BeginHorizontal();
GUILayout.Space(sizeButtons/2);
        if(GUILayout.Button("Down", GUILayout.Width(sizeButtons))) //Move Down
        Move("Down");
        GUILayout.Space(sizeButtons + ecartPanneaux);
        if(GUILayout.Button("180", GUILayout.Width(sizeButtons))) //Rotation 180
        SetRotation(180);
        GUILayout.Space(ecartPanneaux + sizeButtons);
        if(GUILayout.Button("Down", GUILayout.Width(sizeButtons))) //Copy Down
        Copy("Down");
        GUILayout.EndHorizontal();
       
        GUILayout.Space(15);
       
GUILayout.BeginHorizontal();
GUILayout.Space(sizeButtons/2);
        if(GUILayout.Button("Y Up", GUILayout.Width(sizeButtons))) //Move UP on Y
        Move("Y Up");
        GUILayout.Space(sizeButtons*3 + ecartPanneaux*2);
        if(GUILayout.Button("Y Up", GUILayout.Width(sizeButtons))) //Copy UP on Y
Copy("Y Up");
        GUILayout.EndHorizontal();
       
GUILayout.BeginHorizontal();
GUILayout.Space(sizeButtons/2);
if(GUILayout.Button("Y Down", GUILayout.Width(sizeButtons))) //Move Down on Y
Move("Y Down");
GUILayout.Space(sizeButtons*3 + ecartPanneaux*2);
        if(GUILayout.Button("Y Down", GUILayout.Width(sizeButtons))) //Copy Down on Y
Copy("Y Down");
GUILayout.EndHorizontal();
    }

Voilà, en principe avec ça, l'interface devrait être complète.

Horreur ! Ça ne compile plus ! Espèce de vile engeance du démon ! Tu m'a donné du code pété !

Mais non, pas de panique, ce code appelle les fonctions qu'il nous reste a coder. Mettez les appels de fonctions en commentaire si vous voulez voir ce que donne votre interface.

Les fonctions



Bon, avoir une belle coquille toute vide, c'est bien joli. Mais ça sert un peu à rien.

C'est pourquoi on va maintenant coder les 3 fonctions Move(), Copy() et SetRotation() qui vont faire tout le boulot dès qu'on appuiera sur un bouton de l'interface.

Elles sont bien sûr a placer dans la classe Editor2D. Ça peut paraître évident mais bon, perso je n'écris jamais le "classe machinTruc{}" sauf pour mes fenêtres, alors...

On va commencer par les deux fonctions les plus simples: Move() et SetRotation(). Ici il n'y a qu'un seul truc nouveau. Il s'agit de la classe "Selection".

En gros c'est tout con, si on veux accéder aux Transforms qui sont sélectionnés dans l'éditeur Unity, on utilisera:

for(var trans:Transform in Selection.transforms)

Rien de bien difficile ici. Voilà le code des fonctions:

function Move(direction:String)
    {
    if(direction == "Left")
    {
    for(var trans:Transform in Selection.transforms)
    trans.position.x -= sizeOfObject;
    }
    else if(direction == "Right")
    {
    for(var trans:Transform in Selection.transforms)
    trans.position.x += sizeOfObject;
    }
    else if(direction == "Up")
    {
    for(var trans:Transform in Selection.transforms)
    trans.position.z += sizeOfObject;
    }
    else if(direction == "Down")
    {
    for(var trans:Transform in Selection.transforms)
    trans.position.z -= sizeOfObject;
    }
    else if(direction == "Y Up")
    {
    for(var trans:Transform in Selection.transforms)
    trans.position.y += sizeOfObject;
    }
    else if(direction == "Y Down")
    {
    for(var trans:Transform in Selection.transforms)
    trans.position.y -= sizeOfObject;
    }
    }

function SetRotation(rot:float)
{
for(var objet:GameObject in Selection.gameObjects)
   {
    objet.transform.localEulerAngles.y = rot;
   }
}

La fonction Copy() ajoute une nouveauté à cause de la relation des objets avec leurs Prefabs. J'ai donc deux versions de cette fonction, choisissez celle que vous préférez ou ajouter les toutes les deux à l'éditeur avec une petite option de choix.

La première version ne contient pas la nouveauté, voilà déjà son code.

function Copy(direction:String)
    {    
    for(var objet:GameObject in Selection.gameObjects)
    {
    var instObj:GameObject = GameObject.Instantiate(objet, objet.transform.position, objet.transform.rotation);
    instObj.name = objet.gameObject.name; // évite d'avoir (Clone) dans le nom
    }
   
    Move(direction);
    }

L'inconvénient de cette version, c'est que si l'objet qu'on clone est lié à un prefab, la copie (qui en plus se met à la place précédemment occupée par cet objet) ne sera pas du tout liée à ce prefab. Autrement dit, si vous clonez un mur 500 fois, puis que vous ajoutez un script au préfab de ce mur, aucun de ces murs ne subira la modification. Vraiment pas conseillé...

Pour palier à ce problème j'ai écrit une seconde version de la fonction. Cette deuxième version utilise la classe PrefabUtility et va créer un nouvel objet à partir du prefab de l'objet qu'on veux cloner.

La voici :

function Copy(direction:String)
{
   for(var objet:GameObject in Selection.gameObjects)
   {
    var instObj:GameObject;
   
    var prefab:GameObject;
    if(PrefabUtility.GetPrefabParent(objet))
    {
    prefab = PrefabUtility.GetPrefabParent(objet);
        instObj = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
       }
       else
        instObj = Instantiate(objet,objet.transform.position,Quaternion.identity);
       instObj.transform.position = objet.transform.position;
       instObj.transform.rotation = objet.transform.rotation;

       instObj.name = objet.name; // évite d'avoir "(Clone)" dans le nom
   }
   Move(direction);
}

Elle n'est pas sans défaut elle non plus. Comme je l'ai dit, elle crée un objet à partir du prefab de cet objet, et ne tient finalement pas compte de l'objet lui-même.

Le soucis avec ça c'est que si vous avez, par exemple changé la texture d'un mur lié à un prefab, et que vous clonez ce mur, le mur cloné (qui une fois encore prendra la place de l'ancien mur, gardez le en tête si vous utilisez l'éditeur tel quel^^) ne possédera pas la nouvelle texture mais l'ancienne (celle du prefab donc). Dans cette situation, je créerais un nouveau prefab pour le mur possédant une texture différente, mais c'est pas toujours faisable, on ne va pas créer 500 préfabs pour un même objet^^.

Voilà voilà. En principe votre éditeur devrait fonctionner et votre code ressembler à ça:

http://i.imgur.com/gX2L4XI.png

Vous pouvez aussi télécharger mon script complet à cette adresse.

Une dernière petite astuce pour la route



Vous voilà, intrépides codeurs, prêts à conquérir le monde avec vos propres scripts. Et très vite vous allez vous retrouvez a modifier les valeurs d'un script, depuis un EditorScript. Alors retenez bien ceci ! Ne retenez pas forcément la commande ni quoi que ce soit, mais rappelez-vous que dans un coin obscur du web, un mec qu'on appelle Beldarak vous en a parlé, et alors à ce moment là vous pourrez revenir ici, et les yeux larmoyants, me remercier du fond du coeur pour les heures de galère que je vais vous épargner dans quelques instants.

Unity, n'aime pas trop qu'on modifie les valeurs d'un script (attaché à un objet donc) depuis un de nos propres EditorScripts. Il va alors faire un truc très chiant :

  • Dans un premier temps, tout va fonctionner
  • Vous allez ensuite appuyer sur "Play" et tester le jeu... Et vous rendre compte (ou pas, c'est là toute la perfidie du truc) qu'en fait votre modification n'a pas été prise en compte.
Pourquoi ? Parce que si dans un premier temps, Unity aura bien fait le changement des valeurs du script que vous modifiez depuis un EditorScript, il ne l'a en fait pas enregistré. Et donc dès qu'on appuie sur Play, il "oublie" le changement.

Une commande va donc vous sauver la vie, même si vous ne le savez pas encore^^

EditorUtility.SetDirty(leScriptModifié);

Ce que fais cette commande, c'est de dire à Unity que vous avez modifié votre script, et qu'il doit donc le sauvegarder. Appelez là de la manière que vous voulez, quand vous voulez (mais avant d'appuyer sur Pause ou de quitter Unity), mais appelez-là !

Elle prend un script comme argument. Donc par exemple, si vous venez de modifier un objet "Porte" pour que son script "Door" soit en position "ouverte" (un boolean donc), on devrait retrouver ça dans votre EditorScript:

porte.GetComponent(Door).open = true;
EditorUtility.SetDirty(porte.GetComponent(Door));

Et voilà, cette astuce vous sera probablement utile un jour.

Conclusion



Et voilà, c'est tout pour cette partie 3. Encore désolé pour le délai entre cette partie et la deuxième, au moins ça m'aura permis d'apprendre un peu le html entre temps et d'améliorer un peu la mise en page^^.

Voici quelques pistes pour aller plus loin et voir ce qu'il y a moyen de faire avec les EditorScripts (et qu'en principe vous devriez être capables de faire désormais) :


  • Lancer des fonctions toutes simples depuis l'éditeur, genre positionner le joueur sur l'objet sélectionné
Je ne l'ai pas encore dit mais pour lancer une fonction depuis l'éditeur, il suffit d'écrire un EditorScript (n'oubliez pas de le mettre dans le dossier "Assets/Editor") qui n'ouvre pas de fenêtre et de mettre: @MenuItem ("Mes Scripts Persos/Ma Fonction Trop Bien") juste avant la déclaration de la fonction qu'on souhaite exécuter depuis l'éditeur. En fait, c'est ce qu'on a fait tantôt avec la fonction Init() qui appelait la fenêtre de notre éditeur 2D.

Ces petites fonctions peuvent faire gagner un temps fou sur le long terme.

  • Un éditeur de niveaux bien complet (ça j'ai encore jamais tenté^^)
  • Un éditeur de dialogues intégré dans Unity (ça non plus)
Et cetera et cetera,... Les possibilités sont infinies.

Voilà, sur ce, je vous laisse. 

Codez bien, les enfants !