W3C

  • Flux RSS des articles

Recherche

Mercredi 24 mai 2006 3 24 05 2006 21:18
Quand on bosse sur RoR, on a deux façon d'utiliser les liens de N vers N . Une confrontation avait plusieurs joueurs et un joueur avait plusieurs participants.
J'avais parlé de la déclaration has_and_belongs_to_many (poétiquement surnommée hbtm) . Cette déclaration donne le droit d'utiliser @joueur.confrontations  et @confrontation.joueurs pour accéder rapidement aux objets liés à l'autre. Niveau productivité c'est excellent.

Toutefois, si par hasard on a besoin de rajouter une valeur ou deux entre les deux objets, hbtm devient moins pertinent. Encore heureux, nos chers développeurs ont pensé à nous. Ils ont créé la déclaration has_many :through => .  Tout est bien expliqué et assorti d'exemples dans le blog de has_many :through   . Je vais vous présenter l'intérêt de cette déclaration, puis coller un peu du code que j'ai mis pour implémenter tout ça.

Son principe est de créer une table d'objets tiers avec les identifiants des deux (ou plus) objets liés. Des colonnes supplémentaires permettent de rajouter des spécificités à ces liens.

Dans mon jeu, puisque le but est de faire un toolkit simple d'utilisation pour l'administrateur, je devais mettre en oeuvre un moyen rapide de rajouter une unité ou un type de terrain. Chaque unité ayant un coût de déplacement différent par type de terrain, il faut également pouvoir aisément modifier ce coût.

J'ai donc créé trois bases : matériaus (non ce n'est pas une faute d'orthographe, rails ne connait pas les pluriels français) descriptions et couts.

create table materiaus (
    id                              int                    not null    auto_increment,
    nom                          varchar(100)    not null,
    couleur_html              char(6)            not null,
    bonus_defense          int                   not null,
    primary key (id)
);

create table descriptions (
    id                           int                    not null    auto_increment,
    nom                       varchar(100)    not null,
    distance_attaque    int                   not null,
    max_vie                 int                    not null,
    max_degats           int                    not null,
    price                      int                    not null,
    primary key (id)
);

create table couts (
    id                         int not null auto_increment,
    description_id       int not null,
    materiau_id          int not null,
    move_on              int not null,
    primary key (id)
);

Tout est dans le "move_on"  cette valeur là correspond pile poil à mon désir. On appelle cela "l'association riche". J'aurais aussi pu mettre un "default 16", mes unités ayant au maximum 15points de déplacement,  celà voudrait dire qu'oublier de remplir la case empécherait tout déplacement. Mais bon, j'ai opté pour ne rien mettre par défaut car de toute manière l'admin devra changer 95% des coûts.  Autant tout simplifier du côté serveur et code, l'utilisateur changera tous les coûts de déplacement mais au passage il les créera (donc pas la peind d'automatiser cela).

La déclaration niveau classes donne :

class Materiau < ActiveRecord::Base
  has_many :dames
  has_many :couts, :dependent => true
  has_many :descriptions, :through =>  :couts
end

class Cout < ActiveRecord::Base
  belongs_to :materiau
  belongs_to :description
end

class Description < ActiveRecord::Base
  has_many :unites
  has_many :couts, :dependent => true
  has_many :materiaus, :through =>  :couts
end

Maintenant, reste à implémenter tout ça correctement. L'utilisateur doit rapidement pouvoir ajouter une description d'unité ou un matériau de terrain et ne pas oublier de remplir les cases coûts quand on rajoute l'un ou l'autre. Et bien, rien de plus simple ! Regardez bien :

Je fais un scaffold sur chaque classe Materiau Description et Cout. Pour les deux premiers, rien est à changer, si ce n'est changer la redirection aprés la création d'une description/d'un matériau, on renvoie sur les couts pour ne pas les oublier !
Un simple :

redirect_to :controller => 'stationservice' ,:action => 'list' 

dans la méthode create, en cas de création réussie fait l'affaire.

Pour les coûts, seuls des changements mineurs sont a effectués : la présentation ordonnée des coûts en tableau à double entrée. Dans la méthode "list" du controlleur correspondant au coûts on rajoute la recherche des descriptions et des matériaus.

@materiaus = Materiau.find(:all, :order => "nom")
@descriptions = Description.find(:all, :order => "nom")

Il ne reste plus qu'à faire le tableau.

<table>
        <!-- on commence par faire une ligne avec les noms des matériaus -->
 <tr><td>&nbsp;</td>
 <% for materiau in @materiaus -%>
 <td><%= materiau.nom -%></td>
 <% end -%>
 </tr>
        <!-- les autres lignes commencent par le nom de l'unité suivi de toutes les valeurs qui vont bien -->
 <% for description in @descriptions -%>
 <tr><td><%= description.nom -%></td>
    <% for materiau in @materiaus -%>
    <td>
        <% if cout=Cout.find_by_materiau_id_and_description_id(materiau.id,description.id) -%>
            <%= link_to cout.move_on , :action => 'edit' , :id => cout %>
        <% else -%>
                    <!-- si la valeur n'est pas présente, on met un lien pour l'inventer -->
            <%= link_to "nouveau" , :action => 'new' , :description_id => description.id , :materiau_id => materiau.id %>
        <% end %>
    </td>
    <% end -%>
 </tr>
 <% end -%>
</table>

Bon, là j'ai un peu fais de la merde car j'ai rajouté des éléments applicatifs dans la vue, et en prime il y a beaucoup d'accès à la base de donnée. J'ai dérogé à ces règles de codage. Je n'ai pas encore trop eu l'idée magique pour changer ça. Le problème vient du fait que rails ne gère pas les clés primaires doubles.
Peut-être un find_couts(:all) bien ordonné devrait faire l'affaire, mais dans ce cas il faudra alourdir le code de création d'unité et de matériaus en créant par défaut les coûts manquant pour qu'il "n'y ait pas de trous".
Enfin, j'y travaillerai mais puisque rajouter une unité ou un type de matériau n'est pas une action que l'on fait à tour de bras, cela ne charge pas trop le serveur.

Dans le cas où le coût n'existe pas, on passe en paramètre la description et le materiau, et on les rajoute à la main dans la déclaration de la méthode "create" :

@cout = Cout.new(params[:cout])
@cout.materiau_id = params[:materiau_id]
@cout.description_id = params[:description_id]

Ceci n'es pas un choix délibéré de faire des tâches de si bas niveau (je comprends qu'en tant que railers on n'y soit pas habitué), c'est que la déclaration has_many :through ne permet pas la "proxy sélection" (ou l'opérateur << qu'on peut utiliser pour la conseur hbtm).

Bien entendu, il reste une chose à faire : virer tout les trucs inutiles comme la méthode "destroy" dans l'échaffaudage créé sur la classe coûts.

Voilà pour cet article. D'ailleurs je remercie aussi les gens du channel irc #rubyonrails.fr sur freenode qui m'ont bien aidé à combler le "through" (bide inside).
Par FrihD - Publié dans : rubyonwars
Ecrire un commentaire - Voir les 3 commentaires - Recommander
Lundi 22 mai 2006 1 22 05 2006 16:44
Afin de comprendre comment est codé le jeu (bientôt je mettrais les fichiers sources quelquepart), il faut comprendre comment je l'ai organisé :

Les joueurs inscrits peuvent jouer contre d'autres joueurs, et faire plusieurs parties à la fois, une partie à l'avenir pourra avoir plus de deux joueurs, il faut séparer les objets "joueur_inscrit", "partie_de_jeu" et "participant_a_une_partie".

Les noms des tables sont donc :

joueurs
confrontations
participants

J'ai évidemment créé les modèles de classes correspondantes.
Pour relier ces tables là, j'utilise les déclarations has_many, belongs_to et has_and_belongs_to_many de la sorte :
  1. Un joueur a plusieurs participants.
  2. Une confrontation a plusieurs participants.
  3. Une confrontation a plusieurs joueurs.
  4. Un joueur a plusieurs participants.
1 et 2 sont deux dépendances 1 vers n (has_many).
3 et 4 forment une dépendance n vers n (has_and_belongs_to_many).

class Confrontation < ActiveRecord::Base
    has_and_belongs_to_many :joueurs
    has_many                :participants
end

class Joueur < ActiveRecord::Base
  has_and_belongs_to_many :confrontations
  has_many :participants
end

class Participant < ActiveRecord::Base
    belongs_to :confrontation
    belongs_to :joueur
end

Du côté mysql, j'ai opté pour un fichier create.sql (méthode utilisée dans le livre ruby on rails).
Les lignes intéressantes sont :

create table participants (
    id                          int             not null    auto_increment,
    joueur_id               int             not null,
    confrontation_id    int             not null,
    constraint  fk_confrontation foreign key (confrontation_id)  references  confrontations(id),
    constraint  fk_joueur        foreign key (joueur_id)         references  joueurs(id),
    primary key (id)
    );

où l'on voit apparaître les lignes "joueur_id" et "confrontation_id"

Ainsi que la table de jointure permettant à rails de faire la passerelle entre les joueurs et les confrontation :

create table confrontations_joueurs (
    confrontation_id    int not null,
    joueur_id               int not null,
    constraint  fk_cp_confrontation foreign key (confrontation_id)  references  confrontations(id),
    constraint  fk_cp_joueur        foreign key (joueur_id)         references  joueurs(id),
    );
Par FrihD - Publié dans : technique
Ecrire un commentaire - Voir les commentaires - Recommander
Lundi 22 mai 2006 1 22 05 2006 16:22
La question du choix du langage de développement du jeu a été assez déterminante. Et je dois, sans mentir, beaucoup à jean-mi qui m'a indiqué RoR. Il faut savoir que mes compétances web étaient le xhtml et le CSS bruts. Je devais pour réaliser mon rêve fantasmatique, apprendre un langage dynamique.
J'avais deux choix en tête : le fameux php et le ruby on rails. J'ai opté pour RoR, les démos sur le site m'ayant séduit.
Le problème de la jeunesse du langage c'est le peu de documentation disponible. Enfin, peu dans le sens où il faut bien chercher, mais en fait tout est là. J'ai acheté le livre "ruby on rails", et j'ai commencé à suivre le fil du bouquin.
Je m'épatais moi-même, j'ai persévéré, et maintenant je me détache du bouquin, et démarre ma propre application web au bout de deux semaines pas trop intensives (je ne connaissais pas non plus MySQL, et j'avoue ne toujours pas connaître grand chose).
La plupart du temps pour les problèmes que j'ai rencontrés, RoR avait une solution simple à me proposer, et les fois où j'ai du réfléchir à plusieurs reprises, la simplicité du langage ruby m'a permis de me dépatouiller.
En gros, je ne regrette pas le moins du monde d'avoir appris RoR, pour php s'aurait peut-être été la même chose, mais pour sûr, moins rapidement.
Par FrihD - Publié dans : rubyonwars
Ecrire un commentaire - Voir les 1 commentaires - Recommander
Lundi 8 mai 2006 1 08 05 2006 18:19
Et bien bonjour à tous. J'ouvre à l'instant ce blog, qui servira de vitrine pendant l'élaboration d'un jeu vidéo par navigateur internet que je code. N'ayant pas encore d'argent à investire dans un serveur ou autre chose, un petit blog sur ce site me semble un bon compromis. En plus il me permet de gagner du temps sur l'installation de Typo et tout plein de choses encore.

Alors je vais vous parler vite fait de "ruby on wars". RubyOnWars sera un jeu de wargame en tour par tour accessible depuis votre navigateur web. Il sera codé grâce au frameworks Ruby On Rails, d'où le nom de mon jeu. Il profitera entre autre des technologies web2.0 AJAX, pour rendre le jeu plus agréable.

J'ai déjà un peu avancé ce jeu, mais pour l'instant il lui manque pas mal de fonctionnalités. Dès lors qu'il sera agréable à jouer, je mettrais le code sous licence libre et les graphismes également. Comme ça, chacun pourra faire son propre serveur, et faire évoluer le jeu.

Voilà ! Bientôt je mettrais un deuxième article, un peu plus détaillé, sur les problèmes que j'ai eu, les questions que je me pose encore, et celles que j'ai résolues.

--FrihD
Par FrihD - Publié dans : rubyonwars
Ecrire un commentaire - Voir les 2 commentaires - Recommander

Recommander

Calendrier

Décembre 2009
L M M J V S D
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
<< < > >>
Créer un blog sur over-blog.com - Contact - C.G.U. - Rémunération en droits d'auteur - Signaler un abus