jeudi 15 décembre 2011

Métaprogrammation et Ruby

Cet article est disponible en PDF : Lien ver le fichier PDF

Introduction

1 Avant-propos
La métaprogrammation est une manière de manipuler des données et structures décrivant eux même des applications/programmes.
Il s’agit d’une manière de programmer qui peut réduire considérablement la taille d’une portion de code ainsi que la complexité de l’utilisation d’une bibliothèque, d’un outil. La métaprogrammation fait partie d’un ensemble de technique requierant de la rigueure mais pouvant apporter énormément à une application.
J’ai choisi d’étudier ce concept autour du langage de programmation Ruby, qui est un langage que je connais relativement bien et qui admet nativement des
concepts de métaprogrammation.
Je ne suis pas ingénieur et je ne serai pas en mesure d’aborder tous les concepts de métaprogrammation, cependant, considérez que cet article peut être une introduction accessible pouvant amener à la création d’un Domain specific language.
L’objectif de cet article est donc de spécifier certaines choses du langages pour permettre au lecteur d’étendre Ruby pour plus de flexibilité dans la rédaction de code.


2 La métaprogrammation ?
Dans le cas de la programmation orienté objet, la métaprogrammation est une forme de traitement qui vise à modifier la déscription d’un objet et/ou ses comportemments. Cela repose sur plusieurs concepts tels que la réflexivité, l’introspection et d’autre. Cependant, dans cette rédaction, nous nous focaliserons sur des techniques de métaprogrammation sans pour autant rentrer dans une approche excessivement formelle et abstraite. Retenons donc la métaprogrammation va nous aider à faciliter la résolution de problèmes parfois ennuyeux de 1manière élégante. Dans cet article nous n’aborderons pas des méthodes spécifique à la métaprogrammation, mais nous évaluerons des spécifités du langage utilise (et parfois logique) pour la métaprogrammation.




Théorie
Le Ruby est un langage adapté à la métaprogrammation pour des raisons simples : son expressivité, son modèle objet respectant relativement bien les normes du paradigme orienté classes (bien qu’il soit possible de fonctionner par prototypage). D’ailleurs, en programmant en Ruby, vous avez été naturellement confrontés à de la métaprogrammation. Par exemple, la génération automatique d’accesseur et de mutateurs qui utilise de la métaprogrammation (dont je parlerai plus tard).


3 Le MonkeyPatching

le monkeypatching est une manière de modifier/étendre du code sans en modifier la source. C’est relativement courant dans la programmation dite dynamique et cela peut servir à modifier correctement une application/bibliothèque sans en modifier la source. Ce qui rend donc une restauration de données d’origine très facile. Le Monkeypatching de ruby prend son sens avec les possibilité d’aliaser les méthodes. Pour rappel, un alias est une manière de renommer quelque chose. Donc grace aux alias, il sera possible de modifier le comportemment d’une méthode sans modifier la classe dans laquelle elle se trouve physiquement. Voici un exemple avec la classe Array pour laquelle je vais modifier la méthode push (Ajoute un objet dans un tableau) de manière à ce qu’elle nous indique l’objet ajouté. (Je suis conscient que ce n’est pas très pratique mais il ne s’agit que d’un exemple).
  1. class Array
  2.   # Alias de la méthode push
  3.   alias ancien_push push
  4.   # Redéfinition de push
  5.   def push object
  6.     # Appel de l'ancienne méthode
  7.     self.ancien_push object
  8.     print "Ajout de #{object.to_s}"
  9.   end
  10. end

Comme vous pouvez le voir, il est très facile de modifier une méthode. Cependant, il est aussi possible de greffer de nouvelles méthodes à une classe déjà existante. Par exemple, ajoutons une méthode getfirst, cette méthode ne sert a rien mais elle nous retournera le premier objet contenu dans notre tableau :


  1. class Array
  2.   def getfirst
  3.     return self[0]
  4.   end
  5. end


Ce qui nous amène a la conclusion que si une classe porte le même nom qu’une autre classe, elle se fusionneront. Cependant, sans alias, les méthodes seront écrasées. Donc a moins de ne modifier completement le traitement d’une méthode, évitons de réécrire du code inutilement et utilisons les alias.


3.1 Encore plus loin dans la création de patch’s
Nous avons vu qu’il était possible de greffer/modifier des méthodes aux classes. Il est aussi possible de greffer des méhodes à des objets. Par exemple, admettons que j’ai une variable qui contienne une chaine de caractère et que je veuille pouvoir utiliser sur cette variable une méthode qui me retournerait un booléen pour savoir si la première lettre de ma chaine est bien C. Ce genre de méthode ne serait utile que pour une seule variable et il serait dommage de modifier intégralement la classe String pour si peu.


  1. test_string = "Ce que je suis, un Chat"
  2. def test_string.verification
  3.   return (self =~ /^C/) != nil
  4. end


J’en conviens que cette méthode n’est pas vraiment utile, mais comme vous pouvez le voir, il est facile de greffer certaines composantes uniquement à certaines instances et non à toute une classe.




3.2 Une arme à double tranchant
Bien que vu sous cet aspect, le monkeypatching semble une solution agréable de modification de code et donc, par extension, de confort d’utilisation, je trouve (et ça n’engage que moi) que le monkeypatching pose aussi un réel problème de raisonnement du code. Bien qu’il soit très agréable de pouvoir jongler avec les méthodes du langage, la modification abusive entraîne rend souvent un code plus complexe a relire que si des classes spécifiques avaient été définies. Cependant, ce n’est qu’une opinion.



3.3 Conclusion
Le monkeypatching nous amène naturellement à l’affirmation que les classes sont ouvertes. Il est donc possible de les modifier à la volée. Ce qui amène donc à recommander une forme d’éducation de la part des développeurs pour éviter que cet excès de liberté n’amène à la déterioration d’un noyau de code. Je vous laisse analyser cette exemple pour comprendre cette mise en garde.
  1. class Fixnum
  2.   def +(obj)
  3.     self * obj
  4.   end
  5. end


Bien que cet exemple démontre une certaine stupidité de la part du développeur, il serait possible d’imaginer des exemples plus perfide et moins facilement détectable.




4 Un peu plus sur les classes
Un concept utile à la métaprogrammation est qu’en Ruby, les noms de classes sont des constantes. Il est donc possible d’instancier nos objets de cette manière :
  1. class Some
  2.   def initialize
  3.     @attribut = 10
  4.   end
  5. end
  6. classe = Some
  7. test = classe.new


Mais ce n’est pas tout à propos des classes. En Ruby, une classe est définie quand le programme est lancé et non à la compilation (car le langage est interprêté). Entre les blocks méthodes, il est aussi possible d’exécuter du code. Par exemple, imaginons une méthode changeant de comportemment en fonction de son contexte :
  1. $in_debug = true
  2. class Some
  3.   if $in_debug
  4.     def test
  5.       return "méthode en debug"
  6.     end
  7.   else
  8.     def test
  9.       return "méthode pas en debug"
  10.     end
  11.   end
  12. end
  13. t = Some.new
  14. print t.test


Cependant, sachez que cette exécution est effectuée à l'exécution du code et donc, même en changeant la valeur de la variable globale, la classe a été définie comme étant en debugmode.
Il est donc possible de définir les accesseur et les mutateurs au moyen d'une boucle. Je montre cet exemple uniquement pour donner des pistes vers la création d'un DSL (Domain specific language), cependant, je vous déconseille fortemment d'utiliser une boucle pour définir les accesseurs/mutateurs. Il ne s'agit, ici, que d'une simple expérience.
  1. class Some
  2.   for i in 0..3
  3.     attr_accessor "arg#{i}".to_sym
  4.   end
  5.   def initialize
  6.     @arg0 = 2
  7.     @arg1 =9
  8.     @arg2 =18
  9.     @arg3 =27
  10.   end
  11. end
  12. test = Some.new
  13. print test.arg2
la notion importante de cet exemple est que les classes sont définies à l'exécution et qu'il est donc possible de prendre beaucoup de raccourcis syntaxique.


4.1 Les classes sont des objets
En ruby (et dans d'autres langages de programmation orientés objets), une classe est avant tout une instance d'un objet Class. Il est donc tout a fait possible d'accéder au constructeur de Class (et de définir une classe en lui passant un block en paramètre). Cependant, cela va beaucoup plus loin.
Rappellons nous qu'il est possible d'exécuter des actions entre des méthodes en Ruby. C'est d'ailleurs sur ce principe que reposent les attr_accessor, attr_reader,attr_writter qui sont des méthodes qui définissent un comportemment sur l'instance de Classe et non sur la future instance de notre classe.


4.2 Utiliser les module
Bien que le comportemment primaire des modules soit de faire office d'espace nom, il est possible de faire ce qu'on appelle des Mixins. Il s'agit d'utiliser l'inclusion d'un module pour partager ses méthodes avec une classe. Par exemple:
  1. module AModule
  2.   def sayHello
  3.     print "Hello guy's, i'm #{@prenom} #{@nom}"
  4.   end
  5. end
  6. class Michael
  7.   include AModule
  8.   def initialize
  9.     @prenom = "Michael"
  10.     @nom = "Spawn"
  11.   end
  12. end
  13. mick = Michael.new
  14. mick.sayHello
Dans cet exemple, le module utilise les attributs de la classe et l'appelle de la méthode sayHello fonctionne parfaitement.
Si vous avez déjà souvent utilisé Ruby, vous savez qu'il est possible de définir des méthodes de classes de cette manière:
  1. class Some
  2.   class << self
  3.     def test x, y
  4.       return x + y
  5.     end
  6.   end
  7.   # Ou bien
  8.   def self.test2 x , y
  9.     return x * y
  10.   end
  11. end
En utilisant la théorie des mixins, il est possible de relier des méthodes à un contexte statique, donc en tant que méthode de classe (et non d'instance). Voici un exemple:
  1. module AModule
  2.   def somme x, y
  3.     return x + y
  4.   end
  5. end
  6. class Some
  7.   extend AModule
  8. end
  9. print Some.somme 9, 10
Comme on peut le voir dans l'exemple, il est donc possible d'étendre des méthodes au singleton d'une classe. Il faut évidemment prendre ces exemples dans des contextes pertinents, comme par exemple la réalisation d'une bibliothèque réutilisable. Tout ces petits exemples nous amènent petit à petit à la dernière partie de ce cours.





Pratique

Nous allons maintenant mettre en pratique les cas que nous avons soulevés précédemment sous forme de petits exemples rapides.


5 Construire une classe dynamiquement
Une classe est une instance d’un objet Class, il est donc possible d’accèder à son constructeur.


  1. uneClasse = Class.new do
  2.   attr_accessor :argument1
  3.   attr_accessor :argument2
  4.   def initialize args1, args2
  5.     @argument1, @argument2 = args1, args2
  6.   end
  7. end
  8. test = uneClasse.new 1, 2
  9. print test.argument2
Cet exemple assez naïf n'est ici que pour montrer qu'il est possible de créer des classes dynamiques au même titre que n'importe quelle objet.


6 Vers un premier DSL
Grâce aux concepts étudiés précédemment, nous allons pouvoir nous lancer progressivement dans la création d'un petit DSL pour faciliter l'utilisation de nos propres outils.
L'exemple fourni est très simple (et très inutile), il montre comment utiliser de manière intuitive, une méthode pour multiplier un argument par une valeur fournie.
  1. module AR
  2.   def bind_test *args
  3.     return @test if args.length == 0
  4.     @test = args[0]
  5.   end
  6. end
  7. class Some
  8.   extend AR
  9.   bind_test 99
  10.   attr_accessor :arg1
  11.   def initialize arg1
  12.     @arg1 = arg1 * (self.class).bind_test
  13.   end
  14. end
  15. test = Some.new(9)
  16. print test.arg1
Dans le cas d'une construction de librairie, le module AR n'est pas visible (enfin, pas situé dans la même portion de code), il est donc possible de simuler un véritable petit DSL comme par exemple dans la portion de code suivante:
  1. class Weapons < Tables
  2.   set_table_name :weapons
  3.   set_table_fields :id, :cost, :stats
  4.   set_default_value :default_data
  5. end


Conclusion
J'achève ici cette sommaire présentation de certains concepts liés à la métaprogrammation. Il s'agit de méthodes qui peuvent être très amusantes à rédiger. Cependant, il ne s'agit pas d'une approche très orientée "concepts précis" mais plutôt d'une explication générale sur certains concepts relatifs à Ruby pour la construction orienté métaprogrammation. Ce genre de techniques sont généralement utilisées pour le développement de librairies. Je doute qu'il soit réellement nécéssaire de déployer ce genre de méthode dans des petites applications.
Je vous remercie d'avoir lu mon article.
Michaël.

jeudi 1 décembre 2011

VX Ace, la preview

Une première démonstration de VX.Ace vient d'être diffusée (merci à Maws (via http://www.rpg-maker.fr) et Kayser (http://www.koruldia.info) pour avoir proposé rapidement un lien sur leurs forums respectifs.


La première chose qu'on remarque, c'est que les utilisateurs de Rpg Maker VX ne seront pas trop perdus car l'interface semble reprendre les mêmes grandes lignes que son grand frère. Cependant, quelques petits ajouts sont visibles :


Il est maintenant possible de définir les zones de combats manuellement sans s'embêter avec des zones par variables./ Possibilité de mapper directement les ombres sans devoir jouer avec les ombres automatiques (ce qui était foncièrement ennuyant) et le logiciel intègre un charamaker très limité mais qui permet tout de même de gagner beaucoup de temps.

Les zones de combats
Comme nous l'avons dit précédemment, VX.Ace intègre la définition des zones de combats manuellement.
Cette nouveauté est, selon nous, vraiment excellente car elle permet de doser de manière plus minutieuse les fréquence est les zones de combat. De plus, la manière de procéder semble assez intuitive.

Les ombrages manuels
Son prédécesseur avait de sérieux soucis pour les ombrages, (dynamiques et souvent contraignant), VX.Ace règle le soucis en admettant une manière de place des sombres de manière rapide et manuelle.


Il est donc possible de positionner ses ombres comme le dernier des abrutis de manière précise sans être encombré par l'excès d'automatisation.

Le Charamaker
Ne nous voilons pas le face, cet outil ne remplacera pas les créations minutieuses et amoureuse du maker, cependant, il peut réellement faire gagner du temps dans les jeux (pour le moment RTPLike, il faudra tester une version non-bridée pour en dire plus) mais qui permettra de faire gagner du temps pour la réalisation de NPC à l'importance discutable.



Une nouvelle expérience de mapping
Les limitations de tileset faisaient souvent passer VX comme étant la bête noire du mapping (même si certains se débrouillaient très bien avec comme Nusenism), dans VX.Ace, il est possible de relier une map à un jeu de ressources, donc les limitations sont cassées sans devoir passer par un script complexe et peu ergonomique, de plus, VX.Ace admet la notion de direction dans la passabilité (comme sur XP) donc il devient facile de créer des échelles, escaliers, etc.

Améliorations complémentaires
Les nouveautés de RMVX.Ace ne se limite pas qu'à ses trois utilitaires, on notera un RGSS3 qui semble beaucoup plus structuré que l'ancien (La gestion des fichier séparée, plus de variable globale pour les scènes, un main qui repose sur une notion de jeu qui se lance, donc une seule ligne :) ) bref, de quoi rendre heureux beaucoup de scripteurs.
Ajout de commandes événementielles, avancée dans la manipulation de panorama, modification rapide de l'écran titre et possibilité d'afficher un le titre du jeu sur l'image, apparition des  fonds de combats, personnalisation avancée du système de base, gestion native de la chenille, ajout de dynamisme dans les boites de dialogues et pleins d'autres choses.

Ajouts complémentaires
Les découvertes sont progressives:
  • monos nous annonce que les slots de sauvegarde sont passé à 16 (contre 4)
  • Il ajoute que le compteur d'image par de 20 à 100 (arme à double tranchants, pensons au moteur graphique...)

Conclusion
Nous n'avons pas présenté toutes les nouveautés de VX.Ace car nous ne sommes pas à l'aise avec le japonais :),  cependant, cette version promet d'être beaucoup mieux que l'ancienne et de casser un peu les préjugés qui font que RMVX est souvent, à tord, considéré comme le moins bon de la lignée. Vivement la version Anglaise ainsi qu'une hypothétique traduction.
Le seul petit point noir est la non compatibilité avec les anciens projets VX, ce qui est totalement logique étant donnée que le logiciel change radicalement.
Cette brève ne reprend hélas pas toutes les nouveautés, cependant un débriefing plus complet sera surement rédigé lors de la sortie d'une version Anglaise.

Nuki & S4suk3

mercredi 31 août 2011

Gagner du temps lors de la création d'interfaces

Bonjour à tous, c'est avec beaucoup d'émotion que je poste sur funky work pour la première fois. On m'appelle Scriptopathe, Scripto pour les intimes.

Au menu aujourd'hui...


Gagner du temps lors de la création d'interfaces... (oui oui j'insiste dessus !)
La création d'interfaces graphiques est toujours long, fastidieux et ennuyant lorsqu'on a beaucoup de contrôles à disposer pour modifier une structure de données comportant des éléments standards (nombres, strings, booléens, et autres objets composés de ces mêmes éléments).
Il faut créer l'objet contenant ces données, puis l'interface qui le modifie.

C'est pour simplifier et raccourcir ce dur labeur, que j'ai pensé à réunir les 2 choses dans le même objet.

Oh mon Dieu, il réunit les données et l'interface, quel désastre, on ne lui a jamais appris à programmer !

En fait, ce n'est pas vraiment une réunion totale des deux. Le principe est de donner des attributs simples aux données qui définiront la manière de les afficher.

Avant de poursuivre...
Lors de cet article, j'utiliserai Python comme langage de programmation, et wxPython comme toolkit graphique.  Cependant, la technique peut sans difficulté être portée vers une autre librairie et un autre langage (supportant la réflexion pour plus de facilité).

Le principe en lui-même !
L'idée est très simple : on va, dans l'objet contenant les données, donner de simples informations sur la manière de présenter ces données.
  • l'attribution d'attributs de représentation de données en lien avec les attributs affichables/modifiables d'un objet (un peu redondant dans la formulation j'en conviens)
  • La création de l'interface à partir des données récupérées : l'afficheur / modificateur de données.

Les attributs de représentation
Ce sont eux qui vont indiquer à l'interface ce qu'elle doit présenter à l'utilisateur. L'idéal est de créer une classe de base commune à tous les types, et une classe héritée de celle-ci pour chaque type d'élément affichable.
Les éléments de base à rendre disponibles par l'interface sont le nom de l'attribut et de l'objet parent (pour que l'interface puisse elle même modifier les données), et la priorité d'affichage.

Petit truc pythonique
En python, il est très aisé de produire un mécanisme permettant d'utiliser des mots clefs pour les arguments du constructeur de ces attributs, afin d'avoir une meilleure lisibilité, et de permettre d'omettre certains paramètres afin de leur assigner une valeur par défaut.
Exemple :
class DataRepresentation:
    def __init__(self, arg1=0, arg2=""):
         # Traitement

class StringDataRepresentation:
    def __init__(self, label="",  **kwargs)
        # Avec ça, les arguments spécifiques à DataRepresentation lui seront passés.
        # S'il y en a trop, ou qu'ils ne correspondent pas à ceux attendus, des exceptions seront levées.
        DataRepresentation.__init__(self, **kwargs)
        # Traitement...

 
Un raccourci de raccourci....
Pour que tous les attributs de données soient accessibles de la même manière par l'interface, créer une fonction simple qui les attribue elle-même selon une certaine règle est la bienvenue.
Le principe de cette fonction est simple :
  • Elle prend en arguments l'objet parent de l'attribut, le nom de l'attribut, l'attribut de données à lui associer.
  • Elle ajoute un attribut à l'objet parent, correspondant à l'attribut de données qui lui a été passé en argument. Le nom de cet attribut peut être celui de l'attribut qui lui est associé, suivi de "__data_attribute".
  • Elle peut faire tout le pré-processing que vous désirez faire :)

Des infos pour les attributs à la masse...
C'est bien joli de dire qu'on va donner des informations à destination de l'interface, qui varient en fonction de l'objet à représenter, mais, qu'est-ce qu'on lui dit exactement ?
Déjà, pour certains types de données, on peut définir quel contrôle utiliser pour la représenter. Par exemple, pour un int, on peut utiliser un SpinCtrl, afin de le modifier manuellement, ou bien un Choice, afin de choisir entre des propositions correspondant chacune à un numéro...
Et puis, des informations peuvent s'avérer nécessaires, par exemple, pour le cas précédent, quelles chaines affiche-t-on pour quelles valeurs de l'int ?

Utiliser les attributs de représentation pour créer l'interface
Les attributs de représentation peuvent alors être disponibles pour l'interface, via simple passage en argument de l'objet.
L'afficheur de données se contente de :
  • Lister les attributs d'un objet (au début celui passé en argument, puis ceux qu'il contient si c'est le cas !), et en extraire ceux qui sont affichables, c'est à dire, ceux qui ont des attributs de représentation.
  • Pour chacun de ses attributs, vérifier son type et celui de son attribut de représentation, et de placer des contrôles en conséquence.
C'est aussi simple que ça en théorie, et ça permet d'afficher n'importe quel objet.

Modifier les données
Il est certain que le but d'une telle interface n'est pas seulement de visualiser des données, mais aussi de les modifier ! Une fonction de l'afficheur / modificateur d'interface pourrait permettre d'affecter les données dans les champs à l'objet de données, lorsque l'utilisateur cliquerait sur un bouton tel que 'OK'.
L'algorithme récupérant les données est simple, néanmoins, il lui faut certaines informations supplémentaires, qu'il serait bon de sauvegarder quelque part. Ces informations sont des tuples (controle, nom de l'attribut, objet parent), qui peuvent être créés lors de la création du contrôle.
A partir de ces information, il suffit de faire une itération sur chaque tuple, de récupérer et convertir la valeur contenue dans chaque champ, et de l'affecter à l'attribut de l'objet parent, d'où l'utilité de connaître le nom de l'attribut et son objet parent...
En python, cela se fait simplement via :
setattr(obj_parent, nom_attribut, valeur)

Bilan
Cette solution peut être utile lorsque beaucoup d'informations simples et hétérogènes sont à afficher.
Elle est simple à mettre en place, et permet d'économiser du temps en créant en un seul coup un moyen de stocker/afficher/modifier des données !


Code source (data_modifier.py) :
http://nuki.music-all.be/past/index.php?page=Sources&id=1
Ce script n'a pas à être utilisé tel quel, mais sert de support à l'article.  Il doit servir d'exemple pour illustrer la théorie, mais il y a diverses manières spécifiques d'implémenter le concept.

Exemple du progrès :
Créer un interface se résumera alors à faire cela (si on prend le script posté plus haut):
  1. # -*- coding: latin-1 -*-
  2. from wx import *
  3. from data_modifier import *
  4. class Obj2:
  5.     def __init__(self):
  6.         # Création de 10 attributs booléens
  7.         for i in range(12):
  8.             bool = i % 2 == 0 and True or False
  9.             setattr(self, "Attr"+str(i), bool)
  10.             data(self, "Attr"+str(i), BoolDataRepresentation("Attr "+str(i+1)))
  11. class Obj:
  12.     def __init__(self):
  13.         self.Attr1 = 5
  14.         data(self, "Attr1", IntDataRepresentation(label="Attribut 1", type=CHOICE, choices=["Choix 1", "Choice 2", "Choix 3", "Choice 4", "OMG TRO BI1", "Funky work Roxxx"]))
  15.         self.Attr2 = False
  16.         data(self, "Attr2", BoolDataRepresentation(label="Attribut 2", type=RADIO_BUTTON))
  17.         self.Attr3 = True
  18.         data(self, "Attr3", BoolDataRepresentation(label="A la fin hohoho"), 15)
  19.         self.Attr4 = "Rapide oh pinaise"
  20.         data(self, "Attr4", StringDataRepresentation(label="Attribut 4"))
  21.         self.Attr5 = 125.4
  22.         data(self, "Attr5", IntDataRepresentation(label="Un float", is_float=True))
  23.         self.Attr6 = Obj2()
  24.         data(self, "Attr6", ObjectDataRepresentation(label="Un objet !", orientation=GridOrient(4, 4, 5, 5)))
  25.     def __str__(self):
  26.         return "Obj : " + "Attr1 = " + str(self.Attr1) + " Attr2 = " + str(self.Attr2) + " etc.."
  27. class TestObj:
  28.     def __init__(self):
  29.         self.Dat = [Obj(), Obj(), Obj(), Obj()]#[Obj(), Obj(), Obj(), Obj()]
  30.         data(self, "Dat", ObjectDataRepresentation(label=u"Une liste d'objets tiens", obj_type=Obj))
  31.         self.Dat2 = Obj()
  32.         data(self, "Dat2", ObjectDataRepresentation(label=u"Un objet", obj_type=Obj))
  33. class TestApp(wx.App):
  34.     def OnInit(self):
  35.         frame = Frame(None, -1, "Funky Work")
  36.         dat = TestObj()
  37.         DataModifier(frame, dat)
  38.         frame.Show()
  39.         return True
  40. app = TestApp(redirect=False)
  41. app.MainLoop()



Enjoy !

lundi 15 août 2011

Amusons-nous avec une GBA

Le (ou la, je ne sais pas comment on dit :) ) GameBoy Advance de Nintendo est sincèrement une console qui m'a plu.
Alors qu'a la base, je ne m'intéresse que à la programmation sur PC, je me suis dit, pourquoi ne pas plagier Åvygeil, qui s'intéresse à la programmation sur Mégadrive et me lancer vers une console ?
Grâce à une petite recherche Google, je suis tombé sur didacticiel fort agréable à lire.

Brève explication sur le fonctionnement des modes
La machine permet de manipuler 6 modes graphiques. Je vais les présenter brièvement.
Les modes 0, 1 et 2 sont des modes qui sont dit "de Tiles".
Tiles/Tuiles est un concept familier aux utilisateurs de RPG Maker, et même s'il plait souvent aux graphiste car il évite d'obliger le dessin de centaines map et en permet l'assemblage, sur console cette méthode offre un énorme avantage au niveau de la gestion de la mémoire, car les Tiles sont évidemment plus légers que les panoramas et permettent la composition de map presque infinie.
La répartition mémoire est définie de cette manière:
  • 64ko de mémoire vidéo (utilisé pour ces fameuses tuiles)
  • 32ko pour les sprites
Les fond peuvent utiliser la transparence et il est même possible d'effectuer des rotation sur les modes 1 et 2.
Les modes 3, 4, 5 sont les modes dit Bitmap. Leur répartition mémoire est définie de cette manière:
  • 80ko dédiés à l'écran 
  • 16ko seulement pour les sprites
Contrairement aux idées reçues, les mode7 n'appartient pas vraiment aux modes graphiques de la console. En effet, ils 'agit d'une astuce (couplée, sur superNes, avec une puce, la MarioChip, si je ne me trompe pas.. (FAUX, voir plus bas.)
Qui, au moyen de rotation et de zoom, permet la simulation de la profondeur (comme dans le célèbre MarioKart).
Cependant, nous ne nous étendrons pas sur le sujet.
 
Et bien si je me trompais :) merci Sylvain :
" Le "Mathematical, Argonaut, Rotation & I/O", c'est le nom de code du SuperFX, utilisé dans Starfox et Yoshi's Island. "). Sources

Les Sprites
Comme dans le RGSS et RPGMaker, un Sprite est un simple élément graphique  indépendant, déplaçable et transformable au moyen de zoom rotation et translation.
En général, dans un jeu, chacun des élément mobile est un Sprite.

Sons, touches et sauvegarde
Tout ceci est manipulé au moyen de Registres. C'est relativement simple mais l'article ne porte pas vraiment sur l'utilisation du C++ pour créer un jeu GBA.
Je ne m'attarderai donc pas sur ces points. Cependant, si vous avez des questions sur le développement sur GBA, n'hésitez pas à me contacter, je me ferai une joie de vous répondre.

Ce que nous avons vraiment essayé de faire


Il faut dire ce qui est, je me suis méchamment fait fumer par mes ambitions.
Concrètement, la première idée, après avoir vu que faire un "Hello World" (excusez-moi :) ) était relativement facile, je me suis demande pourquoi ne pas me lancer dans le développement d'un framework GBA qui reprendrait une structure proche de RPGMaker, permettant une éventuelle conversion RM > GBA.
Que de belles idées, et des larmes pleins les yeux tant je me sentais révolutionnaire...

Ce qui n'a pas fonctionné
Globalement, faire un jeu pour GameBoy ne demande pas spécialement plus de difficulté que de faire un jeu sur RPGMaker (difficulté scénaristique,   graphismes etc.) si ce n'est le développement pur de chaque module.
Cependant, la conversion de jeu RM > GBA est vraiment une tâche fastidieuse qui, au final, demanderais énormément d'investissement de la part du Maker pour s'adapter (et oui, la résolution pose soucis, les couleurs, le sons et pleins d'autres facteurs).
C'est pourquoi le projet est abandonné.
Oui, je sais que je suis un peu nul, mais je pense sincèrement que sur le long terme, ce genre de projet est impossible à maintenir. Par contre envisager un framework de développement, sous forme de code OpenSource, pourquoi pas. Mes expérimentation GBAsquienne ne sont pas encore terminée. Je garde bien au chaud quelques idées de mini jeu facilement développable que je pourrais déployer en quelques semaines de travail.

Ce qu'on retient de ce cuisant échec
Même si le projet d'éditeur/convertisseur/etc. (on l'aurait appelé GiX :D) n'a pas fonctionné, ce n'est pas dramatique. Je ne suis pas un grand fan de C/C++ mais je me suis bien amusé.
Et j'invite tout ceux qui se sentent un peu fou à ne pas hésiter à créer des mini-trucs bien rigolo sur cette console. Et pourquoi pas imaginer un projet de RPG avec tous les gentils amis du FunkyWork sur GBA !

Liens en annexes
Voici le didacticiel que j'ai utilisé. Il est vraiment bien fait ! Et se trouve évidemment sur developpez.com

Le mot de la fin
Je vous souhaite à tous une bonne fin de vacances !
Merci pour la lecture.
Bien à vous... MOLOK !

Ps: Si vous avez des questions, n'hésitez pas à les poser... ça y est ! FunkyWork revit !

jeudi 26 mai 2011

Une brève news

Pour annoncer que la rubrique "Publications communautaires" se voit doter d'une nouvelle sous-rubrique: Tutoriel et rédaction, qui relayera toutes les publications que nous publieront ailleurs.
En ce moment il y en a 2 (en plus de l'entrevue de KP). Une axée sur l'approche théorique de la notion d'Objet, l'autre axé sur les concepts théoriques utilisés lors de la création d'un système de combat personnalisé aux moyen de commandes évènementielles offerte par le logiciel. Bonne lecture. NUKI.