[C][RESOLU] Moteur physique (trajectoire)
Posté le 03/02/2017 16:58
Salut
.
J'ai du mal à créer un moteur physique basique...
Mes connaissance de physique remonte à loin.
M'enfin, j'ai tout de même retrouver des formules.
Le soucis c'est que en C je n'arrive pas à faire quelques choses de correct
(alors que j'ai réussi en basique) !
J'aimerais obtenir des fonctions permettant donner et gérer des trajectoires à des objets, en fonction d'une vitesse et d'un angle initial.
La collision seulement objet / sol, avec une constante de gravité.
Et en bonus des rebonds et des masses (c'est la que je bloque surtout).
Pour l'instant j'avais un truc de la forme :
// Retourne VRAI si l'objet o possède une trajectoire (mouvement)
int trajectory(const struct Obj *o);
// Termine la trajectoire de l'objet o (stoppe l'objet, généralement au sol)
void end_trajectory(struct Obj *o);
// Initialise la trajectoire de l'objet o, en fonction des vitesse d'abscisse vx et d'ordonnée vy
void start_trajectory(struct Obj *o, const double vx, const double vy);
// Actualise les coordonnées de l'objet o en fonction de sa trajectoire (vx, vy)
// Seule la gravité est prise en compte (unique accélération verticale)
void refresh_trajectory(struct Obj *o);
La dernière fonction est donc celle qui est appelée à chaque frame dans mon moteur, qui tourne à... hum 25fps ?
#define MS_ENGINE 25
SetTimer(ID_USER_TIMER1, MS_ENGINE, engine);
Dans un premier temps déjà je pense modifier les paramètres de "start_trajectory" pour remplacer par l'angle et la vitesse
.
Malheureusement tout ça ne fonctionne pas correctement.
J'effectue actuellement mes tests avec un personnage qui saute.
Avec des paramètres d'angle de 90 degrés = PI/2 radian, saut vertical donc, ça, ça marche.
Pas là peine que je parle de la vitesse car ça par contre, ça fait n'importe quoi...
En fait au final j'aimerais que le personnage saute, enfin, se déplace verticalement de 4 pixels vers le haut lors de la première frame de sa trajectoire, puis commence à chuter lors de la 2nd frame.
Il faut donc choisir une constante de gravitation correcte, et choisir la vitesse qui correspond à ce mouvement.
Et effectuer les bons calculs pour obtenir tout ça.
Voilà voilà, votre aide serait la bienvenue, au plaisir
.
Citer : Posté le 03/02/2017 18:29 | #
Ton timer s'actualise toutes les 25 ms là, soit 40 FPS. Note que le délai ne peut être que multiple de 25 (il est arrondi -tronqué ?- sinon) donc tu as soit 40 FPS, soit 20 FPS pour les fréquences les plus élevées.
Il faut nous montrer ton code si tu veux plus de détails. On pourrait te sortir un programme préfait mais je suis pas sûr que ça t'aide.
Citer : Posté le 03/02/2017 18:36 | #
Alors J'ai fais quelques modification, mais en réalisant quelques calculs à la main, ce n'était pas logique d'avoir ce mouvement de saut de 4 pixels pour retomber instantanément, au final pour le personnage j'ai fait une exception, le moteur n'agit pas vraiment sur lui.
Par contre je vais en avoir besoin pour les principaux objets concerné.
Voici mes fonctions concernées en l'état actuel des choses
#define ANGLE_CONVERT 0.01745329252 // PI/180
double rad(const int a)
{
return a*ANGLE_CONVERT;
}
// Retourne VRAI si l'objet est au sol, FAUX sinon
int obj_grounded(const struct Obj *o)
{
const char height[] = {12, 15, 7};
return (o->y >= 52 - height[o->t]);
}
// Retourne VRAI si l'objet o possède une trajectoire (mouvement)
int trajectory(const struct Obj *o)
{
return o->vx || o->vy;
}
// Retourne VRAI si l'objet o possède une trajectoire (mouvement)
int trajectory(const struct Obj *o)
{
return o->vx || o->vy;
}
// Initialise la trajectoire de l'objet o, en fonction de l'angle a et de la vitesse v
void start_trajectory(struct Obj *o, const int a, const int v)
{
double vx, vy;
vx = v*cos(rad(a)); // Calcul de la vitesse initiale en abscisse
vy = v*sin(rad(a)); // ________________________________ ordonnée
obj_vx(o, vx);
obj_vy(o, vy);
}
// Actualise les coordonnées de l'objet o en fonction de sa trajectoire (vx, vy)
// Seule la gravité est prise en compte (unique accélération verticale)
void refresh_trajectory(struct Obj *o)
{
if(trajectory(o))
{
// Prise en compte de l'accélération gravitationnelle (baisse de vitesse en ordonnée)
obj_vy(o, o->vy + GRAVITY);
// Modification des coordonnées en fonction de la vitesse
obj_x(o, o->x + (int)o->vx);
obj_y(o, o->y + (int)o->vy);
if(obj_grounded(o))
{
while(obj_grounded(o)) obj_y(o, o->y - 1);
obj_y(o, o->y + 1);
end_trajectory(o);
}
}
}
// Fait sauter le personnage (trajectoire verticale)
void perso_jump(struct Obj *p)
{
obj_y(p, 36);
start_trajectory(p, -90, 4);
}
Dans ce cas, le personnage est initialisé à un saut de 4 pixel de manière brute, mais il descend suivant le moteur, de manière à ce que cela se fasse dans la fonction de callback.
Et cela fonctionne d'ailleurs.
J'ai pas encore testé avec d'autres objets.
Pourras-tu survivre plus de 20 secondes dans ce fameux tunnel appelé Graviton
Rebondis entre les murs en évitant les piques dans SpikeBird
Pourras-tu éviter de te faire écraser dans FallBlocs (élu Jeu Du Mois)
La version 2048 tactile amélioré au plus haut point : 2048 Delux !
Pars à la recherche des morceaux d'étoile dans Lumyce (élu Jeu Du Mois)
Citer : Posté le 03/02/2017 18:53 | #
Je raconte ce que je vois dans un ordre aléatoire...
- Le coup de passer l'angle initial et la vitesse en int, c'est fourbe. Je ne te le conseille pas.
- Si tu veux sauter vers le haut, l'angle qu'il te faut c'est 90°, pas -90° a priori.
- De même, si tu veux faire descendre ton personnage la gravité devrait être négative.
Je crois comprendre que tu as fait ça parce que le système de l'écran est de bas en haut. Mais si tu veux un moteur physique, à un moment il va falloir arrêter de plaisanter et te fournir un système de coordonnées digne de ce nom. Donc le sol c'est y = 0 et rien d'autre, la gravité est négative, le haut c'est dans le sens trigo... sinon tu t'en sortiras pas, honnêtement.
Sinon ça a l'air correct, honnêtement. T'as oublié de donner end_trajectory() (t'as donné deux fois trajectory()) mais j'imagine que tu y annules les vitesses.
Citer : Posté le 03/02/2017 18:55 | #
J'ai envie de dire, utilise les constantes réelles.
Avec la méthode d'Euler, x(t+dt) = x(t) + dx(t)/dt
Donc si t'as une accélération de 9.81 m⋅s^-2, sur un dt de 25ms ça fait 0.24525.
Attention aussi à ne pas mélanger le moteur physique et le moteur graphique ! L'un est dépendant de l'unité physique (mètre), l'autre de l'unité d'affichage (pixel). Entre les deux, t'as non seulement une inversion de l'axe Y, mais en plus éventuellement une homotécie + translation (zoom + déplacement de la caméra).
Ajouté le 03/02/2017 à 18:58 :
En fait, je me rend compte que la première partie du message est à coté de la plaque. Mais le passage sur moteur graphique et physique est très adapté à la situation.
Citer : Posté le 03/02/2017 19:08 | #
Okay l'angle et la vitesse en double .
void start_trajectory(struct Obj *o, const double a, const double v)
{
obj_vx(o, v*cos(rad(a)));
obj_vy(o, v*sin(rad(a)));
}
Hum non en fait c'est la subtilité que j'ai expliqué au dessus pour l'angle de -90 .
En fait un tel saut (4 pixels vers le haut puis redescendre instantanée de 4 pixels à la frame suivante) c'est impossible pour garder la logique des trajectoires sur les autres objets. Et j'ai besoin que mon perso puisse enchaîner les sauts extrêmement vite, c'est pour ça qu'en réalité, "sauter", initialise sa hauteur à 4 pixel de haut lors de l'appuis sur une touche (frame 1), pour ensuite lui donner une trajectoire de "chute", pour le faire redescendre de manière automatique avec le moteur (à la frame 2) .
Et bon pour les coordonnées, à partir du moment où j'ai la fonction "obj_grounded", ça défini un sol, et donc, faire chuter les trucs "à l'envers" ne se résume qu'à mettre la gravité à l'envers c'est vrai .
Au final c'est pareil que si j'avais fais tout un bousin pour changer le sens et ça évite des calculs aussi.
Car retourner l'axe implique de faire une convertion (64 - y) pour l'affichage , or mon jeu aura besoin de pas mal de ressources en calculs car il est voué à faire apparaître beaucoup d'objets simultanément à l'écran, tout en calculant leur trajectoire.
Et sinon voilà la fugitive (c'est ce que tu a dis).
void end_trajectory(struct Obj *o)
{
obj_vx(o, 0);
obj_vy(o, 0);
}
Bref donc ça a l'air correct cool , je peux pas encore tester d'autres objets, je dois d'abord coder d'autres trucs, mais j'y reviendrais pour le coup .
Là j'ai la bases des bases.
Maintenant j'aimerais implémenter la masse, pour que les objets lourds tombent plus vite quoi.
Et également le rebond, ça part contre j'ai aucune connaissances...
Ajouté le 03/02/2017 à 19:10 :
Dark je me suis dis que les mesures pouvait se faire en pixel, étant donné que c'est en 2D et que la cam ne se déplace jamais.
Ou alors je fais simple : 1 pixel = 1 mètre .
Pourras-tu survivre plus de 20 secondes dans ce fameux tunnel appelé Graviton
Rebondis entre les murs en évitant les piques dans SpikeBird
Pourras-tu éviter de te faire écraser dans FallBlocs (élu Jeu Du Mois)
La version 2048 tactile amélioré au plus haut point : 2048 Delux !
Pars à la recherche des morceaux d'étoile dans Lumyce (élu Jeu Du Mois)
Citer : Posté le 03/02/2017 19:12 | #
Calculer un repère potable n'est pas un problème pour tes performances. Quand j'aurai testé les perfs' de gint, et si tout se passe comme je le pense, vous verrez que c'est pas ça qui bouffe de la puissance. Vraiment.
Pour le rebond, c'est une force de rappel. Quand tu tombes au sol, tu renvoies une vitesse verticale dans l'autre sens : en gros, tu l'inverses de signe. Il serait intelligent de la multiplier par un coefficient d'amortissement pour que le joueur s'arrête de rebondir un jour.
Accessoirement si tu décides de ne pas mettre d'amortissement, je suis prêt à parier que le joueur s'arrêtera quand même -- enfin.
Citer : Posté le 03/02/2017 19:13 | #
Mouais, alors pour le calcul des trajectoires et la performance, t'es pas encore à ça près. Disons, "beaucoup" ~ 500, faire 1000 additions c'est relativement rapide pour le proco, donc faire une soustraction en plus, ça lui change pas grand chose. x)
Tu perdra beaucoup, beaucoup plus de temps à l'affichage.
Citer : Posté le 03/02/2017 19:22 | #
Okay cimer je note pour le rebond coef d'amortissement .
Ouai bon d'accord pour le changement d'axe, mais là... J'ai vraiment la flemme de changer ça alors que mon truc fonctionne et que je le maîtrise , enfin je veux dire pa là, que je l'ai réfléchie et que je sais comment ça marche et comment l'utiliser .
Par contre c'est à dire "perdre" plus de temps à l'affichage ? Tu veux plutôt dire que c'est l'affichage qui prend le plus de temps dans ce genre de configuration ?
Meh, le truc dommage c'est que ça passe trop mal les déplacement de mon perso à cause de la rémanence ...
Pourras-tu survivre plus de 20 secondes dans ce fameux tunnel appelé Graviton
Rebondis entre les murs en évitant les piques dans SpikeBird
Pourras-tu éviter de te faire écraser dans FallBlocs (élu Jeu Du Mois)
La version 2048 tactile amélioré au plus haut point : 2048 Delux !
Pars à la recherche des morceaux d'étoile dans Lumyce (élu Jeu Du Mois)
Citer : Posté le 03/02/2017 22:27 | #
Le dessin est, je serais prêt à le parier, ce qui bouffe plus de la moitié du temps de calcul dans la plupart des jeux. (D'où l'accent que j'ai mis sur l'optimisation de ce point précis...)
Quant à la rémanence, tu peux la diminuer en ajustant le contraste. Cependant, le contenu de l'écran sera moins visible. Pour le gris c'est encore pire puisque changer le contraste détériore le gris. On est donc coincé avec la rémanence, et honnêtement pour TLT c'est l'aspect graphique qui me gêne le plus.
Citer : Posté le 04/02/2017 01:15 | #
La seule chose à faire pour ça c'est de trouver une bonne fréquence par rapport au mouvement quoi , j'ai réussi à rendre le truc un peu meilleur en modifiant quelques trucs dans l'affichage. Bref.
Pourras-tu survivre plus de 20 secondes dans ce fameux tunnel appelé Graviton
Rebondis entre les murs en évitant les piques dans SpikeBird
Pourras-tu éviter de te faire écraser dans FallBlocs (élu Jeu Du Mois)
La version 2048 tactile amélioré au plus haut point : 2048 Delux !
Pars à la recherche des morceaux d'étoile dans Lumyce (élu Jeu Du Mois)
Citer : Posté le 04/02/2017 10:26 | #
Salut ! Je confirme les propos de Lephé, si tu veux avoir un moteur physique propre, crée toi un vrai repère. Tu as le repère du monde (en mètre, x à droite et y en haut) et le repère de ta caméra (en pixel, x à droite et y en bas). Faire cette dissociation est importante sinon tu vas mettre des coefficients à la louche pour combler le manque de réalisme dans tes trajectoires et tu vas perdre le fil.
J'ajoute un autre point à ce que disais Dark un peu plus haut, utilise les équations du mouvement de Euler (ou Verlet c'est plus precis).
Mais completement ! Parce que je vois que tu veux ajouter la masse à tes objets.
Quand tu veux bouger un objet, tu lui appliques une force qui va modifier l'acceleration.
F = m x a donc a = F / m
Ensuite à chaque frame tu recalcules la position (dt est le temps entre deux frames, et V0 et X0 la vitesse/position actuelle)
V = a * dt + V0
X = v * dt + X0
La gravitée s'ajoute en ajoutant 9.8 à l'acceleration. Il y a un peu de bricolage à ce niveau car la pesenteur est tout le temps appliquée et pas les forces...
Citer : Posté le 04/02/2017 12:31 | #
Bon bon... Okay je ferais ces changement plus tard ...
Donc ma structure Objet, possédera les x, y du repère... comme j'ai codé pour l'utiliser comme un private... hum ce sera pas si compliqué si j'fais ça... puis ça... etc...
Pourras-tu survivre plus de 20 secondes dans ce fameux tunnel appelé Graviton
Rebondis entre les murs en évitant les piques dans SpikeBird
Pourras-tu éviter de te faire écraser dans FallBlocs (élu Jeu Du Mois)
La version 2048 tactile amélioré au plus haut point : 2048 Delux !
Pars à la recherche des morceaux d'étoile dans Lumyce (élu Jeu Du Mois)