¿Cómo hacer que algo suceda cada N segundos en el juego?

8

Quiero que ocurra algo cada N segundos, ¿cómo haría eso? Tal vez use un temporizador, pero ¿cómo?

usuario2957632
fuente
Hice su pregunta más genérica, ya que creo que es más útil para más personas de esta manera.
MichaelHouse
Según los comentarios en una de las respuestas, no creo que el póster esté buscando el bit del temporizador, sino cómo cambiar el marco del sprite. Eso depende más de qué plataforma estamos hablando. De todos modos, ahora que la pregunta ha sido editada, no representa lo que realmente está preguntando. Java 2D fue importante para la pregunta que intenta hacer.
prototípico
Cambiar el sprite y hacer que suceda cada 5 segundos no está relacionado y debe hacerse como preguntas diferentes. Haga una nueva pregunta sobre cómo cambiar el sprite. Asegúrate de incluir lo que has probado y qué no funciona.
MichaelHouse

Respuestas:

14

Un enfoque común para esto es usar el ciclo de actualización y el tiempo delta.

float timerCurrent = 0f;
float timerTotal = 5f;


Update() {
    timerCurrent += deltaTime;
    if(timerCurrent >= timerTotal) {
        //do timer action
        timerCurrent -= timerTotal;
    }
}

Esto supone que tiene acceso a una deltaTimevariable. El tiempo delta es simplemente el tiempo transcurrido desde el último fotograma. Muchos motores tendrán esto a su disposición, o puede obtener más información sobre cómo configurar el suyo aquí .

MichaelHouse
fuente
¿Es esto en algún sentido técnico preferible a iniciar un temporizador explícito como en la respuesta de @ Josh?
Jack M
@JackM Eso dependerá de la implementación del temporizador. La razón por la que respondí así es porque es un lenguaje agnóstico y muy simple de implementar.
MichaelHouse
1
@JackM: También es preferible si alguna vez se ejecuta bajo un depurador. El tiempo de juego acumulado puede detenerse cuando llegas a un punto de interrupción en lugar de que cada temporizador caduque y dispare instantáneamente al continuar.
Ben Jackson
Hay que tener cuidado, ya que el juego podría congelarse, por ejemplo, y las garrapatas podrían perderse. Por lo tanto, uno tiene que invocar la acción de un temporizador para cada tic con el delta especificado que ha pasado y no solo una vez, etc.
Christian Ivicevic
Este método no se basa en contar garrapatas, se basa en el tiempo delta. Si hay un marco extra largo, el tiempo delta también es extra largo. Sin embargo, en muchos casos es preferible limitar el tiempo delta, los marcos extra largos pueden causar problemas con la física y demás. Este método es superior a simplemente usar el tiempo actual menos el tiempo de inicio, ya que se sincronizará mejor con los otros cálculos en el juego al limitar el lapso de tiempo delta.
MichaelHouse
3

En la mayoría de los idiomas, debe iniciar el reloj con algún tipo de llamada a la función clock.startTimer()antes de ingresar al bucle principal. Luego, debe almacenar el valor de la hora del reloj antes de ingresar el bucle principal en una variable con una función como time = clock.getTime(). Almacene su tiempo de paso en variable n.

En su bucle principal, la verificación que desea hacer es algo similar a

if(time + n <= clock.getTime())
     //do stuff
     time = clock.getTime() //important to assign new time value here

Nota: no estoy seguro de qué idioma / biblioteca estás viendo, así que este es solo un algoritmo genérico que pensé en la parte superior de mi cabeza. Puede que no se adapte exactamente a sus necesidades. Algunos idiomas / bibliotecas requieren que cree un objeto de reloj, mientras que otros simplemente puede hacer una llamada de función directamente.

Según los comentarios a continuación, debe tener cuidado con los problemas de subprocesos según el idioma que esté utilizando. También debe usar un flotador doble para almacenar time.

Josh
fuente
1
Buena respuesta. Esto podría tener problemas de subprocesos en algunos idiomas (p. Ej. .NET) donde se implementa un temporizador en un hilo separado de su ciclo de juego.
cenizas999
1
También es importante tener en cuenta aquí que si timeva a almacenar el tiempo transcurrido del juego, debe usar un formato de punto flotante de doble precisión.
kevintodisco
Estos son buenos puntos, los agregaré a la respuesta.
Josh
2

Como ya señalaron otras respuestas, la implementación de un temporizador se puede hacer incrementando un valor almacenado por cada cuadro deltaTimey comparándolo con la duración esperada. Para completar, incluiré código que es muy similar a las otras respuestas:

float elapsed = 0.0f;
float duration = 4.0f;

void update(float deltaTime) {
    elapsed += deltaTime;
    if (elapsed <= duration) {
        // Run code.
        elapsed = 0.0f;
        // Maybe remove from update loop?
    }
}

La forma en que desea manejar la // Run code.porción depende de usted. Tal vez tenga una Timerclase, una instancia de la cual toma un delegate(C #) o un callback(C ++) y la llama cuando expira el temporizador. Además, pausar el temporizador es cuestión de eliminarlo del ciclo de actualización.

Un enfoque alternativo es marcar el tiempo de inicio del temporizador y, en su lugar, hacer un cálculo bajo demanda del tiempo transcurrido:

double startTime = 0.0; // This is a double for a reason, I'll explain below.
bool running = false;

void start() {
    startTime = getTime(); // This is whatever system time call you want to use.
    running = true;
}

double getElapsed() {
    double elapsed = getTime() - startTime;
    return elapsed;
}

Este estilo le da un poco más de control al usuario del temporizador. La estructura en sí no se preocupa por qué hacer cuando el tiempo transcurrido alcanza un cierto punto; Ni siquiera está actualizando. El usuario simplemente puede consultar el tiempo transcurrido cuando sea necesario. Este patrón tiene el desafortunado efecto secundario de no ser amigable con el depurador. La hora del sistema continúa a medida que su programa se detiene en un depurador, por lo que los temporizadores como este se comportarán de manera diferente al recorrer los programas. Una posible solución para eso es mantener un valor de tiempo transcurrido específico del programa que se actualiza cada cuadro utilizando deltas de tiempo del sistema.

Sin embargo, creo que lo más importante son dos peculiaridades del tiempo en una computadora, especialmente cuando se trata del desarrollo de juegos. El primero es la precisión de coma flotante y el segundo es la resolución nativa del temporizador.

Notarás que en el segundo ejemplo, usé un doubletipo en lugar de un float, como en el primer ejemplo (el nombre del tipo, por supuesto, depende del idioma que estés usando). La razón de esto es que el segundo ejemplo es almacenar el tiempo total transcurrido del juego . Si el juego se deja en funcionamiento durante mucho tiempo, los valores de formato de punto flotante de precisión simple tendrán una precisión insuficiente para medir con precisión el tiempo transcurrido , lo que provocará problemas de estabilidad. El primer ejemplo está bien usando el floattipo siempre que no se esperen duraciones enormes.

En el otro extremo del espectro, si el tiempo de actualización que espera es lo suficientemente pequeño, es posible que no pueda lograrlo inicialmente, dependiendo de cómo obtenga la hora del sistema. Los temporizadores a menudo dependerán de la resolución del temporizador del sistema operativo en el que se está ejecutando. Por ejemplo, la resolución predeterminada del temporizador de Windows 7 e inferior es de 15,6 ms . Esto significa que la precisión de temporizador más baja que podría lograr utilizando funciones como timeGetTimeo GetTickCount, es de 15.6 ms, a menos que cambie la resolución del temporizador (también hay peligros asociados con eso).

Bueno, eso resultó un poco largo, pero estas son cosas importantes a tener en cuenta al implementar temporizadores.

kevintodisco
fuente
gracias, pero ahora cómo hacer eso, pero ¿cómo cambiaría el sprite hacia atrás y hacia
delante
@ user2957632 Eso no es lo que pregunta ahora. Por favor házlo más claro.
kevintodisco