¿Hay algún daño en que el ciclo principal del juego se ejecute sin control?

19

Me preguntaba si hay algún daño posible cuando mi ciclo de juego se ejecuta tan rápido como el sistema lo permite.

Actualmente tengo un bucle que, al medir el tiempo transcurrido en nanosegundos, ejecuta la lógica del juego y la lógica de representación a velocidades predefinidas sin ningún problema. De hecho, cualquier lógica que hago en el bucle se sincroniza con una cierta cantidad de llamadas cada segundo.

Sin embargo, el bucle en sí solo funciona tan rápido como le gusta, lo que equivale a alrededor de 11.7 millones de bucles por segundo en mi máquina.

Bucle (pseudocódigo simple):

while(!isGameOver){

 if(canPollInputs){
    pollInputs()
 }

 while(canStepLogic){
    stepLogic()
 }

 if(canRender){
    render()
 }
}

Mi pregunta es, básicamente, si ese simple bucle, si no está funcionando a una velocidad controlada, ¿puede causar algún daño a un sistema?

Editar: Eso significa que mi lógica se está ejecutando 30 veces por segundo (30 tps), mi procesador se está ejecutando a 60 fps, estoy sondeando entradas a 100 veces por segundo y también hay algo de lógica para hacer frente a la lógica o hacer que el procesamiento tarde más de lo esperado . Pero el bucle en sí no está estrangulado.

Editar: usar, Thread.sleep()por ejemplo, Acelerar el bucle principal hasta 250 bucles por segundo conduce a una reducción, pero los bucles se ejecutan a alrededor de 570 bucles por segundo en lugar de los 250 deseados (agregaré código cuando estoy en mi máquina de escritorio ...)

Editar: Aquí vamos, un gameloop de Java que funciona para aclarar las cosas. También siéntase libre de usarlo, pero no lo reclame como suyo;)

private void gameLoop() {

    // Time that must elapse before a new run
    double timePerPoll = 1000000000l / targetPPS;
    double timePerTick = 1000000000l / targetTPS;
    double timePerFrame = 1000000000l / targetFPS;
    int maxFrameSkip = (int) ( (1000000000l / MINIMUM_FPS) / timePerTick);

    int achievedPPS = 0;
    int achievedFPS = 0;
    int achievedTPS = 0;

    long timer = TimeUtils.getMillis();

    int loops = 0;

    int achievedLoops = 0;

    long currTime = 0l;
    long loopTime = 0l;

    long accumulatorPPS = 0l;
    long accumulatorTPS = 0l;
    long accumulatorFPS = 0l;

    long lastTime = TimeUtils.getNano();

    while(!isRequestedToStop) {
        currTime = TimeUtils.getNano();
        loopTime = currTime - lastTime;
        lastTime = currTime;

        loops = 0;

        accumulatorPPS += loopTime;
        accumulatorTPS += loopTime;
        accumulatorFPS += loopTime;

        if(accumulatorPPS >= timePerPoll) {
            pollInputs();
            playerLogic();
            achievedPPS++;
            accumulatorPPS -= timePerPoll;
        }

        while(accumulatorTPS >= timePerTick && loops < maxFrameSkip) {
            tick();
            achievedTPS++;
            accumulatorTPS -= timePerTick;
            loops++;
        }

        // Max 1 render per loop so player movement stays fluent
        if(accumulatorFPS >= timePerFrame) {
            render();
            achievedFPS++;
            accumulatorFPS -= timePerFrame;
        }

        if(TimeUtils.getDeltaMillis(timer) > 1000) {
            timer += 1000;
            logger.debug(achievedTPS + " TPS, " + achievedFPS + " FPS, "
                    + achievedPPS + " Polls, " + achievedLoops + " Loops");
            achievedTPS = 0;
            achievedFPS = 0;
            achievedLoops = 0;
        }

        achievedLoops++;
    }
}

Como puede ver, casi no hay código ejecutado en cada ciclo, pero siempre hay una cierta selección basada en la cantidad de tiempo real que ha pasado. La pregunta se refiere a ese "ciclo de trabajo" y cómo influye en el sistema.

dot_Sp0T
fuente
2
Enlace obligatorio ' Fix Your Timestep ': gafferongames.com/game-physics/fix-your-timestep , léalo . También tenga en cuenta que Thread.Sleep () no se puede usar para regular su paso de tiempo de manera confiable ya que el sistema operativo no garantiza devolver el control a su aplicación después del tiempo solicitado. (Puede variar)
Roy T.
Sí, ese es el problema con el pseudocódigo. Hice la mayor parte de lo que Gaffer escribe (actualmente trabajando en una implementación delta que complementa mi estructura de bucle). Si no puedo usar Thread.sleep (), ¿qué más hay disponible en Java estándar?
dot_Sp0T
1
Usualmente usas un bucle ocupado con Thread.Sleep (0) y esperas lo mejor. Gaffer habla mucho de esto en algunos artículos.
Roy T.
Bueno, eso es básicamente lo que he estado haciendo, solo sin el thread.sleep (0), Y lo que originalmente provocó esta pregunta. ¿Hay algún daño en un bucle de ocupado (resp no ocupado ya que incluso esa pequeña lógica repetitiva se reduce) para el sistema?
dot_Sp0T
Hace un tiempo, un error causó que Starcraft II hiciera esto bajo ciertas condiciones. Terminó literalmente derritiendo algunas GPU. Entonces, sí, es muy posible que un ciclo de juego no controlado cause daño.
Mason Wheeler

Respuestas:

35

Hará que un núcleo de CPU se ejecute siempre al 100%. Esto generalmente no causa ningún daño al sistema. Las CPU están diseñadas para funcionar al 100% durante horas. Pero en un dispositivo móvil agotará la batería rápidamente y calentará el dispositivo, lo que probablemente le costará unas estrellas en las clasificaciones de su tienda. En una computadora de escritorio esto no es un problema, pero consumirá más electricidad de los usuarios, hace que el ventilador de la CPU gire más rápido, lo que podría causar algo de ruido y desperdiciar ciclos de la CPU que de otro modo podrían ser utilizados por otros procesos. Si bien estos no son defectos críticos, siguen siendo de mal estilo, por lo que debe evitarlos cuando sea posible.

No dijiste nada acerca de cómo funciona el bucle lógico del juego internamente, pero cuando estás usando el enfoque delta-time (cada cálculo que estás haciendo toma el tiempo desde la última llamada en cuenta), estás lidiando con un delta muy pequeño. valores de tiempo Esto significa que puede tener problemas con la inexactitud de punto flotante que puede causar todo tipo de comportamientos extraños. Además, la resolución del temporizador del sistema a menudo es limitada, por lo que cuando su bucle lógico es demasiado rápido, puede obtener un valor delta-t de cero, lo que podría causar una división por cero que provocaría un bloqueo.

Para mitigar este problema, debe limitar su velocidad de cuadros (gráfica y lógica) al máximo de lo que el ojo humano puede percibir. Cuánto se disputa y depende del tipo de animación y del tipo de visualización que se muestra, pero las estimaciones oscilan entre 40 FPS y 120 FPS. Eso significa que debe establecer un tiempo mínimo de ejecución de cada ciclo entre 20 ms y 8 ms. Si una iteración de bucle termina más rápido, deje que la CPU se enhebre sleepdurante el tiempo restante.

Philipp
fuente
Gracias por la explicación Philipp. De hecho, me referí al hecho de que estoy ejecutando a ciertos fps y tps, así como a las encuestas solo una cierta cantidad de veces por segundo. Sin embargo, trataré de hacerlo más claro.
dot_Sp0T
@ dot_Sp0T Después de editar su pregunta, solo el primer párrafo y la última oración son relevantes para su caso. Sin embargo, me gustaría retener el segundo y tercer párrafo de mi respuesta porque podrían ser útiles para otros.
Philipp
Presumiblemente aceptaré tu respuesta, ya que el primer párrafo da una respuesta perfecta. ¿Te importaría distinguirlo visualmente de alguna manera del resto de la respuesta?
dot_Sp0T
Juegue un Civilization 2 sin parchar en un escritorio y encontrará que es un problema. Al menos lo hago. encogiéndose de hombros
corsiKa
1
Además de las consideraciones sobre la batería y el ventilador, un mayor uso de la CPU puede generar un exceso de calor que puede ser incómodo (por ejemplo, una computadora portátil, especialmente mi rMBP) e incluso provocar el apagado de la PC (especialmente las PC más antiguas con refrigeradores polvorientos). Estos factores son exasperados si ejecuta todos los núcleos al 100%.
NPSF3000
2

Estás desperdiciando ciclos de CPU. Eso significa menos tiempo de batería en computadoras portátiles, tabletas y teléfonos, facturas de electricidad más altas, más calor generado por la máquina, ventiladores más ruidosos. Además, es posible que comas ciclos de otros procesos importantes del sistema (por ejemplo, el servidor de la ventana podría volverse irregular), lo que podría afectar el juego. Algunos programadores de sistemas en los sistemas multitarea preventivos de hoy también penalizan las aplicaciones que usan demasiados ciclos, por lo que puede ser rápido primero, y luego repentinamente ver extrañas sacudidas cuando el sistema lo estrangula.

Muchos jugadores incondicionales también construyen PC personalizadas desde cero que están al borde de las especificaciones de sus fanáticos, en cuyo caso un día caluroso y el calor que genera su juego pueden causar que las seguridades se activen en la máquina (en el mejor de los casos) o incluso sobrecalentar las piezas y morir (en el peor de los casos). Entonces, si ese es su público objetivo, es posible que desee asegurarse de que siempre deja un poco de espacio "en la parte superior".

Finalmente, hay problemas de juego en los que podrías estar beneficiando a los jugadores con máquinas más rápidas que a las que tienen máquinas más lentas. Tener el ciclo del juego limitado a una determinada frecuencia y usar ciclos adicionales únicamente para aspectos que no son relevantes para el juego (como renderizar gráficos de mayor fidelidad, efectos de sonido envolvente o lo que sea) hará que su juego sea más justo.

uli testigo
fuente
Por el contrario, diría que las personas que construyen sus máquinas apasionadamente usan buenos ventiladores, incluso refrigeración por agua. Solo los casos HTPC o mini ITX pueden tener limitaciones en esa parte. De lo contrario, las personas pobres pero entusiastas utilizarán la caja ventirad. que es suficiente incluso al 100%, caliente pero bien.
v.oddou
Solo estoy repitiendo por experiencia. Si observa Star Trek Online, en realidad tienen una configuración separada en su GUI que insertará sleep () s en su bucle principal para evitar que ciertas máquinas se sobrecalienten, por lo que no puede ser algo raro si el fabricante de Neverwinter y STO vio la necesidad de agregarlo. Tenga en cuenta que la pasión! = Habilidad. Hay muchos expertos y apasionados, pero también hay principiantes y otros que todavía están aprendiendo, y con una tasa de deserción decente, las estadísticas dicen que son la mayoría.
testigo del
Gracias a estos comentarios, vine a releer tu respuesta. Usted enumera puntos válidos, pero lamentablemente no aborda la cuestión con la controversia entre máquinas más rápidas y máquinas más lentas . El bucle lógico (llamado gameloop en su tercer párrafo) está limitado. La pregunta fue sobre la limitación del bucle troncal que simplemente se vuelve a evaluar cada carrera a través de si la entrada oughta ser retirado, se calcula la lógica o una trama se dictarán - por lo que no más rápido lógica a menos que su máquina es terriblemente cojo
dot_Sp0T
1

No deberías tener movimientos nerviosos / jugabilidad

Hay dos formas de implementar la lógica del juego: vinculada a un tiempo real o vinculada a la cantidad de "turnos" / pasos de procesamiento. Si alguna cosa en su juego se mueve hacia la izquierda, ¿será diferente si su stepLogic () se llamó 100 en lugar de 50 veces?

Si su código trata el tiempo transcurrido explícitamente en todas partes y lo hace correctamente, entonces podría estar bien; pero si hay algo en su código que depende de la cantidad de "pasos", obtendrá efectos secundarios no deseados.

Primero, la velocidad de las cosas que deberían moverse constantemente variará de manera impredecible (dependiendo del uso del procesador), y esto hace que los controles sean muy molestos: no puedes dar un golpe preciso o un salto si de repente el juego se acelera o se ralentiza. Incluso para juegos sin 'twitch' en absoluto, se ve molesto y nervioso si las cosas no se mueven bien.

En segundo lugar, es posible que tengas problemas con las habilidades de los personajes dependiendo de la velocidad de la computadora; un ejemplo clásico es el viejo problema de Quake, donde el rango de salto máximo dependía involuntariamente de tus fps.

Estos problemas pueden aparecer incluso si intenta que el código sea independiente de fps, la acumulación de errores de redondeo a menudo puede causar tales errores.

Pedro es
fuente
1
Por mucho que me guste su respuesta, no es relevante para la pregunta, ya que no estoy preguntando sobre el movimiento / juego nervioso, sino sobre el daño que un ciclo incontrolado (que no controla ninguna lógica o renderizado o cualquier otra cosa) puede hacerle al sistema
dot_Sp0T
1

El único daño potencial es su consumo de energía, así que no lo haga en dispositivos móviles. Por el contrario, bastantes sistemas integrados pasan toda su vida en un bucle esperando que sucedan cosas.

Solía ​​ser completamente normal simplemente renderizar los marcos del juego lo más rápido posible, a veces sin siquiera un temporizador para compensar el juego por diferentes velocidades de CPU. El juego de carreras de Bullfrog Hi-Octane fue una víctima particularmente mala de esto, y sospecho que esto es a lo que se refiere el afiche que menciona Civ2.

Le recomiendo al menos sondear las entradas en cada pase si está en un sistema Windows o similar, para garantizar una respuesta de entrada rápida.

pjc50
fuente
No es una cuestión de temporizador, es una cuestión de cronómetro, preferiría decir, o un contador de rendimiento. Uno necesita obtener el tiempo real en algún momento, y un bucle libre funciona perfectamente. Sin embargo, un temporizador (en el sentido de interrupción regular) está intentando regular la velocidad del bucle en el uso general. No lo considero bueno. Es preferible utilizar la función swap/ present/ fliphacer la espera en la señal vsync, para regular el ciclo del juego.
v.oddou