La detección de colisión ralentiza el dibujo de la pantalla

8

Recientemente he estado buscando el desarrollo del juego como un pasatiempo, y decidí que para aprender los entresijos del desarrollo del juego, debería crear un juego y renderizar todo yo mismo (sin el uso de un motor de juego). Esto ha resultado bastante complicado, sin embargo, estoy haciendo grandes progresos. Sin embargo, me encontré con un problema que creo que podría estar relacionado con la forma en que los teléfonos Android procesan sus gráficos y necesitaré algunas aclaraciones sobre este tema.

El problema

Mi juego contiene una serie de bolas en un cañón; cuando el usuario presiona la pantalla, el cañón lanza las bolas y el motor (que estoy implementando) maneja las actualizaciones de la información de ubicación y la detección de colisiones desde allí. Ahora, antes de implementar la detección de colisión, mi juego funcionó de manera muy fluida y receptiva, sin embargo, cuando le dije al motor que solo dibujara la cuenta si estaba dentro de los límites y que la "rebotara" de la pared, de lo contrario, parecería que el motor El ciclo ahora tarda mucho más tiempo en ejecutarse.

Esto estaría bien, si no fuera por la latencia que proporciona a la experiencia del usuario. Por ejemplo, cuando se toca la pantalla ahora , la pelota tarda unos 2 segundos en mostrarse moviéndose por la pantalla y, a veces , no aparece en absoluto . Anteriormente, la reacción fue instantánea.

Además, cuando comento la parte de detección de colisión de mi motor de física, reanuda su comportamiento receptivo habitual.

Lo que creo que está causando este comportamiento

Nota: me he retractado de esta suposición (ver 'Información de depuración' a continuación)

Creo que, dado que no tengo un limitador de fotogramas implementado para mi juego, y que se procesa tan rápido como lo permite el hardware, está dibujando tantos fotogramas antiguos (¿en algún búfer, tal vez?) En la pantalla que está ocupado dibujando mientras debería estar actualizando la física. Aunque mi depuración hasta el momento no ha indicado que este sea el caso, parece que no puedo llegar a ninguna otra conclusión.

Un poco de código

Tenga en cuenta que este código va a ser bastante confuso para entender que no sabe lo que hace todo. Simplemente lo incluí en caso de que alguien fuera particular acerca de tener algún código para trabajar. Las variables se aclaran debajo del extracto.

PhysicsEngine.updateBeadPositions (float) :

private void updateBeadPositions(float delta){

    //Update all of the beads currently on the board.
    beads = control.getBoard().getValues();

    temp_x = 0.0f;
    temp_y = 0.0f;

    //For each row...
    for(Bead[] row : beads){

        //For each bead...
        for(Bead bead : row){

            //If this bead exists...
            if(bead != null){

                temp_y = (float) (bead.getYCoordinate() * bead.getYVelocity() * delta);

                //If the coordinates are within the bounds of the game
                if(outwithVerticalBounds(temp_y, control.getBoard())){

                    //Set the X coordinate equal to the distance * the time differential (delta).
                    bead.setXCoordinate(temp_x);

                    //Set the X coordinate equal to the distance * the time differential (delta).
                    bead.setYCoordinate(temp_y);
                }
            }
        }
    }

    //If the cannon Bead has been set...
    if(control.getCannon().getReleased() != null){

        //Update the cannon bead
        if(control.getCannon().getReleased().getXVelocity() == PhysicsEngine.VELOCITY_STATIC && control.getCannon().getReleased().getYVelocity() == PhysicsEngine.VELOCITY_STATIC){

            control.getCannon().getReleased().setXCoordinate(control.getCannon().getX());
            control.getCannon().getReleased().setYCoordinate(control.getCannon().getY() - Cannon.PIVOT_Y_OFFSET);
        }
        else{

            temp_x = control.getCannon().getReleased().getXCoordinate() + (control.getCannon().getReleased().getXVelocity() * delta);
            temp_y = control.getCannon().getReleased().getYCoordinate() + (control.getCannon().getReleased().getYVelocity() * delta);

            //TODO: Commented out collision checkers!

            //If the horizontal coordinates are within the bounds of the game
            if(!outwithHorizontalBounds(temp_x, control.getBoard())){

                //If the vertical coordinates are within the bounds of game
                if(!outwithVerticalBounds(temp_y, control.getBoard())){

                    //Set the X coordinate equal to the distance * the time differential (delta).       
                    control.getCannon().getReleased().setXCoordinate(temp_x);

                    //Set the X coordinate equal to the distance * the time differential (delta).
                    control.getCannon().getReleased().setYCoordinate(temp_y);
                }
                //Otherwise...
                else{

                    //Bounds off the wall in the y direction
                    control.getCannon().getReleased().setYVelocity(-1.0f * control.getCannon().getReleased().getYVelocity());
                }
            }
            //Otherwise...
            else{

                //Bounce off the wall in the x direction (flip the x velocity)
                control.getCannon().getReleased().setXVelocity(-1.0f * control.getCannon().getReleased().getXVelocity());
            }
        }
    }
}

Aquí, las variables se definen como:

  • controles una referencia a mi controlador de juego. Empaqueta la mayoría del código del juego.

  • beads es una referencia a la matriz 2D que contiene las cuentas en el tablero actualmente (excluyendo la que se está moviendo)

  • delta es el diferencial de tiempo entre las llamadas anteriores al motor de física y la llamada actual

Vea los comentarios dentro del código para cualquier otra explicación.

PhysicsEngine.outwithHorizontalBounds (float, Board) :

private boolean outwithHorizontalBounds(float x, Board board){

    //If the horizontal values are within the bounds...
    if(x > (board.getRight() - bead_radius)){

        return true;
    }

    if(x < (board.getLeft() + bead_radius)){

        return true;
    }

    //Otherwise, it is not.
    return false;
}

El método outwithVerticalBounds(float, Board)es de funcionalidad equivalente, pero en la dirección y.


Mi pregunta

¿Qué pasa con la detección de colisión que provocaría que la representación de la pantalla se inhibiera tan drásticamente? Sé que es una operación muy intensa, pero mi depuración ha demostrado que las actualizaciones físicas se están completando al mismo tiempo que los sorteos.

Información de depuración

Última actualización: 29 de enero de 2013 4:27 p.m. EST

Aquí hay una agregación de la información de depuración que he obtenido hasta ahora. Actualizaré esto a medida que pase el tiempo:

  • El update()método dentro de mi motor, toma, en promedio, solo .018 msejecutarse. Por lo general, el retraso aumenta 0.020 mscuando toco la pantalla para liberar la cuenta.

  • Después de comparar los tiempos de los sorteos y las actualizaciones del juego, parecería que estaba en lo correcto: están ocurriendo simultáneamente . Por lo tanto, este no podría ser el problema, ¿verdad?

  • La mayor parte FPSdel juego es más o menos 87, se dispara al azar (en el extremo inferior) 60 FPS, sin embargo, este pico no está relacionado con la liberación de la cuenta. No hay FPSinconvenientes para hacer esto. Esto tiene sentido ya que la única parte que aumenta su complejidad después de que se suelta el cordón es la update()llamada, el dibujo aún ocurre lo más rápido posible.

  • Después de más pruebas, se ha hecho evidente que este no es el caso de que la pantalla se quede atrás de la física. Probé esto con una simple bandera booleana en la que el fondo de la pantalla se volvería blanco cuando lo tocara, y esto sucede de inmediato . Por lo tanto, debe haber alguna otra causa para que el cordón no se dibuje. Lo actualizaré pronto.

Información suplementaria

Aquí hay información complementaria que debería ayudarlo a comprender mis circunstancias:

  • Estoy probando esto en un Google Nexus 7.

  • Hay bastantes cuentas en el mapa que se están actualizando a la vez (alrededor de 30), pero solo una de ellas se está moviendo.

  • Por lo general, después de que el cordón comienza a moverse (después del retraso inicial y si realmente se dibuja), continúa moviéndose de una manera muy suave.

  • Es importante tener en cuenta que tengo otros elementos de IU en la pantalla que se actualizan en reacción al evento táctil. Por ejemplo, la cuenta cargada en el cañón se convierte en una cuenta nueva cuando se toca la pantalla (lo que significa que se ha liberado), pero la cuenta móvil simplemente no se dibuja.

Squagem
fuente
¿Piensa que su depuración que muestra las actualizaciones físicas en línea con los sorteos podría ser inexacta? ¿Has hecho algún otro perfil con este código?
MichaelHouse
He realizado una depuración extensa utilizando salidas a la consola (LogCat), sin embargo, nada más complicado que esto. Puede ser que mis declaraciones de depuración sean incorrectas, las revisaré ahora.
Squagem
También es interesante si el FPS (cuadros por segundo) cambia al agregar el caso delimitador, o si el problema no está relacionado con la rapidez con la que se ejecuta el programa.
Qqwy
He editado mi pregunta para responder a los puntos que ha planteado.
Squagem

Respuestas:

8

Francamente, me da vergüenza anunciar la solución a mi problema, ya que pasé muchas horas buscándolo, y fue muy simple y podría haberse evitado tan fácilmente con un poco menos de negligencia en mi nombre. En el lado positivo, terminé encontrando algunas otras piezas de código que ahora puedo optimizar para un rendimiento aún mejor.

La solución

El cañón que estaba lanzando la cuenta estaba debajo del límite inferior de mi área jugable.

Esto fue engañoso porque el límite inferior de mi área de juego está un poco por encima de la parte inferior de la pantalla real del teléfono. Entonces, básicamente, el motor de física lo estaba recuperando y cuarto ligeramente (invisible para la inspección humana) durante aproximadamente 5 segundos antes de que se mostrara en la pantalla.

Moví el cañón 50 píxeles más alto y ahora funciona según lo previsto.


¡Gracias a todos por su ayuda! No habría llegado aquí sin tus reflexivas sugerencias.

Squagem
fuente
3
+1 para una pregunta bien formateada y +1 para responderla usted mismo
RoughPlace
¡Vaya, se olvidó de esta pregunta! Gracias chicos :)
Squagem