1

yop,


Je veux concevoir la map d'un jeu style mario : un perso qui bouge, des blocs solides, du vide, des blocs avec quelques capacités (bumper, porte ou autre). L'implémentation se fait en C++, mais je pense qu'on s'en fout ici.
Partant sur ce qu'on fait pour TI, j'ai fait une matrice d'int classique avec des références aux différents blocs : 0 pour du vide, 1 pour un mur, etc...

Partant de là, je voyais bien un tableau d'objets créés dynamiquement en regard de la map de base. Un objet pourrait répondre à différentes questions du moteur : est-ce que le perso peut entrer ? Que se passe-t-il s'il touche le bloc ? S'il rentre cmoplètement dedans ? S'il en sort ?

Donc je voyais plusieurs types d'objets Bloc en fonction des capacités. Or pour faire une matrice de ce type de blocs (en fait, un tableau), il faut qu'ils soient identiques.

Pas de problème, on crée une classe union de tous ces types de blocs, qu'on appellera BlocGénérique. Tous les autres blocs dériveront de ce Bloc de base. Ainsi, le moteur pourra poser les questions qu'il veut aux blocs, ils seront tous capables de répondre.

Bon, c'est bien beau, mais cette méga-union à la Qt, ça me semble bien foireux. J'ai soumis le problème à Zeph ainsi :
La plateforme se base sur un tableau d'int spécifiant le type de bloc (0 = vide, 1 =mur, 2 = porte, etc...)

Le chargeur de niveau lit le talbeau d'int, et crée un tableau d'objets Bloc correspondant

Chaque Bloc a des méthodes :
- si perso touche ()
- si perso entre dans bloc ()
- si pero complètement dans bloc ()
- est-ce que perso peut rentrer dans bloc ? ()
- etc...


Evidemment, chaque bloc peut être un mur, une porte, bref, avoir des méthodes qui vont réagir différemment.

Mon problème est de faire ce putain de tableau de Bloc.

Je peux la jouer Qt : je crée un BlocGénérique qui est une union de tout ce qui existe comme blocs. Le tableau sera donc un tableau de BlocGénérique.
Chaque Bloc (BlocVide, BlocMur, etc...) dérive de BlocGénérique, et redéfinit les méthodes à sa sauce (évidemment, on traverse le vide et pas un mur, etc...).




Je sais pas pourquoi, je trouve pas ça très propre comme méthode. Qu'en penses-tu stp ? Merci d'avance. :)

Voici la conversation qui en a découlé sur IRC (*):
IDZi

Qu'en pensez-vous ? Je ne vois vraiment pas de quel côté attaquer les choses... Comme d'habitude, je n'ai pas besoin d'une solution clef en main, mais d'axes de réflexion sur la manière de concevoir un format de map pour un jeu de ce type. Merci d'avance !



(*) Fac-similé de la missive de Zeph m'autorisant à reproduire publiquement, tout ou partie, et sans redevance de royalties, la conversation que nous eûmes sur IRC : 9MjF
avatar
<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !

2

trisotfl

(sinon pour le problème, je vois pas comment faire autrement, enfin si, j'aurais pas fait de mon bloc générique une union, mais plutot une classe abstraite parente, et les vrais blocs seraient des classes dérivées, si c'est ça que tu veux dire alors j'aurais fait comme toi grin)

edit: mais en lisant votre conversation je vois qu'il a pas tort #triclasse#
donc oui pas besoin d'héritage sur les blocs mais un code global au niveau de la gestion de la map, qui gère les actions selon le bloc en cours.

3

Ah ok. Donc le moteur se contente de lire la matrice de base, et c'est à lui de déterminer ce qu'il fait par rapport au mouvement du perso. Donc pas besoin de tableau d'objets, très bien. smile Mais alors, ça va faire un moteur qui s'alourdira au fur et à mesure où je rajoute des blocs ? Comment faire pour garder ça propre ?
"Le perso veut aller à droite. Ok, voyons si c'est pas un bloc x, y, z, t, u (ah oui, u, sauf si à gauche d'en bas d'à droite, ya ceci-celà, etc... ><)".

ps -> dériver un objet global pour faire des blocs spécialisés, j'y ai pensé aussi, je sais plus pourquoi j'au arrêté d'y penser embarrassed
avatar
<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !

4

moi ce que j'avais fait dans ma première lib c'est associer une valeur "de colision" à chaque tile, chaque bit de cette valeur définissant une propriété

ensuite tu définissais un protocole toi même, genre 0x8 bloque sur le haut, 0x4 à gauche etc

puis dans le jeux quant tu allais à gauche, tu testais les tiles juste à gauche du perso avec ta valeur "gauche", soit 0x4

http://procvor.free.fr/gdl/map.htm
et la le mec il le pécho par le bras et il lui dit '

5

Folco (./3) :
Mais alors, ça va faire un moteur qui s'alourdira au fur et à mesure où je rajoute des blocs ? Comment faire pour garder ça propre ?

J'explicite :
je vais me taper des trucs du genre :

switch (bloc dans lequel je rentre)
case (0):
case (1):
...
case (2042)

switch (bloc sur lequel je me déplace)
...

switch (bloc duquel je sors)
...

switch (bloc dans lequel arrive ma balle de fusil à ultra-ions)
...

Ca risque de faire un truc complètement imbitable sorry
avatar
<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !

6

robinhood -> c'est pas idiot, mais sur PC j'ai de la mémoire, pour moi un int définit un numéro de bloc, pas la manière dont on le traverse ou non. Pour ça, il y a les booléens. Ceci dit, j'aurais sûrement fait ce que tu dis sur une TI (taille et efficacité).

Ah, une chose aussi, je comprends que le moteur pourrait tout gérer à partir d'une simple table d'int, mais je comprends pas pourquoi ma méthode n'est pas bien sad (ou celle de squalyl, dériver un bloc de base qui implémente toutes les méthodes possibles et immaginables)

edit -> toute façon ça marchera pas mon truc, si j'ai un pointeur sur bloc_de_base, un appel à estCeQueJePeuxEntrer () utilisera toujours la méthode du bloc de base, et non la méthode du bloc dérivé ou du bloc défini dans l'union sick
Ya vraiment que votre méthode de "tout dans le moteur" alors ?
avatar
<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !

7

Cette méthode n'est pas mauvaise, mais elle demande de définir une interface très complète, et suivant les fonctionnalités des nouveaux blocs que tu voudras ajouter, tu auras des choses à ajouter. Par exemple, tu touches le bloc, faut-il perdre une vie ? Le niveau du bonhomme change-t-il ? Change-t-on de niveau ? On est à 3 blocs du bloc, est-on attiré ? Repoussé ?

Le plus simple serait de diviser le moteur en blocs. On donne les coordonnées du bonhomme, l'état de la partie ainsi que les actions du joueur au bloc et celui-ci décide tout seul de ce qu'il faut faire en fonction de ses propriétés. Cette méthode est très lourde car il faudrait, dans une implémentation simpliste, interroger tous les blocs du niveau...
avatar

8

ben fait une structure au lieu d'un entier

struct tileProperties {
bool bloque_a_gauche;
bool bloque_a_droite;
bool meurt_au_canon_a_ion;
...
unsigned int nombre_de_point_de_vie;
unsigned int numero_de_tile_apres_destruction;
};

struct tileProperties[nombre_de_tile] tilesProperties;

(désolé de la syntaxe, les structures je connais plus grin)

si tu teste le bloc 42 au canon à ion fait simplement if(tilesProperties[42].meurt_au_canon_a_ion)

bon des constantes avec un tableau au lieu de la structure seraient surement mieux pour faire un système générique tongue

=> testTile(42,MEURT_AU_CANON_A_ION);
et la le mec il le pécho par le bras et il lui dit '

9

robinHood (./8) :
ben fait une structure au lieu d'un entier

Et j'y déporte aussi les méthodes kivonbien, et hop on retombe sur ce que je disais (ou presque).
avatar
<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !

10

RHJPP (./7) :
On est à 3 blocs du bloc, est-on attiré ? Repoussé ?
RHJPP (./7) :
Cette méthode est très lourde car il faudrait, dans une implémentation simpliste, interroger tous les blocs du niveau...

Intéressant. Comment faire ça de manière légère ? Une liste des blocs qui ont cette capa et qu'on interroge à chaque fois ?

Au final, il faudrait pour ce genre d'interactions des listes des blocs (regroupés pas classe) pour ne pas se taper tous les blocs du niveau, mais seulement ceux qui ont une chance d'intervenir, non ?
avatar
<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !

11

On peut en effet faire ça avec des listes de blocs par capacité, et puisqu'on connait la capacité associée à la liste, on n'a pas besoin d'interroger tous les blocs de cette liste comme je l'entendais, mais seulement ceux qui respectent les contraintes (en fait, tester les contraintes ou interroger le bloc risque de prendre le même temps). Mais se pose de nouveau le problème de la connaissance des capacités... Lorsque tu feras de nouveaux blocs qui inversent le sens de la gravité ou qui altèrent la vitesse d'écoulement du temps, il faudra encore revoir la liste des capacités.

Si on ne garde que l'idée de lister les blocs qu'il faut interroger parce qu'ils ont une capacité particulière, il faut faire attention à ce que cette liste reste assez petite. Si tous les blocs se retrouvent avec une capacité ou une autre, ils se retrouveront tous dans la liste et la simplification n'en sera plus une.

Mais interroger tous les blocs n'est pas forcément un problème. Il ne va pas y en avoir énormément dans un niveau et il est surement possible de se limiter à ceux présents à l'écran ou dans un certain rayon.
avatar

12

Bah, en principe, c'est un cas classique de polymorphisme, tu as une classe de base Bloc avec plein de méthodes virtuelles, et des classes dérivées qui les implémentent. Je ne vois pas vraiment où est ton problème.
Folco (./6) :
edit -> toute façon ça marchera pas mon truc, si j'ai un pointeur sur bloc_de_base, un appel à estCeQueJePeuxEntrer () utilisera toujours la méthode du bloc de base, et non la méthode du bloc dérivé ou du bloc défini dans l'union sick

As-tu déjà entendu parler de méthodes virtuelles? roll

(Et pas besoin d'union, just de pointeurs Bloc *, qui acceptent automatiquement des pointeurs vers un objet d'une classe dérivée.)
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

13

J'ai lu assez rapidement le log IRC avec Zeph, mais je pense qu'il a à peu près tous dit. En fait, tu cherches probablement à faire un truc plus complexe que ce dont tu as besoin.
J'ajouterais juste un truc, c'est qu'au final, le prérequis pour avoir un bon design de classes, c'est d'avoir des besoins bien définis, et savoir exactement où tu veux aller.
Si tu sur-spécifie (ie. Je voudrais permettre n'importe quel type de bloc possible même si je n'y ai pas pensé pour l'instant), tu vas te retrouver coincé à un moment donné avec un système qui fait bien plus que ce que tu avais besoin en réalité, mais dont la complexité n'est pas justifiée par l'utilisation que tu en fais, et qui rend ton travail plus compliqué. (Pire encore, tu risques de trouver un cas auquel tu n'avais pas pensé, et peut-être tout devoir recommencer)
D'un autre côté, si tu sous-spécifie, tu risques de devoir modifier tout ton code à chaque nouveau besoin. (ie. J'avais pas pensé aux blocs qui laissent passer le joueur en le ralentissant…)
L'idéal étant de se trouver parfaitement au milieu, mais c'est difficile d'y arriver du premier coup. En tout cas, ça demande une bonne dose de réflexion.


Cela dit, s'il faut trouver une manière "objet" de résoudre ton problème, je pense être en mesure de te proposer un truc plus ou moins potable, mais en fait, ça me gêne parce que je trouve ça un peu pourri malgré tout.
J'ai un problème avec la vision par bloc en ça que c'est super bien pour faire des rectangles, mais que je trouve ça à chier dès que tu veux faire des pentes autres que horizontales ou verticales. Le problème, c'est la gestion des collisions à la frontière entre deux blocs, où il y a potentiellement une discontinuité. (Et c'est certain que tu doives gérer ce cas si tu veux faire des niveaux un peu évolués)

En fait, quel que soit la façon dont tu modélises, il faut qu'à un moment donné, tu doit être capable de connaître la forme exacte d'un bloc (à priori ça sera toujours un quadrilatère mais juste pas forcément un rectangle) afin de déterminer entre deux blocs lequel va entrer en collision le premier.
D'ailleurs, il faut aussi que tu sois en mesure de classifier les blocs un minimum, pour indiquer par exemple si un bloc est "solide" (peut bloquer le joueur) ou "transparent". (Gérer le cas "ça dépend" peut être intéressant aussi).

Partant de là, si tu as les propriétés qui indiquent la forme du bloc, tu es en mesure de demander à un bloc d'ajuster la trajectoire d'un objet (joueur, …) dès que celui-ci va entrer en collision avec lui.
Tu passes par exemple à une méthode "Interact" différentes propriétés de l'objet, notamment l'accélération, vitesse et positon du point avant collision potentielle, et le delta de temps pour lequel les caractéristiques doivent (ou pas) être altérées… (Et ce que tu vas altérer devrait être dans la plupart des cas au minimum : position finale du point, nouvelle vitesse, et nouvelle accélération)
Dans cet méthode, le bloc a la possibilité d’interagir avec l'objet (ça peut être un joueur, un ennemi, un projectile, …) et avec le niveau lui même (inverser la gravité, changer la couleur de l'éclairage, etc…)
Bien sûr, tu passeras les coordonnées du point relativement au centre du bloc, comme ça, le bloc n'a pas besoin de connaître sa position dans le niveau, et tu économises de la mémoire et du temps de calcul.

Après, tu peux représenter ton niveau sous la forme d'un premier tableau indiquant les natures primaires des blocs (solide, transparent), ce qui te permet de rapidement déterminer les blocs qui peuvent interagir avec le joueur (ou autre objet) autour de celui-ci. Et dans une seconde représentation, avoir une instance concrète des blocs pour chaque bloc qui est autre chose que null. (Plus un autre tableau pour les graphismes…)

Mais ça c'est vraiment la solution "usine à gaz" qui peut à peu près tout faire. (Et encore j'ai pas trop détaillé à ce niveau)
Ça marcherait, mais c'est assez lourd à mettre en place, et à moins que tu veuilles concevoir un système totalement générique pour créer x jeux différents, je ne sais pas si ça en vaut la peine. (Je suis presque sûr que non) En éliminant quelques cas, ça peut se simplifier énormément.



En fait, je suis presque certain que la solution "conne" est la meilleure solution à ton problème spécifique.
En tout cas, ça sera plus efficace, plus rapide à coder (peut-être un peu moins "propre" par contre), et il sera plus facile de gérer les cas spéciaux.
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

14

pencil global, l'exercice de style est en soi intéressant, mais je suis pas sûr que le résultat sera à la hauteur de tes attentes.

Je pense qu'il faut que tu formalises d'abord ce que tu veux dans ton jeu, avant de choisir l'implémentation. Sinon tu vas te retrouver avec une usine à gaz, et une fois que t'auras fini de la coder, t'en auras marre et tu laisseras tomber.

Et puis les meilleures solutions ne sont pas toujours les plus "pures". Par exemple il y a des propriétés qui concernent tous les types de blocs (est-ce que le bloc est traversable ? etc.), et d'autres qui seront limitées à un seul type de bloc (est-ce que c'est un item d'invincibilité temporaire ? etc.). Pour le second cas, un simple if me semble plus simple plutôt qu'une méthode ou une propriété qui sera inutile pour 99% des blocs.
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

15

Je suis d'accord avec Kevin (./12) : le polymorphisme parait bien.
Si l'interface parait lourde, pense à ce que tu peux factoriser pour simplifier l'interface...
Par exemple, tu peux avoir
Bloc:receiveEvent(Event) qui factorise toutes les réceptions d'événements.
Tu peux aussi mettre dans la classe Bloc des méthodes par défaut (vides) pour n'écrire que les méthodes nécessaires dans les blocs spécifiques.
La seule fois où j'ai essayé de faire un programme générique avec une super archi modulaire qui fait tout, je me suis complètement planté.
Il vaut mieux rester simple (principe KISS).

16

Pour avoir essayé de faire plusieurs jeux d'échecs et m'être compliqué la vie, et pour être en train de recommencer pour la quatrième fois, voilà mon conseil: ne te complique pas la vie avec des features prévues dont tu n'arrives pas à esquisser les limites tongue

Les gens au dessus ont à peu près tout dit je pense, le polymorphisme devrait faire l'affaire. Perso j'utiliserai ce genre de structure (fait au pied du lit, je me plante peut-être):

class GenericBlock
{
    public:
        GenericBlock(uint32 blockType) : _blockType(blockType) { }
        ~GenericBlock();
        DispatchEvent(EventInfo info);
        virtual void OnPlayerWalk(EventInfo info) { };
        virtual void OnPlayerEnter(EventInfo info) { };
        // Etc etc
    private:
        bool IsEventAllowed(EventInfo info) const;

        uint32 _blockType;
};

class WalkableBlock : public GenericBlock
{
    public:
        WalkableBlock(uint32 blockType) : GenericBlock(blockType) { }
        ~WalkableBlock();
        void OnPlayerWalk(EventInfo info);
};


Typiquement, la fonction DispatchEvent appelle IsEventAllowed en interne et vérifie, SELON le type de bloc, si un handler pour l'event existe: elle l'invoque si la réponse est oui. Tu peux même étendre ça à un bitmask pour un bloc qui, typiquement, est walkable ET avec lequel tu peux interagir (coin, life, etc). Si tu pars sur une approche bitmask, tu peux même créer une classe qui hérite de WalkableBlock et, admettons, InteractibleBlock, et tu n'auras rien à définir, chaque sous-classe possèdent déjà les handler nécessaires.

Pour le problème du switch super long tripo, je pense que c'est normal et relativement inévitable si tu ne travailles pas avec des bitmask, le moteur gagnant en complexité à chaque fois que tu rajoutes un type de bloc.

J'espère ne pas t'avoir perdu, je suis très mauvais pédagogue :P

[EDIT] Pour un truc encore plus générique, tu peux partir sur un tableau avec des pointeurs aux fonctions:

uint8 const MAX_EVENT_HANDLERS = <x>;
EventHandlerMap _eventsInfo[MAX_EVENT_HANDLERS] = 
{
    // Type de bloc           Handler associé
    { BLOCK_WALKABLE, &GenericBlock::OnPlayerWalk },
    // Etc
};


Ça s'invoque DANS ta classe, via (this->*ptr)()

17

dans mon esprit, c'est les sprites qui interagissent avec les tiles, et non l'inverse

après réflexion, la seule fois ou de l'objet m'aurais vraiment servis pour les tiles c'est pour koboG et ses destructions de tiles chaînés non instantanés

kobog.png

les tiles vraiment particulier, eux, devraient être extraits (ou non) de la map et transformés en { sprites, breakpoint au scroll, ... } bref des choses vraiment indépendantes, non statiques, parfaites pour de l'objet ?

genre la :dstar.jpg le curseur et la pierre sont supprimés de la map et définis en sprites par le moteur

dans ton mario se serais par exemple les plateformes qui bougent ! smile

ca va te permettre de filtrer tout l'inutile (tiles d'affichage, décors qui simplement limite les déplacements) et d'augmenter les possibilités des choses spécifiques (un tile ca reste fixe quoi qu'il advienne)

enfin c'est ma vision des choses mais je suis toujours resté basique sur mes jeux :- )
et la le mec il le pécho par le bras et il lui dit '

18

Merci pour tout à tous. smile

Malheureusement, je n'ai aps encore tout compris de vos explications, disons que je ne vois pas tous les tenants et aboutissants de vos réflexions. Manque de niveau certainement... J'ai encore à cogiter, pas de souci.

Kevin -> si ça me parle les méthodes virtuelles, cependant ça fait bien un an que je n'ai pas fait de C++, et j'avais à peine commencé à en faire auparavant. Je suis en train de relire mon bouquin, j'en suis vers la page 450, donc il en reste dans les 300, et les méthodes virtuelles sont abordées dans deux chapitre. Là, je suis dans la dérivation. cheeky
avatar
<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !

19

robinHood (./17) :
après réflexion, la seule fois ou de l'objet m'aurais vraiment servis pour les tiles c'est pour koboG et ses destructions de tiles chaînés non instantanés

Pour ça, il faudrait plutôt de l'évènementiel (timer!) que de l'objet classique.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

20

il n'y à pas de timer sur gp32, juste un getTick, mais la pour le coup tout est compté à la frame ^^"
une destruction se propageant toute les 5 frames cheeky (à 52~53 frames / seconde)

bref une liste de destructions "en cour" lue et mise à jour à chaque frame, décomptant les frames restantes, ajoutant les anims de destruction dans une seconde liste, supprimant les tiles (ou les modifiant vers un noeud sans la branche détruite si l'origine de la chaine n'etais pas une boule rose) et affectant de nouvelles entrées dans la liste pour assurer la propagation, un gros bordel

avec des objets cela aurais été surement plus propre ^^
et la le mec il le pécho par le bras et il lui dit '

21

En évènementiel, c'est simple, chaque bloc est un QObject et a une méthode destroy et un slot qui est connecté à un QTimer par la méthode destroy.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

22

Bon merci. Je vais prendre mes idées, les vôtres, mélanger, essayer de faire un truc grin
avatar
<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !

23

Bon, élargissons le débat cheeky

Comment vous verriez la représentation en mémoire d'un niveau ?

Je veux dire, si quand on appuis sur "start", on crée indépendamment :
- un niveau (basé sur une matrice)
- un perso
- des ennemis
- des armes
- une machine à café (parce que mes jeux font même le café)

On va se retrouver avec un tas d'objets juxtaposés. Pour communiquer entre eux, je vois troi possibilités :
- passer des pointeurs dans tous les sens. Ca fait très C school. Ca me parait lourdingue.
- utiliser des variables globales. Ca fait très crade school. Mais c'est au prime abord ce qui parait le plus simple (avant de devenir le gros bordel probablement).
- utiliser des messages. Alors en avant pour un dispatcher, des listeners, des senders, des écouteurs, des baffles, des micros, quelques DJs and co. J'avais commencé un jeu comme ça, ça devient très lourdingue dès lors qu'on est pas assisté par un soft dédié qui écrit la moitié du code (genre Qt Designer pour citer ce que je connais). On se retrouve à écrire dans les 3541816 messages parce qu'on a une souris et 3 boutons dans une fenêtre SDL.



Autre possibilité pour éviter tout ça : avoir une "hiérarchie d'appartenance" (ça se dit ?fear).
Donc grossièrement, on peut dire que un niveau possède :
- une map de tiles
- une liste de sprites pour dessiner tout ça
- un perso
- une liste d'ennemis

Le perso possède :
- une liste d'armes
- une liste d'items dans son sac à dos

Les ennemis possèdent :
- rien, c'est des connards.


La seule chose à connaitre pour les différents objets appartenant (même de loin en loin) à la map en cours, c'est l'adresse de la-dite map. Ca peut faire une var globale, ou un seul pointeur à transmettre (par exemple, comme membre de chaque objet à sa création, c'et pas meiux d'ailleurs ?).
Je sais que Code::Blocks a une structure de ce genre.


Vala vala, qu'en pensez-vous ? Quelles sont les pièges que je ne vois pas dans ces différentes possibilités ? Où et-ce que je vais m'enliser parce qu'au final, ça ne sera pas viable ?

Merci d'avance. happy
avatar
<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !

24

(le perso et les ennemis possèdent aussi un sprite pour les représenter)
avatar
Webmaster du site Ti-FRv3 (et aussi de DevLynx)
Si moins de monde enculait le système, alors celui ci aurait plus de mal à nous sortir de si grosses merdes !
"L'erreur humaine est humaine"©Nil (2006) // topics/6238-moved-jamais-jaurais-pense-faire-ca

25

Yep, mais ça je dirais que ce sont des membres naturels de l'objet. Un perso a aussi des vies, une osition, une vitesse etc... Je pale ici des éléments constitutifs de l'ensemble d'un niveau.
avatar
<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !

26

Je pense que c'est assez sage de séparer la représentation conceptuelle du niveau de sa représentation graphique, dans une certaine mesure.
Mais la façon d'implémenter ton jeu de manière générale dépend énormément de tes contraintes (objectifs) exactes.

Par exemple, si tu peux avoir 50 000+ sprites (typiquement: projectiles) à l'écran, tu ne vas probablement pas gérer ça de la même manière que si tu ne dépasses jamais la vingtaine.
De même, si ton niveau fait 400 écrans de long, tu vas sans doute vouloir implémenter la gestion du niveau et de l'affichage différemment que ce que tu aurais fait pour des niveaux de 4-5 écrans de long… (C'est sans doute un des points les plus importants d'ailleurs)

Tu dois aussi voir si un niveau est toujours une longue map linéaire, ou si c'est composé de différentes parties reliées entre elles par un mécanisme quelconque (exemple: les tuyaux de Mario…), et si lors d'une transition, les modifications faites (ennemi mort, bonus ramassé…) au niveau "vivant" sont conservées ou perdues.
Si tu choisis que le joueur ne peut jamais revenir en arrière, tu n'as pas trop besoin de te prendre la tête, mais, c'est un peu une expérience de jeu pourrie en général. (Super Mario Bros. 1 était comme ça. Sans doute en partie pour des raisons de mémoire.)

Selon les interactions que tu souhaites entre les objets (bonus, ennemis, joueurs), la façon de gérer les interactions ne sera pas la même. (Tu peux vouloir faire interagir les ennemis entre eux, par exemple deux ennemis qui se touchent se transforment en ennemi plus puissant. Ou bien un ennemi de type x peut tuer un ennemi de type y. Ou n'importe quel projectile peut tuer n'importe quoi. Etc.)

Enfin bref, en fait, c'est pareil qu'avant, il vaut mieux un plan assez bien défini au départ. (Quitte à revoir tes plans plus tard si besoin)
Si tu ne sais pas où tu veux aller, c'est difficile de savoir comment tu vas y arriver, et même de savoir où tu vas arriver cheeky

(Comme tu présentes un truc générique, je trouve ça très difficile de te répondre. Il existe au moins une "meilleure" solution à tous les problèmes (sauf si pas de solution du tout…), mais entre deux problèmes en apparence très similaires, les "meilleures solutions" peuvent être très différentes. La solution absolue n'existe pas. Tout ce qu'on peut te donner c'est des "en général", et "en général", les "en général" ne s'appliquent pas directement à ton problème sans ajustements cheeky)
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

27

Wow, je m'attendais pas du tout à une réponse de ce type. Je voulais juste savoir comment organiser les données pour qu'elles aient accès les une aux autres facilement : une relation d'appartenance entre elles, ou une foultitude d'objets juxtaposés parlant par message ? C'est encore trop vague si un projet n'est pas bien défini ? Restons simple, je compte pas avoir 50k projectiles ni 1000 écrans de long ^^
avatar
<<< Kernel Extremist©®™ >>>
Feel the power of (int16) !

28

il te faut des "couches"

déjà une api bas niveau bien pensée
* collisions standard
* buffers graphique
* blit d'images (tapant dans les buffers)
* animations (utilisant le blit d'image, à toi de voir ensuite si tu veut mater un simple getTick ou te taper des timers/events)
* ...

une autre moins bas niveau utilisant la première
* api de gestion des sprites
(banque d'anims, anims en cour, déplacement, collisions ... )
* api de gestion de la map
(init, affectation de tiles animés, blit, scroll, lecture/modification des tiles (avec des fonctions utilitaires générales genre get du tile à la pos x;y de l'écran))
* ...

encore une autre, reposant sur la précédente, gérant des choses cette fois plus "spécifiques"
* tirs (init, déplacement, collision, ...)
* explosions (addBoom(type, x, y), addBoomOn(sprite), ...)
* joueur (déplacement, interaction avec la map, gestion sac à dos, point de vie, ...)
* ennemis (intéligence, point de vie, ...)
* bonus
* ...

au passage si t'est chaud tu peu faire une belle api de déplacement générique gérant pourquoi pas des forces, direction etc ...

* création d'un objet "jeux" avec les listes d'objet de l'api de plus haut niveau, et des fonctions contenant la logique propre (genre test croisés de toutes les collisions joueur/tirs, ennemis/joueur, ennemis/tirs, tirs/tirs, ..., ..., ...)
(le but est qu'ici tu n'est pratiquement aucun "vrai" code, juste des appels et test croisés des apis)
utiliser des messages.
tu va devoir y passer tout de même, pour faire envoyer les info du joueur physique au coeur de ton moteur (ou du moins à "l'objet" joueur), mais dans un code à part bien spécifique

idéalement tout devrais être portable, les seul bout de code spécifique à la plateforme serais les blit bas niveau, le son, et les évent du joueur physique
et la le mec il le pécho par le bras et il lui dit '

29

GoldenCrystal (./26) :
Tu dois aussi voir si un niveau est toujours une longue map linéaire, ou si c'est composé de différentes parties reliées entre elles par un mécanisme quelconque (exemple: les tuyaux de Mario…), et si lors d'une transition, les modifications faites (ennemi mort, bonus ramassé...) au niveau "vivant" sont conservées ou perdues.
c'est très juste !
et la le mec il le pécho par le bras et il lui dit '

30

./27 > Ben ouais, mais quels objets ont besoin d'interagir avec quels objets ? De quelle manière ? (Pas au niveau implémentation, mais au niveau fonctionnel)
Si un objet x n'a pas besoin d’interagir avec un objet y, c'est pas la peine de t'embêter à rendre ça possible.

De manière générale, tu te contenterais de relations entre les objets. Mais si tu commences à avoir trop de relations entre les objets, alors c'est pas la bonne solution. Mais ça, tu le sais qu'à partir de tes besoins concrets.
Par exemple, pour une map, représenter chaque tile par une instance d'objet distincte, c'est un coup à se tirer une balle dans le pied. Mais pour des objets de jeu (joueur, bonus, ennemi, projectile…) ça se tient tout à fait.
Sauf qu'en général, tu ne voudras pas gérer tous les objets de ton niveau de manière simultanée, parce que tu auras peut-être 5000 objets dans ton niveau mais seulement 30 à l'écran ou proche de l'écran, du coup tu ferais sans doute une seconde liste d'objets. (De fait tu auras deux représentation de ton niveau, une représentation "figée", et une représentation "animée")
Mais si tes objets veulent interagir avec ceux qui ne sont pas encore à l'écran (enfin c'est un peu ce que j'ai l'impression de comprendre que tu veux rendre possible en parlant de message), alors il faut penser à un mécanisme plus complexe.
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes