¿Cómo implementar un rayo tractor?

8

Estoy trabajando en un juego en el que el jugador puede recoger objetos usando algo como un rayo tractor y transportarlos.

Atraer el objeto hacia el centro de la viga no es difícil. Pero una vez que el objeto está lo suficientemente cerca del centro, necesito mantenerlo allí mientras el jugador se mueve, que es con lo que tengo problemas. Se me ocurren dos formas de hacerlo, y ambas tienen problemas:

  1. Actualice la posición del objeto cada vez que cambie la posición del jugador, manteniéndolo centrado en la viga.

  2. Actualice la velocidad del objeto para apuntar directamente hacia el centro del haz, cuanto más lejos, más velocidad.

Mover y rotar funciona bien con ambos enfoques, pero la física es incorrecta cuando el objeto transportado choca con otros objetos:

Con el primer enfoque, la física se ignora por completo. El objeto transportado simplemente empuja cualquier cosa fuera del camino. Esto se debe a que los cambios de posición solo se deben realizar como parte de la física mundial, en función de la velocidad.

Con el segundo enfoque, la física básicamente se comporta como debería, pero reacciona de forma exagerada. El problema es: para mantener el objeto transportado en el centro de la viga incluso cuando gira y se mueve, necesito usar valores de alta velocidad. Entonces, una vez que el objeto transportado toca otro, obtiene demasiada velocidad de la colisión.

¿Cómo puedo implementar esto correctamente? Mi mejor suposición en este momento es ir con el segundo enfoque y agregar un manejo especial para los objetos transportados a la física mundial, reduciendo la velocidad a valores razonables para colisiones o cuando el jugador deja de transportarlos. Pero eso parece una solución poco elegante.

Editar: Agregar un pseudocódigo para ilustrar cómo funciona en este momento (ese sería el segundo enfoque anterior)

void attract_object(object, ticks) {
    Vector distance = beam_center - object.center;
    // If the object is not close to the beam center, attract it slowly
    if (magnitude(distance) > 10) {
        object.velocity += distance.normalized() * ticks * 0.1;
        return;
    }

    // Here comes the part we're talking about. That magic 0.5 is just high enough
    // that the object isn't lost while moving around. But it's still so high that
    // other objects are repelled with way too much force.
    object.velocity = distance * ticks * 0.5;
}

Por lo que veo, esto sucede cuando el objeto transportado empuja a otro objeto:

  1. El objeto transportado choca con otro objeto.
  2. Las velocidades de los objetos se distribuyen correctamente, por lo que el objeto transportado se aleja del centro del haz en el proceso.
  3. El código anterior hace que el objeto transportado regrese al centro del haz, con tanta velocidad que volverá allí rápidamente
  4. Cuando el objeto transportado se mueve hacia el centro del haz, la mitad de su alta velocidad se transfiere al otro objeto y lo repele violentamente. Dado que la velocidad inicial del objeto transportado parece ser sensata, puedo imaginar que los pasos 2 a 4 se repiten varias veces, lo que genera una velocidad tan alta.

Esa parece ser la causa. Sin embargo, no puedo pensar en una buena manera de solucionarlo :(

futlib
fuente
1
Bienvenido a las complejidades de un reactor de fusión Tokomak. Tiene la ventaja de que solo necesita construir un modelo matemático funcional, no la botella magnética funcional, pero las matemáticas son idénticas y no triviales. Lo que está intentando es factible, pero deberá pensar detenidamente en su modelo matemático antes de codificar.
Pieter Geerkens

Respuestas:

1

Esencialmente, lo que está buscando es que el objeto 'con vigas' se comporte exactamente como si lo agarrara con las manos.
Una opción sería hacer que comparta las velocidades de aceleración a / o de la 'mano' que la sostiene en lugar de ajustar su velocidad para llenar el espacio con el centro del haz.

Digamos que el centro de la viga es la mano que sostiene. Si tu personaje gira 90 grados a su izquierda en 1 segundo, entonces la velocidad de la mano sería:

If R = length of the arm: which is the radius of the rotation circle
R^2 *PI /4 would be the distance traveled over a second.
haz que sea el tiempo transcurrido del marco para encontrar la velocidad que debes aplicar a tu objeto. Encuentre la horizontal normal a la viga para encontrar su vector de dirección.

Mi punto es que no tiene que resolver problemas si intenta otras implementaciones que no lo causarán en primer lugar.

Iría a jugar con la pistola de gravedad en HL2 para obtener algo de inspiración sobre el problema, pero tengo otros planes para hoy.

EDITAR: lo siento, pensé que era para una pistola de rayos 3D, pero es esencialmente lo mismo con 2D, excepto que los ejes son diferentes (y no hay una geometría compleja)

icosamuel
fuente
Después de probar varios hacks, esto es lo que me puso en camino, el resultado se ve bien. La velocidad en la colisión sigue siendo un poco demasiado alta, pero creo que puedo entenderlo. Posiblemente simplemente al no atraer ningún objeto con alta velocidad en una dirección diferente.
futlib
Tal vez cuando la 'mano' está chocando con una pared, puede calcular la posición plausible más cercana (que no está colisionando) para la mano y usarla como la 'mano temporal' mientras ocurre la colisión. A menudo me gusta abordar los problemas desde otra perspectiva. Me alegro de poder ayudar con eso;).
icosamuel
4

¿Qué hay de agregar una conexión de resorte, es decir, obligar al objeto transportado a regresar a la posición de transporte en función de la distancia, pero aún así permitir que sea empujado por objetos sólidos (como paredes)?

Ajuste constantemente la velocidad del objeto transportado (cambiando la aceleración según la posición / distancia) para apuntar hacia la posición del haz del tractor (es decir, su segunda aproximación). Si el objeto se empuja demasiado lejos, suelte la conexión (y el objeto).

No estoy realmente seguro de por qué necesitarías altas velocidades. Especialmente el caso de "jugador suelta" indicaría que su velocidad de rotación puede ser demasiado alta o poco realista. Tampoco olvide cosas como la resistencia al aire y la gravedad.


Editar: Teniendo en cuenta el código actualizado, el problema es bastante trivial de encontrar:

if (magnitude(distance) > 10) {
    object.velocity += distance.normalized() * ticks * 0.1;
    return;
}

El problema aquí es el caso en que la distancia del objeto a su posición de objetivo es constantemente demasiado grande (es decir > 10). Mientras esta condición sea verdadera, su velocidad simplemente aumenta una y otra vez (es decir, indefinidamente).

Dos posibles soluciones para esto:

Defina una velocidad máxima:

object.velocity = min(object.velocity + distance.normalized() * ticks * 0.1, max_velocity);

Aplique una velocidad fija en lugar de acelerar:

object.velocity = distance.normalized() * ticks * magic_factor;

Acelerar mientras está demasiado lejos definitivamente es un enfoque equivocado aquí. Una cosa sobre tirar de un resorte o una banda de goma: no importa si lo sostienes durante un segundo o un minuto. Al final se acelerará de la misma manera (considerando que no ha estado en movimiento y no hay otras fuerzas aplicadas).

Mario
fuente
Eso es lo que describí en el método 2, ¿no? Esa es la física básica de primavera AFAIK. Agregaré un código a la pregunta anterior para ilustrarlo.
futlib
También eliminé la parte sobre velocidad incorrecta anterior, en realidad está bien, solo lo probé. Entonces, son solo las colisiones con otros objetos las que están en mal estado.
futlib
Sí, es esencialmente tu segundo enfoque. Actualizando mi respuesta.
Mario
Intenté ambos enfoques, pero no ayuda. Parece que el caso de magnitud (distancia)> 10 no es el culpable aquí. Intenté limitar la velocidad para el caso <=, pero es el problema habitual: la velocidad es tan baja que el objeto se cae o tan alta que repele a los demás violentamente.
futlib
"tan alto que repele a los demás violentamente": debe mover otros objetos según la velocidad efectiva, no la velocidad detrás de escena (es decir, restablecer la velocidad debido a una colisión).
Mario