Bonjour à tous,
Alors depuis quelque temps j'utilise la bibliothèque SFML pour satisfaire ma soif de création ![]()
Je me suis lancé dans un projet didactique qui me permettrait de maîtriser la bibliothèque en question.
Cependant mon programme est lent et en prenant du recul je me demandais si ça ne venait pas :
- ou alors de l'affichage des sprites, et je devrai peut être tout mettre dans un tableau de vertex. Cependant, même en n'affichant pas les entités en question (à l'aide du "draw"), j'ai encore des latences
- ou bien des boucles de collisions. Je veux dire par là que pour programmer la collision, je prends toutes les entités présentes et je mets en place une boucle qui verifiera x fois si l'obstacle numero x est en collision avec une nouvelle boucle qui comporte les entités mobiles (monstres, héros...) (En résumé une boucle dans une boucle). Est ce là la meilleure méthode ? Y'en a t'il pas une moins "gourmande" ?
Voilà, ça serait hyper génial de votre part si vous avez un ou deux conseils à propos ![]()
Comment tu vérifies la collision ? Forme carré/rectangulaire tout le temps ? Fais voir un peu ta fonction ?
C'est normal sinon, perso je faisais comme toi pour les collisions et tout passait crème.
J'ai une petite piste de pourquoi ca ralentit avec et sans draw, je suppose que tu limite les FPS en appliquant un .setFrameRate sur la window non ?
Je crois que cette limitation de FPS, implique un sleep quand tu appel draw, ce qui est normal et bien, afin de ne pas faire tourner ton CPU à 100%.
Mais du coup quand tu ne draw pas, je pense que cette limite n'est pas appliquée et donc ta boucle tourne aussi vite qu'elle peut, ce qui peut poser des soucis de performances.
Donc mes conseils, déjà, oui, vertex array obligé. Tu peux draw toutes tes entités / décors dans des vertexArray, je dis "des" car c'est plus simple d'en avoir plusieurs pour la superposition des couches (typiquement entités au-dessus du décor). Et aussi fait bien gaffe à n'avoir aucune texture de tileset dupliquée.
Ah oui et fait gaffe, c'est une texture (donc un tileset) par vertexArray
Et si ca peut te rassurer, je pense qu'une fois que ton code sera bien pensé (ce qui est le plus important), tu ne devrais pas ressentir de soucis de performances. N'hésite pas à refactor et refactor et refactor jusqu'à avoir un système robuste.
faut faire ta double boucle que sur les éléments affiché à l'écran
tu supprimes bien les éléments qui n'ont plus lieu d'exister ? (balles perdues hors champs...)
Merci de ta réponse.
Alors, pour vérifier la collision, je vérifie d'abord à l'aide d'une hitbox rectangulaire, puis si la condition et respectée, j'utilise le théorème des axes séparateurs vu que j'ai des images en rotation (vue de dessus oblige).
J'utilise effectivement le setframerate de base pour les fenêtres. Donc si je comprends bien, même si je n'affiche pas l'image à l'aide du draw, ça utilise des performances ?
Concernant la collision, en fait, je vais être plus précis. J'ai une classe héros, une classe ennemi, une classe obstacle qui sont héritières d'une classe objet visibles qui regroupe toutes les entités visibles et pouvant bouger. Elles ont toutes une méthode que j'ai appelé "move" qui permet de les déplacer (juste en déplaçant le sprite) qui prend en paramètres un entier x et un entier y. Dans cette méthode, j'ai introduit la boucle qui dit que lorsqu'on demande à déplacer un personnage ou un objet, on vérifie si sa nouvelle position il y a collision avec tous les objets présents (à l'aide de la boucle qui réitère x fois la vérification, x nombre d'obstacle).
J'ai revu mon code et en supprimant la collision avec les objets immobiles (murs etc...), ça va beaucoup mieux. Je pense donc revoir en priorité la boucle de collision.
Sinon j'ai tendance à ne pas utiliser de tableau de vertex. Pour chaque entité (monstres héros, obstacles...), j'utilise une référence vers une variable globale qui est en fait une texture. Est ce une solution convenable ?
Effectivement je supprime les balles hors map. J'ai même fait en sorte de ne pas afficher les sprites quand ils sont en dehors du champ de vue. C'est plus un problème de boucle. En fait j'ai divisé la map en plusieurs cases, qui chacune peuvent comporter un objet que l'on peut poser. La boucle vérifie toutes les cases.
Je pense que tu y gagnerais beaucoup à utiliser les vertexArray pour tes entités. De ce que je me souviens de ma lecture de la doc SFML, dessiner un sprite c'est une opération couteuse, un vertex array regroupant plusieurs shape serait largement plus performant. Tu devrais quand même essayer.
Pour tes textures en global, je ne sais pas si c'est problématique ou non, mais en tout cas la lib nous indique de ne jamais utiliser des objets SFML en global (ou dans un singleton), je ne sais plus pourquoi.
Comment ta méthode moove accède aux reste des entités ?
J'utilise effectivement le setframerate de base pour les fenêtres. Donc si je comprends bien, même si je n'affiche pas l'image à l'aide du draw, ça utilise des performances ?
Oui je crois bien. Enfin si tu draw juste la window sans le reste ca ne devrait pas être le cas.
Afficher plusieurs sprites en même temps avec un vertex array est en effet plus optimisé, mais pour quelques dizaines de sprites affichés en même temps je doute que la différence se ressente vraiment, a moins d'avoir plusieurs centaines de sprites il y a peu de chances que le problème vienne de là.
EDIT: Et afficher un sprite c'est environ aussi couteau qu'afficher un vertex array, basiquement pour l'affichage moins t'appelles la fonction draw par frame plus c'est optimisé, qu'importe ce que tu affiches.
Au passage, commence tot a instrument ton code pour pouvoir profiler rapidement et identifier les bottlenecks et perf kills. A part les choses "evidentes" (structures de donnees sous-optimales, algos naifs, etc.) ca peut souvent etre contre-intuitif.
Je vous remercie pour vos réponses. ![]()
Tout ça me conduit à une nouvelle question : les vertexarrays peuvent aussi être utilisés pour des personnages ? Parce qu'en général ils sont surtout utilisés pour les tilesmap.
Oui sans aucun problème, juste la restriction une texture par vertex array, donc ajuste bien tes tilesets en fonction.
La superposition est très bien gérée aussi, genre avec deux mobs en collision, tu en as un sur l'autre mais t'as pas comme j'imaginais auparavant tout le carré du mob qui écrase l'autre, juste sa partie visuelle, donc ca passe très bien
Ah super ! J'ai trouvé la solution à mon problème. En fait ma collision est gérée par un RectangleShape hitbox présente dans tous les objets de la classe Objet_visible. Et pour vérifier si il y avait collision, je faisais appel à une méthode que j'ai créé getHitbox, or getHitbox renvoie un RectangleShape et pas un pointeur. J'ai changé ça pour qu'il puisse créer un pointeur (j'obtiens donc RectangleShape* hitbox), j'alloue la mémoire et je n'oublie pas le delete au destructeur (petite question passagère, je ne risque pas de fuite de mémoire si je me contente d'un delete au destructeur ?)
En résumé, c'est juste le rectangle shape qui foutait le bazar dans tout ça. C'est pas un peu bizarre, ça veut dire que ça tire énormément sur la performance ?
La copie d'objets devait generer une duplication de donnees enorme. Rien d'etonnant pour des structures de donnees et algos naifs.
Je comprends pas ton truc perso, pourquoi retourner un pointeur et pas une référence ? A quoi ça te sert de faire de l'allocation dynamique ? Pourquoi utiliser un RectangleShape pour représenter une hitbox alors que c'est censé représenter un objet dessinnable (faudrait plutôt utiliser IntRect par exemple) ?
Au passage de ce que j'ai compris de ce que t'as dis (mais j'ai surement mal compris) à chaque appelle de getHitbox tu créé un nouveau pointeur, que tu retourne, si c'est vraiment ça je comprends pas trop la différence niveau perf' entre faire ça et entre copier un objet (sauf si l'objet que tu copiais été en plus créé dans la dite fonction).
Moi aussi j'ai du mal à comprendre. Surtout que si tu fais un simple getHitBox(), te renvoyant une classe sans pointeur, le compilateur optimise en ne faisant pas de copie.
Cf. toutes les questions stackoverflow sur la RVO (jm'en suis tapé pour faire des choix, et justement j'ai du faire ce choix pour les collisions, toutes mes entités renvoyaient une instance de différentes classes permettant d'établir les collisions ou non indépendamment de l'entité concernée, et tout ca en "return by value", et tout s'est très bien passé).
Après vu que c'est une classe SFML, peut-être que y'a des attributs ou des fonctionnements spécifiques qui forcent la copie. Mais ton fix de les faire en allocation dynamique c'est vraiment pas stable car tu fragmentes très très vite ta mémoire dans ton cas (boucle dans boucle).
À mon avis, tu devrais essayer avec une classe à toi, contenant juste des propriétés (genre x, y, width, height, et peut-être rotation si t'as besoin après), et des getters (getLeftX, getRightX ...)
Edit : Lepigeon-888, je pense qu'il cré on-the-fly son objet, d'où l'absence de possibilité d'utiliser une référence. Et personnellement je pense que c'est mieux de ne pas se forcer à avoir un attribut servant uniquement ce genre de situation
Avoir un attribut pour la HitBox c'bien plus pratique, les cas ou tu appellerais getHitBox qu'une seule fois par frame par objet sont je pense assez rare, ce qu'il faudrait c'est que les personnages ne puissent entrer en collision qu'avec le décore, donc pas de projectile, pas d'ennemis qui peuvent te toucher, etc etc.
Nan, avoir un attribut pour la hit box, faire tous tes calculs en utilisant cet attribut, puis juste avant l'affichage update la position du sprite pour qu'elle colle à celle de la hit box c'est bien plus "logique" je trouve, enfin après c'est questions de goûts j'imagine, mais au moins ça évite de créer un même objet plusieurs fois par frame inutilement.
Le 26 juin 2016 à 13:48:45 LEpigeon-888 a écrit :
Avoir un attribut pour la HitBox c'bien plus pratique, les cas ou tu appellerais getHitBox qu'une seule fois par frame par objet sont je pense assez rare, ce qu'il faudrait c'est que les personnages ne puissent entrer en collision qu'avec le décore, donc pas de projectile, pas d'ennemis qui peuvent te toucher, etc etc.
T'as raison la-dessus je n'y avais pas pensé. (Le + de une fois par frame)
Nan, avoir un attribut pour la hit box, faire tous tes calculs en utilisant cet attribut, puis juste avant l'affichage update la position du sprite pour qu'elle colle à celle de la hit box c'est bien plus "logique" je trouve, enfin après c'est questions de goûts j'imagine, mais au moins ça évite de créer un même objet plusieurs fois par frame inutilement.
Oui si comme tu dis tu "fais tous tes calculs en utilisant cet attribut", mais dans un cas contraire, ou tu l'utiliserais juste pour la hitbox, ca implique de le mettre à jour à chaque update d'autres attributs (position notamment), et donc niet pour moi.
Après t'as sûrement raison, c'est une question de gout, et sûrement pas la que le bottleneck de l'application se trouverait, entre un attribut constamment utilisé et un objet (ne contenant que de la data et des getters) créé plusieurs fois
Oui si comme tu dis tu "fais tous tes calculs en utilisant cet attribut", mais dans un cas contraire, ou tu l'utiliserais juste pour la hitbox, ca implique de le mettre à jour à chaque update d'autres attributs (position notamment), et donc niet pour moi.
Non, tu les mets à jour qu'une fois par frame : avant l'affichage.
EDIT: Je comprends pas ton "mais dans un cas contraire, ou tu l'utiliserais juste pour la hitbox", je vois pas ce que tu veux dire par là.