Aller au contenu

Moteur physique #2


daemondragon
 Share

Recommended Posts

Maintenant que l'on peut appliquer une force, il faut pouvoir la rattacher à un corps :P .
On peut distinguer, là encore, deux types de corps : les dynamiques et les statiques.
Les corps dynamiques seront les seuls qu'il est possible de bouger via des forces, les statiques seront considérés comme des élément du décors, il seront tout le temps immobiles (sauf si  vous modifier leur position manuellement bien sur).
Ainsi, les personnages, les caisses... seront dynamiques, et les élément du décors seront statiques.
Voici donc la structure d'un corps :
 

typedef struct s_body
{
    char dynamic;
    float weight;
    t_collision_box box;
    t_force_container force;
    struct s_body *next;
}t_body;

Le poids (weight) sert à savoir à quel point l'objet sera dur a bouger. Ainsi, un objet de 40 kg sera deux fois plus facile
à bouger qu'un objet de 80 kg. il faudra donc deux fois moins de force. Et voici donc l'unité des forces : Mètre par secondes par kilogrammes.
(40 de force pour bouger un corps de 40 kg à 1m/s)
la collision box, servira plus tard pour déterminer les collisions entre les corps, et le tout en liste chainnée, afin de pouvoir les stoker facilement dans une structure world :
 

typedef struct s_physic_world
{
    t_body *static_body;
    t_body *dynamic_body;
}t_physic_world;

Il faut faire la différence entre les corps afin d'éviter de faire des tests qui ne servent à rien (par définition, deux corps statiques ne sont jamais en collisions)
Tout d'abord, la fonction de création de corps :
 

t_body *create_static_body(float x, float y, float w, float h)
{
    t_body *body = malloc(sizeof(*body));
    if (body != NULL)
    {
        body->box.x = x;
        body->box.y = y;
        body->box.w = w / 2;
        body->box.h = h / 2;
        
        body->dynamic = 0;
        body->weight = 1;
        
        body->force.permanent_force = NULL;
        set_contact_force(&(body->force), 0, 0);
        body->force.total_force.x = 0;
        body->force.total_force.y = 0;
        body->next = NULL;
    }
    return (body);
}

t_body *create_dynamic_body(float x, float y, float w, float h)
{
    t_body *body = malloc(sizeof(*body));
    if (body != NULL)
    {
        body->box.x = x;
        body->box.y = y;
        body->box.w = w / 2;
        body->box.h = h / 2;
        
        body->dynamic = 1;
        body->weight = 1;
        
        body->force.permanent_force = NULL;
        set_contact_force(&(body->force), 0, 0);
        body->force.total_force.x = 0;
        body->force.total_force.y = 0;
        body->next = NULL;
    }
    return (body);
}

les deux fonctions sont strictement les mêmes, sauf une initialisation d'une variable. Vous pouvez facilement réunir le tout en une fonction, mais j'ai garder cette présentation car plus claire à utiliser. La fonction retourne un pointeur sur la structure afin de pouvoir la modifier et de l'ajouter à la structure physic_world (la fonction va venir, ne vous inquiétez pas).
Il faut initialiser toutes les forces à 0, ont met le poids à 1 par défaut (ne jamais mettre le poids à 0, sinon il va y avoir un problème de division par 0)
Il faut aussi savoir que pour déterminer les collisions, on à besoin du centre du corps, pas un de ces coins. On à aussi besoin que de la moitié de la hauteur et de la largeur, mais la conversion est faites dans la fonction, pour plus de simplicité.

Et surtout pour ceux qui veulent la structure collision box :

typedef struct s_collision_box
{
    float x;
    float y;
    float w;
    float h;
}t_collision_box;

il faut maintenant aussi, pouvoir détruire une entité :
 

void delete_body(t_body *body)
{
    if (body != NULL)
    {
        t_force *remove = body->force.permanent_force;
        while (remove != NULL)
        {
            body->force.permanent_force = remove->next;
            free(remove);
            remove = body->force.permanent_force;
        }
        free(body);
    }
}

On doit enlever toutes les forces permanentes afin d'éviter les fuites de mémoires !
De plus, l'objet doit aussi avoir le poids que vous voulez :
 

void add_weight(t_body *body, float weight)
{
    if (body != NULL)
    {
        if (weight <= 0)
        {
            weight = 1;
        }
        body->weight = weight;
    }
}

Mais ce n'est pas finit ! Il faut aussi pouvoir ajouter le corps à la structure world. La fonction pour mettre a jour tout les corps ne va pas attendre qu'on lui passe tout les objets 1 par 1 ! Cela possède plusieurs avantages : On n'envoie qu'une grande structure au lieu de plein de petites, et vous pourrez bénéficier de la détection de collisions sans que l'objet soit présent physiquement : cela permet de faire des capteurs, de savoir si on est dans de l'eau sans autant repousser le personnage etc...

Bref suit les fonctions d'initialisation, de suppression du monde physique et d'ajout de corps, rien de bien complexe, presque un copier coller des forces :


t_physic_world *create_physic_world()
{
    t_physic_world *world = malloc(sizeof(*world));
    if (world != NULL)
    {
        world->static_body = NULL;
        world->dynamic_body = NULL;
    }
    return (world);
}

void delete_physic_world(t_physic_world *world)
{
    if (world != NULL)
    {
        t_body *remove = world->static_body;
        while (remove != NULL)
        {
            world->static_body = remove->next;
            delete_body(remove);
            remove = world->static_body;
        }
        remove = world->dynamic_body;
        while (remove != NULL)
        {
            world->dynamic_body = remove->next;
            delete_body(remove);
            remove = world->dynamic_body;
        }
        free(world);
    }
}

char add_to_physic_world(t_physic_world *world, t_body *body)
{
    if (world != NULL && body != NULL)
    {        
        if (body->dynamic)
        {
            body->next = world->dynamic_body;
            world->dynamic_body = body;
        }
        else
        {
            body->next = world->static_body;
            world->static_body = body;
        }
        return (1);
    }
    else
    {
        return (0);
    }
}

Les corps sont encore empilés, car c'est toujours plus simple à faire ;)
Et maintenant, on peut mettre a jour touts les corps :
 


void update_physic_world(t_physic_world *world, int millisecond_time)
{
t_body *update = world->dynamic_body;
while (update != NULL)
{
update_force(update, millisecond_time);
update = update->next;
}
}

void update_force(t_body *body, int millisecond_time)
{
calc_total_force(&(body->force));

body->force.total_force.x *= (millisecond_time / 1000.0) / body->weight;
body->force.total_force.y *= (millisecond_time / 1000.0) / body->weight;

body->box.x += body->force.total_force.x;
body->box.y += body->force.total_force.y;

set_contact_force(&(body->force), 0, 0);
}

Je vous rappelle que seuls les corps dynamiques peuvent être en mouvement, donc il n'y a pas de raison de mettre a jours les corps statiques.

La fonction attend un temps en millisecondes car la SDL utilise cette unité, mais vous pouvez changer l'unité de temps, il faudra juste adapter en conséquence.
Mais vous avez sûrement remarquer la présence de la structure force_container, même chez les corps statiques, alors pourquoi ne pas l'enlever chez eux ?
Tous simplement parce que sinon on doit dupliquer le code en deux : corps statiques et corps dynamiques, que c'est plus complexe à faire, que les risque de bug sont multiplier par deux, et surtout ça empêche le méga truc de la mort qui tue, mais que vous ne saurez pas dans ce tutos.
Les forces de contacts sont remisent à 0 a chaque fois car on les recréer aussi à chaque fois.

Et voilà, vous pouvez bouger vos personnages comme vous voulez, mais il n'y a toujours pas de collisions !

  • Upvote 3
Lien vers le commentaire
Partager sur d’autres sites

Autant pofiner les détails mais très bon sujet encore une fois. Tu devrais ajouter un lien vers le n°2 dans ton premier sujet et inversement pour assurer une lecture linéaire. :)
Gros bravo, j'imagine le travail que ça doit représenter pour toi, félicitation encore une fois. ;)

  • Upvote 1
Lien vers le commentaire
Partager sur d’autres sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Invité
Répondre à ce sujet…

×   Vous avez collé du contenu avec mise en forme.   Supprimer la mise en forme

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Chargement
 Share

×
×
  • Créer...