Little Blog Story

Game-Design & Modding le blog de belzaran.

Articles taggés avec ‘UT3’

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.

Making-of : Hunt

Mardi 17 août 2010

Introduction

Little God Story est un projet passionnant mais particulièrement long à mettre en place. Ayant été habitué ces dernières années à publier du contenu régulièrement (maps, mutator, game type…), je me sens quelque peu frustré par cet aspect-là du développement. Ayant progressé fortement en programmation suite à mes travaux sur Little God Story, j’ai décidé de programmer un game-type pour Unreal Tournament III  : Hunt. Ce game-type était prévu pour Unreal Safari à l’origine (cf le post-mortem consacré au mod). J’ai décidé de le développer tranquillement et de publier ici les différentes avancées, sous forme d’un making-off. Quand j’aurais terminé (si je termine), je publierai les sources annotées pour aider les personnes souhaitant de mettre à l’UnrealScript.

L’idée ici est d’aller vite. Il n’est pas du tout prévu de développer des maps pour ce game-type ou de reprendre le développement d’Unreal Safari.

Concept du mode

Le mode Hunt est assez simple dans son fonctionnement. Un joueur est la cible. Tant qu’il n’est pas tué, il accumule des points. Quand il est fraggé, le tueur devient la cible et ainsi de suite. Il me semble que ce mode existe déjà dans plusieurs jeux mais je suis bien incapable de dire lesquels. N’hésitez pas à combler cette lacune culturelle dans les commentaires…

Une autre particularité (héritée d’Unreal Safari) est que la cible est très fragile. Elle a peu de points de vie mais court vite et saute très haut, pouvant prendre des raccourcis. Les tueurs sont lents mais très résistants. Si bien que la cible a intérêt à fuir plutôt qu’à chercher le combat.

A ce niveau, la question de l’équilibrage ne se pose pas encore. L’idée est de séparer les différentes façons de faire des points et de rendre leur changement aisé. Ici, il y a trois façons de faire des points :

  • Être la cible
  • Tuer la cible
  • Être la cible et tuer un chasseur

On peut également envisager un malus :

  • Être un chasseur et tuer un chasseur

Conception du code

Ici, je parle de la conception du code avant de plonger les mains dans le bousin. Rien ne dit donc que ça va marcher. A l’époque d’Unreal Safari, j’avais commencé à bosser sur ce mode. En fait, il y a une façon dont je l’ai envisagé qui me paraît mauvaise et compliqué : construire le mode comme un jeu d’équipe. L’équipe rouge chasse, l’équipe bleu est chassé. Si la cible est tué, elle passe dans l’équipe rouge et le tueur passe dans l’équipe bleu. Cela permet également de gérer simplement les friendly fire. Cependant, il faut bien prendre en compte que le mode Hunt n’est pas un feu d’équipe ! Chacun joue pour lui et seul le premier sera vainqueur. C’est une variante du mode Deathmatch. Changer le TDM posait d’autres problèmes tout simples :

  • Le HUD montre un score par équipe
  • La table des scores montre un score par équipe
  • Changer d’équipe nécessite de mourir (imaginez la frustration quand le joueur fraggue et qu’il meurt : il ne sait pas tout de suite s’il a été fraggué ou s’il est devenu la cible).
  • Une cible est définie dès le deuxième joueur connecté aléatoirement (c’est-à-dire qu’un joueur va faire des points sans avoir rien fait en début de partie)
  • Le message “VOUS ÊTES ROUGE” à changer pour “VOUS ÊTES LA CIBLE” (il est toujours plus facile d’ajouter simplement un message plutôt que d’en changer)

J’ai donc décidé de tout articuler sur la fonction ScoreKill (qui gère les frags). Elle se lance quand un joueur est tué et permet d’agir sur le tueur et le tué. Ainsi, je pourrais définir par une variable un changement de cible (certainement un booléen : bTarget). Plutôt que de mettre un système d’équipe complet, j’ajoute une variable à chaque joueur qui définit s’il est la cible ou non. L’idée est de faire :

Si le tué est la cible, alors le tueur devient la cible.

Rien de bien compliqué sur le papier, dans la pratique il y aura deux/trois choses à prendre en compte (notamment le friendly fire). Il faut également permettre aux joueurs de reconnaître la cible rapidement (même si elle saute deux fois plus haut que les autres et court comme Usain Bolt). Pour cela, une lumière dynamique suivra la cible pour qu’elle soit reconnaissable (en cela, utiliser un système d’équipe avait l’avantage de bien séparer visuellement les deux types de joueurs).

Dernière chose importante dans la conception pre-code : quand est-ce que je j’ajoute une cible ? En effet, si deux joueurs se retrouvent seuls sur un serveur, quel intérêt de mettre une cible ? Difficile ici de définir correctement. De toute manière, un joueur qui arrive en milieu de partie est toujours désavantagé pour du deathmatch. L’idée est de faire que la partie démarre sans cible. Dès qu’un joueur tue, il devient la cible.

Autres aspects du mode

Contrairement au game type Safari, ce mode demande beaucoup moins de personnalisation visuelle. Safari prenait en compte des niveaux et des points d’expérience qui avaient un impact important sur le jeu. Ici, quelques messages uniquement à afficher au bon moment :

  • Message : “Vous êtes la cible !” (avec un bruit reconnaissable)
  • Message : “Vous êtes un chasseur !” (lors du respawn)
  • Message : “Vous avez tué un chasseur !” (lors d’un friendly fire)

J’ai envie de faire quelque chose de plus personnalisé et fun avec plutôt des phrases du type : “Accident de chasse, arrêtez de boire !” ou “Vous êtes la cible, faites gaffe à vos fesses”. Le tout avec des sons enregistrés pour l’ambiance. Mais là, on parle déjà de finalisation du mode !

Sinon, le game-type Hunt a un nom pourri bien que représentant bien le principe. J’aimerais en trouver un de plus sympa. Hunting Safari sonne mieux je trouve mais je pense que vous voyez le problème…

C’est tout pour aujourd’hui. N’hésitez pas à donner votre avis sur le mode en lui-même. S’il y a des choses qui vous chiffonnent et qui vous paraissent rédhibitoires.

Unreal Safari : Post-mortem (6)

Vendredi 23 juillet 2010

unrealsafari

Il est temps de clore cette série d’articles avec l’arrêt du développement d’Unreal Safari (qui, globalement, a traumatisé pas grand monde !). Les raisons principales sont évidemment toujours les mêmes et elles sont évidemment liées :

1 - La perte de la motivation
2 - La quantité (astronomique) de travail restant à faire

Le but de ce dernier article n’est pas de développer ce genre de propos, très banals,  mais d’expliquer les petits trucs qui petit à petit, amènent à ce ressenti.

1ère raison : je ne suis pas assez compétent

Il n’est jamais très agréable de l’avouer, mais il y a un moment où j’ai pris conscience de ma propre incompétence, notamment en terme de modélisation (pour les personnages et les armes, c’était assez flagrant), d’animation, d’effets visuels, voir de programmation. Quand on voit tout ce que j’ai appris à faire depuis le début, on pourrait penser que j’aurais pu me lancer dans l’aventure et apprendre comme pour les autres domaines. Mais trop c’est trop. Apprendre de nouvelles techniques à la pelle est intéressant et gratifiant mais ne fait qu’ajouter du boulot. Quand je modélisais une caisse à la truelle, ça me prenait 5 minutes. Quand je fais un model sympa avec des textures plus variées et le support de lightmaps par exemple (le minimum syndical en sorte), j’en ai pour la matinée. A force d’apprendre, on augmente aussi son temps de travail. Ce n’est pas nouveau, développer un jeu next-gen prend de plus en plus de temps et j’étais encore loin des techniques next-gen…

2ème raison : j’aime pas les jeux multijoueurs

Patator : Nevermind the Beach

Patator : Nevermind the Beach

Contrairement à ce que l’on pourrait croire, je ne joue quasiment jamais en multi. J’ai joué à une époque un petit peu à Counter-Strike : Source, Unreal Tournament III, Warcraft III ou Company of Heroes, mais jamais très longtemps. Cela vient aussi bien de mon faible niveau que de mon manque d’intérêt. Mais alors pourquoi mapper et modder pour le multi ?!

J’ai toujours eu en projet de faire un mod/jeu solo. J’avais à l’époque un scénario et une personnage, Patator. J’ai essayé de développer Patator sur FPS Creator, Far Cry, Half-Life ² et Crysis, avec des avancées plus ou moins grandes. Et un jour j’ai compris que l’avantage de faire des maps multijoueurs, c’est que ça demande moins de travail pur, car elles sont souvent plus petites et rejouables. Ce sera mon leitmotiv pour plus de 2 ans de rejet de mes pulsions primaires qui adorent les jeux solos.

Patator : Source

Puis ce fut le choc : j’ai joué à The Ball. Une révélation. En effet, contrairement aux screens que j’avais pu voir du mod, quand on joue au premier niveau, The Ball n’est pas renversant. Il est beau mais sans plus, très carré, très couloir et avec des mécanismes simples voire simplistes. Au fur et à mesure des niveaux, il se complexifie. Des NPCs apparaissent, les énigmes deviennent plus ardues, le monde devient plus beau et diversifié… A ce moment-là je me suis dit : “je peux faire ça ! (en moins beau)”. C’était certainement un peu utopiste de ma part, car si ce n’est pas dit clairement, il était sous-entendu que je considérais que j’espérais être capable de le faire tout seul… Bref, l’idée de faire un FPS solo sans combat trotte dans ma tête. A l’époque, je n’ai aucun concept en tête, mais la frustration de modder pour du multi s’installe.

http://www.moddb.com/games/the-ball

3ème raison : je suis frustré artistiquement

Cela peut paraître un peu pompeux, mais je rappelle que je suis allé modder sur UT3 pour pouvoir utiliser des lumières dynamiques. J’adore les jeux d’ombres. En cela, mes références graphiques sont Doom III, F.E.A.R. ou Bioshock. Alors forcément, un mod africain avec une lumière crue et forte, ce n’est pas ce que je préfère. L’univers que je développe est certes original mais n’exploite pas mes qualités de mappeur. En effet, je modélise de façon brute. Or, un éclairage fort et uniforme souligne ce côté simpliste. Comme mes éclairages étaient le point fort de mes maps précédentes, je commence à prendre conscience que si je suis passé sur UT3, ce n’est pas pour faire des maps comme ça.

4ème raison : j’ai perdu ma map !

Modèle de mur pour la mosquée

En commençant réellement la totale conversion, j’ai du créer un nouveau dossier “Unreal Safari” pour intégrer pleinement les armes/personnages/maps et sortir du côté “je bidouille un peu UT3″. Ceci ne s’est pas fait sans douleur et malgré toutes les manipulations, je continuais de devoir stocker mes maps dans le dossier UT3… Un jour de mauvaise manip, j’ai supprimé mes packages Unpublished au lieu de mes packages Published. Je vais tenter d’expliquer un peu la nuance entre les deux :

Unpublished : contient toutes les textures / models / sons / whatever bruts. Ils ne sont pas compressés et restent modifiables
Published : contient uniquement les textures / models / sons / whatever utiles au mod/jeu/map. Ils sont compressés et ne sont pas modifiables.

Modèle de colonne pour la mosquée

On pourrait croire que le fait de ne pas pouvoir modifier mes anciens assets n’était pas si grave, mais UT3 a décidé de me plomber bien plus encore ! Sur ma map SAF-Gizeh, il ne retrouve plus du tout les models et textures. Après quelques tentatives infructueuses, je m’aperçois que la map est fichue. Il faut tout recommencer…

Cela m’a freiné un peu, mais pas arrêté ! Après quelques semaines je m’y remet, décidant d’en profiter pour changer le level-design et de monter d’un cran en terme de graphismes. Quelle erreur ! Le level-design de SAF-Gizeh tenait la route et ne nécessitait pas de changer du tout au tout. Quand à l’amélioration graphique, si elle a été appréciée par les “fans” du mod, elle rejetait la sortie du mod à beaucoup plus tard…

5ème raison : comment vais-je faire jouer à mon mod ?

Si vous suivez un peu les releases de mod, il n’est pas rare de voir comme remarque “ce mod a l’air sympa, mais ils n’ont aucun serveur pour le tester”. En gros, le mod espère que des fans vont mettre en place des serveurs pour y jouer. Ceci est suicidaire car un mod multi est souvent très peu joué d’emblée, alors l’absence de serveurs existants et encore pire. J’ai donc pris conscience à un moment que je devrai investir dans des serveurs pour la promotion d’Unreal Safari. Or, en étant seul, louer des serveurs est loin d’être gratuit… Ce genre de préoccupation a un peu plomber les dernières semaines de développement. Cette idée me perturbait car je ne voulais pas non plus faire un mod injoué. Je ne faisais pas ça dans le but unique de faire un post-mortem après…

6ème raison : L’UDK apparaît

L’Unreal Development Kit est un véritable graal pour les moddeurs. Il donne accès au moteur Unreal Engine plus ou moins gratuitement (il y a quand même des royalties qui traînent). Je traiterai de cet outil plus en détails dans un prochain article.

Il faut comprendre que la sortie (surprise) de l’UDK a été une véritable bombe dans la communauté du modding et notamment du modding UT3. En effet, il devenait possible de faire passer son mod au statut de jeu complet, voire même de jeu complet payant. Toute Totale Conversion qui se respecte avait l’occasion d’élargir son public ! Et quand on connait la petitesse de la communauté UT3…

Je n’ai jamais eu l’envie de convertir mon mod UT3 en jeu UDK. Cependant, les grands mod UT3 (ceux du Make Something Unreal Contest) sont rapidement tous passés sur l’UDK. L’impression, vue de l’intérieur, était que la communauté se délitait.

L’UDK a précipité la chute d’Unreal Safari. En effet, je suivais à l’époque attentivement un jeu complet en développement : Exil. Son concepteur développait son propre moteur. Il abandonna cette idée et migra sur l’Unreal Engine à la sortie de l’UDK, l’aubaine était trop  belle. L’univers me plaisant et la programmation me gonflant de plus en plus, je proposais ma candidature et intégrais l’équipe en tant que mappeur/modeleur (il est difficile de n’être que mappeur sous l’Unreal Engine). Je pouvais ainsi mapper des endroits sombres et contrastés, dans un jeu solo ! Le bonheur fut de courte durée (quelques semaines avant que le projet s’arrête), mais mis une fin définitive à Unreal Safari.

http://www.udk.com/

http://www.makesomethingunreal.com/

http://www.moddb.com/games/exil

Mais alors, qu’aurais-je donc pu faire pour sauver Unreal Safari ?

Faire le post-mortem d’Unreal Safari m’a donné beaucoup de nostalgie et je regrette de ne pas l’avoir poussé plus loin. Cette série d’articles m’a apporté un recul que je n’avais pas imaginé à l’origine. En déterrant les bouts de code, les models, les maps et les concepts, je me suis aperçu que j’ai eu les yeux plus gros que le ventre. Tant que j’ai su rester simple, j’ai fait des releases de produits finis et honorables. Dès que j’ai décidé d’attendre une release plus complète, le travail restant est devenu trop grand. Si j’avais voulu faire une Totale Conversion, il fallait accepter l’idée de recruter des gens. Ce qui était pour moi inacceptable, ne voulant avoir à gérer des gens et des personnalités. C’est un choix de ma part et l’avenir me donnera (j’espère) raison.

Ce que j’aurais du faire, c’est développer le game type Safari. Ajouter un HUD plus personnalisé, des messages plus originaux, un écran en début de partie pour choisir son équipe… Cela m’aurait apporté beaucoup et le game type aurait gagné en personnalité et en originalité. Cela aurait été plus qu’une idée sympa.

L’autre idée (en lien avec la précédent) aurait été de développer des maps pour le game type Safari. Ces maps auraient pu être jouables aussi bien en Deathmatch par équipe qu’en mode Safari. L’occasion de faire jouer sur mes maps et de promouvoir mon game-type en quelque sorte.

Je tiens à préciser qu’en faisant le post-mortem du game-type Safari, il y a une part de moi qui me pousse à en reprendre le développement (notamment car j’ai beaucoup progressé en programmation et que ça me demanderait moins de temps). Mais il est vrai que la communauté UT3 étant tellement petite (et ça ne s’est pas arrangé avec le temps j’imagine), je ne pense pas que je reprendrai ce mode de jeu pour en faire une version 2.0.

Quel bilan en tirer ?

Unreal Safari fut une histoire fantastique et dura près d’un an et demi. Si le fait de vouloir faire une Totale Conversion fut une erreur évidente, elle m’a permis de toucher à tout et de pouvoir lancer mon projet suivant, Little God Story, en connaissance de cause ! De plus, l’apprentissage et l’amélioration de mes techniques lors du projet furent colossales. Je suis parti de très bas pour arriver à faire plein de choses, dans des domaines assez différents.

Même si Unreal Safari n’a pas eu un rayonnement énorme (c’est peu de le dire…), j’ai pu m’apercevoir au bout de plusieurs mois que les différentes releases m’ont permis de changer un peu mon statut dans la communauté, au moins francophone. Quand j’ai présenté mon nouveau projet, je n’étais pas un pauvre noob rêveur. J’avais une expérience concrète, prouvée par des réalisations correctes et surtout publiées. Faire un bout de map impressionnant, c’est à la portée de beaucoup. Faire une jolie map avec une gameplay qui tient la route sans que ça rame, c’est plus difficile. La plupart de mes maps sous Source avaient d’ailleurs fini à la poubelle pour des problèmes d’optimisation.

La sortie de l’UDK m’a permis également d’avoir un statut plus élevé du jour au lendemain. En effet, les développeurs UT3 étaient rares et quand l’UDK est sorti, beaucoup de projets ont vu le jour. Mais un nouvel outil demande un apprentissage. Du jour au lendemain, je devenais quelqu’un d’expérimenté ! Cette prise de conscience et ces réalisations m’ont fait prendre confiance en moi et c’est pour cela que j’ose, aujourd’hui, rédiger des tutoriaux sur l’Unreal Script.

Ce post-mortem est désormais terminé. J’espère que vous l’aurez trouvé intéressant et surtout pas trop narcissique. J’espère surtout qu’il servira aux personnes qui se lancent dans le modding. Car si j’ai fait des erreurs, j’ai fait également à des moments de bons choix qui m’ont permis d’avancer. Tout moddeur rêve de faire son jeu stand-alone qui déchire et qui surpassera Crysis et/ou Stalker. Il ne faut pas hésiter à être plus modeste et/ou plus original. L’industrie du jeu vidéo nous apporte de plus en plus de jeux formatés, alors si les moddeurs se formatent également, on peut dire adieu à l’originalité…

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

Unreal Safari : Post-mortem (2)

Lundi 19 juillet 2010

unrealsafari

La première map : WAR-Gizeh

Après les premières hésitations, je me met à travailler sérieusement sur une map dans l’environnement Gizeh. Pour y aller petit à petit, je tente d’abord des maps uniquement extérieures, avec des temples ouverts sur le ciel. A force de recommencer, je commence à avoir une bibliothèque de modèles assez importante qui me permet, quand je redémarre une version de la map (ce qui arrive assez souvent) de ne pas passer des journées entières à tout refaire. Je travaille pas mal la modélisation modulaire qui permet de modéliser de façon plus souple.

Cependant, les brouillons de maps n’atteignant jamais une version suffisante. Je ne peux donc pas vraiment tester ces layouts en terme de gameplay. Lassé par la grande taille des maps Warfare, je décide alorsd’essayer de faire une version 100% dans un village. Le côté confiné est un désavantage certain, mais il me permet de mieux maîtriser les déplacement des joueurs. Avec le recul, cette idée était très mauvaise. Ce genre de map n’est pas du tout adaptée au mode de jeu. Je revenais à mes (mauvais) réflexes de level-designer pour Counter-Strike : Source.

Techniquement, les rendus rappellent UT2003/2004. J’apprends alors à utiliser à bien les textures de type normal maps (relief), specular maps (reflets) et shadow maps (ombres). Cela m’a pris du temps, me demande souvent plus de travail, mais j’obtiens enfin eu un rendu in-game supérieur à ce que je faisais sous Source. Il m’aura fallu des mois pour ça.

Une fois ces techniques maîtrisées, je redémarre une dernière fois WAR-Gizeh. Globalement, la réalisation de la map ne posera pas de problèmes particuliers. Mon idée ici est de publier une map Warfare (c’est-à-dire pour UT3) que je modifierai ensuite pour mon mode “Warfare pour animaux”. En effet, à ce stade, le mode de jeu prévu pour Unreal Safari est une Warfare sans véhicule. Pour ceux qui ont joué à ce mode de jeu, ils comprendront sans peine que :

1 - inutile de faire un mod pour ça
2 - les véhicules sont un des grands intérêts du mode Warfare

Cependant, l’idée de faire une map publiable est certainement la meilleure idée que j’ai eu ici. Car elle me permettait de me prouver que j’étais capable de faire une map avec 90% d’éléments custom sur l’Unreal Engine. Le layout est pas mal mis en cause et changera au fil des bétas. Très vite, j’apprend à écouter la communauté, même si ses exigences sont parfois décourageantes. Ajouter des points de contrôle par exemple demande énormément de boulot, car il ne faut qu’ils ne soient pas destructible de trop loin (donc il faut les protéger). De même, ajouter des véhicules implique un équilibrage pas toujours évident. Au final, en écoutant les remarques, la map a pris beaucoup en intérêt stratégique car au départ, il n’y avait pas beaucoup de possibilités de jeu et la map devenait vite répétitive. Il est à noter que la communauté m’a poussé à faire une version Necris en plus afin de plaire au plus grand nombre !

layout

WAR-Gizeh ne sera jamais utilisé pour Unreal Safari. Mais ce n’était pas une perte de temps car elle m’a permis de :

1 - Me faire connaître un peu dans la communauté
2 - Constituer une banque de modèles et de textures pour Unreal Safari
3 - Apprendre de nombreuses techniques supplémentaires
4 - Travailler le level-design d’un mode de jeu pour lequel je n’avais jamais mappé
5 - Me prouver que ma “patte” graphique était appréciée et considérée comme original

Il est à noter que plusieurs mois après la release, j’ai été contacté par des joueurs qui voulaient que je corrige les quelques soucis restant sur cette map. Contrairement à ce que je croyais, cette map a donc été jouée sur certains serveurs.

Je termine là-dessus afin de préciser l’importance des avis de la communauté. J’ai eu la chance de faire partie de communautés pertinentes (bien que peu nombreuses) qui m’ont soit aidé techniquement, graphiquement ou au niveau du gameplay. Sans eux, je ne serais pas allé loin. Ces communautés sont Mapping-Area, Unreal.fr et Unreal-Design.

beta_2_01 beta_2_07 beta_2_06

Liens : http://www.moddb.com/mods/unreal-safari/addons/war-gizeh

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…