Existe algum mal em ter o loop principal do jogo rodando sem controle?

19

Fiquei me perguntando se existe algum dano possível quando o loop do jogo é executado o mais rápido que o sistema permite?

Atualmente, tenho um loop que, medindo o tempo passado em nanossegundos, executa a lógica do jogo e processa a lógica em velocidades predefinidas sem problemas. De fato, qualquer lógica que eu faça no loop é cronometrada para uma certa quantidade de chamadas a cada segundo.

O loop em si, no entanto, roda tão rápido quanto ele gosta, chegando a cerca de 11,7 milhões de loops por segundo na minha máquina.

Loop (pseudocódigo simples):

while(!isGameOver){

 if(canPollInputs){
    pollInputs()
 }

 while(canStepLogic){
    stepLogic()
 }

 if(canRender){
    render()
 }
}

Minha pergunta é basicamente se esse loop simples, se não estiver funcionando a uma velocidade controlada, pode causar algum dano a um sistema?

Edit: Isso significa que minha lógica está rodando 30 vezes por segundo (30 tps), meu renderizador está rodando a 60 fps, estou pesquisando entradas 100 vezes por segundo e também há alguma lógica para lidar com a lógica ou renderização levando mais tempo do que o esperado . Mas o loop em si não é regulado.

Editar: Usar Thread.sleep()para, por exemplo, acelerar o loop principal até 250 loops por segundo leva a uma redução, mas os loops são executados em torno de 570 loops por segundo em vez dos 250 desejados (adicionará código quando estou na minha máquina de trabalho ..)

Edit: Aqui vamos nós, um gameloop java de trabalho, a fim de esclarecer as coisas. Também fique à vontade para usá-lo, mas não o reivindique;)

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 você pode ver, quase não há código executado em cada loop, mas sempre uma certa seleção com base em quanto tempo real se passou. A questão está se referindo a esse 'loop de trabalho' e como ele influencia o sistema.

dot_Sp0T
fonte
2
link obrigatório 'fix your timestep': gafferongames.com/game-physics/fix-your-timestep , leia-o. Observe também que Thread.Sleep () não pode ser usado para acelerar seu timestep de maneira confiável, pois o sistema operacional não garante o retorno do controle ao seu aplicativo após o período solicitado. (Pode variar).
Roy T.
Sim, esse é o problema do pseudocódigo. Fiz a maior parte do que o gaffer escreve (atualmente trabalhando em uma implementação delta que complementa minha estrutura de loop). Se eu não puder usar o Thread.sleep (), o que mais está disponível no Java padrão?
dot_Sp0T
1
Você geralmente usa um loop ocupado com o Thread.Sleep (0) e espera o melhor. Gaffer fala muito sobre isso em alguns artigos.
Roy T.
Bem, isso é basicamente o que tenho feito, apenas sem o thread.sleep (0), E o que originalmente provocou essa pergunta. Existe algum dano em um loop ocupado (ou não ocupado, mesmo que essa pequena lógica repetitiva seja reduzida) para o sistema?
dot_Sp0T
Há algum tempo, um bug fez o Starcraft II fazer isso sob certas condições. Acabou literalmente derretendo algumas GPUs. Então, sim, é muito possível que um loop de jogo não controlado cause danos.
Mason Wheeler

Respostas:

35

Isso fará com que um núcleo da CPU seja sempre executado em 100%. Isso geralmente não causa nenhum dano ao sistema. As CPUs são projetadas para funcionar 100% por horas. Porém, em um dispositivo móvel, ela descarrega a bateria rapidamente e aquece o dispositivo, o que provavelmente custará cerca de uma estrela nas classificações da sua loja. Em um computador desktop, isso é menos problemático, mas consome mais eletricidade dos usuários, faz com que o ventilador da CPU gire mais rápido, o que pode causar algum ruído e desperdiçar ciclos de CPU que, de outra forma, poderiam ser usados ​​por outros processos. Embora esses defeitos não sejam críticos, eles ainda têm um estilo ruim; portanto, evite-os quando possível.

Você não disse nada sobre como o loop da lógica do jogo funciona internamente, mas quando você usa a abordagem de tempo delta (cada cálculo que você faz leva um tempo desde a última chamada em consideração), está lidando com delta muito pequeno. valores de tempo. Isso significa que você pode ter problemas com imprecisão de ponto flutuante, o que pode causar todo tipo de comportamento estranho. Além disso, a resolução do timer do sistema geralmente é limitada; portanto, quando seu loop lógico é muito rápido, é possível obter um valor delta-t de zero, o que pode causar uma divisão por zero, resultando em uma falha.

Para atenuar esse problema, você deve limitar sua taxa de quadros (gráfico e lógica) ao máximo do que o olho humano pode perceber. Quanto é contestado e depende do tipo de animação e do tipo de exibição que é mostrado, mas as estimativas variam de 40 FPS a 120 FPS. Isso significa que você precisa definir um tempo mínimo de execução de cada loop entre 20ms e 8ms. Se uma iteração do loop terminar mais rapidamente, deixe o segmento da CPU sleeppelo período de tempo restante.

Philipp
fonte
Obrigado pela explicação Philipp. Na verdade, eu me referi ao fato de que estou rodando a certos fps e tps, além de fazer a pesquisa apenas uma certa quantidade de vezes por segundo. Vai tentar deixar mais claro.
dot_Sp0T
@ dot_Sp0T Após a edição da sua pergunta, apenas o primeiro parágrafo e a última frase são relevantes para o seu caso. No entanto, gostaria de manter o segundo e o terceiro parágrafos da minha resposta, pois podem ser úteis para outras pessoas.
Philipp
Presumivelmente, também aceitarei sua resposta, pois o primeiro parágrafo perfeitamente dá uma resposta. Você se importaria de, de alguma forma, separá-lo visualmente do resto da resposta?
dot_Sp0T
Jogue uma Civilização 2 sem patch em uma área de trabalho e você descobrirá que é um problema. Pelo menos sim. encolher de ombros
corsiKa
1
Além das considerações sobre bateria e ventilador, o aumento do uso da CPU pode levar ao excesso de calor, o que pode ser desconfortável (por exemplo, laptop, esp meu rMBP) e até mesmo desligar o PC (especialmente PCs antigos com coolers empoeirados). Esses fatores são exasperados se você executar todos os núcleos a 100%.
NPSF3000 13/08/2014
2

Você está desperdiçando ciclos da CPU. Isso significa menor tempo de bateria em notebooks, tablets e telefones, contas de eletricidade mais altas, mais calor gerado pela máquina, ventiladores mais barulhentos. Além disso, você pode estar comendo ciclos de outros processos importantes do sistema (por exemplo, o servidor de janelas pode ficar irregular), o que pode afetar a jogabilidade. Alguns agendadores de sistema nos sistemas de multitarefa preventiva de hoje também penalizam aplicativos que usam muitos ciclos; portanto, você pode ser rápido primeiro e depois repentinamente ver uma sacudidela estranha quando você é estrangulado pelo sistema.

Muitos jogadores hardcore também constroem PCs personalizados a partir do zero, que estão no limite das especificações de seus fãs; nesse caso, um dia quente e o calor que seu jogo gera podem fazer com que as seguranças sejam acionadas na máquina (na melhor das hipóteses) ou até mesmo superaquecer as peças e morrer (na pior das hipóteses). Portanto, se esse for o seu público-alvo, convém garantir que você esteja sempre deixando um pouco de espaço "no topo".

Finalmente, há problemas de jogabilidade em que você pode estar aproveitando jogadores com máquinas mais rápidas do que aqueles com máquinas mais lentas. Ter o loop do jogo limitado em uma determinada frequência e usar ciclos adicionais apenas para aspectos não relevantes para o jogo (como renderização de gráficos de alta fidelidade, efeitos de som surround ou qualquer outra coisa) tornarão seu jogo mais justo.

uliwitness
fonte
Eu diria que, inversamente, as pessoas que constroem apaixonadamente suas máquinas usam bons ventiladores, até mesmo resfriamento a água. Apenas os casos HTPC ou mini ITX podem ter limitações nessa parte. Caso contrário, as pessoas pobres, mas entusiastas, usarão a caixa ventirad. o que é suficiente, mesmo a 100%, quente, mas ok.
v.oddou
Estou apenas repetindo por experiência própria. Se você olhar para o Star Trek Online, eles realmente têm uma configuração separada em sua GUI que inserirá sleep () s no loop principal para impedir o superaquecimento de certas máquinas. Portanto, não é incomum que o criador de Neverwinter e O STO viu a necessidade de adicioná-lo. Tenha em mente que paixão! = Habilidade. Existem muitos profissionais qualificados e apaixonados, mas também existem iniciantes e outros que ainda estão aprendendo e, com uma taxa de abandono decente, as estatísticas dizem que são a maioria.
uliwitness
Graças a esses comentários, vim reler sua resposta. Você lista pontos válidos, mas infelizmente não aborda a questão com a controvérsia entre máquinas mais rápidas e máquinas mais lentas . O loop lógico (chamado gameloop no seu terceiro parágrafo) é limitado. A pergunta era sobre tampar o loop backbone que simplesmente reavalia cada corrida através se a entrada oughta ser puxado, a lógica ser calculada ou um quadro deve ser processado - por isso não mais rápido lógica menos que sua máquina é terrivelmente coxo
dot_Sp0T
1

Você não deve ter movimentos / jogabilidade nervosos

Há duas maneiras de implementar a lógica do jogo - vinculada a tempo real ou vinculada ao número de "turnos" / etapas de processamento. Se alguma coisa no seu jogo estiver se movendo para a esquerda, o movimento será diferente se o seu stepLogic () for chamado 100 em vez de 50 vezes?

Se o seu código tratar o tempo decorrido explicitamente em todos os lugares e fazê-lo corretamente, poderá ser bom; mas se houver algo no seu código que dependa do número de 'etapas', você terá efeitos colaterais indesejados.

Primeiro, a velocidade das coisas que devem estar se movendo constantemente variará de forma imprevisível (dependendo do uso do processador), e isso cria controles muito irritantes - você não pode dar um soco ou um salto preciso se, de repente, o jogo acelerar ou desacelerar. Mesmo para jogos sem 'espasmos', parece irritante e instável se as coisas não estiverem indo bem.

Segundo, você pode ter problemas com as habilidades dos personagens, dependendo da velocidade do computador - um exemplo clássico é o antigo problema do Quake, em que o alcance máximo do salto era involuntariamente dependente do seu fps.

Esses problemas podem aparecer mesmo se você tentar que o código seja independente de fps, o acúmulo de erros de arredondamento geralmente pode causar esses erros.

Peter é
fonte
1
Por mais que eu goste da sua resposta, isso não é relevante para a pergunta, pois não estou perguntando sobre movimentos / jogabilidade nervosos, mas sobre o dano que um loop não controlado (que não controla nenhuma lógica ou renderização ou qualquer outra coisa) pode causar ao sistema
dot_Sp0T
1

O único dano potencial é o seu consumo de energia; portanto, não faça isso em dispositivos móveis. Por outro lado, muitos sistemas embarcados passam a vida inteira em um ciclo esperando que as coisas aconteçam.

Costumava ser perfeitamente normal renderizar os quadros de jogos o mais rápido possível, às vezes sem um temporizador para compensar a jogabilidade pelas diferentes velocidades da CPU. O jogo de corrida da Bullfrog Hi-Octane foi uma vítima particularmente ruim disso, e eu suspeito que é a isso que o cartaz mencionando Civ2 está se referindo.

Aconselho você a pesquisar pelo menos as entradas a cada passo, se estiver em um sistema Windows ou similar, para garantir uma resposta rápida das entradas.

pjc50
fonte
Não é uma questão de cronômetro, é uma questão de um cronômetro, eu prefiro dizer, ou um contador de desempenho. É preciso obter o tempo real em algum momento, e um loop livre funciona perfeitamente. No entanto, um temporizador (no sentido de interrupção regular) está tentando regular a velocidade do loop em uso geral. Eu não considero isso bom. É preferível usar a função / swap/ presentpara flipfazer a espera no sinal vsync, para regular o ciclo do jogo.
v.oddou