Aventura, le Royaume Poudingue... Un long projet !
Posté le 06/07/2016 23:44
Aventura, Le Royaume Poudingue est un RPG en Basic Casio (monochrome) entamé en 2016 et repris en 2018. Projet très ambitieux, ce dernier tend à s'emparer des meilleures techniques de Basic Casio pour offrir une expérience de jeu dépassant les attentes du joueur. Il aura fallu deux années complètes pour que la première version complète du jeu sorte enfin ! Ouf !
Le jeu est enfin disponible ! Cliquez ici !
Le projet en 2016
Cliquer pour enrouler
[...]
En réalité, je projette de concevoir un RPG en recommençant absolument tout de zéro. En errant sur ce forum, j'ai amassé une quantité satisfaisante de savoirs nouveaux en ce qui concerne le basic ! Toutes les techniques et les jeux que j'ai entraperçu ou étudié sur ce forum ont fait germer tout un tas de petites idées en moi !
Je vous expose le plan : l'idée n'est qu'à l'état embryonnaire, mais je compte la faire évoluer par débats et expositions d'idées. Pour l'histoire, le scénario, je n'ai pas de problème. J'ai l'imagination qu'il faut pour pondre des trucs farfelus. Ce dont j'ai besoin, c'est d'approfondir le système du jeu et tenter de construire un programme cohérent, très optimisé et performant. Procédons à tout cela par étape, je relèverai chacune de mes interrogations par la suite.
La map : Résolu
Cliquer pour enrouler
C'est ce qui me fait me poser le plus de questions. Voici ce que j'ai décidé : sur une picture sera enregistrée une sidebar qui sera visible pareillement sur le mapmonde et en combat. J'ai pour ambition d'adopter un système de tiles pour générer les maps (qui seront statiques, comme le fameux Zelda PC de Remiweb, et pas de map scrolling). Pour commencer, je projette de concevoir un système de sauvegarde provisoire des maps. Voici un schéma pourri fait à l'arrache :
Lorsque le joueur est sur la map 1, le décor de cette map est enregistrée en picture 1 (par exemple). Arrivé sur la map 2, celle-ci est enregistrée en picture 2. Sur la map 3, elle est enregistrée en picture 3. Lorsque le joueur veut retourner sur la map 2, la picture 2 est directement affichée plutôt que de la redessiner. En passant sur la map 4, la map 1 sur la picture 1 est effacée pour laisser place à la map 4. Ainsi, le personnage peut se ballader entre les map 2, 3 et 4 sans temps de chargement !
Ensuite :Résolu
Cliquer pour enrouler
je vais rencontrer de nombreuses problématiques concernant le dessin d'une map. Pour une map, je vais rentrer dans une matrice les ID de chaque case. Admettons que j'ai un programme "lecteur de matrice" qui dessine chaque case en fonction de l'ID. Il est évident que je vais essayer de dessiner d'une traite tous les tiles identiques avant de passer aux tiles suivants, plutôt que de faire :
For 1→A To 6
For 1→B To 11
Mat A[A;B]=1⇒{1,2,3→List 1
etc.
Next
Next
Drawstat
C'est-à-dire lire chaque case, une à une, même si elle est vide, pour dessiner une tile. après avoir entré dans la liste les coordonnées. Bref. C'est long, sale et un peu bulldozer.
Un génie de Naheulbeuk a écrit :
Baston !
J'avais prévu de faire entrer toutes les données chiffrées dans une chaîne (Str 1) plutôt que dans une matrice(qui est d'ailleurs lourde) pour pouvoir exploiter les fonctions StrSrch, StrMid, etc. Il est tout à fait possible de passer d'une chaîne à une expression numérique, donc ce point ne pose pas problème. Qu'en pensez-vous ? L'idée peut-elle être exploitée ou est-elle incohérente ?
Les tiles en eux-mêmes :Résolu
Cliquer pour enrouler
Je cherche le bonne taille de tiles à prendre. J'avais pensé à utiliser des tiles de 10*10. Chacune de mes maps feraient alors 6*11 cases. Y a-t-il une taille préférable à 10*10 ? Qu'en pensez-vous ? Je ne sais quoi en penser. Je vais rencontrer un autre problème : le stockage de données. Avec les listes, je me demandais si une seule liste avec des coordonnées complexes est plus légère que deux listes pour les abscisses et ordonnées séparées. La différence est-elle notable ? Si oui, ça m'arrangerait bien pour ce que je pensais faire :
Il serait possible que j'exploite la technique Augment( pour dessiner tout d'une traite avec le Graph(X ; Y)=. Cette technique étant très gourmande en mémoire sur l'instant, je pourrais effectuer l'opération plusieurs fois. Mais ça serait assez confortable de pouvoir mettre :
Dim List 1→Tθmax
Graph(X;Y)=(ReP List 1[T];ImP List 1[T
J'aimerais votre avis là-dessus !
Le moteur de combat :
Alors là, j'ai pas mal réfléchi. Sur le modèle de fonctionnement Poule/Renard/Épervier, ou Feu/Eau/Plante dans Pokémon, j'ai l'intention de créer trois types d'attaque :
Puissance ,
Dextérité et
Vitesse.
Le personnage aura trois attaques pour chacun de ces types. Donc une animation différente pour chacun d'entre elle. Il maîtrisera en plus une capacité de soin, et peut-être une capacité secrète.
Il n'y aura pas de niveau à proprement dire : Selon les attaques qu'on emploie, la maîtrise dans la branche correspondante augmente et améliore les capacités du personnage : Force, Défense, Nombre de coups, précision, Esquive, etc.
Bon. Le souci, c'est que je vais devoir à dessiner des monstres et risque d'être limité sur ce point là.
Le personnage est en fait personnalisable. Au début du jeu, vous pourrez choisir une arme et une coiffe (lunettes de soleil, chapeau en pointe...) La machine génère alors sur plusieurs listes trois positions du personnage : une statique, une en tenant l'arme en l'air, une troisième en donnant le coup. Pour cela, je fais tourner l'arme, comme je l'ai évoqué dans ce
topic. ce point précis est résolu. C'est grâce à ses trois dessins pré-enregistrés que je ferai les animations. Une question se pose : comment faire des animations pour les monstres potables et peu spacio-phages ?
Voici à quoi ils ressemblent !
(photos expirées)
Pour le menu :
Pas encore d'idée, si ce n'est un truc enregistré sur une autre picture qui sera beau graphiquement parlant. On y verra l'état du personnage et son niveau de maîtrise dans chaque branche.
Pour l'interaction avec des pnj :
Résolu
Cliquer pour enrouler
Je projette de concevoir un sous-programme "lecteur de texte", si je puis dire. Sur la map, le programme dessinera une boîte de dialogue dans laquelle s'écriront les paroles d personnage. Pour ce point, je compte beaucoup m'inspirer des travaux de Remiweb sur son Zelda PC. J'ai trouvé ce point absolument remarquable et incroyablement bien fait.
Voici la tronche du projet. J'aimerais vraiment avoir vos avis, savoir ce que vous en pensez, avoir de vos précieux conseils et surtout engager le débat pour progresser. Je vous remercie d'avance, et vous remercie aussi de m'avoir lu jusque là.
État des lieux du projet
modifié le 20 09 2018
À ce jour, voici ce qui est fait et ce qui reste à faire pour l'ensemble du programme de jeu.
Graphismes (personnages, monstres, background, carte du monde, title screen...)
Comme vous pouvez le voir, il reste beaucoup de travail.
Le plus difficile sera de faire tenir tout le jeu en ~ 60 000 octets. Seulement, je ne parle pas du programme seul : à présent, il ne fait que 6772 octets. Toutefois, les Pictures prennent déjà 11404 octets (avec y compris les 6072 octets des trois pictures du mon système de sauvegarde de map, et les list peuvent atteindre les 7000 octets. Actuellement, il me reste quelque chose comme 36 000 octets de libres pour faire TOUT LE RESTE.
Le jeu en quelques questions
modifié le 08 06 2018
Que sont les Poudingues ?
Les poudingues sont des petites créatures sans bras ni jambe, au corps semblable à un mélange entre une balle d'arme à feu et un pudding. Leur visage est expressif et ils déterminent généralement leur appartenance sociale par leur couvre-chef.
Quel est le but du jeu ?
Vous incarnez un de ces fameux Poudingues, vivant au sein d'un royaume dirigé par un monarque Poudingue. En tant qu'Explorateur, vous êtes convoqué par le roi lui-même pour vous confier une mission : localiser le repaire des poudingues anti-royalistes qui semblent préparer une révolte dans l'ombre...
Quel type de jeu ?
Aventura, le royaume poudingue est un RPG / jeu d'exploration. Vous devrez, pour une partie du jeu, explorer des terrains, trouver les pnj à qui parler pour faire avancer l'histoire, etc. Durant le jeu, vous devrez aussi affronter les monstres sauvages ou bien les opposants qui se dresseront sur votre chemin ! Au fil des luttes, vous affinerez votre style de combat, renforcerez vos spécialités et augmenterez votre niveau et vos caractéristiques.
Quelles caractéristiques et quel système de combat ?
Le système de combat est à priori un système au tour à tour, en un contre un de profil sur l'écran.
Un poudingue possède trois caractéristiques, avec un niveau de maîtrise pour chacune d'entre elle : Puissance, Technique, Vitesse. En fonction des attaques employées par le poudingue, ses niveaux de maîtrise correspondant augmentent. Lors d'une montée de niveau, chaque caractéristique augmente proportionnellement à son niveau de maîtrise. Utilisez beaucoup de fois l'attaque « Cogne » et votre puissance avancera davantage ! Votre type est ensuite déterminé par la valeur la plus élevée des trois caractéristiques : type Puissance, type Technique, type Vitesse, ou bien Neutre si vos caractéristiques sont équilibrées.
Ces trois types fonctionnent selon une règle similaire au jeu Pierre-Feuille-Ciseaux. Le type Puissance a l'avantage face au type Vitesse, le type Vitesse a l'ascendant sur le type Technique et le type Technique est plus efficace contre le type Puissance. Ce rapport de force se détermine dans le calcul des dégâts infligés, qui sont en pourcentage. Autrement dit, le joueur a 100% de vitalité et meurt s'il tombe à 0%. Le nombre maximal de vitalité ne change pas, il est de 100% pour tout le monde.
À priori, le niveau maximal atteignable serait de 100. Le système de calcul d'expérience n'est pas encore établi.
Les trois caractéristiques interviennent donc dans le calcul des dégâts en fonction du type de l'attaque. La caractéristique du lanceur correspondant au type de l'attaque et la caractéristique de la cible correspondant au type ayant l'ascendant sur celui de l'attaque sont pris en compte dans le calcul des dommages. Exemple : le joueur utilise une attaque Vitesse sur une cible de type Technique. La caractéristique Vitesse du joueur et la caractéristique Puissance de la cible seront pris en compte. Un facteur de dégâts accru s'appliquera sur les dommages totaux puisque le type Technique est faible face aux attaques Vitesse. Au final, si le lanceur a beaucoup de Vitesse et la cible peu de Puissance, l'attaque infligera de lourds dégâts.
Les niveaux de maîtrise augmentent aussi l'efficacité des attaques suivantes : Cogne, Estoc, Rafale. À partir de certains seuil, les niveaux de maîtrise donnent de nouveaux effets / bonus à ces attaques. Il peut donc sembler intéressant de se spécialiser dans un type pour améliorer ses attaques.
Les sorts, quant à eux, n'augmentent pas les niveaux de maîtrise lors de leur utilisation. Si un sort peut s'avérer plus efficace qu'une attaque classique, il ne permettra pas à son utilisateur de bien progresser sur le long terme.
Un aperçu du moteur de combat, le 09 07 2018 (V - 0.21)
Un aperçu du moteur de dialogue, le 21 07 2018 (V - 0.22)
L'Éditeur de mappemonde, 08/2018.Un autre aperçu du moteur de combat, le 08 08 2018 (V - 0.24)
Phase 1 : Écriture. Cette étape consiste à coucher sur le papier ce que je projette de faire, le gameplay et les composants du jeu, dans un idéal. Je ne m'occupe pas tant de savoir si ça tiendra dans le code, ni de savoir si j'aurais le temps et/ou la motivation d'en faire autant. J'essaye d'en écrire le plus possible, dont le scénario avec le plus de détails possible, les personnages, etc.
Phase 2 : Architecture générale. Il s'agit de hiérarchiser les sous-programmes, menus, et les chemins qui s'opèrent entre chaque. Dans cette partie, on ne s'occupe pas du code dans les détails, mais plutôt de « blocs » en tant qu'ensembles de fonctions. Par exemple, j'aurai un bloc « Title screen », un bloc « dessin map », un bloc « dialogue », etc. Il s'agira de définir avec une certaine précision les blocs qui interviendront pour le fonctionnement du jeu, d'estimer si possible leur taille et de les positionner les uns par rapport aux autres, les cheminements de sous-programmes à sous-programmes – sachant qu'on ne peut pas avoir plus de 10 sous-programmes qui tournent à la fois.
Phase 3 : Code. Écriture du code à l'intérieur de chaque bloc, avec seulement quelques données à titre d'exemple. Il ne s'agira pas de faire du bô graphisme ou bien pleins de sprites et de pictures différentes. L'objectif ici est de fabriquer rapidement quelques éléments de dialogue et de graphisme pour pouvoir écrire tout le code qui fera fonctionner le jeu et qui agira comme un squelette. De beaux graphismes sont inutiles si le code sous-jacent n'est pas fonctionnel. J'ai eu tort de commencer à faire des maps et des pictures, mais elles sont là et je ne vais pas les gâcher.
Il me faudra alors écrire de façon individuelle et séparée chaque « bloc » ou « fonction » pour m'assurer que leur fonctionnement propre est opérationnel. C'est aussi l'occasion de revoir certaines ambitions à la baisse si elles demandent trop de place dans le code. Cette partie ne constitue pas l'intégralité de l'écriture du code, loin de là. La phase 4 y occupera une place importante aussi.
Phase 4 : Mise en relation et assemblage des blocs. Cette partie sera délicate, mais aura été largement anticipée avec la phase 2. Il s'agit non plus de faire fonctionner des pièces détachées, mais de les assembler et de penser avec ingéniosité leur articulation pour que le jeu devienne une seule entité. Cette phase comprendra vraisemblablement des modifications à l'intérieur même de chaque bloc pour d'éventuelles optimisations. Il y aura naturellement de nombreux tests et essais quant au bon fonctionnement de l'ensemble de la structure.
Phase 5 : Travail de « remplissage ». Si le squelette du jeu est tout à fait opérationnel et ne présente pas de bug, on passe à cette phase qui consiste à remplir le jeu de son contenu : les graphismes, les maps, les dialogues. C'est donc à ce moment que je m’attellerai activement à la conception des graphismes du jeu et à ses dialogues. Cette phase porte bien son nom puisqu'elle devra également être modulée en fonction de la place restante en mémoire dans la calculatrice.
Phase 6 : Optimisations, essais et tests du jeu intensifs. Cette phase vise à repérer les éventuels défauts dans le code du jeu, mais aussi dans le gameplay. C'est une phase très importante car elle vise à rendre le jeu jouable, appréciable et source de plaisir (oh yeah baby), mais aussi cohérent dans son ensemble. Elle contiendra donc beaucoup de recorrections et peut-être même de frustration chez le concepteur (moi) qui constatera que son jeu est tout pourri.
Phase 7 : Publication. Une fois que les corrections, tests, optimisations et finitions sont finis, il ne me reste plus qu'à publier une version 1.0 (ou Bêta) du jeu et attendre des retours de la communauté, qui sera sans doute enthousiaste de voir un jeu sortir après plus de deux années d'attente, lôl. En fonctions des retours, des finitions et des ajouts auront peut-être lieu pour donner suite à une version 1.1, 1.2, etc. L'objectif sera donc de corriger les éventuels bugs qui seraient passés inaperçus, et prendre en compte des suggestions pour améliorer l'expérience de jeu.
Phase 8 : Supplication. Demander à ce que le jeu jouisse du label de qualité Casio. C'est mon rêve. Je le veux. Au moins une fois dans ma vie !
Les suggestions de sorts par les membres de la communauté :
Lephenixnoir : « Pistole, Assaut, Regard perçant, Empalement, Illusion, Barrage, Retour de l'Hirondelle, Contournement, Vorpal, Survol, Coup ascendant, Berserk, Flèche ? »
Shadow15510 : « Boule de Feu (T) ». Woaw. Incroyable.
Membres ayant manifesté leur volonté d'apparaître dans le jeu :
Lightmare
Shadow15510
Cakeisalie5
Massena
Totoyo
Lephenixnoir
Math680
Git du projet : Soyez au plus près du code et des avancées des versions !
https://gitea.planet-casio.com/PlaneteCasio/Le_Royaume_Poudingue
Mis à jour le : 31/10/2018
Fichier joint
Citer : Posté le 06/07/2016 23:51 | #
Ça a l'air super intéressant !
Bon, j'ai pas trop le temps ce soir de répondre de manière approfondie, mais demain je te fais un pavé.
Enfin, pour le lecteur de texte, tu peux aussi regarder du coté de Clonelab, qui est aussi une référence en la matière.
Citer : Posté le 07/07/2016 09:43 | #
Je me souviens que j'avais implémenté exactement ce système de sauvegarde de maps quand je travaillais en Basic. Y'a un compromis important entre vitesse de dessin et perte de mémoire à gérer.
Nan, t'as pas envie de faire ça. On dispose de 6 fichiers de 26 listes, garde-t-en 1 ou 2 pour stocker les données des tiles et les avoir sous la main. Cette méthode d'itérer simplement sur la matrice est de loin la plus simple et gagne, à mon goût, à être utilisée.
Les chaînes, ça peut être bien. Vérifie cependant que ça ne ralentit pas trop l'exécution, et que tu ne dépasses pas la limite de 255 octets.
Pour la taille des tiles, tu fais vraiment comme tu le sens. Personnellement j'avais déjà utilisé du 7x7 avec une bordure d'espacement, ce qui donnait grosso modo du 9x9. Cette taille est intéressante parce que ça fait des maps de 14*7 qui couvrent 126*63 pixels, ne laissant qu'une colonne inutilisée.
Avec les listes, je me demandais si une seule liste avec des coordonnées complexes est plus légère que deux listes pour les abscisses et ordonnées séparées.
Non, c'est exactement pareil. Une donnée réelle prend 12 octets et une complexe, 24. Personnellement je conseillerai les complexes pour pouvoir stocker un tile par liste. Ça économise des identifiants de liste. Y'en a 156 mais tant qu'à faire. Si vraiment tes tiles sont serrés tu peux utiliser Augment( en indiquant par exemple le nombre de cases au début de chaque « sous-liste », c'est tout à fait faisable. Par contre c'est plus difficile à maintenir peut-être.
Les animations ça va être assez galère. Augment( pourrait permettre d'ajouter les lunettes ou le chapeau à une liste du sprite. Ça semble même raisonnable.
Les animations pour les monstres ? Suffit de mettre la même pour tout le monde. Genre tu dessines un coup de griffe, un choc, quelque chose de générique. Je ne pense pas que tu pourras les faire bouger de toute façon ; tu n'as pas vraiment la puissance.
Citer : Posté le 07/07/2016 10:27 | # | Fichier joint
Alors là, je vois un truc qui m'intéresse :
On dispose de 6 fichiers de 26 listes
Je ne me suis jamais servi que des 26 premières listes ! Comment puis-je me servir des autres fichiers ?
Ce que tu ferais, si je comprends bien, c'est d'avoir à priori les dessins des tiles dans des listes pour les exploiter directement ? Est-ce que ça ne va pas me prendre beaucoup de place ?
Les animations ça va être assez galère. Augment( pourrait permettre d'ajouter les lunettes ou le chapeau à une liste du sprite.
C'est effectivement ce que je voulais faire ! Lors de la validation du choix de l'arme et du couvre-chef, tout est stocké avec Augment( pour avoir un seul sprite qui combine corps, couvre-chef et arme. Ce que je ne sais pas encore cependant, c'est si je vais faire cela en coordonnées complexes ou réelles.
Les animations pour les monstres ? Suffit de mettre la même pour tout le monde. Genre tu dessines un coup de griffe, un choc, quelque chose de générique. Je ne pense pas que tu pourras les faire bouger de toute façon ; tu n'as pas vraiment la puissance.
Je songeais à faire cela, effectivement. Je pourrais, à la limite, indiquer les coordonnées de l’œil du monstre (de profil) pour qu'il se ferme lorsqu'il reçoit un coup, mais ça impliquerait d'avoir des yeux à peu près de la même taille pour tous, de gérer les monstres sans yeux et donc d'ajouter dans leur ID deux indications supplémentaires : X et Y. Mais je ne pense pas pouvoir faire mieux de toute façon.
Pour les tiles et la tronche de la map : voici un screen du sidebar que j'ai dessiné. Tout sera dans le cadre.
Ce qui fait que je ne pars pas avec un écran de 127*63, mais un truc de 110*60, si je me souviens bien, justement adapté aux tiles 10*10.
Citer : Posté le 07/07/2016 10:36 | #
Je ne me suis jamais servi que des 26 premières listes ! Comment puis-je me servir des autres fichiers ?
Je pensais bien Je sais plus trop où (catalogue), se trouve la commande File, qui prend un paramètre de 1 à 6 et change le « LISTFILE » qu'on observe dans le gestionnaire de mémoire. Chaque fichier comporte 26 listes et est indépendant. Le paramètre existe aussi dans le menu SET UP du gestionnaire de stats.
Oui, tous les dessins de tiles ça va prendre de la place. Bien plus que de les hardcoder... (24 octets par valeur, comparé à « x+yi, », qui en prend 5). Après tu peux utiliser ça comme stockage temporaire pour changer de tileset. Si vraiment tu veux une grosse souplesse vaut mieux pas stocker comme je l'ai dit. Après des grosses listes de chiffres ça rend aussi le code long à parser -- au bout d'un moment il faudra du temps pour passer toutes tes conditions, faudra utiliser des labels pour les sauter par bloc mais le problème subsiste plus ou moins.
Ce que je ne sais pas encore cependant, c'est si je vais faire cela en coordonnées complexes ou réelles.
Normalement ça revient au même. Je préfère les complexes parce qu'il n'y a qu'une liste par image, après les deux se défendent. Y'a aussi que les complexes simplifient certaines transformations. Je n'ai jamais vu l'inverse, mais comme je n'ai pas croisé beaucoup ce genre de situations...
Pour les monstres qui reçoivent un coup, j'avais une technique pas mal en C. Il s'agissait de dessiner trois frames : le premier représentait le monstre pour x = x_initial. Le deuxième dessinait par-dessus le monstre à x = x_initial - 1 et x = x_initial + 1. Le dernier ajoutait x = x_initial - 2 et x = x_initial + 2. C'était assez sympa. Peut-être que tu pourrais trouver un système similaire qui soit accessible à la puissance du Basic.
Oui, vu ton cadre ton choix de tiles est pas mal
Citer : Posté le 07/07/2016 10:58 | #
Je pensais bien Je sais plus trop où (catalogue), se trouve la commande File, qui prend un paramètre de 1 à 6 et change le « LISTFILE » qu'on observe dans le gestionnaire de mémoire. Chaque fichier comporte 26 listes et est indépendant. Le paramètre existe aussi dans le menu SET UP du gestionnaire de stats.
Génial ! je ne savais même pas ! Ainsi, je peux peut-être aussi créer un fichier de sauvegarde dans un fichier autre, dans le genre File 4 ! Bon, à creuser.
Oui, tous les dessins de tiles ça va prendre de la place. Bien plus que de les hardcoder... (24 octets par valeur, comparé à « x+yi, », qui en prend 5)
Initialement je voulais les hardcoder. D'où l'utilité de rentrer les ID des tiles de la map dans une str : je pourrais dessiner en premier lieu tous les tiles dont l'ID est identique, puis passer au suivant.
Vient alors cette question : est-ce possible d'écrire :
Le problème des coordonnées complexes, c'est qu'elles sont inexploitables directement en drawstat. Cela implique de séparer à priori dans deux listes différentes parties réelles et imaginaires, donc d'effectuer une tâche supplémentaire qui ralentit le dessin.
Citer : Posté le 07/07/2016 11:02 | #
D'où l'utilité de rentrer les ID des tiles de la map dans une str : je pourrais dessiner en premier lieu tous les tiles dont l'ID est identique, puis passer au suivant.
Dans ce cas là mieux vaut adapter ton format de map. Une matrice classique aurait du mal à supporter cette opération. Mais après faut voir pour gérer les collisions encore
Non, StrSrc() ne prend que des chaînes. Il faut donc écrire le 10 dans une chaîne... ce qui semble trop compliqué pour être tout à fait immédiat.
Pour le Super DrawStat en complexes, il m'est arrivé plein de fois de faire :
C'est peut-être un poil plus lent, il faudrait tester en détail.
Citer : Posté le 07/07/2016 11:05 | #
Pour ce point, je compte beaucoup m'inspirer des travaux de Remiweb sur son Zelda PC. J'ai trouvé ce point absolument remarquable et incroyablement bien fait.
Oh ? Merci
Une fois mit en place ça devient effectivement très pratique !
Attention si tu utilises le même principe et que tu veux un affichage le texte rapidement : ne teste pas les caractères au fur et à mesure comme je le faisais au début, va chercher le prochain signe de retour à la ligne ou de pause avec un StrSrc.
Le système de pictures est pas mal même si 3 pictures prennent déjà beaucoup de place.
Le problème c'est que ça ne marche que si le parcours à suivre est linéaire (entre toujours d'un côté de l'écran, et sort toujours du même) ?
J'y avais pensé pour Zelda mais je ne l'avais pas utilisé. J'avais quand même gardé cette idée de côté pour faire des animations (par exemple garder une pièce en mémoire pour montrer l'ouverture d'une porte si on la déclenche plus tard depuis une autre salle).
Pour le stockage des cartes utiliser des Str me parait être une bonne solution.
Attention cependant aux caractères que tu vas utiliser, certains pèsent deux octets en mémoire au lieu d'un.
Pour les tiles j'avais effectivement pris du 10*10. C'est un bon compromis pour afficher suffisamment de choses sur un même écran et ne pas avoir à en charger trop à chaque fois.
Pour la technique du "traçage" en une fois c'est ce que j'utilisais sur mes essaies les plus récents :
Avec le système de murs en plus ça permettait d'avoir quelque-chose de bien mieux que sur Zelda-PC !
C'est rapide et j'avais des tiles très détaillées.
Enfin pour la séparation partie réelle/complexe je me demande ce que ça donne pour le chargement.
Avec deux listes tu fais quelque-chose du genre :
{1,1,2,2,3,3 -> Liste des Y
Avec les complexes tu vas pouvoir faire ça en une ligne mais il va falloir plein de caractères en plus :
Sachant qu'il faut minimum une 10e de coordonnées par tile ça risque d'être plus lourd.
Et tu verras que niveau poids toute optimisation est bonne à prendre. Avec la map, les sprites, les pictures et pas mal de texte on atteint vite la limite !
En tout cas si tu as des questions techniques sur Zelda-PC ou sur des astuces que j'ai pu utiliser n'hésite pas
Edit : ou si tu veux le code des dernières versions...
Citer : Posté le 07/07/2016 11:28 | #
*Gloup* ! Mais cette vidéo est.... ! C'est du basic, ce truc ?
Alors là ...
Le système de pictures est pas mal même si 3 pictures prennent déjà beaucoup de place.
Le problème c'est que ça ne marche que si le parcours à suivre est linéaire (entre toujours d'un côté de l'écran, et sort toujours du même) ?
Eh bien je réfléchissais à cela, effectivement, mais si j'ai un numéro attribué à chaque map, je peux m'en sortir avec cela.
Je peux par exemple utiliser les listes. mettons, la liste 1 :
J'ai la map 1 dans la picture 1, la map 2 dans la p. 2 et la m.3 dans la p. 3. bref. J'ai donc ceci :
Dans la liste 1. Ainsi, List 1 [A] contient le numéro de la map contenue dans la picture A.
Mais que se passe-t-il quand je vais sur la map 4 ? Je ne remplace non pas la map 3 mais la map un, c'est-à-dire l'avant-dernière map visitée :
Bon. Ce que je vais faire, c'est rajouter une quatrième variable dans la liste qui dicte la prochaine picture à remplacer. Dans ce dernier cas, je dois remplacer la map 2 contenue dans la picture 2 par une éventuelle nouvelle map (la 5). Je vais donc mettre List 1 [4] = 2
Ainsi :
Et si je rentre sur la map 5, je vais avoir :
Pour la technique du "traçage" en une fois c'est ce que j'utilisais sur mes essais les plus récents :
Effectivement, ça semble efficace. Ce que je me demande, c'est comment procéder pour charger la map. Il faut bien dire à la machine d'empiler tous les tiles de la map dans une liste, mais je me demande vraiment comment procéder de manière efficace.
Si je veux utiliser des complexes en hardcode, je ferais plutôt ça :
List 1 +[b] i [/b]{5, 6, 8, 2 ... →List 1
Citer : Posté le 07/07/2016 12:14 | #
Bon, à mon tour, je reprend dans l'ordre.
Pour le coup de dessiner tous les tiles identiques d'un coup, c'est une bonne idée. Ce que tu peux éventuellement faire, c'est dans un listfile vide :
Pour chaque ligne L de la matrice, chaque colonne C
Si Mat[L, C] > 26K et Mat[L, C] <= 26(K+1)
Mat[L, C] → A
L+iC → List A[Dim List A+1] // On ajoute les coordonnées du tile à la liste
Pour J de 1 à 26
Charger le tile J + 26K
Pour chaque élément de la liste J
Afficher les tile chargé aux coordonnées indiquées
À voir si c'est vraiment performant ou non vu la taille réduite des maps. Sur une grande map (50²), je pense que le résultat est sans appel, mais sur les petites j'en suis pas sûr.
Pour les strings, j'ai pas trop compris comment tu voulais les utiliser, mais c'est très souvent plus rapide que de manipuler les matrices ou les listes. Encore une fois, ça dépend de ce que tu veux faire.
Pour les listes de tiles, je pense que le mieux reste de faire un truc comme ça :
Z=1=>{1,2,3,…}+i{4,5,6,…→List X
Z=2=>{7,8,9,…}+i{1,2,3,…→List X
Tes données brutes prennent alors très peu de place au stockage, et au final pas des masses à l'exécution. Bien sûr, c'est une encore meilleure solution si tu retiens ce que j'ai proposé avant, comme ça tu ne charge le tileset qu'une seule fois à chaque nouvelle map.
Citer : Posté le 07/07/2016 15:01 | #
Voici ce que mon imagination farfelue a pondu pour le traitement des données des maps avec les str :
Mettons que j'aie, pour simplifier, 9 tiles différents. Ma carte comporte 6*11 cases, soit 66 cases, donc 66 potentielles tiles à dessiner.
à chaque tiles correspond un ID de deux chiffres. par exemple, mon tile 1 a pour ID 01, mon tile 2 a pour ID 02, etc.
Je code un vide avec "--" ou bien "00" (mais je pense que je vais opter pour le "--" pour plus de lisibilité)
Je vais donc coder une map de cette manière :
On va tenter donc de dessiner tous les tiles 1 (codés par 01) avant les tiles 2 en bleu puis les tiles 7 en vert. Vous remarquerez que j'ai des tiles 1 un peu partout, donc c'est toute la subtilité de la chose.
Voici mon lecteur de str-map. Notez que le dessin final sera effectué, dans l'exemple, avec la liste 3 en Super Drawstat.
"010203040506070809"→Str 2 //définie à priori pour tout le jeu. Contient dans l'ordre tous les ID
For 0→A To 8 // soit 9 boucles, pour le nombre de tiles (9).
StrMid(Str 2, 1+2A, 2→Str 3 // À chaque boucle, Str 3 contiendra "01", puis "02", puis "03"...
If StrSrc(Str 1, Str3 //Si la map (Str 1) ne contient aucun ID du tile correspondant (comme 03, dans notre cas), inutile de faire des boucles supplémentaires.
Then 1→D:ClearList 1
For 0→B to 65 // on a 66 cases
If StrSrc(StrMid(Str 1, 1+2B, 2 //Il s'agit de savoir si sur la position 1+2B Il y a bien l'ID du tile. Sinon on saute.
Then .5(1+StrSrc(StrMid(Str 1, 1+2B, 2→C
[b]i[/b] int(C/11)+MOD(C,11→List 1 [D] //on enregistre la position du tile : il y a 11 colonnes, je le rappelle. Division euclidienne.
Isz D:IfEnd
Next
Prog "-TILE"
IfEnd
Next
BG-Pict 1 // ma belle sidebar 8)
Dim List 3 → Tθmax
Graph(X ; Y)=(ReP List 3 [T], ImP List 3 [T
StoPict 2
Sous-programme "-TILE" :
Z=1⇒{1,2,3,…}+i{4,5,6,…→List 2
Z=2⇒{7,8,9,…}+i{1,2,3,…→List 2
etc. // La Liste 2 contient le dessin du tile.
For 1→X to D // Disons que X est une variable-poubelle.
Augment(List 3, List 2+ 10List 1[X]→List 3 //On ajoute aux coordonnées du dessin de base 10*la position de chaque tile
Next
Bon, ça paraît peut-être vachement complexe, mais je suis certain qu'il y a du potentiel là-dedans !
Si on exécute notre programme avec cet exemple :
Alors le code va d'abord s'arrêter sur le "01", l'ID du premier tile. Il va attribuer 1 à la variable D (un compteur pour la list 1) et réinitialiser la liste 1. ensuite, pour toutes les cases de la map, il va regarder s'il s'y trouve l'ID du tile. S'il n'y est pas, il passe à la case suivante directement. Dès qu'il en trouve un, il enregistre sa position dans List 1 [D] en ligne et en colonne, dans une valeur complexe, incrémente le compteur D et passe à la suite. Une fois l'analyse d'un tile terminée sur toute la str, la machine active le sous-prog "-TILE" pour "empiler" dans une même liste (List 3) toutes les coordonnées des dessins d'un même tile. revient ensuite au début. Remarquez que le programme n'effectue rien de tout cela pour les ID 03, 04, 05 etc., donc s'épargne les tâches inutiles.
Avez-vous compris ? J'en ai un peu bavé pour ne pas n’emmêler.
Ajouté le 07/07/2016 à 15:04 :
Sans les commentaires :
"010203040506070809"→Str 2
For 0→A To 8
StrMid(Str 2, 1+2A, 2→Str 3
If StrSrc(Str 1, Str3
Then 1→D:ClearList 1
For 0→B to 65
If StrSrc(StrMid(Str 1, 1+2B, 2
Then .5(1+StrSrc(StrMid(Str 1, 1+2B, 2→C
[b]i[/b] int(C/11)+MOD(C,11→List 1 [D]
Isz D:IfEnd
Next
Prog "-TILE"
IfEnd
Next
BG-Pict 1
Dim List 3 → Tθmax
Graph(X ; Y)=(ReP List 3 [T], ImP List 3 [T
StoPict 2
Sous-programme "-TILE" :
Z=1⇒{1,2,3,…}+i{4,5,6,…→List 2
Z=2⇒{7,8,9,…}+i{1,2,3,…→List 2
etc.
For 1→X to D
Augment(List 3, List 2+ 10List 1[X]→List 3
Next
Citer : Posté le 07/07/2016 15:09 | #
Petite opti, pour dans le deuxième code, tu peux remplacer If 0≠StrSrc(Str 1, Str3 par If StrSrc(Str 1, Str3
(Je ne peux pas m’empêcher d'optimiser )
PM Générateur
graph100+ bleue
Neuronix9302
2nde GT
Citer : Posté le 07/07/2016 15:11 | #
C'est fait ! Tu as été plus rapide que moi
Citer : Posté le 07/07/2016 15:20 | #
J'ai pas trop compris là, pourquoi tu stockes le dessin de la tile dans une liste ?
Déjà t'utilises pas un système décimal mais tu fais en base 36 (ou base 64 si t'as vraiment beaucoup de tiles). Ca divise le nombre d'octets par 2, donc tu peux déjà stocker 2 fois plus de tiles dans ton string. Ensuite tu hardcodes les dessins :
For 1->I To StrLen(Str 3)-1
Not StrCmp(StrMid(Str 3, I, I+1), "0") => Graph(X,Y) = ({....},{...}) //dessin de la tile correspondant à l'ID 0
Not StrCmp(StrMid(Str 3, I, I+1), "1") => Graph(X,Y) = ({....},{...}) //dessin de la tile correspondant à l'ID 1
Not StrCmp(StrMid(Str 3, I, I+1), "2") => Graph(X,Y) = ({....},{...}) //dessin de la tile correspondant à l'ID 2
//etc
Next
Si tu te dis "omagad mais à chaque tour on fait 9 tests" ce ne sont pas ces 9 tests qui vont te bottleneck, c'est plutôt la vitesse du drawstat (le drawstat est rapide mais son initialisation est longue).
D'après ce que j'ai compris, toi tu veux parcourir le string autant de fois qu'il y a de tiles, ce qui n'est pas optimisé du tout. Il vaut mieux le parcourir une seule fois, puis à chaque caractère dessiner la tile qui lui correspond. La boucle for ci-dessus est tout ce dont tu as besoin pour dessiner la map.
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 07/07/2016 15:29 | #
J'ai pas encore lu tout ce qui a été dit avant mais pourquoi partir sur du changement de base bien lourd alors qu'on a des tonnes de caractères disponibles ?
J'aurais fait comme ça :
Avec Str 1 contenant le caractère correspondant à une tile dont on veut récupérer l'ID.
En effet : ce code va déterminer à quelle position est le caractère dans une autre chaîne que tu définis (ici j'ai prit les chiffres, puis les lettres, puis des caractères au hasard).
Par exemple StrSrc("0123456789ABCDEFG","A") renvoit 11, tu fais correspondre l'ID 11 au caractère "A".
On va donc pouvoir facilement faire correspondre une valeur à un caractère et ne garder qu'un octet par tile !
C'est super rapide et ça évitera les 9 tests qui pour moi restent très lourd.
Ajouté le 07/07/2016 à 15:36 :
Pour la suite c'était simple dans mon Zelda :
Liste pour le sprite 1 -> List 1
Si ID = 2 :
Liste pour le sprite 2 -> List 1
Etc...
Augment(List 2,List 1+x+iy)
Avec List 2 celle qui servira à tout tracer en drawstat, et x et y les coordonnées de la tile qu'on ajoute.
Citer : Posté le 07/07/2016 15:53 | #
Pour moi StrSrc fait une boucle de toute façon, et comme elle ne renvoie qu'une seule valeur il faut faire plein de tests... et les tests seront de toute façon faits.
Prenons 5 tiles, numérotées de 0 à 4. Avec la méthode de Drak (et de remi) on aurait, si j'ai bien compris :
Do
StrSrc("01234", Str 1) -> A
A => Graph(X,Y) ...
LpWhile Not StrSrc("01234", Str 1)
Next
Sauf que ce code teste plusieurs fois le même caractère ! A chaque tour de boucle, la fonction StrSrc testera tous les caractères jusqu'à arriver à la position de la tile, qui est à chaque fois de plus en plus loin.
La solution à ce problème serait de faire un StrMid dans le StrSrc, comme ça pour une seule tile on ne teste qu'une seule fois chaque caractère. Oui mais voilà : on fait un seul test pour chaque caractère du string, mais on le fait 5 fois car il y a 5 tiles. Tandis que dans ma solution, on fait 5 tests pour chaque caractère du string, et on le fait une seule fois. Ce qui revient exactement au même. Donc remi, dans ta solution, tu feras toujours les 9 tests, qui seront d'ailleurs moins optimisés (on appelle plusieurs fois la fonction StrMid alors que dans ma boucle For on ne l'appelle qu'une seule fois par caractère).
D'ailleurs un moyen d'optimiser serait d'utiliser des If/Else au lieu de la double flèche, ce qui donnerait :
If StrMid(Str 3, I, I+1) = "0"
Then Graph(X,Y)....
Else If StrMid(Str 3, I, I+1) = "1"
Then Graph(X,Y)...
IfEnd
IfEnd
Dans la plupart des cas, cela ne ferait que 1 à 3 tests. Bien sûr, il faut placer les tiles les plus utilisées en haut des conditions, et celles les moins utilisées tout en bas des if/else. L'inconvénient est que ça te rajoute quelques octets (~7 par condition) mais bon on est pas à 60 octets près.
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 07/07/2016 16:01 | #
Je suis pas du tout d'accord, pour moi c'est bien plus rapide
Pour moi StrSrc fait une boucle de toute façon, et comme elle ne renvoie qu'une seule valeur il faut faire plein de tests... et les tests seront de toute façon faits.
La différence c'est que StrSrc quand il tourne c'est l'OS de la calculette qui s'en charge !
C'est donc beaucoup que de faire des opérations en basic qui est interprété.
Ensuite j'ai jamais parlé de faire comme ça, j'ai juste évoqué la manière de faire correspondre un caractère à un ID, pas la suite.
Du coup, sans trop y réfléchir, voilà ce que je ferais :
On parcours la chaine qui code une map. Quand on rencontre un nouvel ID par encore traité :
on va traiter toutes les tiles qui ont cet ID en complétant notre liste qui servira à l'affichage
et on marque cet ID comme déjà traité.
Citer : Posté le 07/07/2016 16:05 | #
Ha, excuse moi alors, j'avais pas bien compris ton message
Mais faudrait faire des benchmarks pour vraiment se départager. Ce que doit faire drak c'est tester sa méthode, puis la mienne, et voir combien de temps ça met à charger.
Par contre je comprends pas trop l'utilité de faire correspondre un caractère à un ID o_o
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 07/07/2016 16:09 | #
Par contre je comprends pas trop l'utilité de faire correspondre un caractère à un ID o_o
Réduire le poids pour coder les maps (1 octet par tile) et pouvoir facilement charger les sprites correspondants par la suite.
Le principe est simple
On présente une valeur de la chaine codant une carte : "A"
On récupère l'ID correspondant avec Strmid("012346...","A")->I
Si I=1 ⇒ Liste du sprite 1 -> List 1
Si I=2 ⇒ Liste du sprite 2 -> List 1
Si I=3 ⇒ Liste du sprite 3 -> List 1
etc...
Puis on ajoute le sprite récupéré à la liste 2 qui servira pour le traçage en une fois :
Augment(List 2, List 1)
Ca évite d'avoir avec un test avec un gros bout de code pour chaque If qui fait correspondre un sprite.
Ajouté le 07/07/2016 à 16:12 :
Non ça ne marche pas, il faut faire StrCmp(If StrMid(Str 3, I, I+1),"0")
Maintenant tu te vois mettre cette condition pour chaque sprite ?
Moi je fais un StrSrc au début, puis je n'ai que des petit "I=1⇒" par la suite !
Citer : Posté le 07/07/2016 16:12 | #
Ce ne serait pas mieux de faire un StrCmp au lieu de convertir le sprite en ID ? D'ailleurs au lieu de stocker dans les listes on peut directement faire un Graph(X,Y) en spécifiant la liste dans le code (avec des accolades).
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 07/07/2016 16:16 | #
Ce ne serait pas mieux de faire un StrCmp au lieu de convertir le sprite en ID ?
Toi t'as besoin de faire ça pour chaque valeur possible, bonjour la grosse condition à chaque ligne.
Non seulement c'est lent mais en plus ça rend le poids du programme beaucoup plus important qu'avec ma méthode.
Mais t'as rien suivi
Justement on veut générer une seule liste pour tout tracer en une fois.
Pourquoi ? Parce que c'est beaucoup plus rapide !
Il vaut mieux faire une fois un Graph avec 40 points que 4x un Graph avec 10 points.