Question

J'ai écrit une application Java assez simple qui vous permet de faire glisser votre souris. En fonction de la longueur de la souris que vous avez traînée, elle lancera une balle dans cette direction, en rebondissant sur les murs au fur et à mesure.

Voici une capture d'écran rapide:
texte alt http://img222.imageshack.us/img222/3179/ballbouncemf9.png

Chacun des cercles à l’écran est un objet Ball. Le mouvement des boules est décomposé en un vecteur x et y;

public class Ball {
    public int xPos;
    public int yPos;
    public int xVector;
    public int yVector;

    public Ball(int xPos, int yPos, int xVector, int yVector) {
        this.xPos = xPos;
        this.yPos = yPos;
        this.xVector = xVector;
        this.yVector = yVector;
    }

    public void step()
    {
        posX += xVector;
        posY += yVector;

        checkCollisions();
    }

    public void checkCollisions()
    {
        // Check if we have collided with a wall
        // If we have, take the negative of the appropriate vector
        // Depending on which wall you hit
    }

    public void draw()
    {
        // draw our circle at it's position
    }
}

Cela fonctionne très bien. Toutes les balles rebondissent d'un mur à l'autre.

Cependant, j’ai décidé que je voulais pouvoir inclure les effets de la gravité. Je sais que les objets accélèrent vers la Terre à 9,8 m / s, mais je ne sais pas directement comment cela devrait se traduire en code. Je me rends compte que le yVector sera affecté, mais mon expérimentation n’a pas eu l’effet désiré.

Idéalement, j'aimerais pouvoir ajouter un effet de gravité à ce programme et permettre également aux balles de rebondir plusieurs fois avant de se poser sur le "sol".

Comment puis-je créer cet effet de gravité élastique et rebondissant? Comment dois-je manipuler les vecteurs vitesse de la balle à chaque pas? Ce qui doit être fait quand il frappe le & terrain; sol " afin que je puisse le laisser rebondir à nouveau, mais un peu plus court que la fois précédente?

Toute aide est appréciée pour me diriger dans la bonne direction.

Merci à tous pour les commentaires! Cela fonctionne déjà très bien!

Dans mon étape (), j'ajoute une constante de gravité à mon yVector, comme le suggèrent les personnes. Voici ma checkCollision ():

public void checkCollision()
{
    if (posX - radius < 0)              // Left Wall?
    {
        posX = radius;              // Place ball against edge
        xVector = -(xVector * friction);
    }
    else if (posX + radius > rightBound) // Right Wall?
    {
        posX = rightBound - radius;     // Place ball against edge
        xVector = -(xVector * friction);
    }

    // Same for posY and yVector here.
}

Cependant, les balles continueront à glisser / rouler sur le sol. Je suppose que c'est parce que je prends simplement un pourcentage (90%) de leurs vecteurs à chaque rebond et qu'il n'est jamais vraiment nul. Dois-je ajouter dans un chèque que si le xVector devient une certaine valeur absolue, je devrais simplement le changer à zéro?

Était-ce utile?

La solution

Vous devez constamment soustraire une petite constante (quelque chose qui représente vos 9,8 m / s) de votre yVector. Lorsque la balle tombe (yVector est déjà négatif), cela le ferait aller plus vite. En cas de hausse (yVector est positif), cela ralentirait.

Cela ne prendrait pas en compte les frictions, les choses devraient donc rebondir pour toujours.

edit1: Pour rendre compte du frottement, abaissez légèrement le nombre absolu chaque fois qu'il inverse (et que vous inversez le signe). Comme si cela se produisait à yVector = -500, lorsque vous inversez le signe, définissez-le +480 au lieu de +500. Vous devriez probablement faire la même chose à xVector pour l'empêcher de rebondir d'un côté à l'autre.

edit2: De plus, si vous voulez qu'il réagisse au "frottement de l'air", réduisez les deux vecteurs d'un très petit montant à chaque réglage.

edit3: À propos des choses qui tournent toujours vers le bas - Selon le nombre de vos chiffres, cela pourrait être l’une des deux choses suivantes. Soit vos chiffres sont importants et cela semble prendre très longtemps, soit vous arrondissez et vos vecteurs sont toujours de 5 ou quelque chose comme ça. (90% de 5 est 4,5, alors il peut arrondir à 5).

Je voudrais imprimer une déclaration de débogage et voir à quoi ressemblent les nombres de Vector. S'ils vont quelque part autour de 5 et y restent, alors vous pouvez utiliser une fonction qui tronque votre fraction à 4 au lieu d'être arrondie à 5. Si elle continue à baisser et s'arrête, vous devrez peut-être augmenter votre coefficient de frottement .

Si vous ne trouvez pas facilement & arrondi " fonction, vous pouvez utiliser (0,9 * vecteur) - 1, soustraire 1 de votre équation existante devrait faire la même chose.

Autres conseils

Lorsque les balles roulent toutes sur le sol, vérifiez si la vitesse est inférieure à une valeur minimale et, le cas échéant, définissez-la sur zéro. Si vous regardez la physique derrière ce type de mouvement idéalisé et comparez avec ce qui se passe dans le monde réel, vous verrez qu'une seule équation ne peut pas être utilisée pour expliquer le fait qu'une vraie balle cesse de bouger.

BTW, ce que vous faites s'appelle la méthode d’Euler pour l’intégration numérique. Cela va comme ceci:

  • Commencez par les équations cinématiques du mouvement:
    x (t) = x0 + vx * t + 0,5 * ax t ^ 2
    y (t) = y0 + vy
    t + 0,5 * ay t ^ 2
    vx (t) = vx0 + ax
    t
    vy (t) = vy0 + ay * t
    Où x et y sont la position, vx et vy sont la vitesse, ax et ay sont l'accélération et t le temps. x0, y0, vx0 et vy0 sont les valeurs initiales. Ceci décrit le mouvement d’un objet en l’absence de toute force extérieure.

  • Appliquez maintenant la gravité:
    ay = -9.8 m / s ^ 2
    À ce stade, il n'y a pas besoin de faire quelque chose de délicat. Nous pouvons résoudre la position de chaque balle en utilisant cette équation à tout moment.

  • Maintenant, ajoutez le frottement de l'air: Comme il s'agit d'une boule sphérique, on peut supposer qu'elle a un coefficient de frottement c. Il existe généralement deux choix pour modéliser le frottement de l'air. Il peut être proportionnel à la vitesse ou au carré de la vitesse. Utilisons le carré:
    ax = -c vx ^ 2
    ay = -c
    vy ^ 2 - 9.8
    Puisque l'accélération dépend maintenant de la vitesse, qui n'est pas constante, nous devons intégrer. C'est mauvais, car il n'y a aucun moyen de résoudre ce problème à la main. Nous devrons intégrer numériquement.

  • Nous prenons des pas de temps discrets, dt. Pour la méthode d'Euler, nous remplaçons simplement toutes les occurrences de t dans les équations ci-dessus par dt, et nous utilisons la valeur du timestep précédent à la place des valeurs initiales, x0, y0, etc. :

    // Sauvegarder les valeurs précédentes
    xold = x;
    yold = y;
    vxold = vx;
    vyold = vy;

    // Mise à jour de l'accélération
    ax = -c vxold ^ 2;
    ay = -c
    vyold ^ 2 - 9,8;

    // Mise à jour de la vitesse
    vx = vxold + ax dt;
    vy = vyold + ay
    dt;

    // Mise à jour de la position
    x = xold + vxold * dt + 0.5 * ax dt ^ 2;
    y = yold + vyold
    dt + 0.5 * ay * dt ^ 2;

Ceci est une approximation, donc ce ne sera pas tout à fait correct, mais ça aura l'air OK. Le problème est que pour des pas de temps plus importants, l’erreur augmente, donc si nous voulons modéliser avec précision le mouvement d’une vraie balle, nous devrons utiliser de très petites valeurs pour dt, ce qui causerait des problèmes de précision sur un ordinateur. Pour résoudre ce problème, il existe des techniques plus compliquées. Mais si vous voulez simplement voir un comportement qui ressemble à la fois à la gravité et à la friction, la méthode d'Euler est acceptable.

A chaque coupure, vous devez appliquer les effets de la gravité en accélérant la balle dans la direction descendante. Comme l'a suggéré Bill K, il suffit de soustraire votre "yVector". Lorsque la balle frappe le bas, yVector = -yVector, elle se déplace maintenant vers le haut mais accélère toujours vers le bas. Si vous voulez que les balles cessent finalement de rebondir, vous devez rendre les collisions légèrement inélastiques, essentiellement en supprimant une certaine vitesse dans la direction y-up, éventuellement au lieu de "yVector = -yVector", rendez-le "yVector = -0,9 * yVector ".

public void step()
{
    posX += xVector;
    posY += yVector;

    yVector += g //some constant representing 9.8

    checkCollisions();
}

dans checkCollisions (), vous devez inverser et multiplier yVector par un nombre compris entre 0 et 1 lorsqu’il rebondit sur le sol. Cela devrait vous donner l'effet souhaité

C'est un mouvement balistique. Donc, vous avez un mouvement linéaire sur l'axe des x et un mouvement accéléré uniforme sur l'axe des y.

L'idée de base est que l'axe des y suivra l'équation:

y = y0 + v0 * t + (0.5)*a*t^2

Ou, dans le code C, par exemple:

float speed = 10.0f, acceleration = -9.8f, y = [whatever position];

y += speed*t + 0.5f*acceleration*t^2;

Où j'utilise ici la paramétrisation. Mais vous pouvez utiliser Torricelli:

v = sqrt(v0^2 + 2*acceleration*(y-y0));

Et sur ce modèle, vous devez conserver les dernières valeurs de v et y.

Enfin, j’ai fait quelque chose de similaire en utilisant le premier modèle avec dt (différentiel de temps) fixé à 1/60 de seconde (60 FPS).

Eh bien, les deux modèles donnent de bons résultats, mais sqrt (), par exemple, coûte cher.

Vous voulez vraiment simuler ce que fait la gravité - elle ne fait que créer une force qui agit dans le temps pour modifier la vitesse d'un objet. Chaque fois que vous faites un pas, vous modifiez un peu la vitesse de votre balle afin de "tirer" vers le bas du widget.

Pour résoudre le problème du tassement de la balle sans frottement / rebondissant, vous devez créer le champ "Terrain". La collision a un effet différent de la simple réflexion stricte: elle doit enlever une certaine quantité d’énergie de la balle, la faisant rebondir à une vitesse plus faible après le toucher du sol qu’elle ne le ferait autrement.

Une autre chose que vous voulez généralement faire dans ces types de visualisations rebondissantes est de donner au sol des frictions latérales également, de sorte que, quand il frappe tout le temps, il finira par s'arrêter.

Je suis d'accord avec ce que "Bill K" dit, et ajouterais que si vous voulez qu'ils "règlent" vous devrez réduire les vecteurs x et y au fil du temps (appliquer une résistance). Cela devra être très petit à la fois, vous devrez donc peut-être changer vos vecteurs de type int à virgule flottante ou les réduire de 1 seulement toutes les quelques secondes.

Ce que vous voulez faire est de changer les valeurs de xVector et de yVector afin de simuler la gravité et le frottement. C'est vraiment très simple à faire. (Vous devez modifier toutes vos variables en floats. Lorsque vient le temps de dessiner, contourner les flottants.)

Dans votre fonction step, après avoir mis à jour la position de la balle, vous devriez faire quelque chose comme ceci:

yVector *= 0.95;
xVector *= 0.95;
yVector -= 2.0;

Ceci réduit légèrement la vitesse X et Y, permettant ainsi à vos balles de cesser de bouger, puis applique une "accélération" constante vers le bas. à la valeur Y, qui s'accumule plus rapidement que le "ralentissement" et faire tomber les balles.

Ceci est une approximation de ce que vous voulez vraiment faire. Ce que vous voulez vraiment, c'est garder un vecteur représentant l'accélération de vos balles. Chaque étape, vous produiriez alors ce vecteur avec un vecteur de gravité constante pour modifier légèrement l’accélération de la balle. Mais je pense que mon être plus complexe que vous voulez obtenir, à moins que vous ne cherchiez une simulation de physique plus réaliste.

  

Que faut-il faire quand il frappe le   " sol " afin que je puisse lui permettre de   rebondir à nouveau

Si vous supposez une collision parfaite (c’est-à-dire que toute l’énergie est conservée), il vous suffit de renverser le signe de l’un des scalaires de vélocité en fonction du mur touché.

Par exemple, si la balle frappe les murs droit ou gauche, revoyez le composant scalaire x et laissez le même composant scalaire y:

 this.xVector = -this.xVector;

Si la balle frappe les parois supérieure ou inférieure, inversez le composant scalaire y et laissez le même composant scalaire:

 this.yVector = -this.yVector;
  

mais un peu plus court que le précédent   le temps?

Dans ce scénario, une partie de l’énergie sera perdue lors de la collision avec le mur. Il suffit donc d’ajouter un facteur de perte pour prendre en compte une partie de la vitesse chaque fois que le mur est touché:

 double loss_factor = 0.99;
 this.xVector = -(loss_factor * this.xVector);
 this.yVector = -(loss_factor * this.yVector;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top