Comment faire en sorte qu'un objet en mouvement s'arrête en douceur à la fin d'un chemin?

8

Il y a une douzaine de façons de formuler cette question, mais pour garder ma pensée en ligne, je la formule en fonction de mon problème.

Je crée donc une plate-forme flottante que j'aimerais pouvoir simplement parcourir d'un point désigné à un autre, puis revenir au premier et passer entre les deux en ligne droite. Cependant, juste pour le rendre un peu plus intéressant, je veux ajouter quelques règles à la plateforme.

  1. Je le code pour parcourir des multiples de valeurs de tuiles entières de données mondiales. Donc, si la plate-forme n'est pas stationnaire, elle parcourra au moins une largeur ou une hauteur de tuile entière.
  2. Dans une longueur de tuile, je voudrais qu'il accélère d'un arrêt à une vitesse maximale donnée.
  3. En atteignant la distance d'une longueur de tuile, je voudrais qu'elle ralentisse jusqu'à un arrêt à une coordonnée de tuile donnée, puis répète le processus en sens inverse.

entrez la description de l'image ici

Les deux premières parties ne sont pas trop difficiles, essentiellement j'ai des problèmes avec la troisième partie. J'aimerais que la plate-forme s'arrête exactement à une coordonnée de tuile, mais étant donné que je travaille avec l'accélération, il semblerait facile de commencer simplement à appliquer l'accélération dans la direction opposée à une valeur stockant la vitesse actuelle de la plate-forme une fois qu'elle atteint la longueur d'une tuile de distance (en supposant que la tuile parcourt plus d'une longueur de tuile, mais pour garder les choses simples, supposons simplement que c'est le cas) - mais alors la question est quelle serait la valeur correcte pour que l'accélération augmente pour produire cet effet? Comment pourrais-je trouver cette valeur?

TheBroodian
la source
1
Je n'ai pas le temps pour une réponse complète pour le moment, mais jetez un œil à ceci: red3d.com/cwr/steer/gdc99 , en particulier la section "Arrivée". Imitez ce comportement pour ralentir jusqu'à l'arrêt et inversez-le pour accélérer à partir d'un arrêt.
MichaelHouse
Signet. Voilà une mine d'informations précieuses auxquelles vous venez de m'éclairer, Monsieur.
TheBroodian du
Ce type de "vitesse_désirée = (vitesse / distance clippée) * décalage_cible" a du sens, mais pas exactement. Une fois que j'ai trouvé la vitesse souhaitée, pour la soustraire de la vitesse actuelle de l'objet?
TheBroodian
Je l'utilise pour mettre à jour la valeur d'accélération: acceleration = desired_velocity - currentVelocityAppliquez ensuite cette accélération comme vous le feriez normalement. Je vais créer une réponse un peu plus loin en montrant ce que je fais.
MichaelHouse

Réponses:

5

Utiliser ces comportements de pilotage comme guide. En regardant le comportement d'arrivée:

entrez la description de l'image ici

Le comportement à l'arrivée est identique à rechercher tandis que le personnage est loin de sa cible. Mais au lieu de se déplacer à travers la cible à pleine vitesse, ce comportement ralentit le personnage à l'approche de la cible, ralentissant finalement jusqu'à un arrêt coïncidant avec la cible.

Nous pouvons créer une fonction "arriver à" comme quelque chose de similaire à ceci:

arriveAtPoint(Vector2f position, Vector2f target) {
    float slowing_distance = 1;
    Vector2f target_offset = target - position;
    float distance = target_offset.length();
    float ramped_speed = currentVelocity * (distance / slowing_distance);
    float clipped_speed = Math.min(ramped_speed, currentVelocity);
    targetLinearVelocity = target_offset.scale(clipped_speed / distance);
    acceleration = targetLinearVelocity -linearVelocity;
}

Cela mettra à jour l'accélération que nous devons utiliser pour appliquer à l'objet en mouvement.

MichaelHouse
la source
Et juste pour des éclaircissements, linearVelocity serait la vitesse à laquelle notre objet aurait voyagé actuellement après la mise à jour précédente?
TheBroodian
Votre phrasé est assez étrange. Mais, linearVelocity == currentVelocity.
MichaelHouse
3
Je ne suis pas d'accord avec cette approche au motif qu'elle est trop compliquée. Il est possible de le faire avec une simple interpolation utilisant des équations de mouvement ( les formules SUVAT ). Une petite algèbre et vous pouvez calculer avec précision les entrées nécessaires pour atteindre une cible souhaitée.
Andrew Russell
1
Il serait utile de préciser cela avec une réponse?
TheBroodian
@AndrewRussell J'aimerais aussi voir cette réponse.
MichaelHouse
5

Jetez un œil à cette page: http://sol.gfxile.net/interpolation/index.html

Il semble que vous souhaitiez un effet similaire au smoothstep:

graphique lisse

Si vous avez le point le plus à gauche, le point le plus à droite que la plate-forme devrait atteindre et le temps qu'elle devrait utiliser pour faire une séquence complète, quelque chose comme ceci peut être correct:

cadre avant:

float smoothstep(float t, int level = 1)
{
    float ret = t;
    for(int i = 0; i < level; ++i)
    {
        ret = pow(ret, 2) * (3 - 2 * ret);
    }
    return ret;
}

currentTime += deltaTime;
if(currentTime > fullTime) currentTime -= fullTime;
float halfTime = fullTime / 2.0;
float t = abs(currentTime - halfTime) / halfTime;
t = smoothstep(t, 1);
platformPosition = rightPoint * t + leftPoint * (1 - t);

si vous utilisez un moteur physique, vous pouvez le faire avec des impulsions, cela ne devrait pas être si difficile à traduire. Si vous souhaitez un processus encore plus fluide, vous pouvez augmenter le niveau de douceur. Etapes multiples

Gustavo Maciel
la source
3

Vous pouvez utiliser XnaTweener qui fournit des fonctions d' accélération qui interpolent les valeurs d'un point à un autre de manière simple ...

Voici une réponse avec du code basé sur le projet Xna Tweener et une vidéo montrant comment il fonctionne ...

https://gamedev.stackexchange.com/a/26872/8390

[ÉDITER]

Vous devez avoir une séquence de touches qui définit le mouvement de la plate-forme, comme ceci:

public class MovementKey
{
    public float Time = 0;
    public float Duration;
    public Vector2 Traslation;            // Traslation is relative to previous key
    public TweeningFunction Function;

    internal float GetRatio( float Elapsed )   
    {
        // Always return a value in [0..1] range
        //    0 .. Start position relative to accumulated traslations of previous keys
        //    1 .. Target relative position reached.. then should go to next key if avalaible
        return Function( Elapsed, 0, 1, Duration ); 
    }
}

Et puis vous pouvez gérer le mouvement de cette façon:

public class Movement {

    List<MovementKey> Keys;

    public void Update( float Seconds)
    {

        float ratio;
        if (Playing) Elapsed += Seconds;

        while ( index!= Keys.Count - 1 && Elapsed > Keys[iKey + 1].Time )
        {
            Traslation += Keys[iKey].Traslation;  // Relative
            index++;
        }

       if ( index == Keys.Count - 1 && Elapsed > Keys[iKey].Time + Keys[iKey].Duration )
       {
          ratio = 1;
          if ( DoLoop )
          {
              Elapsed -= (Keys[index].Time + Keys[iKey].Duration);
              Index = 0;
              Traslation = Vector2.zero;
          }
       }
       else {                    
           ratio = Keys[index].GetRatio( Elapsed - Keys[index].Time );
       }

       Position = DefaultPosition + Traslation + ratio * Keys[index].Traslation;        
   }

"DefaultPosition" est la position de départ, "Traslation" accumule le mouvement de plusieurs clés, et chaque traduction de clé est relative à la clé précédente, donc lorsque vous la multipliez par un facteur de rapport [0..1], elle renvoie la traduction relative interpolée parcourue pour atteindre cette clé à partir de la clé précédente ...

Voici une autre vidéo qui montre un mouvement de plateforme défini comme décrit ici ...

http://www.youtube.com/watch?v=ZPHjpB-ErnM&feature=player_detailpage#t=104s

J'ai refait ce code en essayant de le rendre facile à comprendre ... peut-être qu'il a un bug ... le code d'origine gère plusieurs instances du même mouvement mais avec quelques retards entre chaque instance ... Je suis sûr que ce code peut être repensé pour être plus facile ...

Blau
la source
Merci, cela est utile, bien qu'il soit utilisé dans ce qui semble être le contexte d'une animation, j'essaie de le traduire dans ma tête en ce qui serait plus contextuel à ma situation actuelle.
TheBroodian
Je suis perdu. Je ne sais pas du tout comment relier cela à ma situation actuelle. oo ;;
TheBroodian
Vous avez une animation ... vous essayez d'animer en termes de position ...
Blau
Pardonnez-moi, la raison pour laquelle j'ai des difficultés, c'est parce que j'essaie d'amorcer cette idée et de la traduire en un système qui fonctionne sur un système de temps speed = velocity *, alors que ce système pousse simplement l'emplacement de l'objet de manière procédurale (rien de mal à cela, juste difficile de trouver la bonne façon de relier les deux).
TheBroodian