Aide POO sur petit projet

ldfaldfa Membre
Bonjours à  tous ...
Bon après quelques mois de re-prise en main de cocoa, cela commence un peu à  s'éclaircir un peu, même si il me reste sans doute encore beaucoup de zones d'ombres et surtout je commence à  comprendre comment est organisée la doc.
J'ai réalisé le projet cacao ainsi que le jeu d'échec, j'en remercie les auteurs et je suis les "stupid app" qui me permettent de faire quelques exercices sympa.

Inspiré par le jeux d'échec, je suis parti sur cette base pour réaliser un jeux de solitaire français.

Maintenant, je voudrais réaliser ce "projet" avec une approche objet. N'ayant aucune expérience dans l'approche objet, je cherche une âme charitable, qui voudrait bien perdre quelque minute pour m'orienter dans cette décomposition du problème.

Petit cahier des charges tout de même.
- Jeux en multi-documents.
- Resizing possible du jeux.
- Historique des coups joués
- Avance/Recul possible
- Sauvegarde/Chargement d'une partie
- Animation du déplacement des billes non déplacées par le joueur (option)

Voila rien de bien compliqué je pense ...
Merci à  tous.

Réponses

  • AliGatorAliGator Membre, Modérateur
    19:20 modifié #2
    Je ne suis pas sûr de bien avoir compris la question, j'imagine que le but est de savoir comment architecturer tout ça ?

    Il faut déjà  commencer par lister les objets dont tu auras besoin. Ils y en a qui vont venir naturellement (car plutôt concrets), d'autres moins (car plutôt abstraits), mais c'est déjà  un bon début.

    Par exemple tu auras besoin de manipuler des objets (et donc de créer des classes) :
    - Joueur
    - Pion
    - Plateau (ensemble de positions de pions)
    - Partie (un Plateau, un joueur, des points, un historique...)
    - Un Coup (pour décrire un mouvement de pion, et demander au plateau si ce coup est possible, etc)
    - Un Historique de coups (qui peut simplement être un NSArray de Coups, mais peut aller plus loin si tu veux pouvoir annuler un coup, etc)

    Ce ne sont que des premières pistes, au fur et à  mesure de ta réflexion sur comment créer le jeu, il y en aura d'autres à  venir, mais déjà  ça donne une idée
  • ldfaldfa Membre
    19:20 modifié #3
    Oui c'est cela ...
    Avec les héritages si il y en a ...
    Les paramètres des classes, les connections entre les objets etc etc ...
    Je sais pas comment cela s'appelle ni comment cela ce represente.... un diagrame de classes ?
    Bon j'ai déja fait quelques tests avec une classe bille qui hérite d'une NSImageView et une classe plateau qui hérite aussi d'une NSImageView, pour vérifier qu'il était possible de confier le re-sizing au système. Mes tests sont concluants.
    Mais j'ai un peu du mal à  voir qui va faire quoi ...
    En tout cas dans ton analyse, le joueur me pose un problème .... sinon pour le reste je pense que cela va ... la Partie etatit sans doute mon NSDocument, par contre le coup je l'avait integré dans le pion (bille).
    Bon je ferai un dessin, quand je trouverai comment representer cela.

    merci Aligator
  • AliGatorAliGator Membre, Modérateur
    19:20 modifié #4
    Oui c'est sous forme d'un diagramme de classes qu'il faut que tu représente ça si tu veux faire ça proprement.

    Mais attention, n'oublie pas le paradigme MVC !!

    Quand je parlais de Plateau, moi je pensais à  un objet du modèle en fait, qui mémorisait un ensemble de Pions (enfin de Billes, si tu veux) avec leur position, et avait les méthodes pour répondre à  la question "est-ce que ce coup est autorisé", etc.
    Pour l'aspect Vue, une seule NSView, qui sait dessiner le plateau et les pions dessus pourra simplement suffire ! Cette classe qu'on peut par exemple appeler BoardView pourra alors avoir un objet Plateau associé, et saura le questionner, via le contrôleur, pour savoir s'il y a un pion sur telle ou telle case, et dessiner en conséquence.

    Donc pour moi ni Bille (ou Pion) ni Plateau ne dérivaient de NSView ;)
  • ldfaldfa Membre
    19:20 modifié #5
    dans 1169301065:

    Pour l'aspect Vue, une seule NSView, qui sait dessiner le plateau et les pions dessus pourra simplement suffire ! Cette classe qu'on peut par exemple appeler BoardView pourra alors avoir un objet Plateau associé, et saura le questionner, via le contrôleur, pour savoir s'il y a un pion sur telle ou telle case, et dessiner en conséquence.

    Donc pour moi ni Bille (ou Pion) ni Plateau ne dérivaient de NSView ;)

    Bon je répond rapidement à  ce point.

    Ok ça c'est ce qui est fait avec le jeu d'échec, sauf que le contrôleur et le modéle ne sont pas séparés le la NSView.
    Je trouve cela pas terrible:
    - Cela oblige à  redessiner l'ensemble des billes (pions) à  chaque déplacement.
    - Il faut rechercher qu'elle bille est sous la souris
    - En cas d'animation, il va pas être facile de jouer pendant l'animation, voir de gerer plusieurs animations. Enfin si mais cela resemble à  une approche classique.
    - En cas de redimenssionement de la fenêtre .... je pense qu'il faut tout retracer voir recalculer

    Si je sépare plateau et billes ... (37 billes dans le solitaire français):
    - j'ai pas besoin de rechercher qu'elle bille est sous la souris (le systeme s'en charge)
    - Le déplacement est géré par l'objet lui même
    - Pas besoin de redessiner l'ensemble des billes (le système s'en charge)
    - En cas d'animation, c'est mon objet bille qui se charge de s'animer d'une position x à  une position Y, donc il me semble que je peut déplacer une autre bille, voir animer d'autre billes si necessaire.
    - En cas de redimessionement j'ai rien à  faire (le système s'en charge)

    Et comme je suis fénéant  ;) plus je peus me décharger sur le système, mieux c'est ...

    Voila mon point de vue ....
    Mais je ne demande qu'a etre convaincu.

    Pour le diagramme des classes, on dirait que c'est possible de faire quelque chose sous Xcode.

    Merci.
  • AliGatorAliGator Membre, Modérateur
    janvier 2007 modifié #6
    Je laisse la parole aux autres sur ce point. Moi je dirais que tu peux tout à  faire faire les 2 (un "Plateau" pour le modèle, un plateau sous-classe de NSView pour la vue), mais je ne demande aussi d'autres avis, car je ne sais pas si ma solution est la meilleure, et de plus plus on a d'avis et plus on a matière à  réflexion :)
    Je n'ai pas la prétention d'avoir LA solution, et comme ça fait un bail en plus que je n'ai (à  mon grand regret) pas fait de vrai projet Xcode, je ne suis pas non plus forcément la référence de ce côté là  non plus :D

    Donc attend peut-être d'autres avis ;) En tout cas ce sujet promet d'être intéressant si y'a du monde à  donner son point de vue :)

    [EDIT]Je viens de penser à  un truc : il me semble que ce n'est pas conseillé de superposer des vues directement comme ça... ou alors faut qu'elles soient en subView, et que les vues d'un même "niveau" ne se superposent pas. A voir si tu peux tenir cette contrainte avec la vision des choses que tu avais de faire une NSView pour le plateau et une pour chaque bille...
  • WIMPWIMP Membre
    19:20 modifié #7
    dans 1169315617:

    Ok ça c'est ce qui est fait avec le jeu d'échec, sauf que le contrôleur et le modéle ne sont pas séparés le la NSView.
    Je trouve cela pas terrible:
    - Cela oblige à  redessiner l'ensemble des billes (pions) à  chaque déplacement.
    - Il faut rechercher qu'elle bille est sous la souris
    - En cas d'animation, il va pas être facile de jouer pendant l'animation, voir de gerer plusieurs animations. Enfin si mais cela resemble à  une approche classique.
    - En cas de redimenssionement de la fenêtre .... je pense qu'il faut tout retracer voir recalculer

    Et comme je suis fénéant  ;) plus je peus me décharger sur le système, mieux c'est ...


    Moi aussi je suis fainéant, c'est pour ça que je n'ai pas répondu plus tôt :)

    "Cela oblige à  redessiner l'ensemble des billes (pions) à  chaque déplacement." OK, mais j'ai envie de répondre: Et alors? cela va prendre 1 micro seconde au lieu de 16, pas de quoi gêner l'utilisateur,et l'écriture du drawRect est plus simple.
    J'entends déjà  l'objection: "si tu commences comme ça, quand tu écriras un drawRect compliqué, ça prendra 16 secondes au lieu d'une". Justement pas: dans ce cas je coderais un clipping du rectangle à  redessiner (l'argument de drawRect) pour optimiser. Mais j'ai pour principe de ne me compliquer la vie que quand c'est nécéssaire.

    "Il faut rechercher quelle bille est sous la souris"
    Oui, mais de toute manière il faut bien, quelque soit le NSView, convertir les coordonnées écran de la souris en coordonnées locales du NSView, donc le gain n'est pas évident.

    "le contrôleur et le modèle ne sont pas séparés de la NSView."
    Là  encore même principe: pourquoi utiliser un controleur quand ce n'est pas nécéssaire ?
    Je n'en ai mis un dans le jeu d'Echecs qu'à  la denière étape, pour charger séparément le .nib associé
    au dialogue de promotion.
    Auparavant le besoin ne s'en faisait pas sentir.

    Ceci dit, Il est évident que nous avons chacun notre façon d'approcher les problèmes, et que la mienne est influencée par une expérience de plusieurs années en langages non POO.
    Je pense que l'approche POO est d'autant plus utile, voire indispensable, qu'on programme sur de gros projets, où coopèrent plusieurs programmeurs. L'encapsulation des données et méthodes dans les objets est alors un gage de fiabilité.
    De même, un des objectifs du paradigme MVC est de permettre l'interchangeabilité des classes entre plusieurs projets, et leur réutilisation.
    Ce sont des objectifs louables, mais auxquels rien n'oblige à  se plier quand on commence à  programmer en solo.
    Ne pas oublier non plus que multiplier les classes et relier le tout par un controleur peut amener à  plus de clarté, mais cela alourdit le projet par l'obligation d'utiliser des accesseurs, et rend le design moins flexible ensuite.

    Accessoirement, je n'ai pas compris le role de l'animation.

    En espérant que ça t'aidera à  créer ton architecture de projet 


  • ldfaldfa Membre
    19:20 modifié #8
    dans 1169390809:


    "Cela oblige à  redessiner l'ensemble des billes (pions) à  chaque déplacement." OK, mais j'ai envie de répondre: Et alors? cela va prendre 1 micro seconde au lieu de 16, pas de quoi gêner l'utilisateur,et l'écriture du drawRect est plus simple.

    C'est pas pour un problème de temps .... j'uste un problème d'écriture de code  :)
    Dans le cas d'une NSView par bille, je gére le déplacement de la bille dans le  - (void)mouseDragged:(NSEvent*)anEvent pour qu'elle suive la souris et c'est tout.
    Le système fait le reste. Je n'utiise même pas drawRect (NSImagView).
    Maintenenant, si on prend le parametre temps en compte, evidement, une NSView par bille c'est pas ce qui va rendre l'ensemble des plus réactif .... mais c'est juste pour l'exercice.


    "Il faut rechercher quelle bille est sous la souris"
    Oui, mais de toute manière il faut bien, quelque soit le NSView, convertir les coordonnées écran de la souris en coordonnées locales du NSView, donc le gain n'est pas évident.

    Oui tout à  fait, mais toujours dans la même idée, si je peux utiliser le système pour trouver qu'elle bille est sous ma souris ... je l'utilise, et utiliser une NSView par bille me le permet.


    "le contrôleur et le modèle ne sont pas séparés de la NSView."
    Là  encore même principe: pourquoi utiliser un controleur quand ce n'est pas nécéssaire ?
    Je n'en ai mis un dans le jeu d'Echecs qu'à  la denière étape, pour charger séparément le .nib associé
    au dialogue de promotion.
    Auparavant le besoin ne s'en faisait pas sentir.

    Alors la, tu as parfaitement raison. Il est bien évident que pourquoi faire compliqué lorsque l'on peut faire simple....
    Mais c'est juste un exercice, un peu comme si je te disais pourquoi tu as fais un jeu d'echec alors qu'il en existe des dizaines ....
    Peu importe le projet, ou la taille du projet. Le but est d'en faire un exercice, et la ... c'est la POO, et comme je suis comme toi influencée par une expérience de plusieurs années en langages non POO .... j'ai vraiment du mal avec ces notions, en en plus j'arrive pas à  integere le modèle MVC dans le multi-documentd.



    Ce sont des objectifs louables, mais auxquels rien n'oblige à  se plier quand on commence à  programmer en solo.
    Ne pas oublier non plus que multiplier les classes et relier le tout par un controleur peut amener à  plus de clarté, mais cela alourdit le projet par l'obligation d'utiliser des accesseurs, et rend le design moins flexible ensuite.

    Voila, c'est un objectif louable  ;) merci  :)
    Et comme, en fait je rebondis sur ton projet, car c'est parfaitement applicable au jeu d'échec, j'accepte bien volontier un coup de main de ta part pour cette décomposition objet.


    Accessoirement, je n'ai pas compris le role de l'animation.

    Simple,
    -lorsque tu déplaces un pion avec un coup invalide, tu le reposisionnes brutalement à  sa position de départ.
    -lorsque tu prends un piéce, elle disparait.
    -lorsque tu rejoues la partie, tu à  une succesion d'apparition/disparition de pieces.

    Pour éviter cela, une petite animation de la pièce de sa position de départ -> arrivée me semble un exercice interessant.

    Merci
  • BruBru Membre
    janvier 2007 modifié #9
    Pour ajouter de l'eau au moulin de la POO, tu pourrais envisager une gestion de l'affichage à  l'identique de ce qui se fait sous cocoa pour certains contrôles (NSTableView, NSMatrix, etc...) :
    - tu n'as qu'un NSView (celui chargé d'afficher le jeu complet : plateau + billes),
    - chaque bille est un NSCell dessiné dans le NSView au dessus du plateau.

    La position des billes sera mémorisée par un objet de type data-source (que le NSView plateau interrogerait au moment d'afficher les billes), et un mécanisme de delegate permettra d'interroger l'objet chargé de la validation d'un mouvement (par exemple).

    Cette approche est conforme au paradigme MVC, et est programmatiquement parlant très apple.

    De plus, l'avantage est aussi de s'affranchir de la lourde gestion de multiples NSViews, et de 100% maà®triser l'affichage des billes sur le plateau (puisque cet affichage t'est dévolu). Pour ce dernier point, tu ne subiras plus le problème des NSView frères qui se chevauchent (évoqué par AliGator quelques posts plus hauts).

    Par contre, cela va notablement complexifier la gestion de l'affichage (le drawRect: du NSView plateau
    devant prendre en charge l'affichage des NSCell billes), ainsi que la gestion des événements souris clic et drag&drop (événements reçus sur le NSView plateau, mais à  dispatcher ensuite aux NSCell billes).

    Mais après tout, c'est un exercice de style, non ?

    .

  • ldfaldfa Membre
    19:20 modifié #10
    dans 1169460402:

    Pour ajouter de l'eau au moulin de la POO, tu pourrais envisager une gestion de l'affichage à  l'identique de ce qui se fait sous cocoa pour certains contrôles (NSTableView, NSMatrix, etc...) :
    - tu n'as qu'un NSView (celui chargé d'afficher le jeu complet : plateau + billes),
    - chaque bille est un NSCell dessiné dans le NSView au dessus du plateau.

    La position des billes sera mémorisée par un objet de type data-source (que le NSView plateau interrogerait au moment d'afficher les billes), et un mécanisme de delegate permettra d'interroger l'objet chargé de la validation d'un mouvement (par exemple).

    Cette approche est conforme au paradigme MVC, et est programmatiquement parlant très apple.

    Oui j'avais déja pensé utiliser cette approche car elle avait l'avantage (il me semble) de ne plus avoir à  faire de conversion des cordonnées de la vue en coordonnée du jeux. Les NSCell étant fixent, on a directement la conversion en coordonée du jeux.


    De plus, l'avantage est aussi de s'affranchir de la lourde gestion de multiples NSViews, et de 100% maà®triser l'affichage des billes sur le plateau (puisque cet affichage t'est dévolu). Pour ce dernier point, tu ne subiras plus le problème des NSView frères qui se chevauchent (évoqué par AliGator quelques posts plus hauts).

    Cela semble fonctionner pas trop mal, je joins mes tests (pas tapper  :'( )


    Par contre, cela va notablement complexifier la gestion de l'affichage (le drawRect: du NSView plateau
    devant prendre en charge l'affichage des NSCell billes), ainsi que la gestion des événements souris clic et drag&drop (événements reçus sur le NSView plateau, mais à  dispatcher ensuite aux NSCell billes).

    Mais après tout, c'est un exercice de style, non ?

    Par contre, je reste perplexe pour la partie animation des billes ....

    Mais oui tout à  fait, c'est un exercice de style ... et finalement, NSCell ou NSView ne touche que la partie Vue du modéle MVC ... et j'ai plus de mal avec le reste de la décomposition MVC + multi-document. Une aide ?  ;)

    Merci Bru


    [Fichier joint supprimé par l'administrateur]
  • BruBru Membre
    19:20 modifié #11
    dans 1169463208:

    et j'ai plus de mal avec le reste de la décomposition MVC + multi-document. Une aide ?  ;)


    Je te propose un schéma.
    Ce n'est qu'une des possibilités (il y en a d'autres).

    Ici, le concept de partie correspond au document. Il peut donc y avoir plusieurs partie dans une même session (ou application).
    Le M de MVC s'occupe de stocker la position des billes sur le plateau de la partie en cours, et gère les régles du jeu.
    Le C correspond au delegate (qui va interroger les règles de M pour savoir si un coup est permis, si la partie est terminée -plus de coup possibles- ou gagnée), et au data-source (qui interroge M pour connaitre la position de chaque bille).
    Le V affiche le plateau et les billes en sollicitant C, et à  chaque événement utilisateur (ou automatique -timer-), interroge le delegate de C pour savoir ce qu'il faut faire au niveau affichage.

    .

    [Fichier joint supprimé par l'administrateur]
  • ldfaldfa Membre
    19:20 modifié #12
    Merci Bru pour ton aide, je vais essayer de faire avec.
    Bon de toute façon je reste pour le moment sur mon idée de départ, une NSView par bille.
    Je travaille pour le moment sur la classe bille, et j'ai une petit detail que je voudrais améliorer ...:
    Je voudrais que mon objet bille (NSView) ne se drag pas sur les parties transparentes!.
    Bon j'essaye de regarder la couleur du point sous la souris avec
    [self lockFocus];
    NSColor *couleur = NSReadPixel(location);
    [self unlockFocus];
    Mais j'obtient visiblement la couleur de la superview contenant la bille.

    Quelles solutions ?
    - pour obtenir la couleur ....
    - pour éviter le drag sur une partie transparente

    Merci
  • BruBru Membre
    19:20 modifié #13
    dans 1169712732:

    Je travaille pour le moment sur la classe bille, et j'ai une petit detail que je voudrais améliorer ...:
    Je voudrais que mon objet bille (NSView) ne se drag pas sur les parties transparentes!.
    [...]
    Quelles solutions ?
    [...]
    - pour éviter le drag sur une partie transparente


    La manière simple et rapide : puisque ta bille est ronde et se dessine complètement dans le NSView (donc seuls les coins ne sont pas recouverts par le dessin), il te suffit de construire un NSBezierPath rond (méthode bezierPathWithOvalInRect: de NSBezierPath avec en paramètre le bounds du NSView de la bille) de même dimension que la bille, puis de faire un test de clic de souris dans ce bezierPath au début du drag (méthode containsPoint: de NSBezierPath avec en paramètre le point de la souris en coords locales provenant du NSEvent qui a généré le drag).

    .
  • ldfaldfa Membre
    19:20 modifié #14
    Ok c'est une méthode 
    J'avais dans l'idée d'une technique similaire, mais avec l'équation du cercle !!!! c'est éffectivement plus judicieux.

    Bon si je veux que ma classe soit pas trop spécifique, et contienne plutôt des pièces que des billes  ;)
    Il me faut donc la couleur du point de l'image de ma vue qui est dans une NSImage....
    Mais je trouve pas ....

    Merci Bru
  • elfelf Membre
    19:20 modifié #15
    Je te conseil de ne pas utiliser de bezier path etc. pour dessiner tes billes. Utilise une image plutôt, png transparent. Ok, ça prens de la place inutile, mais avant de lancer ton jeu, tu voudras surement le rendre joli, dans ce cas il te suffit de changer l'image, plutôt que changer du code que tu as écrit il y a 3 mois.
  • ldfaldfa Membre
    19:20 modifié #16
    Merci elf pour ton conseil
    Bon j'ai finalement trouver la solution ...
    NSArray *imageRepArray = [pieceImage représentations];
    Qui me donne le Bitmap de mon image
    NSColor *couleur = [pieceBitmapImageRep colorAtX:currentLoc.x y:currentLoc.y];

    2 problèmes tout de même :(
    - J'ai une tableau (imageRepArray) de représentation et je sais pas comment récupérer l'objet de classe NSBitmapImageRep  de mon tableau. Dans mes tests j'ai remarqué que j'avais seulement cet objet... dans pas de problème, mais dans le cas général ????
    - Lorsque l'on resize, c'est un peu plus compliqué pour faire correspondre les coordonnées, car la NSView et la NSImage n'ont plus les mêmes dimensions. J'ai remarqué que la méthode convertPoint:location fromView:nil me donnait une conversion par rapport au Bounds de la NSView  . J'ai donc surchargé - (void)resizeWithOldSuperviewSize:(NSSize)oldBoundsSize pour maintenir le Bounds constant à  la taille d'origine de l'image et cela fonctionne .... quelles sont les problèmes que peuvent poser cette technique ?

    Merci
  • AliGatorAliGator Membre, Modérateur
    19:20 modifié #17
    Utilise plutôt TIFFRepresentation, quitte à  récupérer ensuite le bitmapData de cette NSImageRep. Au moins tu sais quelle représentation tu manipules derrière
  • ldfaldfa Membre
    19:20 modifié #18
    dans 1170063764:

    Utilise plutôt TIFFRepresentation, quitte à  récupérer ensuite le bitmapData de cette NSImageRep. Au moins tu sais quelle représentation tu manipules derrière


    Merci AliGator
    Si j'ai bien compris:
    NSData * imageData =  [pieceImage TIFFRepresentation];
    pieceBitmapImageRep = [[NSBitmapImageRep imageRepWithData:imageData] retain];

    Ok cela fonctionne et contourne le problème  ;)
    Bon cela veut sans doute dire qu'il n'y a pas de méthode pour récupérer les objets de classe 'x' d'un tableau... il faut donc coder.

    Merci
Connectez-vous ou Inscrivez-vous pour répondre.