TDM #XX : Les animations et les structures de données
Posté le 10/02/2021 18:25
Le Tutoriel du Mercredi (TDM) est une idée proposée par Ne0tux, qui recouvre tous les usages de la calculatrice - des applications de Casio à la conception de jeux en passant par la production artistique.
Aujourd'hui, nous allons voir les animations en C/gint et leur rapport avec les structures de données
Niveau : ★★★★☆
Tags : Animations, gint, POO
Bonjour et bienvenue dans ce TDM #21 !
Aujourd'hui nous allons parler des animations en C pour calculatrices et de comment les créer avec les structures !
Je précise que ce tutoriel contient des codes en C pour
gint de
Lephenixnoir, mais le code doit être facilement convertible pour le SDK "officiel" avec un poil de jugeote !
Voici le résultat final :
Durant ce tutoriel, j'utiliserai les frames suivants :
Frame idle
Frame de marche
Sommaire :
1) Qu'est ce qu'une animation ?
1.1) La boucle principale d'un jeu vidéo
2) Animation et boucle principale
2.1) Premières idées
2.2) L'utilité du sinon
2.3) L'apparition de la variable "frame suivant"
3) La POO (Programmation Orientée Objet)
3.1) Definition
3.2) L'exemple du catalogue d'animaux
4) S'inspirer de la POO pour les animations
4.1) Premières idées
4.2) Détection du clavier
5) Les structures de données en C
5.1) Equivalent des classes
5.2) Equivalent des objets
5.3) Affichage
5.4) Lire les entrées du joueur
5.5) Continuer l'animation précédente
5.6) Le code entier
6) Conclusion
7) Liens utiles
Commençons sans plus tarder par des révisions :
Qu'est ce qu'une animation ?
La question paraît bête : oui, je sais ce qu'est une animation : c'est une suite d'images passées très vite qui donne une impression de mouvement !
Oui, effectivement, bravo !
Mais dans un jeu vidéo ?
La boucle principale d'un jeu vidéo
Vous savez peut être qu'un jeu vidéo est, en général, constitué d'une boucle principale. Par exemple, pour détecter si l'utilisateur appuie sur la touche [EXE], il va y avoir une boucle principale, dans laquelle le jeu détecte si l'utilisateur appuie sur la touche. En algorithmique ça donne ca :
Répéter ∞ fois {
Si touche [EXE] est pressée : {
//faire l'action
}
}
et, en C/gint on a approximativement ca :
int main void(){
while(True){
if(getkey().key == KEY_EXE){
//faire l'action
}
}
}
Mais...attendez...une boucle qui change selon l'environnement et qui va très vite...ça ne vous rappelle pas quelque chose ?
Oui, les animations !
Animations et boucle principale
Premières idées
Prenons par exemple une "boucle" d'animation (une animation qui se répète toutes les 3 images par exemple).
On pourrait organiser un truc comme ça : on crée une boucle, et les images s'affichent chacune une répétition de la boucle sur 3.
On pourrait alors imaginer un truc du genre :
A la première répétition : afficher image1
A la deuxième répétition : afficher image2
A la troisième répétition : afficher image3
Puis on recommence au début !
C'est un bon moyen de faire une animation, mais comment la réaliser ?
Avec une variable, du genre :
Nouvelle variable nommée "a" = 1
Répéter ∞ fois : {
si a = 1 alors afficher la première image
si a = 2 alors afficher la deuxième image
si a = 3 alors afficher la troisième image
si a = 1 alors mettre la variable "a" à 2
sinon si a = 2 alors mettre la variable "a" à 3
sinon si a = 3 alors mettre la variable "a" à 1
}
?
En soi cela marche mais ce code est long et n'est pas très lisible, il faut de la concentration pour le comprendre.
Et puis, imaginez que vous vous retrouvez avec une animation contenant 3 images (que je vais appeler dorénavant des "frames"), le code serait complexe et illisible : on appelle cela un code spaghetti
Voici la représentation mathématique d'un code spaghetti :
C'est pas beau à voir, hein ?.
Le second problème, c'est que tous les "si ... alors ... sinon ..." sont placés dans le sinon du premier "si ... alors ... sinon ...", ce qui pose des problèmes : si le premier "si ... alors ... sinon ..." a un problème faisant que le "sinon" ne sera jamais exécuté, tout le reste est à l'arrêt.
On appelle cela les dépendances : certaines lignes de code sont dépendantes d'autres lignes, et cela peut poser problème, et peut créer l'effet domino : en effet, imaginons que dans un code toutes les lignes sont dépendantes (ce qui est très peu probable), un problème qui ne concerne que la ligne 1 va arrêter la ligne 2 car elle est dépendante de la ligne 1, la ligne 2 va arrêter la ligne 3, la 3 va arrêter la 4 etc...
Mais alors, pourquoi mettre tout le code dans des "sinon" ?
Très bonne question.
L'utilité du "sinon"
Pour y répondre je vais prendre un exemple de code en algorithmique que j'appelle " l'interrupteur " : un texte est affiché à l'écran, "1" ou "0", et quand l'utilisateur appuie sur une touche, disons [EXE], l'état du texte change : si "0" était affiché on affiche maintenant "1" et vice-versa.
Cela donne :
Répéter ∞ fois : {
Afficher la variable "a"
Si la touche EXE est pressée alors :
Si la variable "a" = 0 alors mettre la variable "a" à 1
Sinon mettre la variable "a" à 0
}
Le sinon est indispensable : réfléchissons. Imaginons que l'on ne le mette pas, on a ça :
Répéter ∞ fois : {
Afficher la variable "a"
Si la touche EXE est pressée alors :
Si la variable "a" = 0 alors mettre la variable "a" à 1
Si la variable "a" = 1 alors mettre la variable "a" à 0
}
La suite imagine que a = 0 au départ : lorsque EXE est pressé on a cette ligne :
Si la variable "a" = 0 alors mettre la variable "a" à 1
Or a = 0 donc ce if s'exécute et a = 1.
Ensuite :
Si la variable "a" = 1 alors mettre la variable "a" à 0
Depuis la ligne précédente a = 1 donc ce if s'exécute donc a = 0.
Au final, a n'a pas changé, le code ne marche pas.
Le sinon ne vérifie pas la condition avant de s'exécuter mais avant d'exécuter le if, donc ici cela marche.
Attends... j'ai une idée... pourquoi ne pas créer une variable framesuivant pour chaque image ? Et dans le code on dit : affiche le frame actuel et met le frame actuel au frame suivant !
Effectivement, c'est la solution, mais... comment faire ?
L'apparition de la variable "frame suivant"
Les variables sont multipliées par 2 ; pour 3 frames on a ces variables : frame1 ; frame1next ; frame2 ; frame2next ; frame3 ; frame3next.
Et cela en fait 6 !
Encore là, c'est acceptable, mais imaginez une animation de 60 frames : on a 120 variables !
Et si en plus chaque frame a une position x et une position y, ce qui est plus propre, chaque frame a 4 variables ; pour le frame 1 ces variable sont : frame1 (l'image) , frame1next, frame1posx , frame1posy.
Pour une animation de 60 frames, cela représente tout de même 240 variables !
Cela impacte en premier lieu le développeur, qui ne s'y retrouvera pas dans ses variables, mais aussi la machine, car les variables prennent de la RAM, et l'utilisateur, qui va voir le programme ralenti.
Ha, j'ai une idée ! Et si on... heu... ha non...heu...
Bon, j'ai la solution, ne cherchez pas.
Et si on s'inspirait de...
La POO (Programmation Orientée Objet)
On m'informe dans l'oreillette que j'ai dit un mot compliqué...
Ne vous inquiétez pas, je vais expliquer.
Je demande juste aux programmateurs pointilleux de ne pas me cracher dessus : je vais résumer un max.
Résumons... nous aurions besoin de plusieurs variables par frames...
Définition
La programmation orientée objet est un style d'écriture du code, et Wikipédia dit
ici que :
Dans ce style, le code source est une suite de descriptions de classes ou de prototypes, avec la description de leurs caractéristiques ( propriétés ) et de leurs comportements ( méthodes ).
Ne paniquez pas.
En POO, on crée des
objets, et les variables ont des "états" en fonction de ces objets.
L'exemple du catalogue d'animaux
Prenons un exemple : je veux faire un programme simple dans un langage quelconque qui demande à l'utilisateur le nom de l'animal et la caractéristique, et le programme donne le résultat (Exemple : le programme demande le nom de l'animal, l'utilisateur répond "Chien", il demande la caractéristique, l'utilisateur rentre "Cri" et le programme renvoie "Wouf !").
Pour cela on pourrait faire un truc du genre : (en algorithmique)
Afficher : "Quel animal ?"
Attendre la saisie de l'utilisateur et la stocker dans la variable "animal"
Afficher : "Quelle caractéristique ?"
Attendre la saisie de l'utilisateur et la stocker dans la variable "caractéristique"
Si animal = "chien" : {
Si caractéristique = "Cri" : {
Afficher "Wouf !"
}
Si caractéristique = "Poids" : {
Afficher "Le poids moyen se situe entre 15 et 25 kg."
}
Si animal = chat : {
Si caractéristique = "Cri" : {
Afficher "Miaou !"
}
Si caractéristique = "Poids" : {
Afficher "Le poids moyen se situe entre 4 et 6 kg."
}
}
Vous le voyez, ce code est long et il faut du temps pour le comprendre.
L'inventeur de la POO a trouvé ce problème embarrassant, et voici sa solution : on crée deux variables : Cri et Poids.
On crée deux "objets" : chat et chien, et pour chaque objet une "version" différente de la variable est crée : par exemple il existe deux versions de la variable Cri : celle du chat et celle du chien.
Voici une partie du code amélioré avec la POO :
Nouvelle variable "Cri"
Nouvelle variable "Poids"
Nouvel objet "Chien" : {
Sa version de "Cri" = "Wouf !"
Sa version de "Poids" = 20
}
Nouvel objet "Chat" : {
Sa version de "Cri" = "Miaou !"
Sa version de "Poids" = 4
}
Oui, c'est perturbant mais vous allez vous y faire.
Tous mes objets sont des animaux, mais imaginons que je veuille créer un objet "fourmi", pour laquelle je veux enlever la variable "cri".
Facile : on ajoute au code précédent :
Nouvel objet "Fourmi" : {
Sa version de "Poids" = "150 milligrammes"
}
!
En soi oui. Mais la programmation est pleine de règles, et on ne peut pas choisir de mettre ou non une variable. C'est un peu comme pour un livre : si tu écris un roman mais que tu ne trouves pas de nom, tu ne peux pas décider de ne pas en mettre.
Il va falloir créer des "types" d'objets. On va par exemple créer le type "Animal", dans lequel on va créer les variables "Poids" et "Cri", et créer le type "Insecte" dans lequel on va créer la variable "poids" ; en programmation on appelle ces types des "classes".
On peut alors obtenir un truc du genre :
Nouvelle classe "Animal" : {
Elle contient la nouvelle variable "Cri"
Elle contient la nouvelle variable "Poids"
}
Nouvelle classe "Insecte" : {
Elle contient la nouvelle variable "Poids"
}
Nouvel objet "Chien" de type "Animal" : {
Sa version de "Cri" = "Wouf !"
Sa version de "Poids" = 20
}
Nouvel objet "Chat" de type "Animal" : {
Sa version de "Cri" = "Miaou !"
Sa version de "Poids" = 4
}
Nouvel objet "Fourmi" de type Insecte : {
Sa version de "Poids" = "150 milligrammes"
}
On touche au but ! Mais, pour les entrées de l'utilisateur ?
En POO, on peut récupérer la "version" d'une variable via cette syntaxe : (la plus courante)
[Nom_de_l'objet].[nom_de_la_variable]
Par exemple, dans notre cas, écrire dans mon programme
Afficher Fourmi.Poids affichera "150 milligrammes", ou écrire
Afficher Chien.Cri affichera "Wouf !".
On pourrait donc faire ca :
Afficher : "Quel animal ?"
Attendre la saisie de l'utilisateur et la stocker dans la variable "animal"
Afficher : "Quelle caractéristique ?"
Attendre la saisie de l'utilisateur et la stocker dans la variable "caractéristique"
Nouvelle classe "Animal" : {
Elle contient la nouvelle variable "Cri"
Elle contient la nouvelle variable "Poids"
}
Nouvelle classe "Insecte" : {
Elle contient la nouvelle variable "Poids"
}
Nouvel objet "Chien" de type "Animal" : {
Sa version de "Cri" = "Wouf !"
Sa version de "Poids" = 20
}
Nouvel objet "Chat" de type "Animal" : {
Sa version de "Cri" = "Miaou !"
Sa version de "Poids" = 4
}
Nouvel objet "Fourmi" de type Insecte : {
Sa version de "Poids" = "150 milligrammes"
}
Afficher Animal.Caractéristique
Et on arrive au résultat souhaité !
Mais les animations dans tout ca ?
Ca arrive...
S'inspirer de la POO pour les animations
Premières idées
On pourrait par exemple faire un truc comme cela pour relier les 2 sujets...
Nouvelle classe "frames" : {
Nouvelle variable "Image"
Nouvelle variable "Nextframe"
Nouvelle variable "Posx"
Nouvelle variable "Posy"
}
Nouvel objet "Frame1" de type "frames" : {
image = image_frame1
nextframe = "Frame2"
Posx = 1
Posy = 10
}
Nouvel objet "Frame2" de type "frames" : {
image = image_frame2
nextframe = "Frame3"
Posx = 1
Posy = 10
}
Nouvel objet "Frame3" de type "frames" : {
image = image_frame3
nextframe = "Frame1"
Posx = 1
Posy = 10
}
Nouvelle variable "FrameActuel" = Frame1
Répéter ∞ fois : {
Afficher FrameActuel.image
Attendre 25 ms
FrameActuel = FrameActuel.nextframe
}
On commence à y arriver !
Ici, on a une animation qui fonctionne, mais qui ne s'arrête pas.
Détection du clavier
Il faudrait créer une variable de type
boolean isAnimated, la mettre à true si on appuie sur le bouton permettant d'avancer, et l'animation se fait seulement si isAnimated est à true.
Et pour savoir quand l'animation est finie, on crée une variable AnimateTime, sorte de décompte avant la fin du frame.
On a ce code :
Nouvelle classe "frames" : {
Nouvelle variable "Image"
Nouvelle variable "Nextframe"
Nouvelle variable "Posx"
Nouvelle variable "Posy"
}
Nouvel objet "Frame1" de type "frames" : {
image = image_frame1
nextframe = "Frame2"
Posx = 1
Posy = 10
}
Nouvel objet "Frame2" de type "frames" : {
image = image_frame2
nextframe = "Frame3"
Posx = 1
Posy = 10
}
Nouvel objet "Frame3" de type "frames" : {
image = image_frame3
nextframe = "Frame1"
Posx = 1
Posy = 10
}
Nouvelle variable "FrameActuel" = Frame1
Répéter ∞ fois : {
Si touche avancer est pressée : {
Mettre isAnimated à true
Mettre AnimateTime à 3
//3 car il y a 3 frames avant la fin de l'animation
}
Si isAnimated = true : {
Afficher FrameActuel.image
Attendre 25 ms
FrameActuel = FrameActuel.nextframe
Enlever 1 à AnimateTime
Si AnimateTime = 0 : {
//l'animation est terminée
Mettre isAnimated à false
}
}
Sinon : {
//Idle est l'image de la position inactive
Afficher Idle
}
}
Bon, on a la base, maintenant...
Les structures de données en C
Bon, super, nous avons le code en algorithmique !
Mais je vous rappelle que gint, lui, est en C !
Alors comment passer de l'un à l'autre ?
Equivalent des classes
En C, la POO n'existe pas , mais il existe une solution similaire : les "structures".
Pour définir l'équivalent d'une classe, rien de plus simple :
struct name_of_class {
int variable1;
long variable2;
}
Equivalent des objets
Pour définir l'équivalent d'un objet, c'est plus compliqué. Je vais d'abord donner la syntaxe d'un objet faisant partie de la classe "name_of_class" crée précédemment :
struct name_of_class name_of_object[2] {
{12,14}
//Dans l'exemple précédent, la valeur de la variable variable1 pour l'objet name_of_object[1] est 12 et la valeur de la variable variable2 pour l'objet name_of_object[1] est 14
{20,41}
}
Alors, je sais que c'est difficile à comprendre, mais chaque "objet" créé est une liste d'"objets".
Mais en soi cela peut être une bonne idée : on crée une "liste d'objets" pour chaque animations, et chaque objet est un frame.
Concrètement, voyons le code de la structure anim, je vous le donne et vous explique après :
struct anim {
bopti_image_t *img;
int duration;
struct anim *next;
};
La 1ère variable,
bopti_image_t *img; est la variable contenant l'image.
La 2ème,
duration, contient le nombre de frames sur lesquels l'image va rester avant de passer à une autre animation
La 3ème,
struct anim *next;, est un
pointeur.
C'est une variable qui envoie vers une autre variable. Ici, elle contient le frame suivant, qui est aussi de type struct.
Voici donc ce qu'on obtient pour l'animation de marche :
struct anim anim_walk[2] = {
{ &img_personnagemarche, 3 , &anim_walk[1] },
{ &img_personnage, 3, &anim_walk[0] },
};
Pour la facilité du code on va ajouter une animation idle :
struct anim anim_idle[1] = {
{ &img_personnage, 1, &anim_idle[0] },
};
Et voici finalement tout le code des structures, à mettre avant
int main(void) :
struct anim {
bopti_image_t *img;
int duration;
struct anim *next;
};
struct anim anim_idle[2] = {
{ &img_personnage, 40, &anim_idle[1] },
{ &img_personnage2, 40, &anim_idle[0] },
};
struct anim anim_walk[2] = {
{ &img_personnagemarche, 3 , &anim_walk[1] },
{ &img_personnage, 3, &anim_walk[0] },
};
Pour créer la variable current_anim, sachant que celle ci est un pointeur, il faut faire :
struct anim *current_anim = &anim_idle[0];
&anim_idle[0] est l'animation par défaut, sachant qu'en arrivant sur l'add-in le personnage est normalement en position idle.
Et voici toutes les variables à définir avant la boucle :
struct anim *current_anim = &anim_idle[0];
int current_anim_time_left = 0;
extern bopti_image_t img_personnage;
extern bopti_image_t img_personnagemarche;
//État du personnage : 0=arrêté, 1=marche
int state = 0;
//État du personnage au frame précédent
int previous_state = 0;
Affichage
Pour l'affichage de notre animation, rien de compliqué : on affiche aux positions x et y de
current_anim l'image de
current_anim comme ceci :
dclear(C_WHITE);
dimage(current_player->x,current_player->y, current_anim->img);
dupdate();
Lire les entrées du joueur
Ensuite on va lire les entrées du joueur ; et pour cela nous allons utiliser
keydown qui nécessite un
clearevents(); qui va effacer les évènements précédents.
Lors de l'appui sur une touche il va falloir changer d'animation : pour faire cela proprement, on va créer 2 variables :
state, qui va contenir l'état de notre personnage (si la touche permettant d'avancer est pressée, state = 1) et
previous_state, qui contient le
state du frame précédent.
Si state = 1 mais que previous_state = 0, on vient de commencer à marcher, et on peut donc passer à l'animation de marche, et vice-versa.
Ca donne : (entrées + test des variables state et previous_state)
clearevents();
state = 0;
if(keydown(KEY_RIGHT))
state = 1;
if(previous_state == 0 && state == 1)
{
//On vient de commencer à marcher
current_anim = &anim_walk[0];
current_anim_time_left = current_anim->duration;
}
else if(previous_state == 1 && state == 0)
{
// On vient de s'arrêter
current_anim = &anim_idle[0];
current_anim_time_left = current_anim->duration;
}
Continuer l'animation précédente
Bon, ca c'est si on vient de commencer à marcher, ou de s'arrêter, mais si il n'y a aucun changement ?
Et bien, on enlève 1 à
current_anim_time_left et, si il est égal à 0, on met current_frame au frame suivant
et current_current_anim_time_left à
current_anim_time_left = current_anim->duration !
Voici le code : (à placer après le bout de code précédent, le else correspond à " if(previous_state == 0 && state == 1) ")
else
{
//On continue l'anim précédente
current_anim_time_left--;
if(current_anim_time_left <= 0)
{
current_anim = current_anim->next;
current_anim_time_left = current_anim->duration;
}
}
Puis, si state = 1, on ajoute 1 à x pour que le personnage avance comme ceci :
if(state == 1)
{
x = x+1;
}
}
Enfin, on met le délai d'attente, puis on met previous_state à state :
//Délai
sleep_us(25000);
// Préparation des invariants du frame suivant
previous_state = state;
Et voilà !
Le code entier
Voici le code entier :
#include <gint/display.h>
#include <gint/keyboard.h>
#include <gint/clock.h>
extern bopti_image_t img_personnage;
extern bopti_image_t img_personnagemarche;
struct anim {
bopti_image_t *img;
int duration;
struct anim *next;
};
struct anim anim_idle[2] = {
{ &img_personnage, 40, &anim_idle[1] },
{ &img_personnage2, 40, &anim_idle[0] },
};
struct anim anim_walk[2] = {
{ &img_personnagemarche, 3 , &anim_walk[1] },
{ &img_personnage, 3, &anim_walk[0] },
};
int main(void)
{
struct anim *current_anim = &anim_idle[0];
int current_anim_time_left = 0;
extern bopti_image_t img_personnage;
extern bopti_image_t img_personnagemarche;
//État du personnage : 0=arrêté, 1=marche
int state = 0;
// État du personnage au frame précédent
int previous_state = 0;
while(a != 1)
{
//Affichage
dclear(C_WHITE);
dimage(current_player->x,current_player->y, current_anim->img);
dupdate();
//Lecture des entrées ; si on n'appuie sur rien, state=0
clearevents();
state = 0;
if(keydown(KEY_RIGHT))
state = 1;
//Exécution des animations
if((previous_state == 0 && state == 1))
{
//On vient de commencer à marcher
current_anim = &anim_walk[0];
current_anim_time_left = current_anim->duration;
}
else if(previous_state == 1 && state == 0)
{
//On vient de s'arrêter
current_anim = &anim_idle[0];
current_anim_time_left = current_anim->duration;
}
else
{
//On continue l'anim précédente
current_anim_time_left--;
if(current_anim_time_left <= 0)
{
current_anim = current_anim->next;
current_anim_time_left = current_anim->duration;
}
}
//Simulation du monde
if(state == 1)
{
xref = xref-1;
}
//Délai
sleep_us(25000);
//Préparation des invariants du frame suivant
previous_state = state;
}
getkey();
return 1;
}
On a à présent une belle animation, celle ci normalement :
Conclusion
Vous avez appris dans ce tutoriel les bases des animations, la POO et son équivalent en C, les structures de donnés et comment utiliser ces dernières en C/gint !
J'espère que ce TDM vous aura aidé dans la réalisation de vos animations, j'ai été très content de le faire !
Merci à Lephenixnoir et à Dark storm pour leur aide qui m'a été précieuse !
Si vous avez des questions, lâchez vous en commentaires !
Liens utiles
Voir le TDM précédent :
Comprendre et utiliser le path sous Linux
Consulter l'ensemble des TDM
Citer : Posté le 10/02/2021 18:40 | #
Eh ben...
Je ne m'attendais pas à ça on peut dire.
Sur le contenu même, je ne pourrai pas juger, non pas que je ne sois pas compétent là dedans mais si en fait c'est ça .
Par contre sur la forme, là je peux juger, et je suis très agréablement surpris ! C'est plutôt structuré et bien écrit, et puis il y a vraiment une amélioration depuis ton Press Shift par exemple.
Je t'encourage donc à continuer dans cette voie (dans la mesure où tu dis pas n'importe quoi hein) et puis tu pourras te faire exploiter comme rédacteur par les admins
Citer : Posté le 10/02/2021 18:53 | #
Merci pour l'initiative, c'est toujours chouette d'avoir du contenu inédit sur le site
Par contre… Tu mélange beaucoup de choses, y'a aussi pas mal d'inexactitudes voir d'erreurs.
Pourquoi mélanger POO et animation ? Les deux sont complètement différents, et le premier nécessiterait beaucoup plus qu'un TDM pour être abordé.
Le C n'est pas fait pour faire de la POO. Tu peux tricker avec des pointeurs de fonctions dans des structures, mais c'est vite assez lourd, compliqué et par conséquent crade. D'ailleurs utiliser des structures comme tu fais, même si c'est la première étape, n'est pas considéré comme de la POO.
Pour ce qui est des animations avec gint, Lephe a déjà couvert ça dans les tutos gint : https://www.planet-casio.com/Fr/forums/topic14914-1-tutoriels-dutilisation-de-gint.html#177758
Pour faire plus général, c'est très fouilli. Je te conseille de poser le plan de ton tutoriel, le détailler en section, sous section, etc. jusqu'à ce que la structure soit claire et cohérente. Ensuite tu pourra remplir les parties. Commencer par le plan permet d'organiser sa pensée, et par conséquent rendre clair ton exposé.
Au passage, j'ai retiré le numéro du TDM, on t'en attribuera un quand ton article sera prêt à être publié en page d'accueil
Citer : Posté le 10/02/2021 21:02 | #
Oh, intéressant ! Et c'est un article détaillé, super ! À publier, c'est sûr.
Attention quand même, si tu veux publier quelque chose il faut au moins prévenir un rédacteur... même entre nous on ne publie pas d'articles à l'improviste.
Dans l'ensemble c'est un peu confus, mais rien n'est parfait du premier coup et je trouve ça largement suffisant sur le contenu. Je n'ai que deux remarques à faire.
D'une part ce que tu décris ce n'est pas vraiment de la POO, qui comme tu le sais n'existe pas en C ; tu ne parles que de structures. Je suggère de reformuler en disant « structures » à la place de « POO », sachant que la grande idée y est toujours : on groupe dans une structure des données qui vont ensemble (on n'a juste pas le code avec).
Et sinon l'espacement est très irrégulier dans ton texte, avec des indentations qui manquent (ça tu peux pas le laisser passer !) et beaucoup d'espacements différents autour des blocs de code ; en principe tu dois avoir une ligne blanche avant le [code], et pas de ligne blanche après le [/code] (il y a en une automatiquement), ce qui donne ça.
Une passe rapide de 5-10 minutes pour ajuster ces espacements et lndentations donnerait un aspect plus professionnel à ton tuto.
Sinon je suis chaud pour le publier. Contrairement à Darks, je ne pense qu'il y ait de mérite à tout refaire en profondeur. Le code est effectivement inspiré (en partie pris) des tutos d'utilisation de gint, donc évidemment ça recoupe. Et ce serait rude de refuser des efforts de rédaction.
Citer : Posté le 11/02/2021 19:02 | #
Merci beaucoup pour vos retours !
@Lephenixnoir, je ne savais pas qu'il fallait demander à un rédacteur, j'y ferai attention à l'avenir ! Et cela serait encore mieux si je deviens rédacteur hum hum...
J'ai fait les changements que tu m'a indiqué, merci.
J'ai par contre laissé "POO" lorsque je ne parlais pas du C, car je pense que c'est un bon moyen d'amener l'utilisateur aux structureqs.
J'ai rajouté des sous titres dans les sous titres ( ), et je vous ai mentionné à la fin, toi et Dark storm.
Merci beaucoup pour tes conseils, l'article est il prêt à être publié ? (je sais que la vie n'est pas si simple)
Citer : Posté le 11/02/2021 19:08 | #
Yay ! C'est bien ce à quoi je pensais, la distinction structure/POO est vraiment mieux. Encore que les indentations sont toujours un peu, euh... exotiques par endroits. Regarde bien au début, notamment dans l'exemple du catalogue d'animaux qui n'est pas du tout indenté.
Une photo/vidéo du résultat serait pas mal pour aller à la fin de l'article (si tu y arrives).
Sinon j'ai bien prévenu que même entre nous on s'avertit avant de publier hé hé. :3
Citer : Posté le 11/02/2021 19:16 | #
Je précise que c'est à des fins d'organisation qu'on essaye d'avertir les autres quand on poste un article
Ça évite des moments un peu de panique comme lors de la publication de mon dernier Press Shift
L'article en lui même est largement mieux construit que le précédent, tu fais des progrès !
Le truc qui manque selon moi, c'est les photos. Avoir une illustration des étapes est assez importante pour tenir le lecteur jusqu'à la fin de l'article.
Actuellement, on sait qu'on arrive à une animation, mais concrètement, ça peut donner quoi visuellement sur un programme ? C'est une question que risque de se poser le lecteur
Sinon merci, c'est quand même du bon boulot
(Et de toute façon, vous pouvez pas dire le contraire)
MultipliCasio
RDM Calculs
Back Mirror
A Switch To The Top C
Citer : Posté le 11/02/2021 19:28 | #
Beaucoup mieux
J'ai pas testé, mais y'a des incohérences entre les snippets et le code complet, en particulier ce passage : if((previous_state == 0 && state == 1) || (previous_state == -1 && state == 1)) . Tu n'explique pas d'où sort le previous_state == -1, ce qui peut surprendre.
Revoir un poil l'indentation. Que ce soit du C ou du pseudo-code, tiens toi à un style et respecte-le. La page Wikipédia à ce sujet est assez bien faite, tu peux piocher ce qui t'intéresse.
+1 pour les illustrations, c'est toujours mieux avec. On peut t'aider à les héberger sur le site si tu le souhaite.
Pour les commentaires inline, je conseille la syntaxe // Comment par rapport à /* Comment */, d'une part car c'est par convention utilisé comme tel, et d'autre part car si ma mémoire ne me trompe pas, les commentaires inline // sont affichés en vert part le site.
Il ne reste plus grand chose à modifier avant que ce soit un bon article
Citer : Posté le 12/02/2021 07:01 | #
Ah, pour le state = -1 j'ai copié une partie du code d'Elphorina qui contient state = -1 pour reculer, et j'ai oublié de l'enlever !
Modifié !
Je vais indenter, et voir comment mettre des illustrations !
Ajouté le 13/02/2021 à 11:52 :
J'ai rajouté des illustrations et j'ai indenté !
Ajouté le 14/02/2021 à 15:34 :
J'ai rajouté un sommaire fonctionnant avec des ancres ! (et j'en ai bavé )
Citer : Posté le 14/02/2021 15:37 | #
Bien joué ! o/
Citer : Posté le 14/02/2021 15:38 | #
Merci !