Como fazer algo acontecer a cada N segundos no jogo?

8

Quero que algo aconteça a cada N segundos, como eu faria isso? Talvez use um temporizador, mas como?

user2957632
fonte
Tornei sua pergunta mais genérica, pois acho que é mais útil para mais pessoas dessa maneira.
MichaelHouse
Com base nos comentários de uma das respostas, não acredito que o pôster esteja procurando o bit do temporizador, mas como alterar o quadro do sprite. Isso depende mais de qual plataforma estamos falando. Independentemente disso, agora que a pergunta foi editada, ela não representa o que ele realmente está perguntando. O Java 2D foi importante para a pergunta que ele está tentando fazer.
Prototypical
Alterar o sprite e fazê-lo acontecer a cada 5 segundos não tem relação e deve ser feito como perguntas diferentes. Faça uma nova pergunta sobre como alterar o sprite. Certifique-se de incluir o que você tentou e o que não está funcionando.
MichaelHouse

Respostas:

14

Uma abordagem comum para isso é usar o loop de atualização e o tempo delta.

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


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

Isso supõe que você tenha acesso a uma deltaTimevariável. O tempo delta é simplesmente o tempo decorrido desde o último quadro. Muitos mecanismos terão isso disponível para você, ou você pode aprender mais sobre como configurar seu próprio aqui .

MichaelHouse
fonte
Em algum sentido técnico, é preferível iniciar um cronômetro explícito como na resposta de @ Josh?
Jack M
@ JackM Isso vai depender da implementação do cronômetro. A razão pela qual eu respondi dessa maneira é porque é uma linguagem independente e muito simples de implementar.
MichaelHouse
11
@JackM: Também é preferível se você rodar em um depurador. O tempo acumulado do jogo pode parar quando você atinge um ponto de interrupção, em vez de cada timer expirar e disparar instantaneamente ao continuar.
Ben Jackson
É preciso ter cuidado, pois o jogo pode congelar, por exemplo, e os ticks podem ser perdidos. Portanto é preciso invocar a ação de um temporizador para cada carrapato com o delta especificado que passou e não apenas uma vez etc.
Christian Ivicevic
Esse método não depende da contagem de tiques, depende do tempo delta. Se houver um quadro extra longo, o tempo delta também será muito longo. No entanto, em muitos casos, é preferível limitar o tempo delta, quadros muito longos podem causar problemas com a física e tal. Esse método é superior a apenas usar o horário atual menos o horário de início, pois sincronizará melhor com os outros cálculos no jogo ao limitar o intervalo de tempo delta.
MichaelHouse
3

Na maioria dos idiomas, você precisa iniciar o relógio com algum tipo de chamada de função ao longo das linhas clock.startTimer()antes de inserir o loop principal. Então, você deve armazenar o valor da hora do relógio antes de inserir o loop principal em uma variável com uma função como time = clock.getTime(). Armazene seu tempo de etapa em variável n.

No loop principal, a verificação que você deseja fazer é algo parecido com o

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

Nota: Não sei ao certo qual idioma / biblioteca você está procurando, então esse é apenas um algoritmo genérico que eu pensei em cima da minha cabeça. Pode não atender exatamente às suas necessidades. Alguns idiomas / bibliotecas exigem que você crie um objeto de relógio, enquanto outros podem simplesmente fazer uma chamada de função diretamente.

Conforme os comentários abaixo, você deve ter cuidado com os problemas de segmentação, dependendo do idioma que está usando. Você também deve usar um flutuador duplo para armazenar time.

Josh
fonte
11
Boa resposta. Isso pode ter problemas de encadeamento em alguns idiomas (por exemplo, .NET) em que um cronômetro é implementado em um thread separado do loop do jogo.
precisa saber é o seguinte
11
Também é importante observar aqui que, se o timetempo decorrido do jogo for armazenado, ele deverá usar um formato de ponto flutuante de precisão dupla.
Kevintodisco
Estes são bons pontos, vou adicioná-los à resposta.
9133 Josh
2

Como outras respostas já apontadas, a implementação de um cronômetro pode ser feita incrementando um valor armazenado por cada quadro deltaTimee comparando-o com a duração esperada. Para garantir a integridade, incluirei código muito semelhante às outras respostas:

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?
    }
}

A decisão de como você deseja lidar com a // Run code.parte é sua. Talvez você tenha uma Timerclasse, cuja instância usa um delegate(C #) ou um callback(C ++) e a chama quando o cronômetro expira. Além disso, pausar o timer é uma questão de removê-lo do loop de atualização.

Uma abordagem alternativa é marcar o horário de início do cronômetro e, em vez disso, fazer um cálculo sob demanda do tempo decorrido:

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;
}

Esse estilo dá um pouco mais de controle ao usuário do timer. A estrutura em si não está preocupada com o que fazer quando o tempo decorrido atingir um determinado ponto; nem está atualizando. O usuário pode simplesmente consultar o tempo decorrido quando necessário. Esse padrão tem o efeito colateral lamentável de não ser amigável ao depurador. A hora do sistema continua quando seu programa é interrompido em um depurador, portanto, temporizadores como esse se comportam de maneira diferente ao percorrer os programas. Uma solução potencial para isso é manter um valor de tempo decorrido específico do programa que é atualizado a cada quadro usando deltas de hora do sistema.

Eu acho que o mais importante são duas peculiaridades do tempo em um computador, especialmente quando se trata de desenvolvimento de jogos. O primeiro é a precisão do ponto flutuante e o segundo é a resolução nativa do timer.

Você notará que no segundo exemplo, usei um doubletipo em vez de a float, como no primeiro exemplo (o nome do tipo de curso depende do idioma que você está usando). A razão para isso é o segundo exemplo: o tempo total decorrido do jogo . Se o jogo permanecer em execução por muito tempo, os valores no formato de ponto flutuante de precisão única terão precisão insuficiente para medir com precisão o tempo decorrido , causando problemas de estabilidade. O primeiro exemplo é bom usando o floattipo, desde que não sejam esperadas durações enormes.

No outro extremo do espectro, se o tempo de atualização esperado for pequeno o suficiente, talvez você não consiga alcançá-lo inicialmente, dependendo de como você obtém o tempo do sistema. Os temporizadores geralmente dependem da resolução do temporizador do sistema operacional em que você está executando. Por exemplo, a resolução padrão do timer do Windows 7 e abaixo é de 15,6 ms . Isso significa que a menor precisão do timer que você pode obter usando funções como timeGetTimeou GetTickCounté 15,6 ms, a menos que você altere a resolução do timer (também existem perigos associados a isso).

Bem, isso ficou um pouco longo, mas essas são coisas importantes a serem observadas ao implementar temporizadores.

kevintodisco
fonte
obrigado, mas agora eu como fazer isso, mas como eu mudaria o sprite de volta e perverso
user2957632
@ user2957632 Não é isso que sua pergunta está fazendo agora. Por favor, deixe mais claro.
Kevintodisco 11/11/2013