Estoy empezando a aprender a hacer física de bricolaje, y tengo una pregunta sobre la implementación de la integración en el nivel más básico (es decir, esta no es una pregunta de Euler vs. RK4).
Casi todos los ejemplos que encuentro tienen alguna integrate()
función que obtiene el paso de tiempo desde la última actualización, y actualiza la aceleración (y / o velocidad y / o posición) desde la última actualización.
En la forma más simple: position += velocity * deltaTime
Sin embargo, no entiendo por qué se acumula de esta manera cuando se podría obtener fácilmente cambiando una función . Por ejemplo: getPosition = makeNewFunction()
qué podría devolver algo que tiene la firma de Time -> Position
, y el funcionamiento interno de esa función se genera a través de la fórmula matemática adecuada.
De esa manera, no hay acumulación ... cada vez que se necesita obtener la posición, llama a esa función con la hora actual.
Mi comprensión de novato es que esto también evitaría los errores que provienen de la acumulación ... entonces, ¿por qué esto no funciona? ¿Qué me estoy perdiendo?
(fwiw me he reunido una prueba básica del concepto de esta idea-aunque también está probando algunas otras cosas al mismo tiempo, así que no es el ejemplo más limpio: https://github.com/dakom/ball-bounce-frp )
EDITAR 1: como se menciona en los comentarios, probablemente sea importante señalar que aún no he aprendido sobre el cambio de la aceleración, o sobre el manejo de tirones y otras cosas que requieren una integración de orden superior a la aceleración constante.
EDITAR 2: aquí hay un código de muestra básico de la idea y una sintaxis de pseudo javascript: tenga en cuenta que getKinematicPosition
se aplica parcialmente, por lo que devuelve una nueva función de solo Tiempo -> Posición:
Me estoy quedando en la posición aquí, pero podría ser algo más, como getVelocity
, supongo ...
getKinematicPosition = initialVelocity => acceleration => time =>
((.5 *acceleration) * (time * time)) + (initialVelocity * time);
getPosition = getKinematicPosition ([0,0,0]) (GRAVITY);
onTick = totalTime => {
position = getPosition (totalTime);
onCollision = () => {
getPosition = changeTheFunction(totalTime);
//changeTheFunction uses totalTime to base updates from 0
//it could use getKinematicPosition or something else entirely
}
}
fuente
Respuestas:
Esto funcionará para ciertas clases de problemas, y la frase clave para buscar es una solución de forma cerrada . Por ejemplo, en Kerbal Space Program, el movimiento de una nave espacial en órbita se calcula de esta manera. Desafortunadamente, la mayoría de los problemas no triviales (por ejemplo, reentrada atmosférica de dicha nave espacial) no tienen una solución de forma cerrada conocida. De ahí la necesidad de aproximaciones numéricas matemáticamente más simples (es decir, a lo
integrate()
largo del tiempo).fuente
El problema con su enfoque es que no tiene un historial de su objeto. Puede calcular la posición si se mueve en una dirección, pero ¿qué sucede si golpea algo y se recupera?
Si acumula desde su última posición conocida, puede manejar el impacto y continuar desde allí. Si intenta calcularlo desde el principio, debe volver a calcular el impacto cada vez o establecerlo como una nueva posición de inicio.
Tu ejemplo me recordó a un juego de carreras. (No sé si la posición sería controlada por el motor de física, pero creo que funciona muy bien explicarlo)
Si conduce con su automóvil, puede acelerar y reducir la velocidad. No puede calcular su posición sin saber cómo se veía el perfil de velocidad de su automóvil desde el principio hasta ahora. Acumular la distancia es mucho más fácil que almacenar la velocidad que tenía en cada cuadro desde el principio hasta ahora.
Descargo de responsabilidad: no he escrito la física del juego por ahora, así es como veo el problema.
Editar:
en este diagrama puede ver cómo cambian los valores con el tiempo.
rojo = aceleración (desde comenzar a acelerar hasta descender)
verde = velocidad (desde comenzar a detenerse)
azul = el camino que siguió.
La velocidad total es la integral de la aceleración desde su punto de inicio hasta su registro real. (El área entre la línea y el eje)
El camino es la integral de su velocidad.
Si conoce los valores de su aceleración, puede calcular los otros valores. Pero si no me equivoco, las integrales también se calculan por acumulación en las PC. Y es mucho más sobrecarga tener almacenados todos los valores de aceleración.
Además, probablemente sea demasiado para calcular cada cuadro.
Lo sé, mis habilidades de pintura son geniales. ;)
Edición 2:
este ejemplo es para movimiento lineal. La dirección de arrastre hace que esto sea aún más difícil.
fuente
¡Usted puede!
Se llama usando un solución analítica o de forma cerrada . Tiene el beneficio de que es más preciso, ya que los errores de redondeo que se acumulan con el tiempo son inexistentes.
Sin embargo, esto funciona si y solo si conoce de antemano una forma tan cerrada. Para los juegos, esto no suele ser el caso.
El movimiento del jugador es errático y simplemente no se puede poner en alguna función precalculada. El jugador puede y cambiará su velocidad y orientación con bastante frecuencia.
Los NPC podrían utilizar soluciones de forma cerrada, y de hecho a veces lo hacen. Sin embargo, esto tiene otros inconvenientes. Piensa en un simple juego de carreras. Cada vez que su vehículo colisiona con otro vehículo, debe cambiar su función. Quizás el auto se mueva más rápido dependiendo del metro. Entonces encontrar una solución tan cerrada será bastante difícil. De hecho, es probable que haya más casos en los que encontrar una forma tan cerrada sea imposible o tan complicado que simplemente no sea factible.
Un gran ejemplo de uso de una solución de forma cerrada es Kerbal Space Program. Tan pronto como su cohete esté en órbita y no bajo empuje, KSP puede ponerlo "sobre rieles". Las órbitas están predeterminadas en el sistema de dos cuerpos y son periódicas. Mientras el cohete no aplique más empuje, ya sabe dónde estará el cohete, y simplemente puede llamar
getPositionAtTime(t)
(no se llama exactamente eso, pero entiendes la idea).En la práctica, sin embargo, el uso de la integración paso a paso suele ser mucho más práctico. Pero cuando ve una situación en la que existe una solución de forma cerrada y es fácil de calcular, ¡adelante! No hay razón para no usarlo.
Por ejemplo, si tu personaje apunta a un cañón, puedes mostrar fácilmente el punto de impacto previsto de la bola de cañón usando una solución de forma cerrada. Y, si su juego no permite alterar el curso de la bola de cañón (sin viento, por ejemplo), incluso puede usarlo para mover la bola de cañón. Tenga en cuenta que debe tener especial cuidado con los obstáculos que se mueven en el camino de su bala de cañón, entonces.
Hay muchas situaciones similares. Si está construyendo un juego basado en rondas, entonces es probable que haya muchas más soluciones de forma cerrada disponibles que cuando construye un juego RTS, ya que conoce todos los parámetros de antemano y puede decir con certeza que no cambian (nada se mueve de repente en ese camino, por ejemplo).
Tenga en cuenta que existen técnicas para combatir las imprecisiones numéricas de la integración escalonada. Por ejemplo, puede realizar un seguimiento del error acumulado y aplicar un término correctivo para mantener el error bajo control, por ejemplo, Kahan Summation
fuente
En el caso de una simple pelota que rebota, es fácil encontrar soluciones de forma cerrada. Sin embargo, los sistemas más complejos tienden a requerir la resolución de una ecuación diferencial ordinaria (EDO). Se requieren solucionadores numéricos para manejar todos menos los casos más simples.
De hecho, hay dos clases de solucionadores numéricos de EDO: explícito e implícito. Los solucionadores explícitos proporcionan una aproximación de forma cerrada para su próximo estado, mientras que los solucionadores implícitos requieren resolver una ecuación para hacerlo. ¡Lo que usted describe para su pelota que rebota es en realidad un solucionador implícito de ODE, lo supiera o no!
Los solucionadores implícitos tienen la ventaja de poder usar pasos de tiempo mucho más largos. Para su algoritmo de pelota que rebota, su paso de tiempo puede ser al menos tan largo como la duración hasta la próxima colisión (lo que cambiaría su función). Esto puede hacer que su programa se ejecute mucho más rápido. Sin embargo, en general, no siempre podemos encontrar buenas soluciones implícitas a las EDO que nos interesan. Cuando no podemos, recurrimos a la integración explícita.
La gran ventaja que veo con la integración explícita es que las trampas son bien conocidas. Puede abrir cualquier libro de texto de los años 60 y leer todo lo que necesita saber sobre las pequeñas peculiaridades que surgen con técnicas de integración particulares. Por lo tanto, un desarrollador aprende estas habilidades una vez y nunca más tiene que aprenderlas. Si está haciendo una integración implícita, cada caso de uso es ligeramente diferente, con problemas ligeramente diferentes. Es un poco más difícil aplicar lo que aprendiste de una tarea a la siguiente.
fuente
pos (t) = v (t) * t
solo funciona si pos (0) = 0 y v (t) = k
no se puede relacionar la posición con el tiempo sin conocer la condición inicial y la función de velocidad completa, por lo que la ecuación es como una aproximación de la integral
pos (t) = integral de v (t) dt de 0 a t
EDITAR _________
Aquí hay una pequeña prueba por los comentarios (suponiendo pos (0) = 0)
dejar v (t) = 4
ecuación 1: pos (t) = 4 * t (correcto)
ecuación 2: pos (t) = c + 4 * t de 0 a t = 4 * t (correcto)
dejar v (t) = 2 * t
ecuación 1: pos (t) = 2 * t ^ 2 (incorrecto)
ecuación 2: pos (t) = c + t ^ 2 de 0 a t = t ^ 2 (correcto)
Debo agregar que su ecuación ya tiene en cuenta la aceleración constante (es decir, su ecuación es eqn 2 donde v (t) = v0 + a * t y los límites de integración son t0 y t), por lo que su ecuación debería funcionar siempre que actualice La posición inicial, la velocidad inicial y la aceleración permanecen constantes.
EDITAR2 ________
También debo agregar que también puedes calcular la posición con la posición inicial, la velocidad inicial, la aceleración inicial y la sacudida constante. En otras palabras, puede crear una función basada en la ecuación 2 que represente la posición frente al tiempo separándola en sus derivadas, es decir, velocidad, tirón, lo que venga después, etc., etc., pero solo será precisa en su ecuación si v (t) se puede modelar de esa manera. Si v (t) no se puede modelar solo con velocidad, aceleración, sacudidas constantes, etc., entonces debe volver a una aproximación de la ecuación 2, que tiende a suceder cuando tiene cosas que rebotan, resistencia al aire, viento, etc. .
fuente