Little Blog Story

Game-Design & Modding le blog de belzaran.

Articles taggés avec ‘programmation’

Making-Of : Hunt (2)

Mardi 24 août 2010

J’ai commencé à programmer le mode Hunt pour Unreal Tournament III. Le plus gros problème de départ a été de configurer UT3 pour qu’il compile le code créé et que cela fonctionne in-game (si en soit, l’Unreal Script a peu changé entre UT3 et l’UDK, la façon dont ces deux logiciels le compile est légèrement différentes). J’ai alors rapidement programmé un embryon de gameplay que je suis allé testé. Les mauvaises surprises n’ont pas tardé !

Etat actuel du projet

Actuellement, un joueur démarre avec 200 points de vie et une vitesse ralentie. Lorsqu’il réussit un frag, il passe à 75 points de vie, une vitesse et un saut doublé. Le système de score (plus je reste longtemps en cible, plus je marque des points) n’est pas encore implanté. Actuellement, j’ai le choix de n’avoir aucune cible réelle ou la possibilité d’en avoir plusieurs en même temps. Je n’ai pas encore bloqué le fait qu’il n’y ait qu’une seule cible à la fois.

Le début de partie

Le début de partie est évidemment le problème majeur à régler au départ. Comment choisir la cible ? Mon code actuel dit que si l’on tue la cible, on devient cible. Pas de problème, ça fonctionne. Sauf qu’avec ce code, il faut une cible au départ. Ici, ce n’est pas le cas.

Plusieurs solutions s’offrent à moi. La première serait de choisir un joueur au hasard ou de prendre le premier connecté. Hors de question étant donné que cela donnerait un avantage à quelqu’un sans une bonne raison. La deuxième possibilité est de modifier le principe du « First kill ». En effet, quand la première personne est fraggué sur UT3, un ‘announcement’ signale qu’il a eu lieu et qui l’a fait. Il suffirait alors de mettre une ligne de code afin de faire devenir la cible le joueur qui a réussi le frag.

Je pensais fortement aller dans cette voie quand j’ai songé à un problème que je n’avais pas imaginé : que se passe-t-il si la cible quitte le serveur ? Afin de pallier à ce problème (et à tous les autres), il faut que je programme une fonction qui récupère les données de tous les joueurs afin de voir s’il y a une cible. Cette fonction pourrait ainsi permettre :

  • De signaler aux joueurs s’il n’y a pas de cible (dans ce cas-là, on peut fragguer tout le monde)
  • De pallier au problème du lancement de la partie (sans cible)

Les bots

UT3 étant un jeu déserté actuellement, j’ai peu d’espoir de voir mon mode de jeu joué sur de nombreux serveurs. Il est ainsi essentiel qu’il soit jouable avec des bots. Or, en supprimant la notion qu’équipe (comme signalé dans le précédent article), je perturbe les bots. Ceux-ci continuent à jouer comme en Deathmatch, ignorant les règles nouvelles. En cela, c’est un problème énorme car je n’y connais absolument rien en programmation d’IA. C’est l’occasion de m’y lancer, mais le projet que j’imaginais simple devient d’un coup bien plus ardu. Certes, il me reste la possibilité de publier un mode de jeu jouable uniquement entre humains, mais je perdrais l’occasion de le faire tester au préalable (voir tester tout court).

Etant donné l’absence d’avancées significatives et d’un prototype fonctionnel, je ne publie pas encore le code actuel du projet.

Little God Story - DevBlog #2

Jeudi 29 juillet 2010

Les premiers pas

Dans un projet, il y a toujours le moment où il faut ramener son concept sur un plan plus pragmatique. Après le “j’aimerais faire ça“, il faut passer au niveau “suis-je capable de le faire ?” Plus expérimenté depuis mon projet précédent, je me suis penché immédiatement sur la partie programmation. En effet, Unreal Safari m’avait montré qu’il était essentiel d’avoir un prototype minimal pour commencer à faire un level-design un tant soit peu intéressant.

L’embryon de Little God Story est, sur le papier assez simple. Voilà les features qui m’intéressaient au départ :

1 - Le joueur peut changer d’élément et cela affecte certaines caractéristiques (vitesse et hauteur de saut)
2 - Certaines parties d’un niveau sont liées à un élément et interagissent avec le joueur selon l’élément dans lequel il est (par exemple, le joueur meurt dans le Feu sauf s’il est dans l’élément Eau)
3 - La vue est à la troisième personne

Coder cela m’a pris deux jours. En effet, il n’y a rien compliqué à cela. J’ai d’abord utilisé mes connaissances d’Unreal Safari pour faire changer les caractéristiques de saut et de déplacement (en gros, c’était le même principe). Pour la partie Eléments du monde, cela n’a pas été très difficile. En effet, il existe déjà un système de Volumes dans l’Unreal Engine dont les biens nommés UTWaterVolume et UTKillZVolume. En les combinant, il a été facile de créer des volumes qui ne blessent que selon l’élément utilisé.

Le prototype créé est ultra-simple et encore pas mal buggué (le temps de réaction aux touches est de l’ordre de la seconde, les volumes blessent le joueur systématiquement quand on entre dedans…). Cependant, il est suffisamment avancé pour commencer à faire des maps de tests et à déterminer les distances de saut pour chaque élément, les différences de vitesse entre feu et terre (ce qui est très important pour les puzzles incorporant un besoin de rapidité). En quelques clics, je teste le saut à travers le Feu (base originelle de mon concept). Je trouve ça sympa de devoir changer d’élément en plein saut. Voilà un aspect plateforme qui est déjà implanté et déjà agréable.

A peine le prototype terminé, j’ai ressenti le besoin de le perfectionner bien qu’il atteigne les objectifs que je m’étais fixés. En effet, il est très désagréable de ne pas pouvoir savoir avec quel élément on joue. Même si il y a des ressentis en jeu (ne serait-ce que la vitesse de déplacement), l’intégration d’un HUD, même sommaire, paraissait essentiel, ne serait-ce que pour contrôler que les éléments changeaient bien quand j’appuyais sur la touche voulue (c’est ainsi d’ailleurs que je me suis aperçu du temps de réponse de mon code…). Le premier HUD est alors ultra-simple et moche, mais fonctionnel.

Les contrôles

Premier HUD

Premier HUD

Deuxième souci qui va vite se manifester : l’ergonomie des contrôles. Même s’il est prévu que le joueur puisse les changer, le rôle de la souris va ici être prépondérant. En effet, à l’origine, il n’y a pas d’armes dans Little God Story. Résultat, les clics gauche et droit sont utilisés pour changer d’éléments. Or, dans le cadre d’une permutation d’élément, ce n’est pas forcément ce qu’il y a de plus pratique. Les contrôles sont encore un sujet de discussion avec les testeurs. En effet, une arme a été ajouté (clic gauche) et il y a de forte chance qu’un tir secondaire soit implanté (clic droit). Résultat : le changement d’élément risque de devenir plus compliqué à gérer et la partie plateforme moins ergonomique. Dès le départ (même quand le jeu était plus simple et proposait moins de features), le souci d’ergonomie s’est posé. Je précise quand même que l’idée d’adapter les contrôles à un pad ne m’a jamais effleuré l’esprit…

Rapidement, je me suis aperçu également qu’il faudrait restreindre l’utilisation des éléments. En effet, en jouant aux premiers niveaux de test, j’ai pris l’habitude de me balader en mode Feu systématiquement pour aller plus vite et à sauter en mode Air pour assurer la hauteur de saut. J’ai donc du intégrer une jauge de mana pour contrôler tout ça. Je reviendrais là-dessus dans un prochain article, ce genre de feature méritant beaucoup plus qu’un simple paragraphe…

Première modélisation du personnage

Première modélisation du personnage

La vue à la troisième personne

Dernière chose qui fut changée dès les premiers tests : la vue à la troisième personne. Les premiers testeurs ont en effet critiqué cet aspect là, en arguant qu’elle n’apportait rien. J’ai donc du réfléchir à l’éventualité de supprimer ou non cette vue. J’avais prévu de donner une identité visuelle originale à mon personnage afin de renforcer l’idée de “petit dieu”. Abandonner la troisième personne me faisait perdre un aspect prépondérant. En revanche, passer à une vue à la première personne faisait que la modélisation et l’animation du personnage n’étaient plus essentielles, du moins dans l’immédiat. De plus, cela m’évitait de nombreux problèmes de caméra inhérents au style plateforme vue de derrière. J’ai donc fait sauter sans trop de remord la vue à la troisième personne.

Cela a eu beaucoup d’influence dans le développement du jeu en lui-même. Ce changement de perspective a changé radicalement les sensations de jeu et m’a poussé ensuite à ajouter des armes car Little God Story avait perdu fortement en dynamisme. J’avoue être également un très gros joueur de FPS. C’est clairement ce que je préfère. Je pense être plus à même de comprendre ce que veulent des testeurs sur ce genre de jeu que pour un jeu à la troisième personne où mes seules références sont Prince of Persia : The Two Thrones et Prince of Persia : Warrior Within… (quoiqu’on me reproche pas mal de faire un FPS puzzle-game sans avoir joué à Portal…)

L’intégration des boutons

Rapidement, je comprend que l’aspect plateforme sera insuffisant pour faire un jeu réellement intéressant. Tous les jeux plateforme intègre un aspect différent, souvent des combats (Prince of Persia, Tomb Raider…). Le problème, c’est que faire des combats demande beaucoup de boulot :

  • Faire des personnages non-joueurs
  • Animer ces personnages
  • Animer leurs armes
  • Donner une IA à ces personnages
  • Ajouter des armes au personnage principal
  • Ajouter les animations de ces armes
  • Coder les effets de ces armes (impacts, muzzleflash, dégâts…)
  • Équilibrer les combats
  • … etc

Le bouton Terre, le plus fréquemment rencontré dans le jeu

Le bouton Terre, le plus fréquemment rencontré dans le jeu

J’ai donc vite rejeté cette idée. Non seulement parce-que cela me paraissait infaisable seul, mais aussi parce-que je ne voulais pas faire des combats peu intéressants avec des ennemis imbéciles. Je me suis donc orienté vers une partie réflexion. Je ne l’ai pas fait que par fainéantise, dès le concept j’avais quelques idées ce genre (un peu comme Prince of Persia comporte quelques énigmes). Je me suis donc lancé dans la programmation d’un système d’interrupteurs basé sur les éléments. Par exemple, voilà le genre d’énigme prévu :

  1. Pour ouvrir la porte, je dois activer un interrupteur Feu
  2. Je dois donc me changer en Feu
  3. L’interrupteur Feu est immergé dans l’Eau
  4. Je ne peux donc pas l’activer (sinon je meurs car en Feu, je ne peux aller dans l’Eau)
  5. Comment changer ça ? (retirer l’eau, faire sortir l’interrupteur, etc.)

Il est évident qu’avec les quatre éléments, ce genre d’interrupteurs permet de créer de nombreuses énigmes. Malheureusement, le jeu s’est vite orienté vers le principe : actionner les interrupteurs dans le bon ordre. Dès les premiers tests, il a été important de corriger cela et d’éviter un aspect un peu rébarbatif, sans supprimer ce concept pour autant, qui donne une identité visuelle et de gameplay au jeu. Il fallait dynamiser le jeu. J’ai donc été rapidement rattrapé par mes démons : si je voulais faire un jeu varié et intéressant, je n’avais pas le choix : il fallait ajouter des “armes”… (pour rappel : les armes sont une des étapes primordiales que je ne suis jamais arrivé à passer pour Unreal Safari, tant en terme d’imagination que de réalisation).

A ce stade, je suis un peu taraudé entre deux tendances : je tiens un concept intéressant mais j’ai conscience qu’il faut que je le développe un peu pour qu’il atteigne un niveau autre qu’un simple concept intéressant (comme on voit beaucoup pour les projets étudiants par exemple). Il est toujours difficile de passer le cap d’une idée à un jeu complet. Malgré le fait que je voulais être très prudent sur ce point-là, je me sentais obligé d’être plus ambitieux… Pour le meilleur et pour le pire !

Unreal Safari : Post-mortem (4)

Mercredi 21 juillet 2010

unrealsafari

Premier game type:  Safari

Pour information : la plupart des problèmes techniques que j’ai rencontré sur ce game type, je les ai traités par une série de tutoriels publiés sur ce même blog. Je conseille donc aux lecteurs de mes tutoriels de récupérer les sources de Safari afin de les lire. Elles seront un très bon compléments aux tutoriels.

Après avoir publié mon premier mutator, je me lance dans la conception de la deuxième partie du gameplay : la partie niveau/expérience et bonus/malus. Le but est, je le rappelle, de faire que les niveaux des joueurs influent sur le nombre de points qu’ils marquent lorsqu’ils fraggent quelqu’un. Schématiquement, quand un noob tue le meilleur joueur, il fait gagner 10 points à l’équipe, alors que quand le meilleur joueur tue un noob, il ne fait gagner qu’un point. Le tout avec 6 niveaux possibles de jeu, sachant que l’on peut perdre un niveau si on se fait fragger.

Très rapidement, je m’aperçois qu’intégrer deux mutators dans un même game type peut amener des problèmes de conflits entre eux. Je décidé alors de développer un game type, Safari, qui intègrera d’office le mutator Opposite Team. Contrairement à ce que l’on peut croire, cette histoire de niveau m’a posé beaucoup de problèmes. D’abord parce-que certaines fonctions que j’avais utilisé auparavant n’existaient que pour les mutators. De plus, ce système est beaucoup plus perméable aux bugs de fonctionnement (remise à niveau des points d’XP ou du score, problème dans le décompte des points…). Bref, plutôt qu’une adaptation d’un tutoriel existant, je me suis rapproché un peu du vrai boulot du programmeur : traquer les bugs ! Ce game type m’a aussi montré les problèmes inhérent au jeu multijoueur. En effet, la programmation ne se fait pas de la même façon pour un jeu solo ou un jeu multi. Ce côté plus complexe m’a obligé à comprendre un minimum ce que je faisais et j’ai commencé à mieux appréhender l’Unreal Script.

Autre problème : quand on fait un jeu avec des points d’expérience et des gains de niveau, on est un peu obligé de les afficher à l’écran. La première version de Safari (non publiée) était fonctionnelle mais opaque. Impossible de voir quoi que ce soit. J’ai donc commencé à travailler sur un HUD et un système de message. Pour simplifier, il me fallait :

1 - un affichage à l’écran permanent du niveau, des points d’XP, de l’équipe et du nombre de frags
2 - un message annonçant un changement de niveau (positif ou négatif)
3 - un message affichant le nombre de points d’XP gagné lors d’un frag

Ce genre d’aspect paraît relativement simple mais quand on est un débutant en programmation, tout est compliqué. Au niveau des messages, j’ai vite su faire deux messages différents : l’un disait qu’on avait gagné un niveau et s’affichait en vert, le deuxième disait qu’on avait perdu un niveau et s’affichait en orange. Très vite, il a paru essentiel de remplacer “Level Up !” par “You are Level 2 !” Après des difficultés, j’ai commencé par créer une classe par niveau. Ce qui me donnait en gros :

LevelUpOne.uc
LevelDownOne .uc
LevelUpTwo.uc
LevelDownTwo.uc

Et ainsi de suite pour les six niveaux. On voit très bien les problèmes. Car si multiplier les classes est déjà pénible et peu optimisé, c’est encore plus compliqué à l’intérieur du code (je vais essayer d’expliquer ça simplement). Quand on change de niveau (c’est détectable par du code assez simplement), le jeu envoie un message. Si les classes de messages sont séparées comme ici, on doit alors mettre des conditions :

Si je suis niveau 1 -> Message 1
Si je suis niveau 2 -> Message 2
Si je suis niveau 3 -> Message 3

Et ceci, une fois pour les gains de niveau, puis pareil pour les pertes de niveaux. Même avec seulement 6 niveaux, cela faisait des lignes de codes à n’en plus finir. Or, ajouter des lignes de code, c’est s’ajouter des risques de bugs. J’ai donc appris à optimiser un peu mes messages afin de faire :

Si je suis niveau X -> MessageUp (X)

Evidemment, il faut faire la même chose si on perd un niveau. Cette façon de faire est beaucoup plus souple également si l’on souhaite changer des choses plus tard (par exemple ajouter 5 niveaux supplémentaires !). Avec le recul, je pense qu’il est tout à fait possible d’intégrer la notion de perte/gain dans une classe de message générique, mais le gain n’était pas forcément énorme. Cette “optimisation” (car après tout faire le bourrin fonctionnait très bien !) me permettra plus tard de coder des messages qui affiche le nombre de points d’XP gagnés lors d’un frag. Pour information, le fait d’afficher le niveau qu’on a atteint lorsque l’on change de niveau n’arrivera qu’à la toute dernière version du game type !

Au niveau du HUD, je n’ai pas rencontré de difficulté fondamentale si ce n’est mon inexpérience. L’affichage d’un HUD permet aussi de débugguer le code car on a accès aux changements de données en temps réel (quand le HUD fonctionne en tout cas !). Avec le recul, c’est peut-être une des premières choses à faire quand on code un game type un peu différent.

La première beta de Safari souffre de quelques bugs que je considère comme mineurs mais qui seront abhorrés par les joueurs. Par exemple, quand on respawne, un freeze de l’écran dure une seconde. En arrivant à corriger ce genre de bugs, je me suis senti pour la première fois un programmeur.

Octobre 2009 : je publie la version 1.0 de Safari. La version est tellement proche du gameplay que je souhaite pour Unreal Safari que je décide d’apposer ma “marque” sur le nom. Ce game type intègre les deux composantes du gameplay final : expérience et équipes opposés. Techniquement, cette version est stable et fonctionnelle. Cette release aura un impact plus fort encore que les autres sur la visibilité d’Unreal Safari. En effet, en lui donnant un nom extrêmement proche, j’alimente la confusion entre le game type (=Safari) et le mod (=Unreal Safari). Résultat des courses : ModDB annonce la release de mon mod en première page et l’intègre dans les releases de sa newsletter. A ce moment là, j’atteins des pics de fréquentation sur la page d’Unreal Safari.

safari3 safari4

Malgré tout, le game play Safari reste très confidentiel (à cause d’une communauté restreinte) mais les retours sont plutôt positifs. Je travaille sur une version 1.1 publiée un mois plus tard et apportant des changements importants comme le support des bots ou l’affichage des points d’XP gagnés ou perdus lors d’un frag. J’en profite pour préciser que si une release est toujours un moment excitant, publier un correctif ou une nouvelle version est encore plus fort. En effet, on a vraiment l’impression de progresser en lisant la liste des améliorations apportées.

Au niveau des releases, j’ai impliqué une nouvelle fois la communauté française. J’ai ainsi publié le game-type sur les forums spécialisés. Une fois que j’ai pu constater qu’il n’y avait pas de bug majeur, je lançais une release plus large sur des forums et sites plus fréquentés (forums Epic, BeyondUnreal et bien sûr ModDB). J’en profite pour remercier une nouvelle fois la communauté Unreal.fr, dont les conseils en terme de gameplay sont toujours pertinents !

Au final, la mise en place du gameplay Safari m’a permis de vraiment comprendre comment fonctionnait le code de l’Unreal Script et m’a obligé à apprendre à la débugguer et surtout l’optimiser. La version 1.1, aboutie et fonctionnelle, est la preuve vivante que l’Unreal Script (contrairement à ce qu’on lit partout !) est facile d’accès si on est prêt à mettre les mains dans le cambouis. En partant de zéro, il m’aura fallu 4 mois environ pour coder un game type pas si simpliste qui fasse pas trop bidouillé (c’est-à-dire avec un habillage cosmétique cohérent et ergonomique).

Avec le recul, ce game type est peut-être mon plus grand accomplissement dans l’aventure Unreal Safari. Car j’ai ajouté une cartouche à mon fusil de moddeur : celle de programmeur. Et vu le manque de codeurs dans le paysage du modding, ce n’est pas rien !

http://www.moddb.com/mods/unreal-safari/downloads/safari-11-game-type

Unreal Safari : Post-mortem (3)

Mardi 20 juillet 2010

unrealsafari

Premier mutator : Opposite Team

Après avoir publié une map, j’ai pris la décision de me lancer dans la programmation. En effet, j’ai eu conscience à ce moment là que tant que je n’aurais pas un embryon de gameplay codé, je ne pourrais pas réellement faire du level-design. Un exemple simple pour illustrer ça : certains animaux sautent plus haut que les autres et un des intérêts est de pouvoir prendre des raccourcis auxquels les autres animaux n’ont pas accès. Mais tant que je ne sais pas à quelle hauteur saute tel animal, difficile de construire une map…

Dans le cadre d’une modification d’un jeu Unreal Tournament 3, il y a plusieurs niveaux :

1 - le mutator : change un aspect mineur du jeu (par exemple, les joueurs démarrent avec un seul point de vie)
2 - le game type : change les règles du jeu (par exemple, un capture the flag avec un seul flag central)
3 - le mod : change le game-type mais aussi des éléments du jeu (par exemple, de nouvelles armes)
4 - la totale conversion (TC): change entièrement le jeu (normalement, on ne doit plus voir que l’on joue à UT3)

Evidemment, comme beaucoup de modders, mon rêve était la TC. C’est la façon de modder la plus prestigieuse mais aussi la plus difficile. M’appuyant sur des tutoriaux, je décide de faire petit à petit, à savoir de faire d’abord un mutator, de le complexifier en game-type, puis d’ajouter les armes, puis de tout finaliser. Je pense que c’est à ce moment que j’ai pris la meilleure décision… En effet, en devant coder du gameplay, ça me poussait à fixer mon gameplay.

C’est à ce moment-là que j’ai finalisé mon gameplay. Au final, il était bien plus original qu’à l’origine. Il se basait sur deux équipes, les hippopotames et les gazelles. Deux idées principales fixaient le gameplay :

1 - Les deux équipes sont très différentes : les hippopotames sont lents mais résistants, les gazelles sont rapides, sautent haut mais meurent vite
2 - Quand un joueur fragge un autre, il gagne un certain nombre d’XP et le joueur fraggé en perd… De plus, tuer un bon joueur rapporte plus de points d’XP.

Pour la première idée, rien de très nouveau, cela a déjà été exploité, mais je préférais cette idée à un système de classe encore plus classique et utilisé dans 90% des mods existants… La deuxième idée, avec la perte d’un niveau, était selon moi plus originale. En effet, il devenait possible de perdre un niveau (qui représentait plutôt le skill plus que l’expérience d’ailleurs). De plus, le meilleur joueur devient clairement l’homme à abattre. Plutôt que de le fuir (oui, je suis comme ça, j’avoue), ça vaut le coup d’essayer de l’abattre. C’est valorisant pour un noob qui peut devenir utile à l’équipe (car plus on est nul, plus on rapporte de points quand on fraggue). Ce sentiment d’injustice est compensé par le fait qu’un gros joueur se sent une grosse responsabilité : il ne doit absolument pas mourir.

L’idée de départ était de développer un mutator pour chaque idée et de les mixer dans un game type. En effet, développer un mutator (simple) n’est pas trop compliqué avec les tutoriaux adaptés. Le tout est d’avoir une idée. Au final, je ferai différemment.

oppositeteam1-1

Je commençais donc à coder le premier mutator (avec les équipes antagonistes) : Opposite Team. Bien qu’annoncé comme une part d’une future Unreal Safari, j’avais alors prévu de le publier, afin de tester son effet réel, de me faire de la pub. En effet, une release, c’est toujours mieux qu’un screenshot (sauf si elle est bugguée à mort bien sûr). Je passe sur les difficultés pour un noob de la programmation Unreal Script (je n’avais codé qu’en Visual Basic des années avant et uniquement des choses très simples). Globalement, ce ne fut pas très compliqué, et j’ai pu ne publier qu’une version, car elle était fonctionnelle. Par contre, graphiquement, le tout était très limité. Peut-être ai-je négligé à ce moment-là l’aspect cosmétique (changer la phrase : “You are in Red Team” en “You are in Heavy Team”).

Une nouvelle fois, je m’appuyais sur la communauté pour tester et critiquer mon travail. Je publiais également le code du mutator afin d’aider les apprentis codeurs (je le conseille toujours d’ailleurs pour ceux qui se lancent là-dedans). La communauté a confirmé plusieurs choses très importantes

1 - pas de bug
2 - équilibrage correct
3 - idée sympathique et rafraîchissante

Certains détails inhérents à UT3 m’étaient complètement passé au-dessus de la tête. Ainsi, un joueur m’a dit que le gros avantage de l’équipe rapide était d’aller piquer les power-ups… Je n’y avais pas du tout pensé ! Encore une fois, il est essentiel de tester son jeu aussi bien avec des joueurs moyens qu’avec des joueurs aguerris.

Outre le fait de me prouver que j’étais capable de programmer, j’ai augmenté la visibilité de mon travail et eu un début de confirmation que les idées de gameplay d’Unreal Safari pouvaient être sympas. En revanche, en tant que mutator, Opposite Team n’a a priori pas eu de succès. Il faut dire que la promotion a été minimale. Ici, j’ai peut-être été pénalisé par le fait que je jouais peu à UT3. N’étant pas régulier sur certains serveurs, je n’ai pas eu l’occasion de le faire implanter afin d’élargir ma publication. Après, il faut aussi rappeler qu’UT3 étant peu joué, un mutator anecdotique a peu de chances de trouver un endroit où être joué !

En conclusion, aussi simpliste soit Opposite Team pour un codeur expérimenté, une fois encore j’ai publié quelque chose. J’ai finalisé une partie de mon projet. Je me suis prouvé que j’étais capable de programmer quelque chose tout comme je m’étais prouvé que je pouvais publier une map correcte. Et à force de publier des créations, on prend confiance. De façon plus narcissique, publier des produits finis permet aussi plus tard d’être pris au sérieux lorsque l’on veut intégrer une équipe (sérieuse). Avoir modélisé une salle ou avoir des bouts de code, ça ne sert pas à grand chose. Mieux vaut voir petit, faire simple et finir les choses. Car finir les choses, ça amène d’autres problèmes, comme la correction de bugs. On en avait parlé pour WAR-Gizeh, on en reparlera dans la prochaine partie !

http://www.moddb.com/mods/unreal-safari/downloads/opposite-team-mutator

UnrealScripter #3

Vendredi 16 juillet 2010

unrealscripter

IV. D’autres variables utilisables

PREREQUIS : il est important d’avoir lu les tutoriels correspondants précédents afin d’avoir la suite du code. Le code des anciens tutoriaux a été corrigé, il comportait quelques petites erreurs.

Certaines variables sont déjà dans le code de l’UDK et il n’est pas nécessaire de les réécrire dans votre propre code. Les plus utiles seront notamment :

GroundSpeed : vitesse de déplacement

JumpZ : hauteur de saut

Health : points de vie

Ces variables sont définies dans le fichier Pawn. Je Si on farfouille un peu dans le fichier Pawn.uc (dont dérive ensuite UTPawn.uc) on peut voir les multiples variables qui existent. Je vous laisse le faire car il y en a énormément. Inutile donc de s’embêter à tout refaire. Pour les définir, on utilise l’arborescence du code :

Controller -> Pawn -> Health

C.Pawn.Health = 100 ;

Pour notre jeu, nous allons faire en sorte que le joueur retrouve ses points de vie quand il gagne un niveau et qu’il gagne en vitesse et en hauteur de saut. Ceci, bien qu’apparemment très simple, va nous perdre de réfléchir un peu à la conséquence de nos actes. En effet, on aurait tendance à écrire simplement une ligne de code donnant 100 points de vie à notre joueur. Faux ! Si jamais le jeu a des power-ups permettant de dépasser cette limite, il faut vérifier avant que le joueur n’a pas déjà plus de 100 points de vie. Sinon, ça reviendrait à lui baisser la santé ! Comme récompense, il y a mieux…

Ici, on va utiliser deux variables OldLevel et NewLevel qui vont nous permettre, simplement, de vérifier si le joueur a changé de niveau. C’est une astuce toute bête, mais très pratique. Il suffit de définir OldLevel avant que l’on lance la fonction proprement dite, puis de définir NewLevel juste après et de les comparer.

Function CheckLevel(Controller C)

{

Local TT_PRI PRI ;

Local int OldLevel;

Local int NewLevel;

PRI = TT_PRI(C.PlayerReplicationInfo) ;

OldLevel = PRI.Level; //définissons le niveau actuel du joueur

if (PRI.XP <= 100)

{

PRI.Level = 0;

}

else if (PRI.XP <= 200)

{

PRI.Level = 1;

C.Pawn.GroundSpeed = 520.0000 ;

C.Pawn.JumpZ = 400 ;

}

else

{

PRI.Level = 2;

C.Pawn.GroundSpeed = 600.0000 ;

C.Pawn.JumpZ = 450 ;

}

NewLevel = PRI.Level;

if (NewLevel != OldLevel)

{

if (C.Health <= 100)

{

C.Health =100;

}

else

{

}

}

else

{

}

}

Ce code est intéressant car il comporte une condition (if, else if, else…) à l’intérieur d’une autre condition. D’où l’importance de hiérarchiser son code (avec des tabulations). Techniquement, cela ne change rien lors de la compilation, mais cela permet de mieux visualiser ce que l’on a fait. Imaginez vous reprendre un code écrit des mois auparavant qui fait 4 pages… Evitons donc de nous poser des problèmes !

V. Messages

Comme mon code ne marchait pas (il se compilait mais in-game, rien ne se passait), j’ai programmé en vitesse l’affichage d’un message afin de voir si les niveaux changeaient. Maintenant que c’est fait, autant l’intégrer dans le tutoriel !

Pour afficher des messages proprement, il nous faut une autre classe. Appelons là TT_MessageLevel.uc . On verra plus tard qu’il est tout à fait possible d’intégrer plusieurs types de messages dans une même classe. Une classe de messages dérivent la plupart du temps de LocalMessage.uc. Une bonne idée est d’ouvrir cette classe pour voir ce qu’elle comporte ! Ici on va utiliser uniquement la fonction GetString(), sans utiliser toutes ses options :

class TT_MessageLevel extends LocalMessage;

static function string GetString(

optional int Switch,

optional bool bPRI1HUD,

optional PlayerReplicationInfo RelatedPRI_1,

optional PlayerReplicationInfo RelatedPRI_2,

optional Object OptionalObject

)

{

return “Level UP”;

}

defaultproperties

{

bIsPartiallyUnique=True

bIsConsoleMessage=False

bBeep=False

Lifetime=5

DrawColor=(G=255,R=50,B=50)

PosY=0.9

FontSize=3

}

Rien de bien compliqué ! Pour information je vous ai laissé quelques propriétés par défaut très pratiques comme Lifetime (en secondes), bBeep (qui permet de faire du bruit lorsque le message s’affiche), PosY (qui ici indique que le message se affiché en bas de l’écran), FontSize (plus le nombre est grand, plus l’écriture est grande) et DrawColor (qui permet de définir la couleur du message). On voit qu’avec quelques propriétés, on a déjà de quoi personnaliser un peu son message ! Bien sûr, je n’ai pas inventé ces propriétés, il suffit d’ouvrir LocalMessage.uc pour les découvrir. Je me répète mais il est essentiel de lire (ou même juste parcourir) les classes dont dérivent les autres pour bien comprendre l’UnrealScript.

Cependant, il paraît évident que l’on préfèrerait un message qui nous précise notre niveau. Pour ça, on va utiliser la variable Switch de la fonction GetString(). Simplement, quand on va envoyer le message, on peut ajouter un nombre comme variable (=Switch).

static function string GetString(

optional int Switch,

optional bool bPRI1HUD,

optional PlayerReplicationInfo RelatedPRI_1,

optional PlayerReplicationInfo RelatedPRI_2,

optional Object OptionalObject

)

{

return “YOU ARE LEVEL ” @Switch;

}

Retenez bien le coup du “@” car c’est très important pour faire des HUD/Messages intéressants et personnalisés.

Reste maintenant à envoyer le message. On retourne donc dans TT_Game.uc. Un message se lance de la façon suivante :

PlayerController(C).ReceiveLocalizedMessage(class’TT_MessageLevel’, UnInteger);

Ici, “UnInteger” sera le niveau du joueur et définira le Switch du message. Avec la fonction complète, ça donne :

Function CheckLevel(Controller C)

{

Local TT_PRI PRI ;

Local int OldLevel;

Local int NewLevel;

PRI = TT_PRI(C.PlayerReplicationInfo) ;

OldLevel = PRI.Level; //définissons le niveau actuel du joueur

if (PRI.XP <= 100)

{

PRI.Level = 0;

}

else if (PRI.XP <= 200)

{

PRI.Level = 1;

C.Pawn.GroundSpeed = 520.0000 ;

C.Pawn.JumpZ = 400 ;

}

else

{

PRI.Level = 2;

C.Pawn.GroundSpeed = 600.0000 ;

C.Pawn.JumpZ = 450 ;

}

NewLevel = PRI.Level;

if (NewLevel != OldLevel)

{

if (C.Health <= 100)

{

C.Health =100;

}

else

{

}

PlayerController(C).ReceiveLocalizedMessage(class’TT_MessageLevel’, NewLevel);

}

else

{

}

}

Quand j’ai commencé à coder, j’avais créé un message pour chaque niveau et j’avais remis plein de conditions pour savoir quel message coder. Ici, le code est plus malléable et permet de fonctionner, quel que soit le nombre de niveaux.

Dans le prochain tutoriel, on verra comment ajouter un HUD très simple (je l’ai créé car le code ne marchait pas et que j’avais besoin de visualiser les XP/Level).

ps : pour les personnes déçues qui espéraient un mode Furie, j’ai eu des problèmes pour le coder. Quand on programme, on a beau être quasiment sûr de savoir faire quelque chose, on ne peut jamais être certain d’aller au bout…

pageTracker._initData(); pageTracker._trackPageview(); } catch(err) {}