Probleme avec les pointeurs intelligents

Le problème exposé dans ce sujet a été résolu.

Bonjour a tous Je bosse actuellement sur un petit jeu (Github) Cependant j'ai un probleme lors de la construction de mes entites dans le constructeur de Game j'appelle le constructeur de Entity (commanter dans le code pour des tests) et j'ai remarquer qu'il donnait un "segmantation fault" du aux pointeurs std::shared_ptr<HandleEvent> et je comprends pas pourquoi Si quelqu'un de gentil pourrait m'eclairer sur mon probleme je lui en serait reconnaisant :) Au passage j'accepte toutes les critiquent constructives Bonne journee

Salut,

Dans les choses étranges, tu inclus memory dans game.cpp mais tu ne l'utilises que dans le .hpp.

Sinon, en débogant ton code, j'ai trouvé dans Entity.cpp que :

  • une entité était créée avec le constructeur par défaut à l'adresse this=0x7fffffffe010 (chez moi)
    • son pointeur d'HandleEvent est initialisé sur 0xb544a0
  • celle-ci est copiée vers this=0xcd59a0 et prend bien le pointeur du dessus
  • enfin, la fonction qui provoque le crash est appelée sur this=0x7fffffffe460 et son Event handler est 0

Si tu as trouvé ça avec le débogueur, tu peux te rendre compte que c'est la création de l'entité qui est dans le constructeur de Game.cpp qui a provoqué les premiers appels, et que le troisième est celui de Player. Player n'a donc pas été initialisé. Soit, décommentons la ligne dans le constructeur qui initialise Player, maintenant gdb nous sort :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Program received signal SIGSEGV, Segmentation fault.
0x00000000004028e2 in __gnu_cxx::__atomic_add (__mem=0x4066e8 <__libc_csu_init+8>, __val=1) at /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.2.1/../../../../include/c++/6.2.1/ext/atomicity.h:53
53    { __atomic_fetch_add(__mem, __val, __ATOMIC_ACQ_REL); }
(gdb) bt
#0  0x00000000004028e2 in __gnu_cxx::__atomic_add (__mem=0x4066e8 <__libc_csu_init+8>, __val=1) at /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.2.1/../../../../include/c++/6.2.1/ext/atomicity.h:53
#1  0x0000000000402899 in __gnu_cxx::__atomic_add_dispatch (__mem=0x4066e8 <__libc_csu_init+8>, __val=1) at /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.2.1/../../../../include/c++/6.2.1/ext/atomicity.h:96
#2  0x0000000000402d9e in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_add_ref_copy (this=0x4066e0 <__libc_csu_init>) at /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.2.1/../../../../include/c++/6.2.1/bits/shared_ptr_base.h:134
#3  0x0000000000402d58 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count (this=0x7fffffffe198, __r=...) at /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.2.1/../../../../include/c++/6.2.1/bits/shared_ptr_base.h:669
#4  0x0000000000402d1a in std::__shared_ptr<HandleEvent, (__gnu_cxx::_Lock_policy)2>::__shared_ptr (this=0x7fffffffe190) at /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.2.1/../../../../include/c++/6.2.1/bits/shared_ptr_base.h:926
#5  0x00000000004029d8 in std::shared_ptr<HandleEvent>::shared_ptr (this=0x7fffffffe190) at /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.2.1/../../../../include/c++/6.2.1/bits/shared_ptr.h:107
#6  0x00000000004031c1 in Game::Game (this=0x7fffffffe250) at Game.cpp:7
#7  0x00000000004063f8 in main () at main.cpp:5

beaucoup d'insultes pour dire que ton pointeur intelligent est initialisé après ton objet, or tu essaies d'incrémenter ton compteur de référence sur rien en le passant à Player. Tu as donc deux solutions :

  • Déplacer les déclarations-initialisations des shared_ptr de Game.hpp au dessus de m_Player;
  • Déplacer les déclarations des shared_ptr de Game.hpp au dessus de m_Player et les initialiser dans le constructeur de Game.hpp

Je te conseille d'utiliser la seconde méthode, qui te permettra de te rendre compte plus facilement de l'ordre dans lequel seront initialisés tes variables, et de réserver ainsi l'initialisation dans le .hpp pour les objets plus simple où tu n'as éventuellement pas de constructeur.

+1 -0

Merci pour ces réponses J'ai tester ta solution et elle fonctionne unidan merci.Et plus important j'ai compris pourquoi :)

Cela me parait logique que les entités répondent aussi aux événements comme pour la gestion des entrées. Pourquoi tu as des suggestions? (je suis ouvert aux autres points de vue ;)

+0 -0

Je pense que Ksass`Peuk fait référence aux rôles que tu fais jouer à entité : En plus de tout ce qu'on peut attendre qu'elle soit, elle récupère et traite les événements (en le déléguant à HandleEvent).

Après en soi ça marche bien tant qu'on reste sur de petites architectures, mais tu perds et éparpilles ta gestion des évènements en faisant ça.

Cela me parait logique que les entités répondent aussi aux événements comme pour la gestion des entrées. Pourquoi tu as des suggestions? (je suis ouvert aux autres points de vue ;)

Papy_Redstone

Tes entités sont une modélisation d'un point de vue logique des éléments du jeu. Actuellement, tu n'as aucun moyen de changer la gestion des événements du jeu autrement qu'en allant modifier cette classe "Entity". Donc niveau découplage ça coince un peu. La partie logique de ton jeu (donc ne comprenant pas l'affichage, ni la gestion des événements) devrait pouvoir fonctionner indépendamment du reste. Par exemple, je devrais pouvoir créer une interface en mode console de ton programme sans avoir à toucher une seule ligne dans la partie logique (donc par exemple la classe Entity).

Typiquement, on a plutôt envie d'avoir un ensemble de fonctions de commandes sur les entités (probablement restreint, mais va savoir …). Et d'avoir une classe pour justement lier ces commandes aux événements. Cela aura notamment l'effet positif de pouvoir binder très facilement les événements à des actions, voir même à de multiples actions pour un même événement.

Dans l'idée, quelque chose comme :

1
2
3
4
5
6
class EventHandler{
public:
  void onClick(std::function<void(int,int)> action);
  void onKeyPressed(std::function<void(Key k)> action);
  //...
};

Après, tu peux également regarder les systèmes signal/slot qui sont particulièrement adaptés pour de tels schémas.

Papy_Redstone: ça dépend, rien ne t'empêche de garder l'état dans onKeyPressed, pas plus que dans ton architecture à toi.

Le système réellement utilisé en pratique dans une bonne majorité des jeux est de rajouter une couche d'abstraction au dessus de tes inputs matériels pour les convertir en ordre, puis de lier ces ordres à des actions dans le jeu.

Par exemple, tu as un premier système qui va convertir tes flèches en GO_FORWARD, GO_BACKWARD, ou en GO(direction), puis les autres couches de ton jeu qui vont interpréter ces ordres et déplacer l'entité qui représente le joueur en conséquence.

Le désavantage, c'est que comme toute architecture elle demande plus de travail de mise en place. L'avantage, c'est que maintenant n'importe quel support d'input peut servir à bouger ton personnage, y compris le réseau, ou la lecture dans un fichier par exemple.

Un exemple minimaliste :

 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
32
33
34
35
36
37
38
39
40
#include <functional>
#include <iostream>
#include <SFML/Graphics.hpp>

class KeyEventHandler{
public:
  void resolve(sf::Event event){
    if(event.type == sf::Event::KeyPressed)
      for(auto a : actions) a();
  }

  void onKeyPressed(std::function<void()> action){
    actions.push_back(action);
  }

private:
  std::vector<std::function<void()>> actions;
};

void foo(){ std::cout<<"foo()"<<std::endl; }

int main(){
  sf::RenderWindow window(sf::VideoMode(200, 200), "SFML works!");
  KeyEventHandler keh;
  keh.onKeyPressed(foo);
  keh.onKeyPressed([](){ std::cout<<"lambda()"<<std::endl; });

  while (window.isOpen()){
    sf::Event event;
    while (window.pollEvent(event)){
      keh.resolve(event);
      if (event.type == sf::Event::Closed)
        window.close();
    }
    window.clear(); 
    window.display();
  }

  return 0;
}

En restant appuyé, tu verras que les actions sont bien appelées en continu.

Il y a quelque chose que je comprends pas normalement un evenement n'est déclencher que quand on l'effectue l'evenement (par ex un evenement est déclencher quand j'appuie sue une touche)

mais ici cela fonctionne est je vois pas d'ou cela ca vient dans ton code Donc je me demande d’où ça vient

Est ce que c'est comme pour une texte quand on reste appuyer ca rentre plusieurs caracteres?

Donc c'est l'os qui gère ça?

+0 -0

Bonsoir,

En général, c'est une très mauvaise idée de se fier sur la répétition des touches dans un jeu. C'est un paramètre qui dépend de l'OS, et donc il sera facile de « tricher » en configurant la vitesse de répétition au max.

D'une façon encore plus générale, on évite de traiter un évènement directement dans la boucle des évènements du système, c'est beaucoup mieux de le faire dans un autre thread (la boucle du jeu). Ainsi on est certain que le jeu s'exécute à la même vitesse partout, quelle que soit ta config et tes FPS.

Lors du keydown, on marque quelque part le fait que telle touche est appuyée, et lors du keyup, on enlève cette marque. La boucle des évènements ne fait que des choses très simples et donc elle est fluide. Si tu fais autre chose, genre déplacer ton personnage, ah mais oui mais après il faut que j'actualise l'affichage, puis checker si je ne touche pas un ennemi, et puis encore envoyer ça sur le réseau pour dire à tout le monde que j'ai bougé… et c'est comme ça que le jeu finit par ramer ou faire des trucs bizarres du style passer à travers les murs alors que les vérifications d'obstacles sont pourtant blindées (je l'ai expérimenté à mes débuts, c'est hyper frustrant et en plus c'est une source de bug assez difficile à découvrir). Non, il faut décoreller tout ça. Dans la boucle de jeu, c'est là que tu vas vérifier: si la touche flèche droite est marquée comme actuellement appuyée, alors on déplace le perso d'une case à droite et on test s'il est sur un mur; et on ne fait rien d'autre. L'affichage c'est dans une autre boucle encore.

Je mettrais bien ça dans le top 10 des erreurs classiques du débutant en développement de jeux vidéo.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte