-
Mouvement plateforme custom basé sur le timer
Dans ma quête pour une application stable et fluide sur (presque) chaque ordinateur je m'efforce de développer une application à framerate variable. Pour un résultat optimum le framerate de l'application est réglé sur le taux de rafraîchissement de l'écran. Ceci est fait en entrant une valeur élevée pour le framerate et en activant V-Sync.
L'exemple fourni comprend un mouvement plateforme custom utilisant l'extension Move Safely 2. Ce mouvement est basé sur l'article Bullet-Time écrit par Dines sur le forum anglais, donc un grand merci à lui.
Chaque changement de valeur appliqué aux variables utilisées par le mouvement est multiplié par TimeDelta. Cette Valeur Globale est le résultat du calcul du temps passé depuis la dernière boucle. Ainsi le mouvement devrait tourner à la même vitesse quelquesoit le framerate de l'application. C'est secondaire, mais le tout est également multiplié par une Valeur Globale appellée TimeScale qui produit l'effet Bullet-Time.
Mon problème est que ce mouvement ne fonctionne pas correctement avec un framerate très bas/élevé. Ça déconne de façon subtile mais visible. Par exemple plus le framerate est bas, moins l'objet saute haut. Et avec un framerate élevé l'objet reste collé au plafond pendant un court instant avant de retomber. Même chose pour un mur situé à gauche de l'objet, mais pas avec un mur situé à droite de l'objet! Étrange.
L'exemple est brièvement commenté, mais si vous avez lu l'article de Dines vous devriez tout comprendre. Toutes les commandes sont expliquées à l'écran:
http://www.clickteam.com/epicenter/ubbthreads.php?ubb=download&Number=3170
Donc si vous lancez l'application telle quelle, tout devrait bien fonctionner. Vous pouvez courrir et sauter sans problème car le framerate de l'application devrait osciller entre 60 et 85 fps (le taux de rafraîchissement de votre moniteur en Hz).
Maintenant si vous désactivez V-Sync l'application tournera aussi vite que votre ordinateur le permet. Si vous obtenez un framerate supérieur à 200 vous devriez rencontrer les problèmes que j'ai décrit plus haut. Pas de panique ceci ne va pas endommager votre ordinateur! [img]/epicentre/images/%%GRAEMLIN_URL%%/smile.gif[/img]
Si vous avez une idée de ce qui peut bien se passer merci de m'en faire part. Je suis à peu près certain d'avoir fait des erreurs de codage car rester collé à droite d'un mur et pas à gauche c'est vraiment (pas) marrant.
Mais la chose la plus important est pourquoi rester collé avec un framerate élevé? Cela a-t-il quelquechose à voir avec les événements qui sont eux parcourus plus vite? (les événements ne sont pas basés sur le timer)
Même si vous ne pouvez pas m'aider, l'exemple est peut-être intéressant si vous pratiquez les mouvements custom. [img]/epicentre/images/%%GRAEMLIN_URL%%/wink.gif[/img]
-
Re: Mouvement plateforme custom basé sur le timer
Bonsoir Olivier,
Je viens de regarder ton exemple, c'est bizarre je remarque une différence de code entre ton déplacement droit ou gauche pour la gestion de ton actif est ce volontaire de ta part ? Pour le reste désolé de ne pouvoir t'aider mais là ça me dépasse, surtout sur certaines lignes. Je pense bien à un truc, mais ça m'étonnerait que cela puisse t'aider, un plafonnement du frame rate s'il serait trop rapide ou trop bas selon une fourchette, mais bon peut-être une idée stupide. Enfin j'aurai au moins fait la démarche de t'aider...
-
Re: Mouvement plateforme custom basé sur le timer
-
Re: Mouvement plateforme custom basé sur le timer
Oh, des maths [img]/epicentre/images/%%GRAEMLIN_URL%%/wink.gif[/img]
J'ai pas pu lire ton fichier mais n'oublie pas que lorsque tu a une accélération, ton mouvement dépend globalement de dt mais avec un terme en t^2. par exemple avec une accélération de A pendant dt :
new_y = old_y + old_v*dt+A/2*dt^2
new_v = old_v + A*dt
-
Re: Mouvement plateforme custom basé sur le timer
Merci d'avoir jeté un oeil samuel. Ta suggestion n'est pas du tout stupide, bien au contraire. Spécifier une valeur min/max pour le framerate fait partie de cette solution que j'aimerai bien implémenter à mon code. Mais je crois que c'est impossible à intégrer en l'état dans MMF car l'article parle de séparer le runtime du rendu:
<div class="ubbcode-block"><div class="ubbcode-header">Code:</div><div class="ubbcode-body ubbcode-pre" style="height: 34px;"><pre>updateGame(); /* Update game state a variable number of times */
drawScene(); /* Draw the scene only once */</pre></div></div>
Cependant MMF est déjà capable de le faire, car il me semble que c'est exactement ce qu'il fait lorsque l'option "Machine independent speed" est activée. Ce serait hyper chouette si Clickteam pouvait nous rendre disponible ces deux fonctions system. D'ailleurs n'y avait-il pas des fonctions similaires dans Jamagic?
À propos du déplacement gauche/droite je ne vois pas de différence. Dans l'un j'additionne AccelerationX à SpeedX, et dans l'autre je fait l'inverse c-à-d une soustraction. Mais il y a tout de même quelquechose qui doit clocher (on reste collé à gauche et pas à droite), donc il va falloir que je revois cela.
-
Re: Mouvement plateforme custom basé sur le timer
Comme d'habitude je n'ai pas tout compris Batchy. [img]/epicentre/images/%%GRAEMLIN_URL%%/smile.gif[/img] Voici en résumé ce qui se passe dans mon code pour la coordonnée Y de mon objet:
<div class="ubbcode-block"><div class="ubbcode-header">Code:</div><div class="ubbcode-body ubbcode-pre" style="height: 150px;"><pre>
// Add Gravity to SpeedY, Capped by SpeedMaxY
• Always
> "Active 6" Set SpeedY to Min(SpeedMaxY( "Active 6" ), SpeedY( "Active 6" )+GravityY( "Active 6" )*TimeDelta*TimeScale)
// Update the object's PosY coordinate with its newly calculated SpeedY
• Always
> "Active 6" Set PosY to PosY( "Active 6" )+SpeedY( "Active 6" )*TimeDelta*TimeScale
// Move the object safely at its newly calculated PosY coordinate
• Always
> "Active 6" Set Y position to PosY( "Active 6" )
</pre></div></div>
Tu remarqueras que TimeDelta intervient deux fois dans le calcul de la coordonnée Y. Une première fois en ajoutant GravityY à SpeedY, et une deuxième fois en ajoutant SpeedY à PosY.
Il me semble que cela est équivalent à ce que tu écris plus haut à propos de dt^2, mais je n'en suis pas certain.
-
Re: Mouvement plateforme custom basé sur le timer
Malheureusement je ne comprends toujours pas les équations de Batchy. Je crois que c'est trop compliqué pour moi. [img]/epicentre/images/%%GRAEMLIN_URL%%/frown.gif[/img]
J'ai remarqué le même problème avec le Movement Platform de Clickteam. C'est très net si on met un FrameRate très bas genre 10 FPS, l'actif saute beaucoup moins haut. Presque deux fois moins haut!
Comment ça se fait? Le gameplay n'est pas du tout le même dans ces conditions.
-
Re: Mouvement plateforme custom basé sur le timer
<div class="ubbcode-block"><div class="ubbcode-header">Posté à l'origine par: BatchyX</div><div class="ubbcode-body">new_y = old_y + old_v*dt+A/2*dt^2
new_v = old_v + A*dt</div></div>
Dis-donc ça ressemble très fortement à la formule de Verlet pour la vélocité ça, non? Malheureusement je n'ai pas encore réussi à l'implémenter dans mon code, mais ça va venir.. enfin j'espère.
-
Re: Mouvement plateforme custom basé sur le timer
petard, effrayant tout ça...
-
Re: Mouvement plateforme custom basé sur le timer
Bonjour Olivier,
j'ai regardé ton exemple. Pour l'objet qui saute moins haut, je pense que cela vient de l'approximation faite sur ta vitesse sur le calcul de la distance parcourue. Tu considères que la distance parcourue sur une durée DeltatT l'est sur la base de ta vitesse à t+DeltatT. Or la vitesse a varié sur DeltaT, donc plus le temps entre chaque calcul est long (framerate faible), plus l'erreur commise est grande.
Si tu essaies une vitesse moyenne, comme ci-dessous, tu verras que la distance est beaucoup plus homogène sur différents framerate.
PosY( "Active 6" )+((SpeedY( "Active 6" )*TimeDelta)-(GravityY( "Active 6" )*TimeDelta*TimeDelta/2))*TimeScale
Pour l'objet qui colle au plafond et sur la gauche, j'ai une piste. Je rééditerai le post dès que j'aurai trouvé.
-
Re: Mouvement plateforme custom basé sur le timer
Dis donc dampat, tu vas avoir une place de choix dans les crédits/remerciements de mon prochain jeu! [img]<<GRAEMLIN_URL>>/laugh.gif[/img]
Effectivement ta modif règle complètement le problème. Entre 10 et 1000 FPS la différence dans la hauteur du saut n'est que de 0,045 pixel environ.
Il va me falloir une aspirine et un maxi café pour que je puisse assimiler ta modif. Mais je t'adresse déjà un super grand merci dampat.
Concernant l'actif qui reste collé je crois que cela vient de l'extension Move Safely. Il faut séparer la routine "Push Out" en deux, et faire l'axe X séparé de l'axe Y .
L'auteur utilise cette technique dans l'exemple fourni avec l'extension, frame "Advanced".
-
Re: Mouvement plateforme custom basé sur le timer
De rien Olivier. Je voulais t'envoyer une image explicative du principe mais apparemment, on ne peut pas. Je vais essayer d'être clair. Ca correspond à un calcul d'intégrale. Tu as ta vitesse v1 qui évolue linéairement entre 2 frames jusqu'à v2. Cette droite ça nous donne l'évolution de la vitesse v. Nous ce qu'ont veut c'est la distance parcourue entre 2 frames donc il faut intégrer, ce qui revient à calculer la surface délimitée par la droite V1V2. On a deux possibilités: soit on prend V2xDeltaT (ce que tu as fait) et on lui retire le triangle délimité par V1V2 soit on prend le carré V1xDeltatT auquel on ajoute le triangle V1V2. Et là dans cette deuxième possibilité on retrouve ce que disait BatchyX:
Y2=Y1+V1*DeltatT+(A/2)*DeltatT^2, A étant la pente de notre droite V1V2. Et comme de par hasard, on retrouve la formule de Verlet. Mais ce que je t'ai donné revient au même.
C'est un peu compliqué à expliquer sans schéma, j'espère que ça ira.
-
Re: Mouvement plateforme custom basé sur le timer
Effectivement j'ai comparé ta formule avec celle de BatchyX et elles sont équivalentes, c'est bien la formule de Verlet. Je n'arrivais pas à la faire fonctionner car je faisais une addition au lieu d'une soustraction avec GravityY.
Cependant je ne serai pas contre un petit dessin pour illustrer le principe. Car c'est bien le principe qu'il faut comprendre. Et je suis certain que cela va aider d'autres développeurs.
Pour afficher une image dans ton post il suffit d'utiliser le code ci-dessous, ça marche avec GIF, JPG et PNG:
<div class="ubbcode-block"><div class="ubbcode-header">Code:</div><div class="ubbcode-body ubbcode-pre" ><pre>http://domaine.com/image.gif</pre></div></div>
-
Re: Mouvement plateforme custom basé sur le timer
Hmmm quelquechose d'important doit m'échapper. J'ai tenté de faire la même chose avec l'axe X, mais lorsque l'on cesse de se déplacer l'actif ne s'immobilise jamais complètement.
<div class="ubbcode-block"><div class="ubbcode-header">Code:</div><div class="ubbcode-body ubbcode-pre" ><pre>PosX( "Active 6" )+((SpeedX( "Active 6")*TimeDelta)-AccelerationX( "Active 6" )/2*TimeDelta pow 2)*TimeScale</pre></div></div>
Ceci est dû à cette portion de code qui n'est jamais égale à zéro:
<div class="ubbcode-block"><div class="ubbcode-header">Code:</div><div class="ubbcode-body ubbcode-pre" ><pre>AccelerationX( "Active 6" )/2*TimeDelta pow 2</pre></div></div>
-
Re: Mouvement plateforme custom basé sur le timer
Oui pour X, c'est différent car l'accélération et la décélération n'agissent que quand le personne bouge, contrairement à la gravité qui agit tout le temps. Il faut rajouter des conditions et remanier un peu pour éviter que tu es de l'accélération alors que le personnage s'est complètement arrêté.
Sinon j'ai passé ma journée à faire mon site pour y mettre le tuto proposé précédemment. Je devais le faire depuis un bon moment de toute façon. Le site n'est pas du tout opérationnel, c'est juste pour que les personnes que ça intéresse puissent jeter un oeil au tuto.
Tuto sur la formule de Verlet
Si c'est pas clair, tu n'hésites pas pas à me le signaler.
-
Re: Mouvement plateforme custom basé sur le timer
Meci beaucoup pour avoir écrit ce petit tutorial, j'apprécie vaiment ton aide Damien. Je pensais avoir compris, mais il faut croire que non [img]<<GRAEMLIN_URL>>/frown.gif[/img] car je n'arrive pas à appliquer le principe à l'axe X.
J'ai remarqué que plus le FPS est faible, plus la distance parcourue par l'actif pour ralentir et atteindre 0 (= freiner) est grande. Ça doit être la même chose pour accélérer et atteindre MaxVelocityX.
-
Re: Mouvement plateforme custom basé sur le timer
C'est vachement balaize,
Un peu compris le principe sur l'axe des y mais alors
sur la relation avec l'axe des X, je dirais même que le tableau m'a vachement foutu la trouille [img]<<GRAEMLIN_URL>>/eek.gif[/img]
Il y a parfois des sujets ou je me sens vraiment décroché,
un jour je comprendrais peut-être. [img]<<GRAEMLIN_URL>>/frown.gif[/img] [img]<<GRAEMLIN_URL>>/blush.gif[/img]
-
Re: Mouvement plateforme custom basé sur le timer
Une question en ayant regardé ton exemple, pourquoi tu prends 2 timer pour calculer le TimeDelta: 1.0/Framerate n'aurait pas été plus simple ?
-
Re: Mouvement plateforme custom basé sur le timer
On avait parlé de la façon de calculer le TimeDelta (temps écoulé depuis la boucle précédente) dans cette discussion:
http://www.clickteam.com/epicentre/ubbthreads.php?ubb=showflat&Number=9179&pa ge=all
La conclusion était que cette méthode était plus précise.
-
Re: Mouvement plateforme custom basé sur le timer
Bon, j'ai cherché pour rien où je pouvais avoir fait une erreur. Si tu rajoutes cet événement:
Toujours -> Fixer la position X de Active 6 à Pox X
après On Safety AND ("Active 6") est au dessus du décor -> Stop safety procedure and push out, l'objet s'arrête comme il faut.
Le freinage ralenti vient certainement de l'objet move safety.
Il y a bien une erreur dûe au passage par zéro de la vitesse: environ 3-4 pixels mais elle est à peine visible par le joueur. Si la précision n'est pas suffisante, ça doit pouvoir se régler mais à cette heure ci, il faut plus compter sur moi.
-
Re: Mouvement plateforme custom basé sur le timer
Je n'ai pas réussi à faire fonctionner ta solution dampat. Pour ma part j'ai utilisé cette condition afin de stopper complètement l'objet lorsque qu'il n'y a plus d'accélération horizontale:
<div class="ubbcode-block"><div class="ubbcode-header">Code:</div><div class="ubbcode-body ubbcode-pre" ><pre>Abs(VelocityX( "Active 6" )) > 0</pre></div></div>
Concernant la distance de freinage/accélération il est certain que la différence entre différents FPS est relativement négligeable (quelques pixels). Mais ça m'intrigue cependant, car le calcul me semble être identique à celui de l'axe Y.
Si c'est bien le passage par zéro qui pose problème, qu'est-ce qui peut être fait?
Je poste l'exemple en version 0.2. J'ai appliqué les modifs suggérées jusqu'à présent, et fais quelques changements qui je l'espère sont pertinents. Les explications sont dans les commentaires du code et dans le log intégré au MFA. J'espère que ce sera assez clair:
http://www.clickteam.com/epicentre/ubbth...e=CTBPMv0.2.mfa
-
Re: Mouvement plateforme custom basé sur le timer
J'ai regardé ton fichier, ça m'a l'air très bien. Pour l'erreur qu'il reste, il y a la solution qui est pour moi la plus évidente: ce serait de calculer la position exacte d'arrivée à partir de la vitesse initiale et position initiale avant décélération puis forcer par min ou max la valeur lorsque la vitesse s'annule. On aurait alors la position vraie au final pour tous les cas de framerate. Cette méthode pourrait même permettre d'utiliser uniquement une relation de la position en fonction du temps et virer les relations de vitesse.
Pour ce qui est de la différence entre le Y et le X, je ne sais pas encore. Toute la difficulté est de récupérer l'erreur de position sur X entre 2 mouvements identiques à framerates différents. Comment fais-tu pour déterminer cette erreur ?
-
Re: Mouvement plateforme custom basé sur le timer
Voilà j'ai re-posté la v0.2 en ajoutant un groupe de code Debug et deux objets. En gros je désactive le contrôle joueur lorsque l'objet est au-dessus d'une certaine zone. Ça déclenche le freinage. Et la valeur PosX de l'objet est affichée pour une meilleure comparaison.
J'obtiens une différence de quelques pixels (de 1 à 4 environ) entre différents FrameRates. Mais le comportement n'est pas identique en allant vers la gauche qu'en allant vers la droite.
Question: le calcul en ligne 36 est-il exact dans tous les cas? Car parfois AccelerationX est positif, parfois négatif. Le signe '-' ne doit-il pas se transformer en '+' si AccelerationX est négatif?
<div class="ubbcode-block"><div class="ubbcode-header">Code:</div><div class="ubbcode-body ubbcode-pre" ><pre>PosX( "Active 6" )+(VelocityX( "Active 6" )*TimeDelta)-(AccelerationX( "Active 6" )/2*TimeDelta pow 2)</pre></div></div>
Le truc c'est qu'il y a une différence majeure entre l'axe X et l'axe Y. Je crois que tu l'as souligné Damien.
Sur l'axe Y il y a en permanence une accélération vers le bas, la gravité. Et pour sauter on modifie bruquement VelocityY avec une valeur négative. C'est comme si on appliquait ponctuellement une force (contraire à la gravité).
Sur l'axe X on a parfois, et seulement parfois, une accélération (positive = accélération, négative = décélération). Et parfois on a rien du tout.
Je me demande si il ne faudrait pas traiter l'axe X comme l'axe Y. On aurait en permanence une décélération, on pourrait considérer que c'est la friction avec l'air ou l'inertie. Et pour accélérer on appliquerait une force contraire.
Je ne sais pas si j'emploie les bons termes. La notion exacte de Force et d'Inertie, telle qu'elle est traitée en physique m'échappe encore. Mais je m'instruis:
http://en.wikipedia.org/wiki/Equation_of_motion
http://en.wikipedia.org/wiki/Force
-
Re: Mouvement plateforme custom basé sur le timer
Changer le mode de fonctionnement de la vitesse en X ne changera rien. L'erreur vient de l'échantillonnage de notre fonction. Pour avoir une erreur moindre il faut avoir une accélération/vitesse initiale multiple du temps entre chaque frame. Par exemple, pour l'axe y, le passage en zéro se fait à 450/1100=0,409s ce qu est très proche de 4*0,1 (0,1=1/framerate). Pour y le passage en zéro se fait à 600/200=0,333, ici beaucoup plus éloigné d'un multiple du framerate. Si on prend decelx=400 à la place de 600, l'erreur est quasiment nulle.
La seule solution est de considérer comme je le disais la valeur vraie au passsage en zéro pour annuler totalement cette erreur.