Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse

NodeBB

  1. Home
  2. Programmation
  3. Développement de logiciels
  4. C
  5. Moteur physique #3

Moteur physique #3

Scheduled Pinned Locked Moved C
2 Posts 2 Posters 1.9k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • daemondragonD
    daemondragonD
    daemondragon
    wrote on last edited by
    #1

    <p>Et voila la fin de la création d'un moteur physique !<br/>
    Il ne nous restent plus qu'a détecter les collisions et les corriger, et le moteur est finit. :lol: :lol:</p>
    <p> </p>
    <p><span style="color:#ff0000;"><span style="font-size:18px;"><u><strong>Détection des collisions :</strong></u></span></span></p>
    <p><span style="color:#008000;"><u>La théorie :</u></span><br/><br/>
    Pour que deux rectangles soit en collisions, il faut qu'ils se "rentrent" dedans.<br/>
    Pour cela, la distance (sur un seul axe pour l'instant) entre les deux points doit être inférieur<br/>
    à la somme de la moitié de leur largueur (dans le cas de l'axe x, la hauteur pour l'axe y).<br/>
    C'est pour cela que dans le précédent tuto (<iframe data-embed-src="<base_url>/topic/1043-moteur-physique-2/?do=embed&embedDo=findComment%3Fcomment%3D11169%2C" data-embedcontent="" frameborder="0" src="//melinyel.net/applications/core/interface/js/spacer.png"></iframe> lors de la création de corps, on ne gardait que la moitié de la largueur et de la hauteur.<br/>
    Ont peut ainsi tester chaque axe pour voir s'il y a une collision sur celui-ci.<br/>
    S'il n'y a une collision que sur un seul axe, alors l'objet n'est pas en collision avec l'autre, il est simplement à coté.<br/><br/>
    Une magnifique image qui résume cela :</p>
    <p><img alt="carre.png" src="<base_url>/applications/sslimageproxy/interface/image.php?url=http://www.turboconnard.com/b/wp-content/uploads/2010/11/carre.png"/></p>
    <p><span style="color:#008000;"><u>La pratique :</u></span></p>
    <p>En code, on teste chaque axe l'un après l'autre. Si sur le premier axe il n'y a pas de collision, alors on a pas besoin de tester le deuxième.<br/>
     </p>
    <pre class="ipsCode prettyprint">
    float mlb_abs(float number)
    {
    if (number < 0)
    {
    number *= -1;
    }
    return (number);
    }

    float penetration_x(t_body *f_body, t_body *s_body)
    {
    return (mlb_abs(f_body->box.x - s_body->box.x) - (f_body->box.w + s_body->box.w));
    }

    float penetration_y(t_body *f_body, t_body *s_body)
    {
    return (mlb_abs(f_body->box.y - s_body->box.y) - (f_body->box.h + s_body->box.h));
    }

    char is_collide(t_body *f_body, t_body *s_body)
    {
    if (penetration_x(f_body, s_body) <= 0.
    && penetration_y(f_body, s_body) <= 0.)
    {
    return (1);
    }
    return (0);
    }

    </pre>
    <p>La fonction mlb_abs est strictement équivalente a abs() de math.h, mais à chaque fois que le moteur serait inclus dans un projet, il faudrait linker la biblio, et c'est pas pratique, donc créer la fonction est pour moi plus simple. Vous pouvez évidement la remplacer par celle de math.h si cela vous arrange.<br/>
    On calcule donc les collisions sur chaque axe, et la fonction renvoie 1 en cas de collision.</p>
    <p> </p>
    <p><span style="color:#ff0000;"><span style="font-size:18px;"><u><strong>Correction des collisions :</strong></u></span></span></p>
    <p> </p>
    <p>Il faut tester tout les corps entre eux pour savoir s'il y a une collision. Je vous rappelle que deux corps statiques ne seront jamais en collisions, donc on ne vas pas les tester entre eux.<br/>
     </p>
    <pre class="ipsCode prettyprint">
    void check_collision(t_physic_world *world)
    {
    t_body *update = world->dynamic_body;
    t_body *collide_with = world->static_body;;
    update = world->dynamic_body;
    while (update != NULL)
    {
    /collide with static body/
    while (collide_with != NULL)
    {
    if (is_collide(update, collide_with))
    {
    correct_collision(update, collide_with);
    }
    collide_with = collide_with->next;
    }
    /collide with dynamic body/
    collide_with = update->next;
    while (collide_with != NULL)
    {
    if (is_collide(update, collide_with))
    {
    correct_collision(update, collide_with);
    }
    collide_with = collide_with->next;
    }

        update = update-&gt;next;
    }
    

    }
    </pre>
    <p>On teste d'abord les collisions avec les corps statiques, car cela permet d'éviter que le corps soit repoussé a travers un mur s'il est poussé par un corps dynamique, ce qu'il ne faut surtout pas.<br/><br/>
    Et maintenant, il faut pouvoir corriger ces collisions une fois quelle sont détectées !<br/>
    Tout d'abord, les corrections ne seront pas les mêmes en cas de collisions dynamique / statique que des collisions dynamique / dynamique.<br/>
    Les première collisions pousseront le corps dynamique hors du corps statique alors que les deuxième collisions, les deux corps se pousseront mutuellement, et celui avec la plus grande force l'emportera.<br/>
    Il faut d'abord dispatcher les corps dans les bonnes collisions :<br/>
     </p>
    <pre class="ipsCode prettyprint">
    void correct_collision(t_body *f_body, t_body *s_body)
    {
    if (f_body->dynamic && !s_body->dynamic)
    {
    correct_static_collision(s_body, f_body);
    }
    else if (!f_body->dynamic && s_body->dynamic)
    {
    correct_static_collision(f_body, s_body);
    }
    else if (f_body->dynamic && s_body->dynamic)
    {
    correct_dynamic_collision(f_body, s_body);
    }
    }
    </pre>
    <p>Normalement, les collisions entre corps statique et dynamique se font dans cette ordre dans la fonction, mais cela permet de tester les collisions entre un corps qui n'appartient pas a la structure physic_world avec ce que vous voulez, sans vous souciez de l'ordre dans lequel les corps doivent être mis.<br/>
    On va commencer avec les collisions avec des corps statiques :<br/>
     </p>
    <pre class="ipsCode prettyprint">
    void correct_static_collision(t_body *static_body, t_body *dynamic_body)
    {
    if (penetration_x(static_body, dynamic_body) > penetration_y(static_body, dynamic_body))
    {
    /x axis penetration/
    float direction = dynamic_body->force.total_force.x / mlb_abs(dynamic_body->force.total_force.x);
    dynamic_body->box.x = static_body->box.x - (dynamic_body->box.w + static_body->box.w) * direction;

        add_contact_force(&amp;(dynamic_body-&gt;force), -dynamic_body-&gt;force.total_force.x, static_body-&gt;force.contact_force.y);
    }
    else
    {
        /*y axis penetration*/
        float direction = dynamic_body-&gt;force.total_force.y / mlb_abs(dynamic_body-&gt;force.total_force.y);
        dynamic_body-&gt;box.y = static_body-&gt;box.y - ((dynamic_body-&gt;box.h + static_body-&gt;box.h) * direction);
    
        add_contact_force(&amp;(dynamic_body-&gt;force), static_body-&gt;force.contact_force.x, -dynamic_body-&gt;force.total_force.y);
    }
    

    }
    </pre>
    <p>On corrige la collision en fonction de l'axe ou le corps est le moins en collision, afin de ne pas voir un corps se faire repousser violemment alors que l'on peut faire une chose moins visible.<br/>
    La fonction est simple : 1ere ligne ont calcule la direction de la collision, deuxième ligne, on la corrige, et troisième ligne, on met la force contraire de l'axe ou il y a eut la collisions dans le corps dynamique afin de l’empêcher de passer a travers. Mais pourquoi le faire, si on corrige simplement, il ne passera pas non plus non ?<br/>
    Ce qu'il faut savoir, c'est que le moteur physique ne peut pas être sur qu'il va être appelé suffisamment pour éviter l’effet "tunnel", le corps passe alors a travers alors qu'il aurait du y avoir une collisions. Si l'ont ajoute une force contraire mais de contact, elle ne disparaîtra que lorsque le moteur physique sera remis a jour, donc on pourra alors recorriger la collision.</p>
    <p>De plus on peut voir que dans l'axe ou il n'y a pas de colliisons, on rajoute un force. Mais qu'est ce que c'est ?<br/>
    C'est le méga truc de la mort qui tue du deuxième tuto ! Grâce a cela, on peut rajouter facilement des tapis roulant et des plate-formes mobiles, alors que sinon, dans les autres moteurs physique, ont doit tricher en rajoutant une force lorsque le personnage se trouve sur celui-ci.<br/>
    Evidemment, la force de contact n'est là que si vous l'ajouter, elle est de base à 0.<br/><br/>
    Et maintenant, la correction la plus difficile : les collisions dynamiques / dynamiques dont voici la fonction :<br/>
     </p>
    <pre class="ipsCode prettyprint">
    void correct_dynamic_collision(t_body *f_body, t_body *s_body)
    {
    t_force common_force;
    t_force total_force;
    float weight;

    if (penetration_x(f_body, s_body) &gt; penetration_y(f_body, s_body))
    {
        /*x axis penetration*/
        /*calculate the total force for the total body and divise it*/
        calc_total_force(&amp;(f_body-&gt;force));
        calc_total_force(&amp;(s_body-&gt;force));
        total_force.x = f_body-&gt;force.total_force.x;
        total_force.y = s_body-&gt;force.total_force.x;
        
        common_force.x = total_force.x + total_force.y;
        weight = f_body-&gt;weight + s_body-&gt;weight;
        common_force.y = common_force.x / weight * s_body-&gt;weight;
        common_force.x = common_force.x / weight * f_body-&gt;weight;
        
        add_contact_force(&amp;(f_body-&gt;force), common_force.x - total_force.x, 0);
        add_contact_force(&amp;(s_body-&gt;force), common_force.y - total_force.y, 0);
    
        /*correct collision*/
        float average_x = (f_body-&gt;box.y * s_body-&gt;weight + s_body-&gt;box.y * f_body-&gt;weight) / weight;
        float direction = average_x - f_body-&gt;box.x;
        if (direction &lt; 0)
        {
            direction = -1;
        }
        else
        {
            direction = 1;
        }
        f_body-&gt;box.x = average_x - (f_body-&gt;box.w * direction);
        s_body-&gt;box.x = average_x + (s_body-&gt;box.w * direction);
    }
    else
    {
        /*y axis penetration*/
        /*calculate the total force for the total body and divise it*/
        calc_total_force(&amp;(f_body-&gt;force));
        calc_total_force(&amp;(s_body-&gt;force));
        total_force.x = f_body-&gt;force.total_force.y;
        total_force.y = s_body-&gt;force.total_force.y;
        
        common_force.y = total_force.x + total_force.y;
        weight = f_body-&gt;weight + s_body-&gt;weight;
        common_force.x = common_force.y / weight * f_body-&gt;weight;
        common_force.y = common_force.y / weight * s_body-&gt;weight;
    
        add_contact_force(&amp;(f_body-&gt;force), 0, common_force.x - total_force.x);
        add_contact_force(&amp;(s_body-&gt;force), 0, common_force.y - total_force.y);
    
        /*correct collision*/
        float average_y = (f_body-&gt;box.y * s_body-&gt;weight + s_body-&gt;box.y * f_body-&gt;weight) / weight;
        float direction = average_y - f_body-&gt;box.y;
        if (direction &lt; 0)
        {
            direction = -1;
        }
        else
        {
            direction = 1;
        }
        f_body-&gt;box.y = average_y - (f_body-&gt;box.h * direction);
        s_body-&gt;box.y = average_y + (s_body-&gt;box.h * direction);
    }
    

    }
    </pre>
    <p>Les fonctions sont exactement les mêmes sur les deux axes, il n'y a que des ajustement au niveau des axes a faire.<br/>
    La fonction marche comme cela : elle calcule les forces des deux corps, calcule la moyenne de l'endroit de leur collisions en fonctions de la force de l'autre corps (car plus un corps aura de force, moins il devra être bougé car c'est lui le plus fort).<br/>
    On transfère les forces dans les deux corps afin qu'ils aient la même vitesse en sortie, le tout dans des forces de contact, cela permet de faire des caisses que l'ont pourra pousser !<br/>
    Et ont corrige a la fin la collision, et c'est bon.<br/>
    Malgré que la fonction soit longue, elle reste relativement simple.<br/><br/>
    Et voila, le moteur physique est fini !<br/>
    J'espère qu'il vous aura plus, et que vous vous en servirez 😄 </p>

    1 Reply Last reply
    1
    • AzadA
      AzadA
      Azad
      wrote on last edited by
      #2

      <p>Bien joué pour ce troisième sujet. 🙂 </p>
      <p>Tu as été cité dans la newsletter de Melinyel, continue comme ça !</p>
      <p>
      +1 rep</p>

      Administrateur du forum.
      Contactez-moi par message privé ou par mail.

      1 Reply Last reply
      0

      Hello! It looks like you're interested in this conversation, but you don't have an account yet.

      Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

      With your input, this post could be even better 💗

      Register Login
      Reply
      • Reply as topic
      Log in to reply
      • Oldest to Newest
      • Newest to Oldest
      • Most Votes


      • Login

      • Login or register to search.
      Powered by NodeBB Contributors
      • First post
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • World
      • Users
      • Groups