Little Blog Story

Game-Design & Modding le blog de belzaran.

Archive pour la catégorie ‘UDK’

Dans cet article, une réflexion sur les choix effectués en terme de HUD pour Little God Story. Comme d’habitude, beaucoup de changements ont eu lieu entre le début et l’état actuel des choses. Comme je pense avoir atteint un équilibre suffisant, il y a peu de chance que l’état actuel du HUD évolue fortement dans la suite du développement.

Mes choix ont été fortement influencés par les tests et donc par les testeurs. Merci à eux pour leurs idées qui permettent de rendre le jeu meilleur.

Le premier HUD purement utilitaire

Le premier HUD purement utilitaire

Introduction

Le HUD (Head up display) est un élément que l’on pourrait croire essentiel dans un jeu vidéo. Il affiche les points de vie, les munitions restantes, les points d’Xp ou plein d’autres choses encore. Un véritable changement s’est effectué ces dernières années. Certains jeux tentent de le supprimer le plus possible ou de l’intégrer au monde afin d’augmenter l’immersion (Far Cry 2, Metro 2033), d’autres persistent à en faire quelque chose d’envahissant (Borderlands avec ses messages explicatifs qui te cachent la moitié de l’écran pendant que tu vises).

En tant que développeur, je tiens à dire que le HUD est une façon facile de personnaliser un jeu/mod. Dès qu’on le change, l’impression de jouer à un jeu unique se renforce. Cela peut aller d’un simple texte (”XP = 325/640″) à une refonte graphique complète. Ainsi, pour un développeur, faire un HUD minimaliste, c’est difficile psychologiquement. Cela enlève une possibilité simple de donner une patte graphique à son jeu.

Dernier élément essentiel : le public visé. Comme je l’ai dit dans un précédent article, certains testeurs (dont moi) ont fait tester le jeu à des publics plus casuals (féminins et/ou enfants). Il est évident que le HUD choisi influencera fortement l’accessibilité du jeu. Ce choix n’est pas si évident, car Little God Story ne propose pas de combat et beaucoup de réflexion. En cela, il est tout à fait possible de l’orienter vers le casual gaming. Cependant, le jeu étant prévu sur PC, il paraît évident que les gens qui y joueront seront des joueurs assidus, adeptes de jeux indépendants !

Concept du HUD

Le premier HUD personnalisé

Le premier HUD personnalisé

Avant de commencer à créer le HUD de Little God Story, j’ai créé un prototype qui devait me permettre simplement de vérifier que le code fonctionnait. Le premier HUD m’affichait l’Elément en cours avec un texte de couleur correspondante. Il a fallu ajouter ensuite une simple barre de vie, de mana et de munitions (chaque barre étant d’une couleur différente !).

Le problème du HUD, c’est qu’il sous-entend que le concept du jeu doit être finalisé un minimum avant de s’y attaquer. En effet, tout changement majeur dans le gameplay peut induire d’introduire de nouveaux éléments ou de les supprimer. Dès la deuxième version de test, j’ai ainsi du ajouter la barre de mana. Avant le mana n’existait simplement pas… J’ai donc établi une liste de ce que le joueur aimerait bien pouvoir savoir à chaque moment :

  • L’élément actuellement utilisé
  • Les points de vie
  • Le Mana

Cela peut paraître peu, mais il n’y a rien de plus dans Little God Story. En effet, le Mana sert aussi de munitions et il n’y pas de carte à afficher (étant très linéaire). Je ne parle pas ici de certains concepts non-finalisés que le joueur peut afficher ou voir ponctuellement :

  • Résumé du tutoriel (aussi appelé Journal)
  • Affichage de l’objectif en cours
  • Messages divers et variés qui s’affichent à l’écran

En effet, ces informations ne sont pas utiles en permanence à l’écran. Certes, l’objectif pourrait être affiché tout le temps (comme dans Borderlands) mais ce serait occuper une partie de l’écran pour rien. Concernant le tutoriel et les messages, rien n’est finalisé actuellement. J’y reviendrai plus tard, quand tout sera beaucoup plus avancé.

Dernière chose : l’arme utilisée fait entièrement partie du HUD puisqu’elle peut afficher des informations également (les munitions sont directement affichées sur l’arme dans Doom 3, la jauge d’air comprimé dans Metro 2033…). Cela facilite fortement l’immersion.

Premières moutures

Lors des premiers tests, les joueurs connaissent mal le jeu. Il n’y a pas ou peu du tutoriel, les niveaux sont parfois bancals et les idées d’énigmes mal faites. Bref, faire un HUD intuitif n’est pas la meilleure chose à faire. Ainsi, la première mouture un peu personnalisé du HUD de Little God Story contenait une barre de vie rouge, une barre de mana bleue et un logo de l’élément utilisé. Les codes couleurs classiques sont utilisés afin de ne pas perdre le joueur. Enfin, l’élément est symbolisé par un logo afin d’aider le joueur à comprendre le jeu. En effet, il existe quatre interrupteurs différents dans Little God Story reliés à un élément. Le logo du HUD renvoie ainsi directement au logo de l’interrupteur, expliquant implicitement au joueur le lien entre les deux.

Cette version n’a pas posé de problème pratique aux joueurs. Les informations étaient facilement accessibles. Certains se sont plaints d’un logo d’élément trop envahissant (qui fut réduit pour la version suivante). Cependant, cette version était assez efficace, bien qu’assez moche. Le côté très coloré contrastait trop avec l’aspect sombre du jeu. De plus, Little God Story veut se doter d’une ambiance forte, à la fois sombre et mélancolique. Avec un HUD discret, cela est beaucoup plus simple à réaliser.

Le crosshair renvoie au graphisme de lélément utilisé

Le crosshair renvoie au graphisme de l'élément utilisé

En recherche d’immersion

Les derniers tests, plus pointus, m’ont amenés à rechercher plus d’immersion (au détriment d’un apprentissage plus intuitif). En effet, le changement d’élément se traduit par un changement visuel à l’écran et dans les déplacements. Les joueurs ont ainsi signifiés qu’ils n’étaient que rarement perdus, ils savaient dans quel élément ils jouaient. De plus, la main du joueur (qui sert d’arme) changera selon l’élément (en flammes pour le mode feu, semi-transparente en mode air…) et donnera ainsi automatiquement l’indication au joueur. On peut donc enlever le gros logo qui n’est donc plus nécessaire. Une autre indication sert également à signifier l’élément : le crosshair. Ce dernier a un design qui rappelle l’élément utilisé. Ainsi, peu de risque que le joueur soit perdu.

La barre de vie disparaîtra également, remplacé par les effets classiques vus dans les jeux actuels. En effet, les tests ont montré qu’il était rare dans le jeu que les joueurs soit blessés. L’erreur équivaut souvent à la mort dans Little God Story. Résultat : si un joueur se blesse, sa vie revient petit à petit (ce qui est cohérent avec le fait que le joueur est censé être un dieu !). La notion de barre de vie peut donc sauter.

Reste la barre de mana. Je remercie ici un testeur en particulier qui m’a donné l’idée : le curseur disparaît petit à petit quand le mana s’épuise. Cet effet, facile à implanter, est très visible. Il permet au joueur de voir son mana disparaître. Et comme il est situé au centre de l’écran, il est beaucoup plus simple à voir.

Au final, le HUD de Little God Story est discret : un simple curseur blanc et la main du joueur sont affichés à l’écran. Il est certain que cela est possible qu’avec l’avancée d’autres domaines :

  • personnalisation de la texture et du matériel de la main selon l’élément utilisé
  • ajout d’effets graphique qui permettent de mieux ressentir l’élément avec lequel on joue
  • ajout de sons personnalisés lors du changement d’élément
  • ajout de messages lorsque le Mana est épuisé

Si je suis arrivé à accepter d’avoir un HUD moins présent et donc de moins personnaliser graphiquement ce même HUD, c’est aussi parce-que le jeu en lui-même a une patte graphique plus affirmée. A partir du moment où le jeu semble présenter un aspect plus personnel (par les armes à l’écran, les interrupteurs, l’ambiance…), le HUD peut redevenir un élément d’immersion et non plus uniquement une façon d’imposer sa patte graphique.

Le HUD actuel, beaucoup plus discret

Le HUD actuel, beaucoup plus discret

Je me permet de faire une petite digression sur Unreal Safari. J’avais commencé à travailler sur le HUD que je voulais très africain et délirant. J’avais ainsi mis des fruits (kiwis, oranges) et des feuilles de bananiers. Je vous invite à lire le post que j’avais laissé sur WeFrag et les remarques qui avaient alors été faites : http://www.wefrag.com/forums/entre_aide/topics/138233

Unreal Safari : un HUD flashy !

Unreal Safari : un HUD flashy !

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.

UDK - Pour ou Contre ?

Lundi 16 août 2010

The Ball

The Ball

L’Unreal Engine

L’Unreal Engine est un moteur de jeu développé à l’origine par Epic Games pour le jeu Unreal. Il a depuis subi de nombreuses améliorations et en est à sa troisième version : l’Unreal Engine 3. Unreal Tournament III a servi de vitrine technologique à Epic. Ce jeu était en effet très beau à sa sortie et bien optimisé. Malheureusement, UT3 n’a pas eu le succès escompté. Contrairement aux version antérieures d’Unreal Tournament, UT3 n’a donc pas fait des émules sur la scène du modding. A l’inverse, Half-Life² a toujours proposé plus de projets de mods. En revanche, l’Unreal Engine 3 se vendait remarquablement bien et était à l’origine de nombreux hits (Rainbow Six : Vegas, Bioshock, Gears of War…). Son aspect multi-plateforme, ainsi que sa facilité d’emploi (notamment grâce à un gros support d’Epic pour les développeurs) le rendent particulièrement bien vendable, contrairement à la concurrence (CryEngine en tête…)

Qu’est-ce que l’UDK ?

En novembre 2009, Epic surprend tout le monde avec la sortie de l’UDK. Globalement, cet outil permet de créer des jeux à partir de l’Unreal Engine sans posséder un jeu en particulier. La Totale Conversion ultime en quelque sorte… La sortie de l’UDK est accompagnée de l’annonce du portage de The Ball (mod célèbre pour UT3) sur l’UDK. Très vite, la plupart des mods majeurs sur UT3 annoncent leur passage sur l’UDK (Prometheus, Sanctum, AFF : Planetstorm). Ces annonces sont très vite suivis d’une démo, souvent très proche (voir identique) de la dernière version beta de leur mod.

Globalement, l’UDK c’est un peu UT3 sans avoir les assets. Il faut partir quasiment de zéro et tout reconstruire. L’avantage est qu’un projet amateur dispose désormais d’un moteur extrêmement puissant librement. Inutile de perdre du temps à développer un moteur graphique moyen et mal optimisé, le meilleur de la technologie est à portée de main. D’un coup, la scène du jeu indépendant et du modding se rejoignent.

Les opportunités offertes par l’UDK

FPS : Terminator

FPS : Terminator

Les nombreuses équipes qui développent des mods ambitieux (souvent des TC) ont désormais la possibilité de faire un stand-alone, c’est-à-dire d’ouvrir leur public non plus à un jeu unique, mais à toute la communauté du gaming.

Le cas de FPS : Terminator est ici assez criant. Ce mod était prévu pour Gears of War (PC). Il est alors aisé de comprendre que vu le nombre de joueurs qui ont Gears of War et sont prêts à l’installer pour joueur à son mod est extrêmement faible. En passant sur l’UDK, le mod devenu jeu a fait un buzz énorme (notamment grâce à son ambiance exceptionnelle) et est désormais suivi par beaucoup de joueurs.

Un autre cas intéressant est celui de Chivalry. A l’origine, Age of Chivalry est un mod pour Source. Avec la sortie de l’UDK, les développeurs ont décidé d’en faire un jeu complet sur l’Unreal Engine 3. Or, changer de moteur n’est pas de tout repos…

L’UDK permet également de vendre son jeu. La licence proposée par Epic est assez souple et évite aux équipes de développement de payer à l’avance pour le moteur. Le studio ne paye rien sur les premiers 5000$ gagnés. Puis Epic récupère 25%. Ce deal est évidemment très avantageux pour Epic. Et pour les devs, la prise de risque est moindre.

Il est à noter que l’UDK est depuis sa sortie mis à jour tous les mois. Si cela apporte son lot de galères parfois (non-compatibilité du code selon les versions), c’est très appréciable. Et je ne pense que personne ne niera que les dernières versions sont bien plus efficaces que les toutes premières.

Il est à noter que, comme pour Crysis, certains mappeurs se servent du moteur pour faire des maps où seul le souci esthétique prévaut. Certains codeurs codent des features pour tester le moteur. Sans forcément développer un projet complet, l’UDK est également un outil assez simple qui peut permettre d’apprendre simplement (importation des modèles et textures rapide et facile, visualisation en temps réel des maps…)

L’UDK a ainsi boosté la scène indépendante. Des démos de jeu sortent actuellement régulièrement avec une qualité graphique très appréciable et des contenus souvent originaux.

Les problèmes posés par l’UDK

Q.U.B.E.

Q.U.B.E.

Certains parlaient déjà avant l’UDK du problème de l’Unreal Engine 3 : il est utilisé par beaucoup de jeux et est très reconnaissable (les lumières font très artificielles, ça brille, c’est du next-gen quoi). Avec l’UDK, la scène indépendante risque de réutiliser massivement ce moteur et d’uniformiser d’autant plus les productions actuelles. Certes, la plupart des jeux UDK ont des rendus très colorés et originaux, mais pas mal de joueurs n’aiment pas ce côté artificiel.

Il est flagrant de voir que beaucoup d’écoles de jeux vidéo utilisent désormais l’UDK comme moteur pour leurs projets étudiants. On peut ainsi citer Q.U.B.E. , ColourRunnersGerridae ou Maglev. Ces projets sont souvent très propres et originaux, mais il y a peu de chances qu’ils dépassent le statut de démo sympa. La démo UDK devient un tremplin plus qu’un jeu à part entière.

Un autre problème est bien sûr la taille des projets. Sur l’UDK, les projets sont démesurés et très ambitieux. Il y a un risque que les mods périclitent devant la possibilité de faire un jeu complet. Alors que les mods arrivaient déjà rarement à une version jouable satisfaisante, on peut se poser la question sur des jeux complets… En tout cas, l’UDK a carrément enterré le modding pour UT3. Or, il faut rappeler qu’un mod n’est pas toujours une totale conversion et démarre souvent par des modifications mineures qui, mises bout à bout, finissent par de plus permettre de reconnaître le jeu original.

Le problème des jeux payants va également se poser. The Ball, premier jeu UDK annoncé, est passé de mod gratuit pour UT3 (de très bonne qualité) à jeu payant. Or, les qualités requises d’un jeu pour qu’un joueur accepte de le payer sont bien plus élevées. Que ce soit la durée de vie, les graphismes ou le côté rébarbatif du gameplay. D’ailleurs, l’équipe a beaucoup travaillé ces aspects-là en améliorant le graphisme de certains éléments importants (personnages, boutons…), en ajoutant un tutoriel et en permettant de voir à travers la boule (grand défaut rébarbatif du mod). On peut légitimement se poser la question de la valeur réelle de tous ces projets qui fleurissent.

Conclusion

alien Swarm

Alien Swarm

Si l’UDK a donné un coup de pouce évident à la scène amateur/indépendante, il paraît nécessaire qu’il aie des concurrents. Le moteur Source, de Valve, reste très concurrentiel et la sortie d’Alien Swarm (jeu gratuit) ou l’aide aux campagnes customs de Left 4 Dead 2 confirme l’envie de Valve de favoriser le modding. De même, Crytek envisage de publier une version libre de leur CryEngine 3. Il est vrai que cela fait des années que Crytek essaie de vendre (en vain) son moteur, la faute à un support trop limité. En permettant aux amateurs de faire leur armes sur le CryEngine, ils peuvent espérer vendre leur moteur plus facilement ensuite.

UDK : http://www.udk.com/

The Ball (FPS Puzzle Game): http://theball.toltecstudios.com/

Sanctum (FPS Tower Defense): http://www.moddb.com/mods/sanctum

Prometheus (Puzzle Game) : http://www.moddb.com/mods/prometheus

AFF : Planetstorm (FPS + Combats spatiaux) : http://www.moddb.com/mods/angels-fall-first-planetstorm

FPS Terminator (FPS) : http://www.moddb.com/games/fps-terminator

Q.U.B.E. (FPS Puzzle Game) : http://www.qubegame.co.uk/

ColourRunners (FPS pas très clair) : http://colourrunners.blogspot.com/

Gerridae (plateforme) : http://gamagora.univ-lyon2.fr/gerridae/blog/

Maglev (plateforme) : http://www.pauseponderplay.com/

UnrealScripter #4

Lundi 19 juillet 2010

unrealscripter

VI. HUD

Attaquons-nous aujourd’hui à notre premier HUD. Comme je l’ai dit précédemment, j’ai codé le HUD avant tout pour contrôler que les modifications dans le code fonctionnaient ! Et cela a toujours été le cas dans mes projets. Il est important de savoir faire un HUD (même moche) afin de suivre en direct l’évolution des variables du jeu. Il est également possible, dans la même optique, d’ajouter des messages dans le console, mais ce n’est pas l’objet de ce tutoriel.

Une fois pris en considération qu’il n’y aura ici AUCUN souci esthétique, on va pouvoir se lancer. Avant tout, il existe désormais deux types de HUD dans l’UDK. Depuis la version de mai (attention à bien vérifier la version que vous utilisez) a été intégré Scaleform GFX, une technologie permettant l’affichage d’interfaces 3D interactives. L’avancée en termes graphiques est importante mais l’accessibilité est beaucoup moins évidente. Heureusement, il est toujours possible de créer des HUD à l’ancienne en ajoutant une simple ligne de code dans les defaultsproperties de TT_Game.uc :

bUseClassicHUD = true;

Surtout, n’oubliez pas de changer ça le jour où vous voulez faire une interface 3D !

Pour être honnête, je précise que l’on va ici utiliser une fonction qui ne vient pas de moi, DrawTexting(). Je l’ai adaptée à partir d’un tutoriel ou d’un exemple mais je ne sais plus d’où.

Commençons par créer le fichier, TT_HUD.uc :

TT_HUD extends UTHUD;

Je précise qu’il existe une classe UTTeamHUD qui prend en compte l’équipe dans laquelle on joue pour dessiner le HUD (simplement, ça permet de faire que si on est dans l’équipe bleue, certains textes et images du HUD se colorent en bleu). Je vous laisse donc adapter à ce que vous voulez faire. A ne pas oublier : dire à notre jeu qu’il doit utiliser ce HUD ! On doit doit ajouter la ligne suivante dans les defaultproperties de notre [b]TT_Game.uc[/b].

HUDType=Class’TT_HUD’

Ici, nous allons utiliser (comme toujours pour faire un HUD) des fonctions issues de Canvas.uc. Une nouvelle fois, je vous conseille d’aller jeter un coup d’oeil à cette classe pour voir un peu l’éventail de fonctions qui y existent.

simulated function DrawTexting (UTHUD H, float X, float Y, string Text)

{

if (H == None || H.Canvas == None) return;

// Simule une résolution de 1024*768

X = X * H.ResolutionScaleX;

Y = Y * H.ResolutionScale;

H.Canvas.Font = class’Engine’.static.GetMediumFont();

H.Canvas.SetPos (X, Y);

H.Canvas.DrawColor=WhiteColor;

H.Canvas.DrawText (Text, false);

}

En observant les variables de cette fonction, on a d’abord un HUD (pour savoir pour quel joueur on affichera le texte), des positions X et Y sur l’écran et le texte à écrire. La première condition (H == None || H.Canvas == None) vérifie si le HUD cité correspond à quelque chose et si ce n’est pas le cas, la fonction ne fait rien. Ensuite, la première partie simule un écran d’une taille donnée. En effet, chaque joueur ayant une résolution différente, il faut arriver à coder quelque chose de générique qui fonctionnera sur chaque écran. Globalement, l’idée est d’arriver à une notion de pourcentage plutôt que de nombre de pixels.
Les conditions suivantes sont celles qui vont déterminer la police d’écriture. On utilise ce genre de fonction pour les appeler :

class’Engine’.static.GetMediumFont()

On verra dans un autre tutoriel comment changer les polices d’écriture par défaut.

On utilise ensuite SetPos pour déterminer la position, DrawColor pour la couleur et DrawText pour dessiner le texte. On peut remarquer qu’une fonction, DrawText(), existe donc déjà. Cependant, en créant la notre, on la complexifie beaucoup (il suffit de voir déjà le nombre de variables) et on l’adapter à nos besoins. Il existe également une fonction qui dessine un texte en le centrant. Sans surprise, on trouve DrawTextCentered() dans Canvas.uc.

Pour le moment, on a codé notre base, c’est-à-dire la fonction qui nous permettra d’afficher du texte. Maintenant on va coder la fonction qui permettra d’exécuter cette fonction (un grand classique). Appelons la DrawLevel. Cette fonction a pour but d’afficher le niveau du joueur et son nombre de points d’expérience. Pour cela, on aura shématiquement deux lignes lançant deux fois la fonction DrawText. Reste à récupérer le niveau et points d’XP. Pour cela, on va créer deux nouvelles fonctions…

function int PickLevel(Controller c) //Récupère le Niveau

{

local int WhatLevel;

WhatLevel = TT_PRI(c.PlayerReplicationInfo).Level;

Return WhatLevel;

}

Encore une technique classique. Ici, la fonction PickLevel a pour variable un Controller, car on veut récupérer le niveau d’un joueur précis. On remarque dans la déclaration de la variable le “int”. Cela veut dire que quand je lance la variable, elle va me donner un nombre entier. Par exemple, si je fais PickLevel(Joueur); cela donnera 1, 2, 3 ou 4, etc. On peut évidemment déclarer des functions avec des floats, strings, booléens, etc.

Ici, on a donc déclaré une variable locale, on la définit comme égale au niveau du joueur. Et ensuite on retourne cette variable. Le résultat de la fonction est donc “WhatLevel”. Rien de bien compliqué ici. Je vous laisse faire la même fonction PickXP qui est exactement la même, il suffit de changer “Level” en “XP” (essayez de la refaire sans regarder l’autre, ce sera formateur !).

Maintenant que l’on a toutes nos fonctions, on peut passer à celle qui nous intéresse réellement !

simulated function DrawLevel(UTHUD H)

{

DrawTexting (H,10, 180, ” XP : ” @ PickXP(PlayerOwner));

DrawTexting (H,10, 200, ” Level : ” @ PickLevel(PlayerOwner));

}

A force de faire des fonctions annexes, notre fonction de base se trouve bien simplifiée ! Il faut comprendre que le fait de faire des sous-fonctions permet d’éviter de s’embrouiller dans les variables et surtout de les réutiliser ailleurs. Typiquement, une fonction comme DrawLevel sera certainement utilisable ailleurs (pour afficher des résultats par exemple). On remarque que j’ai décalé en hauteur (3ème variable, Y) les textes pour qu’ils ne soient pas l’un sur l’autre. La variable PlayerOwner est à retenir. C’est un PlayerController est défini dans HUD.uc (et donc aussi à UTHUD.uc qui en dérive) et désigne le joueur qui utilise ce HUD.

Terminé ? Pas du tout ! Nous n’avons pas lancé la fonction DrawLevel ! Pour cela, rien de compliqué, le tout c’est ne pas oublier de le faire !

function DisplayScoring()

{

Super.DisplayScoring();

DrawLevel (self);

}

La fonction DisplayScoring affiche le score (ha bon ?). On y ajoute discrètement notre fonction. Ici aussi, il est intéressant de voir la variable “self” qui dit donc que DrawLevel doit utiliser le HUD dans lequel il est lui-même. Cela peut paraître évident, mais ça ne l’est pas du tout. Par exemple, on pourrait très bien faire un jeu qui afficherait les points de vie de ses coéquipiers !

Voilà, c’est tout pour aujourd’hui. Notre HUD est très simple, voir moche mais permet de jouer en visualisant son niveau et ses points d’XP. Avec nos compétences actuelles, on peut très bien afficher également le nombre de points d’XP restants jusqu’au prochain niveau. N’hésitez surtout pas à utiliser la base du code pour l’adapter à vos besoins.

Dans le prochain tutoriel, on tentera (j’espère) le mode Furie !

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…

UnrealScripter #2

Samedi 10 juillet 2010

unrealscripter

On continue dans notre apprentissage de l’UnrealScript avec la modification de nos variables préalablement définies.

III. Modifier les variables du PRI

Pour que nos variables aient un intérêt, il faut qu’elles évoluent selon les évènements in-game. Ici, nous allons bien sûr faire en sorte que fragger un joueur/bot nous rapporte des points d’expérience, que les points d’expérience nous fasse changer de niveau et que le mode Furie se déclenche selon les souhaits du développeur.

Pour faire cela, nous allons utiliser la fonction ScoreKill(Controller Killer, Controller Killed). Comme la fonction l’indique, ScoreKill nous permet d’accéder au joueur qui a tué et à celui qui a tué. Si ici nous allons mettre des bonus au tueur, on pourrait également donner des malus au tué. Nous allons ici utiliser un système de points d’expérience très simple, mais facilement personnalisable par la suite (avec des « if » partout !)

Pour simplifier le code (et comme nous n’avons aucun souci d’équilibrage actuellement), nous allons décider que tous les 100 points, notre perso gagne un niveau (sur trois au total). Chaque fois qu’il fraggue quelqu’un, il gagne 10 points. Cela donne :

Function ScoreKill(Controller Killer, Controller Killed)

{

Local TT_PRI KillerPRI;

KillerPRI =  TT_PRI(Killed.PlayerReplicationInfo);

//je précise ici dans la variable KillerPRI que c’est le tueur

//afin d’éviter des confusions si je veux finalement

//utiliser le PRI du tué plus tard

KillerPRI.XP += 10 ; //ajoute 10 points d’XP à ceux déjà existants

CheckLevel(Killer) ;

}

Rien de compliqué ici. Vous remarquez que j’ai ajouté une fonction CheckLevel(Killer) que l’on va créer tout de suite. Elle a simplement pour but de savoir si, maintenant que les points d’expérience ont changé, le joueur a gagné un niveau. On ajoute alors le code suivant (à TT_Game.uc toujours)

Function CheckLevel(Controller C)

{

Local TT_PRI PRI ;

PRI = TT_PRI(C.PlayerReplicationInfo) ;

If (PRI.XP <= 100)

{

PRI.Level = 0;

}

Else if (PRI.XP <= 200)

{

PRI.Level = 1;

}

Else

{

PRI.Level = 2;

}

}

Nous avons donc un joueur qui augmente de points d’expérience et de niveau. Attention, ici, s’il meurt, il perd tout. Dans cette partie, je pense qu’il est nécessaire de bien comprendre ce que ScoreKill() peut apporter. Cela commence par « je tue, j’ai un point » à des systèmes de bonus/malus complexes selon l’arme utilisée, s’il y a headshot, si le joueur tué a un niveau supérieur, etc. Les possibilités sont immenses.

Pour le moment, notre joueur n’a aucun bénéfice à gagner un niveau. Nous allons faire en sorte que ça serve à quelque chose en déclenchant le mode Furie ! Mais pour cela, il faudra attendre le prochain tutoriel.

UnrealScripter #1

Vendredi 9 juillet 2010

unrealscripter

L’UDK est un outil puissant qui permet de faire plus ou moins n’importe quel type de jeu, le problème est de pouvoir le programmer. Cette série de tutoriels a pour but d’aider à la programmation en UnrealScript. Le but ici n’est pas de coder des features incroyables, mais de comprendre comment fonctionne l’UnrealScript.

Introduction

Le PlayerReplicationInfo est quelque chose de relativement peu connu lorsqu’on commence à coder en UnrealScript. Pourtant, beaucoup de codeurs nécessitent d’en comprendre l’usage pour arriver à leurs fins. Surtout que l’usage du PRI est relativement simple.

Le PlayerReplicationInfo est créé pour chaque joueur (en jeu multijoueur ou solo) et contient simplement les informations de ce joueur (points de vie, nom, équipe…). En créant notre propre PRI, on va pouvoir ajouter des caractéristiques à notre joueur.

Ici, le dossier où sont enregistrés mes fichiers est TrashTeam et tous les suffixes sont TT_

Le code ci-dessous est inspiré par mes travaux sur Unreal Safari et Little God Story. Il est donc flexible et adaptable à différents projets.

Prérequis

Savoir compiler un code écrit en Unreal Script et l’utiliser in-game.

I. Créer le fichier PRI

Pour créer un fichier PRI, c’est très simple comme toujours en Unreal Script. Il suffit de dériver du PRI de l’UDK et de l’enregistrer sous le nom correct, ici TT_PRI.uc

class TT_PRI extends UTPlayerReplicationInfo;

Il est ensuite très simple de créer de nouvelles caractéristiques à notre joueur. Ici on va créer plusieurs choses, basé sur un mode RPG classique. On va lui donner des points d’expérience, sous la forme d’un integer et d’un niveau, sous la même forme. On ajoute aussi une feature plus originale (ou pas) : le joueur peut (parfois) passer en mode Furie. On va donc définir une variable booléenne qui dira si le joueur est en mode furie (1) ou pas (0) Même si je ne le fais pas ici, il est tout à fait possible de déclarer des textes (string) également.

Pour cela, il suffit de déclarer les variables, puis de lancer la replication.

class TT_PRI extends UTPlayerReplicationInfo;

var int XP;

var int Level;

var bool bFurie;

//Furie Off = 0 On = 1

replication

{

if ( bNetDirty && (Role == Role_Authority) )

XP, Level,bFurie;

}

Je vous conseille de toujours commenter votre code, notamment pour les valeurs booléennes ou les nombres. Cela vous évitera de vous poser la question à quoi 1 correspond quand vous aurez oublié… Dernière chose, il est d’usage de mettre un petit « b » devant une variable booléenne. Cela permet de les reconnaître facilement.

Evidemment, il est primordial que notre fichier soit reconnu. Direction TT_Game.uc (fichier de base de votre jeu). Il suffit d’ajouter une ligne dans les propriétés par défaut de votre jeu pour que ce soit pris en compte (je vous conseille au passage de regarder dans UTGame tous les fichiers utilisés par UTGame, afin de deviner ceux qu’il faudra modifier).

class TT_Game extends UTGame;

defaultproperties

{

PlayerReplicationInfoClass = class’TrashTeam.TT_PRI’

}

Voilà pour notre fichier PRI, reste à voir désormais comment définir les variables et les appeler dans d’autres classes.

II. Définir les variables du PRI

Afin d’éviter de se retrouver avec des surprises, il est bon de définir les variables au début du jeu. Ici, le joueur démarre avec XP = 0 et un niveau de 0 également. Il n’est pas en mode Furie en revanche (bFurie = false). Attention à bien prendre certaines choses en compte. Il existe de multiples fonctions qui se lancent au démarrage du jeu. Il faut d’abord que le joueur soit réellement « créé » sinon on n’aura rien à définir. Pour cela, rien de mieux que l’évènement PostLogin(PlayerController PC). Etudions d’abord la fonction. Un joueur arrive dans la partie. Il se logue. L’évènement PostLogin est donc activé. Cet évènement dépend donc d’un joueur précis, d’où le PlayerController défini en même temps que l’évènement. Tout ce que l’on fera dans l’évènement PostLogin ne concernera donc que le PlayerController qui vient de se loguer (et heureusement si jamais on veut faire un jeu multijoueur !)

La façon d’appeler un fichier PRI nécessite d’avoir sous la main, défini, un PlayerController ou un Controller (ou un Pawn, mais c’est légèrement différent). Cela tombe bien, ici on en a un sous la main. Pour définir une variable, voilà une commande d’exemple :

TT_PRI(PC.PlayerReplicationInfo).XP = 0 ;

D’où l’évènement :

event PostLogin(PlayerController PC)

{

super.PostLogin(PC);

TT_PRI(PC.PlayerReplicationInfo).XP = 0;

TT_PRI(PC.PlayerReplicationInfo).Level = 0;

TT_PRI(PC.PlayerReplicationInfo).bFurie = false;

}

Fini ? Non ! Si votre perso meurt, il ne va pas se re-loguer. Utilisons alors la fonction  RestartPlayer(Controller C). En gros, c’est la même chose, sauf qu’ici au lieu d’avoir un PlayerController défini, c’est un Controller tout court. Pour nous, ça ne change rien heureusement, attention seulement aux copier-coller (erreur classique…).

Il suffit donc d’ajouter ces lignes de code :

function RestartPlayer(Controller C)

{

super.RestartPlayer(C);

TT_PRI(C.PlayerReplicationInfo).XP = 0;

TT_PRI(C.PlayerReplicationInfo).Level = 0;

TT_PRI(C.PlayerReplicationInfo).bFurie = false;

}

Evidemment, pour le moment, on définit des variables qui ne servent à rien. Mais ça viendra dès la partie III !

Une dernière chose qui permettra à votre code d’être plus propre (et plus lisible). Il est possible de simplifier l’écriture de la définition des variables. Pour cela il suffit de déclarer une variable PRI local, de la définir et on pourra simplifier pas mal le résultat (ceci est utilise dès que vous définissez un paquet de variables dans une même fonction ou un même évènement). Un exemple sur la dernière fonction vue :

function RestartPlayer(Controller C)

{

Local TT_PRI PRI;super.RestartPlayer(C);

PRI = TT_PRI(C.PlayerReplicationInfo);

PRI.XP = 0;

PRI.Level = 0;

PRI.bFurie = false;

}

C’est mieux non ?

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