Pregunta

He escrito una aplicación Java bastante simple que te permite arrastrar el mouse y, según la longitud del arrastre del mouse que hiciste, disparará una pelota en esa dirección, rebotando en las paredes a medida que avanza.

Aquí hay una captura de pantalla rápida:
texto alternativo http://img222.imageshack.us/img222/3179/ballbouncemf9.png

Cada uno de los círculos en la pantalla es un objeto Ball. El movimiento de las bolas se divide en un vector x e 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
    }
}

Esto funciona muy bien. Todas las bolas rebotan de una pared a otra.

Sin embargo, he decidido que quiero poder incluir los efectos de la gravedad. Sé que los objetos aceleran hacia la Tierra a 9.8 m / s, pero no sé directamente cómo se debería traducir esto en código. Me doy cuenta de que el yVector se verá afectado, pero mi experimentación con esto no tuvo el efecto deseado que quería.

Idealmente, me gustaría poder agregar algún efecto de gravedad a este programa y también permitir que las bolas reboten varias veces antes de asentarse en el suelo ".

¿Cómo puedo crear este efecto de gravedad elástico que rebota? ¿Cómo debo manipular los vectores de velocidad de la pelota en cada paso? ¿Qué se debe hacer cuando golpea el "suelo"? para que pueda volver a recuperarse, ¿pero algo más corto que la vez anterior?

Cualquier ayuda es apreciada al señalarme en la dirección correcta.


¡Gracias por los comentarios a todos! ¡Ya está funcionando genial!

En mi paso () agrego una constante de gravedad a mi yVector como la gente sugirió y este es mi 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.
}

Sin embargo, las bolas continuarán deslizándose / rodando en el piso. Supongo que esto se debe a que simplemente estoy tomando un porcentaje (90%) de sus vectores cada rebote y nunca es realmente cero. ¿Debo agregar una comprobación de que si el xVector se convierte en un cierto valor absoluto, debería cambiarlo a cero?

¿Fue útil?

Solución

Lo que tiene que hacer es restar constantemente una pequeña constante (algo que representa sus 9.8 m / s) de su yVector. Cuando la pelota cae (yVector ya es negativo), esto lo haría ir más rápido. Cuando sube (yVector es positivo) lo ralentizaría.

Esto no explicaría la fricción, por lo que las cosas deberían rebotar para siempre.

edit1: Para tener en cuenta la fricción, siempre que se invierta (y usted invierta el signo), baje un poco el número absoluto. Al igual que si llega a yVector = -500, cuando invierta el signo, hágalo +480 en lugar de +500. Probablemente debería hacer lo mismo con xVector para evitar que rebote de lado a lado.

edit2: Además, si desea que reaccione a la fricción del aire, reduzca ambos vectores en una cantidad muy pequeña en cada ajuste.

edit3: Sobre lo que está rodando en el fondo para siempre: dependiendo de cuán altos sean sus números, podría ser una de dos cosas. O sus números son grandes y parece que tarda una eternidad en terminar, o está redondeando y sus vectores siempre son 5 o algo así. (90% de 5 es 4.5, por lo que puede redondear hasta 5).

Imprimiría una declaración de depuración y vería cómo son los números de Vector. Si van a algún lugar alrededor de 5 y simplemente permanecen allí, entonces puede usar una función que trunca su fracción a 4 en lugar de redondear a 5. Si continúa bajando y finalmente se detiene, entonces podría tener que aumentar su coeficiente de fricción .

Si no puede encontrar un "redondeo" fácil , podría usar (0.9 * Vector) - 1, restando 1 de su ecuación existente debería hacer lo mismo.

Otros consejos

Cuando todas las bolas ruedan por el suelo, sí, verifique si la velocidad está por debajo de un cierto valor mínimo y, de ser así, ajústelo a cero. Si observa la física detrás de este tipo de movimiento idealizado y compara con lo que sucede en el mundo real, verá que no se puede usar una sola ecuación para explicar el hecho de que una bola real deja de moverse.

Por cierto, lo que estás haciendo se llama el método de Euler para la integración numérica. Va así:

  • Comienza con las ecuaciones cinemáticas del movimiento:
    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
    Donde x e y son posición, vx y vy son velocidad, ax y ay son aceleración y t es tiempo. x0, y0, vx0 y vy0 son los valores iniciales. Esto describe el movimiento de un objeto en ausencia de cualquier fuerza externa.

  • Ahora aplique gravedad:
    ay = -9.8 m / s ^ 2
    Hasta este punto, no hay necesidad de hacer nada complicado. Podemos resolver la posición de cada bola usando esta ecuación en cualquier momento.

  • Ahora agregue fricción de aire: como es una bola esférica, podemos suponer que tiene un coeficiente de fricción c. Normalmente hay dos opciones para modelar la fricción del aire. Puede ser proporcional a la velocidad o al cuadrado de la velocidad. Usemos el cuadrado:
    ax = -c vx ^ 2
    ay = -c
    vy ^ 2 - 9.8
    Debido a que la aceleración ahora depende de la velocidad, que no es constante, debemos integrarnos. Esto es malo, porque no hay forma de resolver esto a mano. Tendremos que integrarnos numéricamente.

  • Tomamos pasos de tiempo discreto, dt. Para el método de Euler, simplemente reemplazamos todas las ocurrencias de t en las ecuaciones anteriores con dt, y usamos el valor del paso de tiempo anterior en lugar de los valores iniciales, x0, y0, etc. Así que ahora nuestras ecuaciones se ven así (en pseudocódigo) :

    // Guardar valores anteriores
    xold = x;
    yold = y;
    vxold = vx;
    vyold = vy;

    // Actualizar aceleración
    ax = -c vxold ^ 2;
    ay = -c
    vyold ^ 2 - 9.8;

    // Velocidad de actualización
    vx = vxold + ax dt;
    vy = vyold + ay
    dt;

    // Actualizar posición
    x = xold + vxold * dt + 0.5 * ax dt ^ 2;
    y = yold + vyold
    dt + 0.5 * ay * dt ^ 2;

Esta es una aproximación, por lo que no será exactamente correcta, pero se verá bien. El problema es que para pasos de tiempo mayores, el error aumenta, por lo que si queremos modelar con precisión cómo se movería una bola real, tendríamos que usar valores muy pequeños para dt, lo que causaría problemas de precisión en una computadora. Para resolver eso, hay técnicas más complicadas. Pero si solo quiere ver un comportamiento que se parece a la gravedad y la fricción al mismo tiempo, entonces el método de Euler está bien.

Cada vez que se corta, debes aplicar los efectos de la gravedad acelerando la pelota en dirección hacia abajo. Como sugirió Bill K, eso es tan simple como hacer una resta de su " yVector " ;. Cuando la pelota toca el fondo, yVector = -yVector, por lo que ahora se mueve hacia arriba pero sigue acelerando hacia abajo. Si desea que las bolas finalmente dejen de rebotar, debe hacer que las colisiones sean un poco inelásticas, básicamente eliminando algo de velocidad en la dirección hacia arriba, posiblemente en lugar de `` yVector = -yVector '', hágalo `` yVector = -0.9 * yVector " ;.

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

    yVector += g //some constant representing 9.8

    checkCollisions();
}

en checkCollisions (), debe invertir y multiplicar yVector por un número entre 0 y 1 cuando rebota en el suelo. Esto debería darle el efecto deseado

Es un movimiento balístico. Entonces tienes un movimiento lineal en el eje xy un movimiento acelerado uniforme en el eje y.

La idea básica es que el eje y seguirá la ecuación:

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

O, en el código C, por ejemplo:

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

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

Donde aquí uso la parametrización tiem. Pero podrías usar Torricelli:

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

Y, en este modelo, debe mantener los últimos valores de v e y.

Finalmente, hice algo similar usando el primer modelo con dt (diferencial de tiempo) fijado en 1/60 de segundo (60 FPS).

Bueno, ambos modelos dan buenos resultados reales, pero sqrt (), por ejemplo, es costoso.

Realmente quieres simular lo que hace la gravedad: todo lo que hace es crear una fuerza que actúa con el tiempo para cambiar la velocidad de un objeto. Cada vez que das un paso, cambias un poco la velocidad de tu pelota para `` tirar ''. hacia la parte inferior del widget.

Para lidiar con el problema de asentamiento de la pelota sin fricción / rebote, debe hacer que el "terreno". La colisión ejerce un efecto diferente al de la reflexión estricta: debería eliminar cierta cantidad de energía de la pelota, haciendo que rebote a una velocidad menor después de tocar el suelo de lo que lo haría de otra manera.

Otra cosa que generalmente quieres hacer en este tipo de visualizaciones hinchables es darle al suelo una cierta fricción lateral, de modo que cuando toque el suelo todo el tiempo, finalmente se detenga.

Estoy de acuerdo con lo que "Bill K" dicho, y agregaría eso si desea que se "resuelvan" necesitará reducir los vectores x e y con el tiempo (aplicar resistencia). Esto tendrá que ser una cantidad muy pequeña a la vez, por lo que puede que tenga que cambiar sus vectores de int a un tipo de punto flotante, o solo reducirlos en 1 cada pocos segundos.

Lo que quiere hacer es cambiar los valores de xVector e yVector para simular la gravedad y la fricción. Esto es realmente bastante simple de hacer. (Necesita cambiar todas sus variables a flotadores. Cuando llegue el momento de dibujar, simplemente redondee los flotadores).

En su función de paso, después de actualizar la posición de la pelota, debe hacer algo como esto:

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

Esto reduce ligeramente la velocidad de X e Y, permitiendo que tus bolas finalmente dejen de moverse, y luego aplica una aceleración constante hacia abajo ''. al valor Y, que se acumulará más rápido que la "ralentización" y hacer que las bolas caigan.

Esta es una aproximación de lo que realmente quieres hacer. Lo que realmente quieres es mantener un vector que represente la aceleración de tus bolas. En cada paso, salpicaría ese producto con un vector de gravedad constante para cambiar ligeramente la aceleración de la bola. Pero creo que puedo ser más complejo de lo que quieres conseguir, a menos que estés buscando una simulación física más realista.

  

¿Qué se debe hacer cuando golpea el   `` suelo '' para que pueda permitir que   recuperarse de nuevo

Si asume una colisión perfecta (es decir, se conserva toda la energía), todo lo que tiene que hacer es revertir el signo de uno de los escalares de velocidad dependiendo de qué pared fue golpeada.

Por ejemplo, si la bola golpea las paredes derecha o izquierda revele el componente escalar x y deje el componente escalar y igual:

 this.xVector = -this.xVector;

Si la bola golpea las paredes superior o inferior, invierta el componente escalar y y deje el componente escalar x igual:

 this.yVector = -this.yVector;
  

pero algo más corto que el anterior   hora?

En este escenario, parte de la energía se perderá en la colisión con el muro, así que solo agregue un factor de pérdida para tomar algo de la velocidad cada vez que se golpea el muro:

 double loss_factor = 0.99;
 this.xVector = -(loss_factor * this.xVector);
 this.yVector = -(loss_factor * this.yVector;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top