Débutant en programmation c++ j'ai suivit le cour d'OC jusqu'au chapitre sur l'introduction à Qt ainsi que le cours de Gbdivers puis j'ai réalisé un petit jeux console.
Maintenant j'aimerais créer un jeux en 2d (pas de moteur de jeux *voir pourquoi en bas) mais après avoir lu quelques sujets sur le forum j'ai quelques questions avant de me lancer:
Pour commencer j'aimerais développer un jeux similaire à épée et sandale 2 mais à ma sauce
1. SFML SLD ? ( dans ce que j'ai lu la SFML était plus recommandée mais la SDL serait plus compète mais moi avec mon petit projet j'ai vraiment besoin d'avoir une librairie archi complète ? )
2. Ce qui est OpenGL , Ogre ou Irrlicht c'est pour la 3d ?
3.J'ai aussi vu des topics qui parle de l'ECS je me suis un peut renseigné mais y a t-il une utilité pour moi d'utiliser cette architecture ?
4. Si vous avez un cours ou de la documentation à me proposer ou des conseils
*Concernant mon choix pour la non utilisation d'un moteur de jeux, je rentre en L1 informatique en septembre si j'obtiens mon Bac et la partie du programme sur la programmation est fortement axé sur le c++ donc le but de ce projet est plus d'apprendre que de sortir un jeux 100% fonctionnel.
.pour une lib 2d en c++ cela sera plus SFML qui est elle -même écrite en c++.
.2) je pense que c'est plutôt pour les optimisations et de meilleur rendu, et non tu peut aussi l'utiliser dans la 2d.
.3) Aucune idée, si tu trouve un moyen plus simple, alors utilise ce moyen au lieu de l'ECS. Car c'est vachement complexe à implémenter et long à comprendre le fonctionnement. il faut lire beaucoup de documentation.
4)Si tu choisit la SFML alors le site officiel de la SFML. VOIR ICI
Dernier conseil: ne commence pas par un jeu complexe mais par des jeux simples comme pong, snake, pacman, tetris ...et augmente le niveaux à chaque nouveaux projet. Car si tu t’attaque de suite à ton jeux, ( je ne connais pas trop, j'irais voir après ce message ) tu risque d'être bloqué et être déssus de ce que tu ne pourra pas faire et tu risque d'abandonner très vite sans avoir de résultat. alors que si tu suit mon conseille, tu progressera et tu aura vite des résultats.
par curiosité : Le jeu que tu as fait en console, c'est quoi ? il est complexe ?
En fait ci je connais ce jeu, ( sauf le nom ). C'est quand même un jeu complexe, tu ne peux pas démarrer sur ça et réussir du premier coup ( Sauf si tu es un génie ) de plus il faut savoir qu'il y a surement eu plusieurs personnes sur le développement.
- Edité par blackProgrammeur 31 mars 2016 à 13:17:51
Etre conscient que l'on est ignorant est un grand pas vers le savoir. [Benjamin Disraeli]
1. SFML SLD ? ( dans ce que j'ai lu la SFML était plus recommandée mais la SDL serait plus compète mais moi avec mon petit projet j'ai vraiment besoin d'avoir une librairie archi complète ? )
SFML est à la base conçue pour C++, après certaines personne ont laissé entendre qu'il y avait des bugs parfois dedans que le développeur de la bibliothèque n'est pas en mesure de corriger pour le moment. A mon sens, cela est marginal et ne devrait pas rentrer en ligne de compte pour un débutant qui ne tombera probablement pas sur ce genre de bugs;
SDL2 est une bibliothèque à l'origine conçue pour le C. L'utiliser en C++ nécessite un travail préalable d'encapsulation (de wrapping) pour rendre son usage conforme au RAII, travail qui n'est pas en soit difficile mais qui est fastidieux et casse bonbon.
Brun0 a écrit:
2. Ce qui est OpenGL , Ogre ou Irrlicht c'est pour la 3d ?
Simplification un peu rapide mais oui. OpenGL est un composant bas niveau pour dialoguer avec ton driver graphique de manière à ne pas avoir à se soucier de la gueule et de l'évolution du-dit driver.
Ogre et Irrlicht sont plutôt des moteurs minimaux et donc plus haut niveau.
Brun0 a écrit:
3 .J'ai aussi vu des topics qui parle de l'ECS je me suis un peut renseigné mais y a t-il une utilité pour moi d'utiliser cette architecture ?
L'ECS est fait pour répondre à un besoin : manier des entités potentiellement nombreuses ayant des comportements très divers pouvant évoluer dynamiquement. Il n'y a pas de nécessité en soit d'utiliser un tel design, ce qu'il faut se rendre compte c'est que le design à base de hiérarchie de classe présenté dans le cours d'OC est typiquement ce qu'il ne faut pas faire car ce n'est pas flexible du tout et très chiant à faire évoluer.
1. SFML SLD ? ( dans ce que j'ai lu la SFML était plus recommandée mais la SDL serait plus compète mais moi avec mon petit projet j'ai vraiment besoin d'avoir une librairie archi complète ? )
SFML. Tu peux aussi utiliser SDL à condition que tu crées une surcouche pour que SDL soit RAII conforme en C++. Tu as aussi Allegro "soit-disant plus complète", mais c'est aussi du C, si tu souhaite l'utiliser, tu devras aussi créer une surcouche C++.
Brun0 a écrit:
2. Ce qui est OpenGL , Ogre ou Irrlicht c'est pour la 3d ?
Oui. OpenGL est une API très bas-niveau qui te permet d'afficher des choses très simples avec beaucoup de lignes de code. Ogre et Irrlitch sont des moteurs graphiques "surcouche" d'OpenGL.
Brun0 a écrit:
3.J'ai aussi vu des topics qui parle de l'ECS je me suis un peut renseigné mais y a t-il une utilité pour moi d'utiliser cette architecture ?
heu ... Je suis un débutant en ECS, mais de ce que j'ai compris, l'approche Orienté Objet va rapidement te montrer ses limites. Néanmoins, elle reste la meilleur solution quand tu as très peux d’entités
Brun0 a écrit:
4. Si vous avez un cours ou de la documentation à me proposer ou des conseils
J'avais coder le reste depuis un autre pc il faudrait que je mette à jour les fichiers quand j'aurais accès au pc.
Et tu as raison je pense que commencer par un jeux simple qui a servit d'exemple me permettra d'avoir de quoi comparer mon code et demander conseil.
Ksass`Peuk a écrit:
Brun0 a écrit:
3 .J'ai aussi vu des topics qui parle de l'ECS je me suis un peut renseigné mais y a t-il une utilité pour moi d'utiliser cette architecture ?
L'ECS est fait pour répondre à un besoin : manier des entités potentiellement nombreuses ayant des comportements très divers pouvant évoluer dynamiquement. Il n'y a pas de nécessité en soit d'utiliser un tel design, ce qu'il faut se rendre compte c'est que le design à base de hiérarchie de classe présenté dans le cours d'OC est typiquement ce qu'il ne faut pas faire car ce n'est pas flexible du tout et très chiant à faire évoluer.
D'accord , aurais tu un lien avec un design correct ?
Et sinon j'ai trouvé sa cela pourrait-être pas mal comme première approche pour l'introduction à la SFML https://www.youtube.com/watch?v=eyjSXgOm5Fg
CITATION :Brun0 C'est une évolution de l'exemple du cours du site du zero , tu créer un gladiateur qui vas tour par tour combattre contre un gladiateur adverse
En ayant vu ton code sur Github, sans doute devrais-tu rester encore un peu en console.
Il y à quelques erreurs comme des problème de constructeur par copie sur une classe qui ne devrais pas en avoir.
Ton code est peu découpé et pourrais être encore plus court et généraliste.
Quelques pistes d'apprentissage:
- Les templates (Pour avoir des méthodes plus générique tu vas en avoir besoin plus tard)
- Les pointeurs *intelligents*
- La move semantics
- Tests Unitaires (Pour t'assurer du bon fonctionnement d'un module de ton programme) <Optionnel mais intéressant>
Ces petits points sont je dirais essentiels lorsque tu fait du c++ moderne.
Ksass`Peuk pourra le confirmé vu son nombre de messages sur le sujet
Merci de soulever cela , je vais étudier sa en même temps que je me lance dans mon projet et si sa coince vraiment je retourner faire du programme console
Je pense que c'est pas forcément la peine que tu retourne complètement en console, Tu peux très bien faire de la 2d en même temps que la console. Dans la console, tu expérimente, apprend. Et en 2d tu utilise ce que tu as appris en console. Mais cela ne sers en aucun cas de s'acharner sur la console. Le plus importent c'est de bien assimiler les concept et notions, c'est pour cela qu'il faut beaucoup lire et se documenter. De plus en restant en console tu auras certainement des résultats qui ne te satisferont pas ( même si ont peut faire des trucs sympa ) et tu auras l'impression de rester sur place. Alors que si tu commence par un projet simple qui te plait tu auras plus de chance d'arriver à ton but et tu apprendra de nouvelle choses et tu auras un résultat qui te satisfera. Alors commence par un pong, se sera déjà un grand pas vers la réalisation de ton projet.
Merci de soulever cela , je vais étudier sa en même temps que je me lance dans mon projet et si sa coince vraiment je retourner faire du programme console
cela coincera forcément, c'est obligé mais il ne faudra pas abandonner dés qu'il y aura une difficulté. Ne te dit pas chaque jour à tient je passe à la SFML, à tient y à un truc que je connais pas alors maintenant que de la console pour me perfectionner. Cela ne sers à rien de faire cela juste à faire perdre du temps ( c'est ce que j'ai fais pendant longtemps ). Fixe toi un projet simple et fait tout pour le réaliser et ainsi tu progressera.
Ceci était mon avis personnel.
Pour le pong c'est pas bien compliqué, si tu as besoins d'aide, je serais ravi de t'aider sur le forum.
- Edité par blackProgrammeur 31 mars 2016 à 18:47:16
Etre conscient que l'on est ignorant est un grand pas vers le savoir. [Benjamin Disraeli]
@Brun0 : comme l'a dit @necros211, tu as un problème avec les sémantiques de classes. Go lire plus profondément le cours de @gbdivers ou encore la FaQ de développez.com.
Sinon, console ou 2D, ça n'a aucune importance. La partie logique de ton jeu doit fonctionner seule et son affichage en mode console, ou 2D ou 3D, ou whatever doit être simple comme bonjour à changer une fois le module d'affichage développé.
D'accord je voyais la console et le 2d comme deux opposés , @blackProgrammeur j'aime bien l'idée du pong je vais partir dessus.
Ksass`Peuk a écrit:
@Brun0 : comme l'a dit @necros211, tu as un problème avec les sémantiques de classes. Go lire plus profondément le cours de @gbdivers ou encore la FaQ de développez.com.
d'accord mais aurais-tu un exemple concret ? j'ai appris via OC et je pense avoir appris un mauvais résonnement en plus des notions qu'il me manque comme les Accesseurs et Modificateur que je vais aller étudier
1) services de bases qu'on est en droit d'attendre d'une classe. Cette sémantique, forme, est appelée la forme canonique orthodoxe de Coplien. Elle permet au minimum de fournir les méthodes suivantes:
constructeur par défaut
constructeur de copie
opérateur d'affectation
destructeur
le constructeur par défaut est le constructeur que tu peux appeler sans paramètres ou avec des paramètres par défaut pour instancier un objet de la classe cible qui aura ici sémantique de valeurs.
/** Prototype d'un constructeur par defaut */
NameClass( short number = 0 );
/** Utilisation */
NameClass;
/** OU avec un paramètre */
NameClass( 5 );
Note l’absence de parenthèse pour appeler sans paramètre, si tu met des parenthèses c'est considéré comme un appel de fonction. Donc, quand tu créer une classe si tu ne précise pas une autre sémantique explicitement alors par défaut toutes les classe que tu construira auront sémantique de valeur.
Avec cette sémantique il n'y as aucun problème pour avoir 2 éléments identiques contrairement à la sémantique d'entité ( voir au bas ). Par exemple 2 nombres ou 2 voitures peuvent sans problèmes être identiques. Au contraire 2 personnes seront toujours différentes, chaque personne est unique donc ont utilisera plutôt une sémantique d'entité pour les représenter. sauf peut être pour des jumeaux ( à vérifier ).
Ces classes ayant cette sémantique peuvent donc être comparer par égalité, plus petit, plus grand .... en fonction des services qu'on attend de notre classe.
2) Ces types de classe ne permettent pas les services suivants :
constructeur par copie
l'affectation
On interdit la copie pour ne pas nous retrouver avec 2 éléments identiques ce qui ne serrait par réaliste.( voir exemple Perso). Cette sémantique est idéal pour l'héritage public.
Voilà j'ai plus ou moins abordé certaines notions mais pas toutes. pour plus d’info sur les sémantiques. c'est -----> ICI
- Edité par blackProgrammeur 31 mars 2016 à 20:41:42
Etre conscient que l'on est ignorant est un grand pas vers le savoir. [Benjamin Disraeli]
Concernant le pong j'ai commencé à lire la doc de la sfml , j'ai codé des rond rectangle... je me suis amusé a changer la taille , la couleur , déplacer des objets. Je commence a me familiariser avec la base de la librairie. Quand le projet commencera a ressembler a quelques chose je ferais un github pour pouvoir linker facilement le code.
Cependant quand j'ai commencé a penser le jeux sur feuille gros problème je ne sais pas comment découpé mon programme , je voit plusieurs possibilités mais elles ont tous leurs avantages et défauts. Je m'explique dans le cas du pong premièrement je doit créer deux barres verticales donc j'imagine plusieurs approches:
1. la moins bien selon moi à exclure : directement dans le main
//Création de la barre gauche
sf::RectangleShape leftBar;
leftBar.setSize(sf::Vector2f(x, x));
leftBar.setPosition(sf::Vector2f(x, x));
//Création de la barre droite
sf::RectangleShape rightBar;
rightBar.setSize(sf::Vector2f(x, x));
rightBar.setPosition(sf::Vector2f(x, x));
2. simples et propre mais pas trop évolutif : création d'une fonction dans un autre fichier
sf::RectangleShape LeftBar(int posX, int posY, int width, int height) {
sf::RectangleShape leftBar(sf::Vector2f(width, height));
leftBar.setPosition(posX, posY);
return leftBar;
}
sf::RectangleShape RightBar(int posX, int posY, int width, int height) {
sf::RectangleShape rightBar(sf::Vector2f(width, height));
rightBar.setPosition(posX, posY);
return rightBar;
}
3. Plus compliqué mais évolutif: je créer une classe Bar qui hérite de RectangleShape , puis je créer LeftBar et RightBar qui hérite de Bar avec chacun leur particularité et puis je continu en créant une classe move qui hérite de LeftBar pour les déplacements du joueurs et une classe ia qui hérite de RightBar pour la gestion de l'ordinateur. ( je vais pas mettre tout l'exemple du code vous m'avez compris sans je pense )
class Bar : public sf::RectangleShape {
};
class LeftBar : public Bar {
};
class RightBar : public Bar {
};
Le seul hic ici c'est que je ne connait pas ce que contient la classe RectangleShape et sa m'énerve
4. Manuel peut-être la meilleur façon mais elle me fait peur : Même schéma que le 3 mais la classe Bar est la classe parent je fait tout moi même pas d'héritage de RectangleShape mais bon a quoi sert la sfml alors
Je suis désolé pour la torture mental de ceux qui vont répondre car sa doit être naturelle pour vous , c'est normal que je me pose ce genre de questions ou j'ai rater une étape de l'apprentissage ?
Commences par oublier la partie graphique. Je le répète : ton jeu doit être fonctionnel sans la partie graphique (même si atrocement lent).
Une fois que tu as la partie logique du jeu, tout ce que dois faire la partie graphique c'est observer l'état des éléments qu'on doit afficher et les reproduire à l'écran. Et c'est tout. De la même manière, tout ce que dois faire la partie contrôle, c'est écouter les événements venant du clavier/de la souris, et les relayer vers la partie logique du jeu. Et c'est tout.
Donc commence par concevoir la partie logique (qui dans le cas d'un pong ne contient pas grand chose).
edit : je viens d'écrire une 40 de lignes et j'ai actualisé la page, sa c'est fort. enfin bref donc je recommence.
pour la partie non graphique de ton jeu, tu peut déjà réfléchir aux éléments minimum dont tu as besoin.
Les paddles
La balle
La detection des collisions
Update pour mettre à jour la logique du jeu suite à un déplacement, une collision ...
Pour la balle tu doit te poser la question quels service je souhaite attendre de sa par pour réussir à l'implémenter correctement. Une idée serait de la faire dérivée de cercle ( sf::circleShape ) mais en fait ce n'est pas vraiment une bonne idée. ( pour ma part, car c'est contestable ). Car pour moi une balle possède un invariant : l'origine de la balle est toujours son centre. Et c'est justement cet invariant qui empêchera l'héritage. Car si elle en hérite alors elle possédera la méthode setRadius() et si ont l'utilise et bien on se retrouve avec une balle qui ne respecte pas son invariant imposé; car setRadius() de la classe cercle, ne fera pas le nécessaire pour respecter l'invariant. De même pour deplacer la balle avec move, on voudrait la déplacer selon une vitesse ( celle de la balle ) et pas n’importe laquelle. Ainsi voici une implémentation possible d'une telle classe. ( surement pas la meilleur mais c'est fonctionnelle ).
/** Permet de créer des balles,
*
* @invariant l'origine de la balle est toujours son centre
*/
class Ball : private NoCopyable
{
sf::CircleShape m_circle;
sf::Vector2f m_speed;
sf::SoundBuffer m_buffer;
sf::Sound m_sound; //pour joueur du son lors d'un rebond, c'est plus cool
sf::RenderWindow& m_window; // la fenetre de dessin pour ne pas la passer en param à chaque fois..
void chargeSound( std::string const& path );
void respectInvariant(); //Comme je l'est dit, assure le respect de l'invariant.
public:
Ball( sf::RenderWindow& window, float radius, sf::Vector2f const& speed, std::string const& path );
void inverseX();
void inverseY();
void changeSound( std::string const& path );
void playSound();
void setPosition( float x, float y );
sf::Vector2f const& getPosition()const;
void setFillColor( sf::Color color );
sf::FloatRect getGlobalBounds()const;
void init();
float getRadius()const;
bool isDirectionLeft()const;
bool isDirectionRight()const;
sf::Vector2f const& getOrigin()const;
void move( float deltaTimes );
void draw();
//... encore d'autres.
};
bien sur tu peut apporter des amélioration comme la redimensionner, changement du contour de la forme, la couleur du contour.. mais tous cela en respectant l'invariant imposé.
Maintenant, pour les paddles, Tu fait de même, pour moi encore une fois l'origine est le centre du paddle ( un simple rectangle ). Ensuite quant tu auras ton paddle de base avec si possible la méthode move en virtuel pur. tu peut faire dériver Player( qui est un paddle jouable ) et IA qui est un paddle qui se deplace automatiquement selon la balle. Le rôle principal de ces 2 classes qui dérivents de PAddle( base ) est d'implémenter la méthode move à leurs sauce. c'est à dire Player avec la gestion des entrées clavier et IA avec un mode send et un mode Receive. en mode send il se replace au centre. Pour les modes ne pas chercher à se casser la tête, c'est juste 2 booléens qui changent d'état selon la position de la balle et en fonction du mode, jouer avec les déplacements. :
/** Permet de créer des paddles simples
* cette classe est pas instaciable.
*
* @invariant l'origine du paddle est toujours
* son centre
*/
class Paddle : private NoCopyable
{
protected:
sf::RenderWindow& m_window;
sf::RectangleShape m_rectangle;
float m_speed;
CAMP m_camp;
void respectInvariant();
public:
Paddle( sf::RenderWindow& window, sf::Vector2f const& size, float speed, CAMP camp );
void setPosition( float x, float y );
sf::Vector2f const& getPosition()const;
void setFillColor( sf::Color color );
sf::FloatRect getGlobalBounds()const;
void init();
sf::Vector2f const& getSize()const;
sf::Vector2f const& getOrigin()const;
virtual void move( float deltaTimes ) = 0;
void draw();
CAMP getCamp()const;
}
/** Permet de gerer le clavier
*/
struct Input
{
inline bool upPressed()const { return sf::Keyboard::isKeyPressed( m_up ); }
inline bool downPressed()const { return sf::Keyboard::isKeyPressed( m_down ); }
inline void changeKeyUp( sf::Keyboard::Key up ) { m_up = up; }
inline void changeKeyDown( sf::Keyboard::Key down ) { m_down = down; }
private:
sf::Keyboard::Key m_up;
sf::Keyboard::Key m_down;
};
/** Permet de créer des paddles joueur controlable avec
* le clavier
*/
class Player : public Paddle
{
Input m_input;
public:
Player( sf::RenderWindow& window, sf::Vector2f const& size, float speed, CAMP camp );
void move( float deltaTimes )override;
};
/** Permet la création de paddle
* intelligent qui se deplacent tous seule selon
* la position de la balle
*/
class AI : public Paddle
{
Ball const& m_ball;
bool m_modeReceive;
bool m_modeSend;
public:
AI( sf::RenderWindow& window, sf::Vector2f const& size, float speed, CAMP camp, Ball const& ball );
void move( float deltaTimes )override;
};
Ensuite le reste est assez simple, DetectCollision est un ensemble de méthode qui retourne true si il y a collision entre deséléments et update à une seule réel méthode qui prend comme param " deltaTimes " pour ainsi déplacer les éléments ( paddles, balle ) et après les déplacement vérifie les collisions et agit si il y à collision en replacent l'objet.
Au final cela s'organise comme ceci dans Update:
DEPLACE LES ELEMENTS ---> TEST LES COLLISIONS ET REAGIT ---> DESSINE TOUS LES ELEMENTS.
et update se trouve dans la boucle principal de ton programme. le while( window.isOpen() ). du coup tu vérifie aussi les événements comme fermeture de la fenêtre demandé.
ainsi tu auras certainement un main comme cela :
int main()
{
sf::Vector2u sizeWindow( 1000, 600 );
sf::Vector2f sizePaddle( 20, 100 );
float speedBall( 300 );
float speedPaddle( 400 );
/** CREATION DE LA FENETRE */
sf::RenderWindow window( sf::VideoMode( sizeWindow.x, sizeWindow.y ), "PONG", sf::Style::Titlebar | sf::Style::Close );
window.setPosition( sf::Vector2i( 200, 10 ) );
window.setMouseCursorVisible( false );
sf::Event event;
/** CREATION DE LA BALLE DE JEU */
Ball ball( window, 10, sf::Vector2f( speedBall, speedBall ), "ressources/ball.wav" );
ball.init();
ball.setFillColor( sf::Color::Green );
/** CREATION DU PADDLE */
AI paddle( window, sizePaddle, speedPaddle, CAMP::LEFT, ball );
paddle.setFillColor( sf::Color::Blue );
paddle.init();
/** CREATION DU PADDLE IA */
AI ai( window, sizePaddle, speedPaddle, CAMP::RIGHT, ball );
ai.setFillColor( sf::Color::Red );
ai.init();
/** CREATION DE UPDATE POUR LA MISE A JOUR
* DES ELEMENTS
*/
Update update( paddle, ai, ball, sizeWindow );
/** CREATION DU TEMPS */
sf::Clock clock;
while( window.isOpen() )
{
while( window.pollEvent( event ) )
{
if( event.type == sf::Event::Closed )
window.close();
}
update.start( clock.restart().asSeconds() ); // start est la méthode principal d'on je parlais
window.clear( sf::Color::Black );
ball.draw();
paddle.draw();
ai.draw();
window.display();
}
return 0;
}
Voila, si tu n'as pas tout compris je réexpliquerais car je suis allais un peu vite à cause de mon problème.
- Edité par blackProgrammeur 1 avril 2016 à 15:34:38
Etre conscient que l'on est ignorant est un grand pas vers le savoir. [Benjamin Disraeli]
@blackProgrammeur : justement en terme de conception là, c'est un peu dommage. Si demain je veux changer mon affichage et ne plus le faire avec SFML mais SDL, ou encore si je veux le faire en 3D, ou whatever ... Je suis bien embêté : parce que le code qui s'occupe de la partie purement logique du jeu est mélangé avec le code d'affichage. On ne respecte pas le SRP .
blackProgrammeur a écrit:
Une idée serait de la faire dérivée de cercle ( sf::circleShape ) mais en fait ce n'est pas vraiment une bonne idée. ( pour ma part, car c'est contestable ). Car pour moi une balle possède un invariant : l'origine de la balle est toujours son centre. Et c'est justement cet invariant qui empêchera l'héritage. Car si elle en hérite alors elle possédera la méthode setRadius() et si ont l'utilise et bien on se retrouve avec une balle qui ne respecte pas son invariant imposé.
Attention, tu prends le LSP à l'envers. Si on fait hériter Ball de sf:CircleShape, c'est à Ball de respecter les invariants de sf::CircleShape et de ce côté (à supposer que l'on n'exprime pas de besoin contraire) on n'a pas de problèmes : on n'a pas accès aux éléments internes de sf::CircleShape et donc aucun moyen de briser ses invariants.
Concernant ton invariant, il n'est pas tiptop, j'aurais plutôt posé : la balle est toujours sur l'aire de jeu (rayon compris).
Mais cela ne change rien au fait qu'on veut séparer l'affichage de la logique et que cette partie affichage ne devrait pas avoir à se coltiner des questions comme la vitesse ou la direction de la balle.
blackProgrammeur a écrit:
Ensuite quant tu auras ton paddle de base avec si possible la méthode move en virtuel pur. tu peut faire dériver Player( qui est un paddle jouable ) et IA qui est un paddle qui se deplace automatiquement selon la balle. Le rôle principal de ces 2 classes qui dérivents de PAddle( base ) est d'implémenter la méthode move à leurs sauce. c'est à dire Player avec la gestion des entrées clavier et IA avec un mode send et un mode Receive.
Pourquoi ? Il n'y aucune différence entre le paddle du joueur et le paddle de l'IA en terme de code. La seule différence, c'est de savoir qui va appeler "move". Ce qu'on a c'est plutôt deux contrôleur différents capable de dialoguer avec un paddle : une classe IA qui prendra son paddle piloté en entrée et une classe Controller qui prendra aussi un paddle en entrée.
De cette manière, la classe paddle ne s'occupe bien que d'une tâche : bouger en fonction des ordres qu'on lui donne. Et les ordres seront juste reçus de deux classes différentes.
Concernant le protected pour tes variables membres je te conseille la lecture de ce post.
C'est vrai que ta façon de penser est bien supérieur à la mienne. je n'avais pas du tout penser à ces choses là . La balle est toujours sur l'aire de jeu, comment peut tu vérifier cela ?, ou ce que tu veut dire c'est : la balle ne dépasse pas une certaine taille. pour l’héritage, si je remplace par un attribut membre y a plus de problème niveau conception ?
pour les paddles cela me paressais la meilleur solution mais en faite non car pour le déplacement auto j'aurais plûtot implémenté dans move de même pour le déplacement manuelle avec certe des comportements mais comme tu le dit, c'est pas très évolutif et c'est un peu bourrin. du coup cela serait mieux un truc comme ceci :
class Paddle
{
//TRucs ...
}
class AI
{
Paddle& m_paddle;
public:
//constructeur ...
move( float deltaTimes );
}
AI ai ( paddle );
ai.move( Times );
Je vais de suite voir le lien.
suite à la lecture du lien, pour être sûr d'avoir bien compris.
class A
{
private:
short number;
protected:
short getNumber()const;
public:
A();
A( short number );
}
class B : public A
{
private:
short numberOfB;
public:
B();
B( short number );
short numberPlusnumberOfB()const
{
return getNumber() + numberOfB;
}
}
c'est bien cela ? si je n'est aucune raison de permettre à l'utilisateur ( public ) d'avoir accés à number Alors je le permet à la classe fille en déclarant une méthode protected pour uniquement les classes fille. Pour quand même permettre le calcul par la méthode de B : numberPlusnumberOfB. J'avoue que mon exemple est pas top mais je pense que le principe y est présent.
- Edité par blackProgrammeur 1 avril 2016 à 16:32:37
Etre conscient que l'on est ignorant est un grand pas vers le savoir. [Benjamin Disraeli]
Concernant le pong j'ai commencé à lire la doc de la sfml , j'ai codé des rond rectangle... je me suis amusé a changer la taille , la couleur , déplacer des objets. Je commence a me familiariser avec la base de la librairie. Quand le projet commencera a ressembler a quelques chose je ferais un github pour pouvoir linker facilement le code.
Cependant quand j'ai commencé a penser le jeux sur feuille gros problème je ne sais pas comment découpé mon programme , je voit plusieurs possibilités mais elles ont tous leurs avantages et défauts. Je m'explique dans le cas du pong premièrement je doit créer deux barres verticales donc j'imagine plusieurs approches:
1. la moins bien selon moi à exclure : directement dans le main
//Création de la barre gauche
sf::RectangleShape leftBar;
leftBar.setSize(sf::Vector2f(x, x));
leftBar.setPosition(sf::Vector2f(x, x));
//Création de la barre droite
sf::RectangleShape rightBar;
rightBar.setSize(sf::Vector2f(x, x));
rightBar.setPosition(sf::Vector2f(x, x));
2. simples et propre mais pas trop évolutif : création d'une fonction dans un autre fichier
sf::RectangleShape LeftBar(int posX, int posY, int width, int height) {
sf::RectangleShape leftBar(sf::Vector2f(width, height));
leftBar.setPosition(posX, posY);
return leftBar;
}
sf::RectangleShape RightBar(int posX, int posY, int width, int height) {
sf::RectangleShape rightBar(sf::Vector2f(width, height));
rightBar.setPosition(posX, posY);
return rightBar;
}
3. Plus compliqué mais évolutif: je créer une classe Bar qui hérite de RectangleShape , puis je créer LeftBar et RightBar qui hérite de Bar avec chacun leur particularité et puis je continu en créant une classe move qui hérite de LeftBar pour les déplacements du joueurs et une classe ia qui hérite de RightBar pour la gestion de l'ordinateur. ( je vais pas mettre tout l'exemple du code vous m'avez compris sans je pense )
class Bar : public sf::RectangleShape {
};
class LeftBar : public Bar {
};
class RightBar : public Bar {
};
Le seul hic ici c'est que je ne connait pas ce que contient la classe RectangleShape et sa m'énerve
4. Manuel peut-être la meilleur façon mais elle me fait peur : Même schéma que le 3 mais la classe Bar est la classe parent je fait tout moi même pas d'héritage de RectangleShape mais bon a quoi sert la sfml alors
Je suis désolé pour la torture mental de ceux qui vont répondre car sa doit être naturelle pour vous , c'est normal que je me pose ce genre de questions ou j'ai rater une étape de l'apprentissage ?
Je peux te proposer une autre approche: le joueur est affiché comme une barre. Il y a un joueur à droite et un à gauche. Le joueur est une entité: la classe joueur sera donc à sémantique d'entité. Ce qui va le représenter (un rectangle) a sémantique de valeur. 2 rectangles peuvent être identiques. Cependant, c'est le joueur qui va savoir où placer sa raquette en fonction de l'environnement qui lui aura autorisé son mouvement ou non.
Ainsi, la représentation du joueur dépendra de l'environnement graphique. Le joueur en lui-même n'en dépendra pas.
Alors aujourd'hui après m'être cassé le cerveau pendant un bon moment ( au moins 3h ^^) en lisant vos com , de la doc et les exemples de @blackProgrammeur je me suis lancer et j'ai pondu quelques lignes ( l'implémentation des paddles sans ia juste déplaçable) . Cependant étant donné que j'ai plein de choses à voir , apprendre et que j'ai pas envie de me démotiver je ne ferais pas comme @Ksass`Peukme la conseillé en faisant d'abord la partie logique puis la partie graphique je fait les deux en même temps cela me permet d'obtenir un premier résultat qui évolue au fur à mesure.
Par contre je me suis vraiment cassé la tête pour avoir une partie logique indépendante de la partie graphique et j'aimerais ce que tu me dises ce que tu en pense de ce coter la (c'est qu'un début soit indulgent ) . Avant de linker le code je rajoute deux ou trois truc , je me suis procuré SFML Game Development By Example et SFML Essentials ( que je commencerais à lire ce soir) même si je ne suis pas ouf en anglais sa pourras que me faire progresser ( si vous avez des avis , recommandation concernant les livres) et donc maintenant je compte m'attaquer à la balle et les collisions.
Edit: Les floats j'ai pas eu le choix c'est ce que reçoit le vector2f
Edit 2 : ha oué aussi j'ai pas bien compris le système de time (clock) dans tes exemples blackProgrammeur
main.cpp
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <iostream>
#include <string>
#include <cstdlib>
#include "Paddle.h"
int main()
{
//Création de la fenêtre
sf::RenderWindow window;
window.create(sf::VideoMode(640, 480), "Pong");
//Modification de la position de la fenêtre
window.setPosition(sf::Vector2i(150, 50));
//Définition des images par secondes maximal
window.setFramerateLimit(60);
//Création du Paddle de gauche
Paddle lPaddle(10,60,40,210);
sf::RectangleShape leftPaddle(sf::Vector2f(lPaddle.width,lPaddle.height));
leftPaddle.setPosition(sf::Vector2f(lPaddle.getm_x(), lPaddle.getm_y()));
//Création du Paddle de droite
Paddle rPaddle(10, 60, 590, 210);
sf::RectangleShape rightPaddle(sf::Vector2f(rPaddle.width, rPaddle.height));
rightPaddle.setPosition(sf::Vector2f(rPaddle.getm_x(), rPaddle.getm_y()));
//Tant que la fenêtre est ouverte...
while (window.isOpen()) {
sf::Event event;
//Test des évènements
while (window.pollEvent(event)) {
//Si on clique sur fermer
if (event.type == sf::Event::Closed)
//On ferme la fenêtre
window.close();
}
/* Gestion des déplacements clavier */
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up) ) {
leftPaddle.move(0, -lPaddle.speed);
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) {
leftPaddle.move(0, lPaddle.speed);
}
window.clear();
window.draw(leftPaddle);
window.draw(rightPaddle);
window.display();
}
return 0;
}
Bonjour, le système de times et de clocks permet le calcul du temps : voir ici. Je l'utilise pour déplacer mes éléments de la même façon, et cela se nomme un deltaTimes : Voir ici.
Pourquoi les dimensions du paddle sont en Public? . Et surtout pourquoi constant en les initialisant dans le prototype de la classe, alors que tu les demandes en paramètre dans le constructeur. Je pense que ce serrait mieux comme cela :
class Paddle {
public:
//Constructeur
Paddle(const float width, const float height, float m_x, float m_y);
//Vitesse du Paddle
int speed = 2; //pourquoi
float getm_x() const;
float getm_y() const;
protected:
private:
//Position du paddle
float m_x;
float m_y;
//Dimention du paddle
float width; // potentiellement const si tu veut pas les modif
float height;
};
De même pour la vitesse, pourquoi en public?
et utilise une liste d'initialisation dans le constructeur. ton implémentation du constructeur deviens donc :
Paddle::Paddle( float height, float width, float x , float y)
: height( height ), width( width ), x( x ), y( y )
{
}
dans ton constructeur je pense que le const devant les floats est inutile.
Je ne pense pas que c'est une boone facon de procéder mais c'est une idée. Je pense que ton début de paddle est bien et ensuite il faut ajouter un sf::rectangleShape et ainsi la partie logique et graphique est séparé. ( je pense, je ne suis pas vraiment sur de ce qu'a dit Ksass`Peuk ), du coup :
class Paddle {
public:
//Constructeur
Paddle(const float width, const float height, float m_x, float m_y);
//d'autre methode sans se baser sur rectangle
float getm_x() const;
float getm_y() const;
private:
//Position du paddle
float m_x;
float m_y;
//Dimention du paddle
float width; // potentiellement const si tu veut pas les modif
float height;
//vitesse ... autres...
//et enfin rectangle pour la partie graphique
};
En gros je pense que tu créer ta partie logique et enfin ta partie graphique qui se base sur la partie logique. Mais d'un autre côté, je ne sais pas si il faudrait pas créer une nouvelle classe : Graphics qui s'occupe d'afficher les éléments en fonction des données ( partie logique ). Pour cela je pense qu'il faudrait attendre un exemple de la part de KSSAS ' PEUK.
- Edité par blackProgrammeur 2 avril 2016 à 17:36:19
Etre conscient que l'on est ignorant est un grand pas vers le savoir. [Benjamin Disraeli]
Bonjour, le système de times et de clocks permet le calcul du temps : voir ici. Je l'utilise pour déplacer mes éléments de la même façon, et cela se nomme un deltaTimes : Voir ici.
Pourquoi les dimensions du paddle sont en Public? . Et surtout pourquoi constant en les initialisant dans le prototype de la classe, alors que tu les demandes en paramètre dans le constructeur. Je pense que ce serrait mieux comme cela :
- Edité par blackProgrammeur il y a 8 minutes
1.D'accord merci
2. j'ai coder un peut vite je vais aller corriger sa et rajouter leurs accesseurs pour pouvoir les utilisé dans le main , concernant le const j'avais lu que dès qu'on pouvait mettre const il le fallait afin d'éviter de vouloir la modifier par inadvertance par la suite.
3. Je ne comprend pas pourquoi ajouter sf::rectangleShape dans ma classe?
2_ ajouter un const pour un paramètre est plûtot utile dans un passage par référence, c'est à dire un passage ou il n'y as pas de copie de la variable. Cela permet donc d'éviter à la fonction de modifier par inadvertance la variable elle même, c'est donc une sécurité.
void function( short a ); //le param sera copié, a est donc un e nouvelle variable
void function( short& a ); //& = passage par réference, la fonction peut modifier la variable de base.
//ayant une nouvelle étiquette : a
void function( short const& a ); //pareil que précédent sauf que a est non modifiable donc
// aucun changement sur la vrai variable
3_ je ne suis vraiment pas sûr, d'ailleurs même pas du tout mais j'attend un exemple de Kssas' peuk pour mieux comprendre son idée. Après cela ne doit pas te bloquer pour autant. Tu peut très bien réfléchir à la suite de ton programme.
- Edité par blackProgrammeur 2 avril 2016 à 17:44:57
Etre conscient que l'on est ignorant est un grand pas vers le savoir. [Benjamin Disraeli]
Bon voila sa fait maintenant 3 semaines que j'ai commencé le c++ j'ai enfin réussi à faire "jeux" jouable j'ai upload tout sa sur github : https://github.com/Chaanks/First-Pong
Dites moi ce que vous en penser , il manque plein de chose mais je compte bien faire évoluer ce petit pong j'aimerais juste des avis sur la base de mon code pour le corriger et évoluer avec une base correcte (je me doute bien qu'il y pas pas mal de chose qui vont pas aller ^^)
Tu as une classe Paddle qui actuellement ne sert à rien, de même que la classe Ball. Actuellement, ce sont juste deux conteneurs de données jamais modifiés, et les classes ne sont pas faites pour stocker des données, elles sont faites pour rendre un service, et elles peuvent stocker des données POUR rendre ce service.
Si je te dis qu'il faut que tu sépares les deux, ce n'est pas sans raison. Apprend à concevoir correctement maintenant, pendant que c'est facile, cela te permettra de ne pas avoir à l'apprendre plus tard quand ce sera sur de gros projets (donc quand ce sera difficile).
Ce que tu veux, c'est quelque chose comme :
class non_copyable{
non_copyable (non_copyable const&) = delete;
non_copyable& operator=(non_copyable const&) = delete;
public:
non_copyable() = default;
};
class Paddle : private non_copyable{
public:
Paddle(/* params */);
void up();
void down();
unsigned width() const;
unsigned height() const;
unsigned x() const;
unsigned y() const;
};
class Ball : private non_copyable{
public:
Ball(/* params */);
void give_speed(unsigned x, unsigned y);
void invertX();
void invertY();
void update();
unsigned x() const;
unsigned y() const;
unsigned radius() const;
};
class Pong{
public:
enum class Pad { LEFT, RIGHT };
void give_controller (Pad p, Controller& c){
c.control ( (p == Pad::LEFT) ? left : right );
}
void give_paddle_viewer (Pad p, PaddleViewer& pv) const {
pv.show( (p == Pad::LEFT) ? left : right );
}
void give_ball_viewer (BallViewer& bv) const{
bv.show( ball );
}
void update();
private:
Paddle left;
Paddle right;
Ball ball;
};
int main(){
Pong pong { /* params */ };
//c'est pong viewer qui va faire les appels à "give_<truc>_viewer"
Viewer pong_viewer{ pong };
Keyboard_pong_controller( pong_viewer.window(), /*touche haut*/, /*touche bas*/ );
auto must_continue = [&Keyboard_pong_controller](){ Keyboard_pong_controller.must_continue(); };
pong.give_controller(Pong::Pad::LEFT, Keyboard_pong_controller);
IA_controller ia { pong };
pong.give_controller(Pong::Pad::RIGHT, ia);
while(must_continue()){
Keyboard_pong_controller.action();
ia.action();
pong.update();
pong_viewer.update_graphics();
}
}
J'ai volontairement omis beaucoup de choses, le but est juste de donner une idée du fonctionnement et de la séparation.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C
Architecte logiciel - Software craftsmanship convaincu.
Architecte logiciel - Software craftsmanship convaincu.
Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C
Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C
Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C
Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C