IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

algorithme pour translation "smooth"


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Par défaut algorithme pour translation "smooth"
    Bonjour à tous,

    Dans le cadre de mon petit projet graphique perso, j'ai besoin de simuler des déplacements en 3D tenant compte d'une certaine accélération. Seulement voilà, j'utilise SFML, et si je souhaite avoir des déplacements "smooth" réalisés via clavier (ou pas) je dois donc passer par des booléens. Rien de complexe dans tout ça, mais je pense que ma manière de faire est loin d'être la plus optimale.

    Par exemple, j'aurais tendance à pondre un code comme suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
     
    glm::mat4 transform(1.0f);   // tenseur identité pour les transformations
     
    glm::vec3 speed(.0f, .0f, .0f);  // vitesse sur x, y et z
    glm::vec3 accel(1.0f, 1.0f, -9.81f); // accélération sur x, y et z (je pense que tout le monde comprends que c'est en m.s-²)
    glm::vec3 decel(.5f, .3f, 2.2f);    // décélération 
     
    bool trans_x;           // on veut aller en x positif
    bool trans_x_inv;     // on veut aller vers x négatif
    bool trans_y;          // etc...
    bool trans_y_inv;
    bool trans_z;
    bool trans_z_inv;
     
    if(trans_x)
    {
        if(speed.x < 15.0f)
        {
            speed.x += accel.x;
        }
        if(speed.x > 15.0f)
        {
            speed.x = 15;
        }
    }
    else if (!trans_x && !trans_x_inv)
    {
        if(speed.x > .0f)
        {
            speed.x -= decel.x;
        }
        if(speed.x < .0f)
        {
            speed.x = .0f;
        }
    }
     
    // on imagine facilement la suite...
    // et enfin : 
     
    transform = glm::translate(transform, speed);
     
    // et on continue le code nécessaire
    Donc voilà l'idée en fait... Comme ça, on a quelque chose qui peut marcher, mais il est certainement envisageable de faire plus optimal (un code moins lourd à écrire). j'imagine bien une ou deux autres méthodes possibles, mais dans le doute, je préfère m'orienter vers vous, qui avez certainement plus d'expérience que moi en développement & algorithmique et/ou simplement une meilleure idée.

    Pour résumer, un algorithme qui, en fonction de simples booléens dans les 3 dimension d'un espace euclidien ( x2 pour les inverse aussi), tenant compte d'accélérations et décélérations différentes pour chacun des axes, me fourni en sortie un vecteur vitesse.

    Précision complémentaire : oui, si l'objet est déjà en mouvement, le simple fait de stopper tout mouvement (en gros passer tout les booléens à 0) induit une décélération (liée à l'inertie de l'objet).

    Merci d'avance pour vos retours ! Si le sujet n'est pas clair ou que vous avez besoin de plus d'infos, n'hésitez pas à me demander.

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Tu pourrais déjà utiliser quelques éléments qui pourraient te simplifier la vie, tels que:
    • une matrice 3*3 (dmat3x2 ) pour représenter l'accélération et la décélération, étant entedu que l'on peut accélérer sur un axe et décélérer sur un autre
    • un système de masque qui te permettrait de préciser la direction d'un déplacement (d'une accélération ) sous une forme proche de move{Xplus | Yminus | Znone}; et qui te permettrait de le tester sous une forme proche de if(move & Xplus == Xplus){/* ... */}
    • une énumération représentant les différents axes
    • une énumération pour représenter l'accélération et la décélération

    Cela ne se fera sans doute pas sans peine, ne serait-ce que parce que tu devras veiller à être cohérent pour ton masque : les deux valeurs de X (XDecel et XAccel) de Y (YDecel et YAccel) et de Z (ZDecel et ZAccel)) sont exhaustives entre elle (on ne peut pas avoir, dans un même temps XDecel et XAccel dans le même masque ) mais chaque valeur de X, de Y et de Z peut être combinée avec n'importe quelle valeur des deux autres axes

    De cette manière, tu pourras écrire un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    /* mat :  matrice à mettre à jour
      type : accélération ou décélération
      axis  : axe à manipuler
      diff   : différence à appliquer
    void updateDiff(matrice & mat, updateType update, axisType axis, double diff){
        //appliquer la différence à l'élément update, axis de la matrice
     }
    (bien sur, la différence peut avoir une valeur arbitraire, ce qui te permettrait d'éviter à avoir la transmettre à chaque fois, si sa valeur devait être fixe )
    et, à partir de là, tu pourras avoir un code qui applique les différences sur les éléments de ta matrice de manière adéquate à partir du masque fourni, qui pourrait prendre une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /* mat: matrice à mettre à jour
       mask : masque des transformations à appliquer
       diff : différence à appliqer à la matrice
    */
    vpod updateDiff(matrice & mat, UpdateMask mask, double difff){
        if(mask & XDecel == XDecel)
            updateDiff(mat, decelerate, Xaxis, diff);
        if(mask & XAccel == XAccel)
            updateDiff(mat, accelerate, Xaxis, diff);
        if(mask & YDecel == YDecel)
            updateDiff(mat, decelerate, Yaxis, diff);
        if(mask & YAccel == YAccel)
            updateDiff(mat, accelerate, Yaxis, diff);
        if(mask & ZDecel == ZDecel)
            updateDiff(mat, decelerate, Zaxis, diff);
        if(mask & ZAccel == ZAccel)
            updateDiff(mat, accelerate, Zaxis, diff);
    }
    Une fois que tu aurais cela, tu pourrais calculer la vitesse de l'élément sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /* speed : vitesse actuelle
       mat :matrice d'accélération / décélération
       mask : masque devant être utilisé
       diff : différence à appliquer
    vec3 computeSpeed(vec3 speed, matrix & mat,  UpdateMask mask, double difff){
        updateDiff(mat, mask, diff);
        auto x = speed[Xaxis]+mat(accelerate,Xaxis)-mat(decelerate,Xaxis); // voir comment accéder à un élément spécifique de la matrice
        auto x = speed[Yaxis]+mat(accelerate,Yaxis)-mat(decelerate,Yaxis); // dito
        auto x = speed[Zaxis]+mat(accelerate,Zaxis)-mat(decelerate,Zaxis); // dito
    }
    Et tu pourras utiliser la valeur renvoyée par cette fonction pour déterminer la nouvelle position de tes objets à partir de leur position "d'origine"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre éclairé Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Par défaut
    Bonjour, et merci pour ce retour !

    Effectivement, je n'avais pas vraiment penser à tout concaténer dans un tenseur... Je vais réfléchir à cette idée telle que tu la décris ; mais comme tu dis, il faut bien veiller à ce que le masque soit cohérent...
    mais cela me surprend ; il faut bien entendu réfléchir à cette solution, mais le problème me semble avoir une solution plus simple que ça pourtant.... Peut être que d'autres pourront confirmer ou non cette idée que je me fait du sujet ?

    En tout cas, merci pour cette première idée ; je vais voir si je peux la développer un peu ce soir.


    Petit sujet annexe, je vais tester ça plus tard dans la soirée aussi mais est-il possible de multiplier un booléen par une valeur quelconque (un int ou float) ? Je pense que oui mais je me demande... Si ce n'est pas le cas, la solution est assez simple mais bon. Je me demande ça car finalement, on pourrait traiter les situations via un vecteur contenant les booléens et le multiplier par le tenseur des accélérations.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
     
    // cas des accélérations : 
     
    glm::vec3 isAccelerating(0, 0, 0); // vecteur des booléens d'accélération
    glm::vec3 isDecelerating(0, 0, 0); // vecteur des booléens de décélération
     
    glm::vec3 speed(.0f, .0f, .0f);  // initialisation du vecteur vitesses
    glm::mat3 accel(.0f);  // initialisation du tenseur des valeurs d'accélération (tenseur diagonal)
    glm::mat3 decel(.0f);  // initialisation du tenseur des décélérations (tenseur diagonal)
     
    // je ne détaillerais pas ici, mais on défini les valeurs de chaque tenseurs et chaque vecteurs
    // [...]
    // et du coup, on fait le calcul suivant : 
     
    speed += isAccelerating * accel;
    speed += isDecelerating * decel;
    Comme tu dis, on ne peut pas accélérer et décélérer en même temps. Il me reste à réfléchir à la manière faire pour s'en assurer, mais je pense que, dans un premier temps, ce sujet peut être très simplement réglé par conception du code (on fait attention à ne pas demander d'accélération en même temps qu'une décélération simplement)...
    Autre point restant à valider aussi : le contrôle des valeurs aux limites. Cette méthode ne me permet pas, par le calcul, de stopper les accélérations et décélérations aux valeurs seuil... En fait il faudrait même piloter les accélération / décélérations aussi par le calcul, mais là ça ferait basculer mon très simple projet (qui a pour vocation de rester simple pour le moment) vers quelque chose de trop complexe en intégrant à minima une dimension temporelle... C'est un sujet très intéressant mais je pense que je développerais quelque chose de plus précis lorsque j'aurais plus d’aisance en programmation (et plus de temps !).

    En tout cas, merci pour ce retour. Si d'autres ont des idées à partager, n'hésitez pas !

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par BioKore Voir le message

    Petit sujet annexe, je vais tester ça plus tard dans la soirée aussi mais est-il possible de multiplier un booléen par une valeur quelconque (un int ou float) ?
    Je ne suis pas sur de comprendre ta question, et encore moins de comprendre le but recherché

    Le fait est que toute valeur numérique (qu'elle soit entière de type réel) peut être converti en valeur booléenne, selon une règle simple :
    1. 0 est converti en false
    2. tout ce qui n'est pas false est true

    Cependant, un booléen est la plus petite des informations que tu peux partager avec l'ordinateur, car il suffit que le courent passe (ou non) dans (la section d')un cable pour permettre de le représenter:
    • si le courent passe, on considère que le booléen vaut true
    • sinon, on considère que cela vaut false

    De plus, la multiplication n'existe pas en algèbre booléenne. on a :
    • la conjonction : le résultat de la conjonction de deux valeurs vaut true si les deux valeurs sont égales à true (ET)
    • la disjonction : le résultat de la disjonction entre deux valeurs booléen vaut true si l'une des deux valeurs (ou les deux) vaut true (OU)
    • la négation : le résultat d'une négation est vrai si la valeur d'origine était à faux, et inversement
    • la disjonction exhaustive : le résultat de la disjonction exclusive est vrai si une seule des valeur testées ést égale à vrai (et que l'autre est égale à faux) (ou exclusif)

    (NOTA: la disjonction exclusive n'est que la combinaison des trois première sous la forme de (A&!B) | (!A & B) )

    Donc, j'aurais tendance à dire que non, il est impossible de multiplier un booléen avec une valeur entière ou réelle
    Comme tu dis, on ne peut pas accélérer et décélérer en même temps. Il me reste à réfléchir à la manière faire pour s'en assurer, mais je pense que, dans un premier temps, ce sujet peut être très simplement réglé par conception du code (on fait attention à ne pas demander d'accélération en même temps qu'une décélération simplement)...
    Le problème est toujours le même : la loi de l'emmerdement maximum (aussi connue sous le nom de "loi de finagle") plane toujours sur ta tête comme l'épée de Damoclès sur un coupable : si tu laisse à qui que ce soit (*) l'occasion de faire une connerie, il ne sert à rien de perdre du temps à te demander SI il fera la connerie, car tu peux partir du principe qu'il la fera forcément à un moment ou à un autre.

    Tu peux directement passer à la question suivante : QUAND fera-t-il la connerie

    Pour ton propre malheur, la réponse sera toujours la même : au pire moment qui soit

    (*) et ne vas pas croire que, parce que c'est toi qui prend la décision, tu ne feras jamais l'erreur!!! Trop de choses peuvent se passer et faire que, pour une connerie (peut-être deux fonctions appelées dans un mauvais ordre) est trop vite arrivée)
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Membre éclairé Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Par défaut
    Héhé, entièrement d'accord avec toi sur la seconde partie de ton retour ; dans la mesure du possible, même s'il s'agit en premier lieu de "tests", cela n’empêche rien à l'importance de faire quelque chose de propre. Je vais réfléchir au moyen de rendre impossible des accélérations et décélérations en même temps.

    Par contre, en ce qui concerne ma question sur la "multiplication" d'un booléen, c'est en fait pour savoir si le booléen est considéré comme 0 ou 1 par le compilateur (en valeur numérique), ou pas.. Tout simplement.
    Dans l'absolu, entièrement d'accord avec toi, cela n'a aucun sens de "multiplier" un booléen par un réel ou un entier... Si le compilateur ne souhaite pas comprendre ce que je veux faire, je peux passer directement par des entiers ; en plus, dans le cas présent, je pense que ce serait même plus simple.
    Le but recherché est tout simplement de multiplier une valeur par 0 si le booléen est faux, et le multiplier par 1 s'il est vrai...
    C'est ce que j'ai illustré dans mon précédent post avec le bout de code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    speed += isAccelerating * accel;   // isAccelerating vaut 0 ou 1

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 153
    Billets dans le blog
    4
    Par défaut
    En théorie si tu static_cast un bool tu devrais avoir 0 ou 1.
    Sinon, je ne comprends pas comment tu veux un déplacement smooth avec des booléens ? Pour qu'un déplacement soit smooth, il doit être surtout fait en fonction du framerate.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

+ Répondre à la discussion
Cette discussion est résolue.

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo