Rom Hack & Fan Game

Input, Graphics et première interface

Nous approchons de la pratique mais avant, voyons deux modules qui nous seront assez utile pour faire fonctionner notre interface : Le module Input et le module Graphics !

Sommaire :

Input

Ce module est simplement le gestionnaire de la détection d'appuis de touche, il vous permet de vérifier si une touche est appuyée, maintenue sur la durée ou simplement baissé. Biensur comme vous faites un jeu, vous n'avez pas énormément de touches disponibles (à moins d'en coder vous-même), ces touches sont réglable en appuyant sur F1 quand le jeu démarre :

Modification des paramètres d'Input

Méthodes du module Input

Ce module s'axe sur 6 grandes méthodes :

  • Input.update : Met à jour la détection des touches, il faut appeler cette méthode avant de traiter les entrée de touche. Généralement on place ça après Graphics.update.
  • Input.press?(touche) : Permet de vérifier si une touche est enfoncée, il faut donner le code de la touche (le code défini par le RGSS).
  • Input.trigger?(touche) : Permet de vérifier si une touche est appuyé puis relâché dans l'instant.
  • Input.reapeat?(touche) : Permet de vérifier si une touche est maintenu sur la durée. C'est très utilisé pour les déplacements dans le menu car ça ne retourne true que tout le 4 frame (à peu près) et non de manière continu. Ça évite au curseur de se perdre :p
  • Input.dir4 : Donne la direction vers laquelle le joueur se dirige avec ses touche. Le retour est un nombre : 2, 4, 6, 8, si vous regardez votre PAD numérique, les flèches sur ces nombres vous indiquent les directions.
  • Input.dir8 : Pareil sauf que ça peut donner les directions comme Haut Gauche, Haut Droite, Bas Gauche et Bas Droite. Le retour est un nombre : 1, 2, 3, 4, 6, 7, 8, 9.

→ Retour au Sommaire

Constantes du module Input

Pour utiliser ces méthodes il faut connaitre les touches qu'elles peuvent recevoir alors je vous liste ces touche avec ou non une description :

  • Input::A : Généralement la touche Shift ou Z.
  • Input::B : Généralement X/Echap/Return, elle sert pour l'annulation et ouvrir le menu (c'est pas cohérent je l'accorde).
  • Input::C : Généralement C/Entrée/Espace, elle sert pour la validation.
  • Input::X
  • Input::Y
  • Input::Z
  • Input::L : Ça doit servir dans le Pokédex pour défiler vite. (PSP 0.7)
  • Input::R : Même chose.
  • Input::DOWN : Touche bas. (C'est valide si le joystick de votre manette pointe vers le bas)
  • Input::LEFT : Touche Gauche.
  • Input::RIGHT : Touche Droite.
  • Input::UP : Touche Haut.
  • Input::SHIFT : Les touches Shift
  • Input::CTRL : Les touches contrôle, en débug ça vous permet de traverser les murs et pas rencontrer de Pokémon.
  • Input::ALT : Les touches ALT.
  • Input::F5, Input::F6, Input::F7, Input::F8, Input::F9 : touches fonction. (F9 pour ouvrir le menu debug.)

→ Retour au Sommaire

Graphics

Le module Graphics elle vous permet de mettre à jour les graphismes du jeu (si ce n'est pas fait pendant 10 secondes le jeu est suspendu). Ce module vous permet de faire deux trois réglages ou transitions :

  • Graphics.update : Met à jour l'affichage Graphique.
  • Graphics.freeze : Permet de bloquer la mise à jour des graphismes et préparer une transition entre la frame bloquée et la frame qui sera affichée. (Graphics.update ne change plus les graphismes mais il conserve son délai de 0.025 secondes.)
  • Graphics.transition : Fait une transition durant 8 frame entre la frame freezé et la frame qui doit être affiché après l'appel de cette méthode.
  • Graphics.transition(duration) : Fait une transition durant duration frame.
  • Graphics.transition(duration, filename) : Fait une transition durant duration frame et en utilisant l'image pointée par filename comme modèle de transition.
  • Graphics.frame_reset : Ca remet à zéro les paramètres donné à Graphics. Le RGSS appel cette méthode lorsque vous appuyez sur F12 pour faire le ménage de manière efficace.
  • Graphics.frame_rate : Indique ou vous permet de modifier le taux de rafraichissement en frame par secondes. Ça vaut 40 par défauts et ça peut aller à 120 au maximum. Vous pouvez mettre Graphics.frame_rate=60 dans la fonction Main de l'écran titre afin que le jeu s'affiche à 60fps. Mais attention, le RGSS n'est pas conçu pour avoir des framerate de malade donc en fonction des PC ça sera 60 ou moins.
  • Graphics.frame_count : Indique le nombre de frame écoulé depuis que le jeu a commencé, cette valeur peut être modifiée. Elle est souvent utilisé pour déterminer le temps de jeu, mais je vous déconseille fortement de l'utiliser pour ça : c'est pas une mesure de temps précise du tout.

→ Retour au Sommaire

Notre Interface

Maintenant que nous avons vu ça, nous allons faire notre première Interface, une horloge. Comme pour la programmation du système de baies. Il faut une petite réflexion, là je vais vous faire un truc rapide :

  • Il nous faut des ressources : [Télécharger le Pack] (Mettez le dossier dans Graphics/Pictures/)
  • Une horloge affiche douze heures, il faut donc agir en conséquence. Ce qui est bien c'est que dans nos ressources on a le fichier am_pm qui permet de savoir si on est le soir ou le matin.
  • Il faut définir des variables indiquant l'heure au jeu. Sous PSP DS et PSP 4G+, il y a un système qui prend l'heure du PC, je vous conseille de le désactiver car cette heure à moins d'être admin, le jeu ne peut pas les modifier (et c'est tordu de faire ça ingame bien que ça reste possible -._-.).
  • Petite réflexion personnelle, je vais faire en sorte à ce que le script puisse s'afficher sur plusieurs type d'écran avec un certain zoom qui sera automatiquement défini par la taille de l'écran.
  • Il y a une horloge pour la fille, une horloge pour le garçon, nous allons utiliser une condition pour charger la bonne image en fonction du sexe. (Dans RSE c'est dépendant de la salle donc il faut plutôt faire une condition qui dit quel fichier utiliser en fonction de la salle :p)

I - Définir les constantes

Les constantes sont toujours au début du script, il faut les utiliser pour pouvoir modifier le script facilement. Par exemple, je défini deux constantes Width et Height qui sont la taille de l'écran de jeu et implicitement du viewport que je définirais. Par ailleurs, si je modifie ces constantes, tous mes sprites seront correctement déplacés car je me sers de ces constantes. Si vous auriez mis des nombres directs, vous aurez été emmerdé pour les modifications car il faut chercher tous les nombres à modifier.
Je me sers aussi des constantes pour les fichiers. C'est à mon sens plus propre mais faites comme vous voulez.

→ Retour au Sommaire

II - Initialiser la scène

Je recommande vivement de faire une méthode initialize pour la scène car il y a beaucoup de choses à définir et ça fait un truc très long. Si vous perdez votre lecteur (donc vous) dans les méthodes de traitement comme main, c'est mal barré.

Dans cette méthode, je vais commencer par créer le viewport, comme je suis sur PSP DS, je mets 13,13 pour x,y et comme je vais utiliser des constantes pour la taille de l'écran je mets Width,Height (les constantes) pour width,height.
La coordonnée z, j'ai mis 10001, on sait jamais qui appel le script.

Après avoir initialisé le Viewport, j'initialise tous les sprites. Les coordonnées des sprites sont dépendantes de plusieurs données : Les aiguilles et le cadre seront au centre de l'écran, quel que soit la taille de l'écran, les origines des aiguilles seront leur centre de rotation, car dans cette interface elles tournent.
L'image am/pm elle sera au centre de l'horloge mais déplacée de quelque pixels vers le bas.
L'indicateur qui dit qu'il faut utiliser droite et gauche pour changer l'heure sera en bas à droite, dans le script je mettrai ses origine aussi en bas à droite pour ne pas me mindfuck à chercher les bonnes coordonnées x et y.

On chargera les ressources avec RPG::Cache.picture qui va chercher les bitmaps dans Graphics/Pictures et mettre ça en cache (au cas où l'horloge serait utilisée plusieurs fois).

Si vous avez suivi les tutoriels précédent, définir les positions et tout le tralala devrait être facile, il y a une dernière chose à faire dans initialize et c'est très important dans toutes les classes : définir les variables d'instance. Je vais définir @heure et @minute. J'utilise des variables d'instance pour le cas où finalement je ne voulais pas modifier l'heure. Ça évite de toucher aux variable de jeu pendant le traitement et c'est par ailleurs plus rapide de travailler avec des variables d'instance que des tableaux caché dans des méthodes cachés dans des variables globales.

→ Retour au Sommaire

III - La mise à jour

La mise à jour est appelée par la boucle de main, dedans on vérifie les Inputs, ici nous vérifierons 4 choses :

  • On a Droite enfoncée : +1 minute et mise à jour de la position des aiguilles.
  • On a Gauche enfoncée : -1 minute et mise à jour de la position des aiguilles.
  • On appuie sur C : On valide le changement de l'heure et on retourne sur la Map.
  • On appuie sur B : On ne valide pas les changements et on retourne sur la Map.

Ce n'est pas sorcier la mise à jour, des conditions et le tour est joué.

III.bis - Mise à jour de la position des aiguilles

Dans cette méthode nous allons faire deux choses, corriger l'heure, mettre à jour les informations des Sprites :
Pour la correction de l'heure, il suffit de vérifier si les minutes sont supérieurs à 60 pour enlever 60 et ajouter une heure. Si les minutes sont inférieures à 0 pour ajouter 60 et soustraire une heure. Il faut fait attention à l'heure, si supérieur à 23 => 0, si inférieur à 0 => 23. J'ai utilisé des boucles while au cas où l'heure était de base vraiment très mal réglé dans les variables de jeu. (Ça peut arriver si vous faites pas attention.)

Pour la position des aiguilles c'est tout simple. Un sprite a un angle de rotation anti-horaire en degré. Nous allons donc convertir nos minutes et notre heure en degrés. Pour les minutes c'est tout bête, il suffit d'utiliser une règle de trois entre 60, 360 et les minutes. Ça nous donne un angle en degrés que l'on va rendre négatif car rappelez-vous, la rotation des Sprites est dans le sens anti-horaire.
Pour les heures c'est autre chose :

  • Nous avons 24 heures, l'horloge en affiche 12 donc il nous faut un rapport entre l'heure affiché, l'heure réelle et 12. Nous allons utiliser la fonction modulo (nombre%12) pour retrouver le format 12 heures à partir de l'heure réelle.
  • L'aiguille des heures ne saute pas d'une heure à une autre : dans les horloges classiques l'aiguille des heures n'est jamais exactement à graduation de l'heure (sauf mécanisme spécifique), dans notre interface ça sera la même chose car c'est plus beau à voir : pour cela on va déjà définir l'angle réel de l'aiguille des heures avec la même relation que pour les minutes sauf qu'on remplace 60 par 12. Et nous allons ajouter l'effet déplacement de l'aiguille des heures en utilisant l'angle des minutes. L'angle des minutes varie de 360 degrés, l'aiguille des heures entre deux heures varie d'un douzième de cadrant donc il suffit tout simplement d'ajouter le douzième de l'angle des minutes pour déplacer l'aiguille des heures entre les deux heures qu'elle indique.

→ Retour au Sommaire

IV - Le résultat et script final

Après ces explications, vous allez coder ça, je ne vous ai pas donné les lignes intermédiaires pour que vous fassiez vous-même et que vous vous reportez à un script « solution » qui fait ce qui a été choisi dans la réflexion.

Le résultat :

Horloge1Horloge2Message résultat

Le script :

#===  #¤Scene_Horloge  #---  #Petit script permettant d'afficher une horloge réglable  #© 21/08/2014 - Nuri Yuri  #===  module GamePlay #Je range ma classe dans ce module    class Scene_Horloge #Je défini la classe de ma scène       BG="horloge/bg"      AiguilleHeure="horloge/heu"      AiguilleMinute="horloge/min"      IndicateurMoment="horloge/am_pm"      IndicateurCommande="horloge/indi"      HorlogeGars="horloge/hg"      HorlogeFille="horloge/hf"      Width=256 #Largeur de notre interface      Height=192 #Hauteur de notre interface      VAR_Fille=11      VAR_HEU=21      VAR_MIN=22      #===      #>Initialisation de l'interface : On va créer les sprites et le reste      #===      def initialize        @viewport=Viewport.new(13,13,Width,Height)        @viewport.z=10001 #Je m'assure qu'il est bien visible        @zoom_factor=Width/256 #Ca nous servira a faire une redimension automatique si l'écran fait 640x480 par exemple        @interface_du_dessous=Interface_Echap.new #C'est pour PSP DS :p        #>On définit le background avec un Plane car il va se répéter        @background=Plane.new(@viewport)        @background.bitmap=RPG::Cache.picture(BG) #Va charger le fichier "Graphics/Pictures/horloge/bg" et le mettre en cache        @background.z=0 #>Pas nécessaire là mais bon :p        @background.zoom_x=@background.zoom_y=@zoom_factor #Zoom automatique du background        #>On définit maintenant l'horloge        @horloge=Sprite.new(@viewport)        @horloge.bitmap=RPG::Cache.picture($game_variables[VAR_Fille]==2 ? HorlogeFille : HorlogeGars)        #>On met l'origine au milieu        @horloge.ox=@horloge.bitmap.width/2        @horloge.oy=@horloge.bitmap.height/2        #>On affiche l'horloge au centre de l'écran        @horloge.x=Width/2        @horloge.y=Height/2        @horloge.z=1        @horloge.zoom_x=@horloge.zoom_y=@zoom_factor        #>On définit l'aiguille des minutes        @aiguille_minute=Sprite.new(@viewport)        @aiguille_minute.bitmap=RPG::Cache.picture(AiguilleMinute)        #>L'origine de l'aiguille des minutes doit être le centre de rotation        #>Il faudra donc placer cette origine là où visuellement l'aiguille pivote        @aiguille_minute.ox=@aiguille_minute.bitmap.width/2        @aiguille_minute.oy=@aiguille_minute.bitmap.height-6        #>Maintenant on la place au même endroit que l'horloge.         #>Si l'horloge est bien paramétré l'aiguille cera au centre :p        @aiguille_minute.x=@horloge.x        @aiguille_minute.y=@horloge.y        @aiguille_minute.z=4        @aiguille_minute.zoom_x=@aiguille_minute.zoom_y=@zoom_factor        #>On définit celle des heures de la même manière        @aiguille_heure=Sprite.new(@viewport)        @aiguille_heure.bitmap=RPG::Cache.picture(AiguilleHeure)        #>Pareil que pour les minutes, il faut placer au centre de rotation        @aiguille_heure.ox=@aiguille_heure.bitmap.width/2        @aiguille_heure.oy=@aiguille_heure.bitmap.height-5        #>On positionne l'aiguille des heures        @aiguille_heure.x=@horloge.x        @aiguille_heure.y=@horloge.y        @aiguille_heure.z=3 #Il faut afficher l'aiguille des heures en dessous de celle des minutes :p        @aiguille_heure.zoom_x=@aiguille_heure.zoom_y=@zoom_factor        #>On définit l'indicateur AM/PM        @am_pm=Sprite.new(@viewport)        @am_pm.bitmap=RPG::Cache.picture(IndicateurMoment)        #>Je veux centrer l'ox mais l'oy n'est pas trop important        @am_pm.ox=@am_pm.bitmap.width/2        #>Maintenant je le positionne sur l'horloge        @am_pm.x=@horloge.x        @am_pm.y=@horloge.y+24*@zoom_factor #Il est déscendu de 24 pixels        #>Je défini son SRC_RECT car il y a deux images dedans :p        @am_pm.src_rect.set(0,0,@am_pm.bitmap.width,@am_pm.bitmap.height/2)        @am_pm.z=2 #Il doit être sous les aiguilles        @am_pm.zoom_x=@am_pm.zoom_y=@zoom_factor        #>La dernière chose, l'indicateur de touche        @indi=Sprite.new(@viewport)        @indi.bitmap=RPG::Cache.picture(IndicateurCommande)        #>Je vais le placer en bas à gauche alors pour être plus précis, je vais mettre son origine en bas à gauche :p        @indi.ox=@indi.bitmap.width        @indi.oy=@indi.bitmap.height        #>Maintenant je le positionne sur l'écran        @indi.x=Width-4*@zoom_factor        @indi.y=Height-4*@zoom_factor        @indi.z=10 #Il sera au-dessus de tout mais ce n'est pas important        @indi.zoom_x=@indi.zoom_y=@zoom_factor        #>Maintenant je définis mes petites variables indiquant l'heure        @heure=$game_variables[VAR_HEU]        @minute=$game_variables[VAR_MIN]        #>Et là, je corrige la position des sprites en fonction de ces heures        ajust_sprite_position      end            def main        Graphics.transition(5)        while $scene==self          Graphics.update          Input.update          update        end        Graphics.freeze        dispose      end            def update        #>On va inspecter tous les inputs        if(Input.press?(Input::RIGHT))          @minute+=1          ajust_sprite_position #Ajustement utile si et seulement si l'heure change        elsif(Input.press?(Input::LEFT))          @minute-=1          ajust_sprite_position        elsif(Input.trigger?(Input::C)) #Validation          #>On modifie l'heure du jeu          $game_variables[VAR_HEU]=@heure          $game_variables[VAR_MIN]=@minute          $scene=Scene_Map.new #On retourne sur la Map        elsif(Input.trigger?(Input::B)) #Annulation          $scene=Scene_Map.new #On retourne sur la Map        end      end            def dispose        #>On a pas de bitmap particulier à disposer donc on pourrait faire direct @viewport.dispose        #>Mais je m'adapte au cas où vous on n'utiliserait pas de viewport        @background.dispose        @horloge.dispose        @aiguille_minute.dispose        @aiguille_heure.dispose        @am_pm.dispose        @indi.dispose        @viewport.dispose        @interface_du_dessous.dispose      end            def ajust_sprite_position        #>Mise à jour de l'heure        while @minute>=60 #>Tant que les minutes sont supérieur à 59          @minute-=60          @heure+=1          @heure=0 if @heure>23        end        while @minute<0 #>Le cas inférieur à zéro peut arriver !          @minute+=60          @heure-=1          @heure=23 if @heure<0        end        #>Calcul des positions des aiguilles        angle_minute=-360*@minute/60        heure=@heure%12 #>On affiche que 12 heures sur cette horloge :p        angle_heure=-360*heure/12        #>On va ajouter l'effet mécanique induit par les roue avec les minutes        angle_heure+=(angle_minute/12) #Une heure c'est un douzième du cadrant        #>On met à jour l'angle des sprites        @aiguille_minute.angle=angle_minute #>Normalement l'angle est corrigé par le RGSS        @aiguille_heure.angle=angle_heure        #>On met maintenant le AM/PM à jour        #>AM : 0->11 PM : 12->23, on a d'abbor AM puis PM dans le fichier        @am_pm.src_rect.y=(@heure>11 ? @am_pm.src_rect.height : 0)      end    end  end  

Lancer la scène :

Pour changer d'une scène à une autre, c'est tout simple $scene=Nouvelle_Scene.new (si Nouvelle_Scene est une classe).
Ici, je le lance depuis l'évènement de l'horloge avec une commande de script :

évent

Comme vous pouvez le constater, j'ai mis une commande Attendre x frame, je l'ai mise pour que l'évent s'arrête le temps que la scène de l'horloge s'exécute. (Si je ne fais pas ça, l'évent va vouloir afficher le message en même temps que la scène se lance et vous allez vous retrouver avec une boite de message vide qui traine sur l'écran x)).

→ Retour au Sommaire

Voilà, c'était tout pour ce tutoriel, dans le prochain, nous allons aborder la classe Window (ça me prendra beaucoup de temps car cette classe fait plein de choses et j'en suis pas fan) et nous allons voir plus en détails la classe Font car sans elle, la classe Window ne sert pas à grand-chose.


← Utilisation des Viewports, Sprites et Plane Font et Window →

Par Nuri Yuri

Par Loris