Je me suis heurté à un autre problème dans mon petit jeu de balle rebondissante.
Ma balle rebondit très bien, sauf pour les derniers moments où elle est sur le point de se reposer. Le mouvement de la balle est fluide pour la partie principale mais, vers la fin, la balle se branle pendant un certain temps alors qu'elle s'installe au bas de l'écran.
Je peux comprendre pourquoi cela se produit mais je n'arrive pas à le lisser.
Je serais reconnaissant pour tout conseil qui pourrait être offert.
Mon code de mise à jour est:
public void Update()
{
// Apply gravity if we're not already on the ground
if(Position.Y < GraphicsViewport.Height - Texture.Height)
{
Velocity += Physics.Gravity.Force;
}
Velocity *= Physics.Air.Resistance;
Position += Velocity;
if (Position.X < 0 || Position.X > GraphicsViewport.Width - Texture.Width)
{
// We've hit a vertical (side) boundary
// Apply friction
Velocity *= Physics.Surfaces.Concrete;
// Invert velocity
Velocity.X = -Velocity.X;
Position.X = Position.X + Velocity.X;
}
if (Position.Y < 0 || Position.Y > GraphicsViewport.Height - Texture.Height)
{
// We've hit a horizontal boundary
// Apply friction
Velocity *= Physics.Surfaces.Grass;
// Invert Velocity
Velocity.Y = -Velocity.Y;
Position.Y = Position.Y + Velocity.Y;
}
}
Je devrais peut-être aussi le souligner Gravity
, Resistance
Grass
et ils Concrete
sont tous du genre Vector2
.
Réponses:
Voici les étapes nécessaires pour améliorer votre boucle de simulation physique.
1. Timestep
Le principal problème que je peux voir avec votre code est qu'il ne tient pas compte du temps de pas physique. Il devrait être évident qu'il y a quelque chose qui ne va pas
Position += Velocity;
parce que les unités ne correspondent pas. Soit ceVelocity
n'est pas une vitesse, soit quelque chose manque.Même si vos valeurs de vitesse et de gravité sont mises à l'échelle de sorte que chaque image se produise à une unité de temps
1
(ce qui signifie par exemple queVelocity
signifie réellement la distance parcourue en une seconde), le temps doit apparaître quelque part dans votre code, soit implicitement (en fixant les variables de sorte que leurs noms reflètent ce qu'ils stockent vraiment) ou explicitement (en introduisant un pas de temps). Je pense que la chose la plus simple à faire est de déclarer l'unité de temps:Et utilisez cette valeur partout où vous en avez besoin:
Notez que tout compilateur décent simplifiera les multiplications
1.0
, de sorte que cette partie ne ralentira pas les choses.Maintenant, ce
Position += Velocity * TimeStep
n'est pas encore tout à fait exact (voir cette question pour comprendre pourquoi), mais il le fera probablement pour l'instant.En outre, cela doit prendre en compte le temps:
C'est un peu plus difficile à corriger; une façon possible est:
2. Double mise à jour
Vérifiez maintenant ce que vous faites lorsque vous rebondissez (seul le code pertinent est affiché):
Vous pouvez voir qu'il
TimeStep
est utilisé deux fois pendant le rebond. Cela donne au ballon deux fois plus de temps pour se mettre à jour. C'est ce qui devrait arriver à la place:3. Gravité
Vérifiez cette partie du code maintenant:
Vous ajoutez de la gravité pendant toute la durée du cadre. Mais que se passe-t-il si la balle rebondit réellement pendant cette image? La vitesse sera alors inversée, mais la gravité ajoutée fera alors accélérer le ballon loin du sol! Ainsi, l' excès de gravité devra être retiré lors du rebond , puis rajouté dans la bonne direction.
Il peut arriver que même en rajoutant de la gravité dans la bonne direction, la vitesse s'accélère trop. Pour éviter cela, vous pouvez soit ignorer l'ajout de gravité (après tout, ce n'est pas tant que ça et cela ne dure qu'une image), soit fixer la vitesse à zéro.
4. Code fixe
Et voici le code entièrement mis à jour:
5. Ajouts supplémentaires
Pour une stabilité de simulation encore améliorée, vous pouvez décider d'exécuter votre simulation physique à une fréquence plus élevée. Cela est rendu trivial par les changements ci-dessus
TimeStep
, car il vous suffit de diviser votre cadre en autant de morceaux que vous le souhaitez. Par exemple:la source
velocity += gravity
c'est faux et n'a develocity += gravity * timestep
sens que. Cela peut donner le même résultat à la fin, mais sans un commentaire disant "je sais ce que je fais ici" cela signifie toujours une erreur de codage, un programmeur bâclé, un manque de connaissances sur la physique, ou juste un code prototype qui doit Soyez améliorés.gravity
est en fait… pas la gravité. Mais je peux clarifier cela dans le post.Ajoutez une coche pour arrêter le rebond, en utilisant une vitesse verticale minimale. Et lorsque vous obtenez le rebond minimal, placez la balle dans le sol.
la source
Donc, je pense que le problème de pourquoi cela se produit est que votre balle approche d'une limite. Mathématiquement, la balle ne s'arrête jamais à la surface, elle s'approche de la surface.
Cependant, votre jeu n'utilise pas de temps continu. Il s'agit d'une carte qui utilise une approximation de l'équation différentielle. Et cette approximation n'est pas valable dans cette situation limite (vous pouvez, mais vous devrez prendre des pas de temps plus petits et plus petits, ce qui, je suppose, n'est pas faisable.
Physiquement parlant, ce qui se passe, c'est que lorsque la balle est très proche de la surface, elle y adhère si la force totale est inférieure à un seuil donné.
La réponse @Zhen serait bien si votre système est homogène, ce qui n'est pas le cas. Il a une certaine gravité sur l'axe y.
Donc, je dirais que la solution ne serait pas que la vitesse soit inférieure à un seuil donné, mais la force totale appliquée sur la balle après la mise à jour devrait être inférieure à un seuil donné.
Cette force est la contribution de la force exercée par le mur sur le ballon + la gravité.
La condition devrait alors être quelque chose comme
if (newVelocity + Physics.Gravity.Force <seuil)
notez que newVelocity.y est une quantité positive si le rebond est sur la paroi du botton et que la gravité est une quantité négative.
Notez également que newVelocity et Physics.Gravity.Force n'ont pas les mêmes dimensions, comme vous l'avez écrit dans
ce qui signifie que, comme vous, je suppose que delta_time = 1 et ballMass = 1.
J'espère que cela t'aides
la source
Vous avez une mise à jour de position dans votre contrôle de collision, elle est redondante et erronée. Et il ajoute de l'énergie à la balle, l'aidant ainsi potentiellement à se déplacer perpétuellement. Avec la gravité non appliquée à certains cadres, cela donne un mouvement étrange. Retirez-le.
Maintenant, vous pouvez voir un problème différent, le fait que la balle se "bloque" en dehors de la zone désignée, rebondissant perpétuellement d'avant en arrière.
Un moyen simple de résoudre ce problème consiste à vérifier que la balle se déplace dans la bonne direction avant de la changer.
Vous devez donc faire:
Dans:
Et similaire pour la direction Y.
Pour que le ballon s'arrête bien, vous devez arrêter la gravité à un moment donné. Votre implémentation actuelle garantit que la balle refera toujours surface car la gravité ne la freine pas tant qu'elle est souterraine. Vous devez changer pour toujours appliquer la gravité. Cela conduit cependant à la balle s'enfoncer lentement dans le sol après s'être installée. Une solution rapide pour cela est, après avoir appliqué la gravité, si la balle est en dessous du niveau de la surface et se déplace vers le bas, arrêtez-la:
Ces changements au total devraient vous donner une simulation décente. Mais notez qu'il s'agit toujours d'une simulation très simple.
la source
Avoir une méthode de mutation pour tous les changements de vitesse, puis dans cette méthode, vous pouvez vérifier la vitesse mise à jour pour déterminer si elle se déplace assez lentement pour la mettre au repos. La plupart des systèmes de physique que je connais appellent cela «restitution».
Dans la méthode ci-dessus, nous limitons le rebond à chaque fois qu'il est sur le même axe que la gravité.
Une autre chose à considérer serait de détecter chaque fois qu'une balle est entrée en collision avec le sol, et si elle se déplace assez lentement au moment de la collision, réglez la vitesse le long de l'axe de gravité sur zéro.
la source
Autre chose: vous multipliez par une constante de friction. Changez cela - abaissez la constante de frottement mais ajoutez une absorption d'énergie fixe sur un rebond. Cela amortira ces derniers rebonds beaucoup plus rapidement.
la source