Jogos que possuem taxa de quadros independentes são jogos que funcionam na "mesma velocidade", não importa a taxa de quadros relativa de cada computador. Por exemplo, um jogo pode rodar a 30 FPS (Frames per second[b]Quadros por segundo) em um computador mais devagar, e a 60 FPS em outro mais rápido. Um jogo com a taxa de quadros independente progride na mesma velocidade, nos dois computadores (Os objetos aparentam se movimentar na mesma velocidade). Em contrapartida, um jogo com a taxa de quadros dependente, no computador mais lento, funcionará literalmente com metade da velocidade (30 FPS), acompanhando exatamente a taxa de quadros em uma espécie de efeito "câmera lenta". Criar um jogo com a taxa de quadros independente o torna mais acessível, "jogável" e consequentemente, mais divertido para qualquer um, não importando o tipo de computador que este usuário tenha. Jogos que tem perda de FPS, e ficam lentos por conta disso, afetam gravemente a jogabilidade, frustrando os jogadores que desistem do mesmo!
Este tutorial descreve como você pode tornar a taxa de quadros do seu jogo independente. A mesma técnica cria uma escala temporal(timescaling), que possibilita que o usuário, deliberadamente, crie efeitos de câmera lenta e pausa facilmente.
A expressão
dt do sistema
A chave para uma taxa de quadros independente nos nossos jogos é a expressão dt do sistema. dt é a abreviação de delta-time (ΔT). Onde Delta significa uma alteração quantitativa, logo, "delta-time" significa a variação de tempo. No nosso contexto, ele representará o tempo (em segundos) que cada ciclo de processamento gráfico leva para ocorrer.
Por exemplo, a 100 FPS, o dt será de 0.01 (Um centésimo de segundo, ou 10 milissegundos), e a 10 FPS, dt será de 0.1 (Um décimo de segundo, 100 ms). Na prática, dt varia ciclo após ciclo, sendo improvável que ele seja constante por longos períodos de tempo.
Note que se você adicionar o valor de dt a uma variável a cada ciclo, este adicionará 1 por segundo, isso, porque o tempo entre todos os ciclos no período de um segundo devem somar 1! Eis um exemplo mostrando isso. (Adicionar o valor dt a uma variável em uma instância de um objeto, é a melhor maneira de se criar um contador de tempo (um timer) neste mesmo objeto.)
Como usar o
dt
Tipicamente, movimentos com taxa de quadros dependentes são montados com eventos como esse:
A cada ciclo (Every tick), um por quadro, o objeto se moverá um pixel para direita. Note que então, a 30 FPS, ele se moverá exatamente 30 pixels por segundo, logo, a 60 FPS, isso significam 60 pixels por segundo. São velocidades diferentes, que dependem da taxa de quadros.
Lembre-se que no exemplo mencionado mais acima, dt sempre somará 1 a cada segundo. Então, se nós alterarmos o evento como a seguir...
...o objeto se moverá para direita, 60 pixels por segundo em qualquer taxa de quadros. Desde que dt adicione 1 por segundo, 60 [] dt* adicionará 60 por segundo. Isso significa que em ambas a taxas (30 e 60 FPS), nossos objetos se moverão a 60 pixels por segundo - a mesma velocidade, independentemente da taxa de quadros.
Use o
dt em todo lugar
Toda hora você move um objeto a uma velocidade fixa, são nessas horas que você deve usar o dt para alcançar a independência da taxa de quadros. Por exemplo, em um evento onde um "Sprite" Avança em relação ao ângulo do mesmo (Move forward), você pode usar como expressão o valor 60 [] dt* para que seu objeto se mova 60 pixels por segundo no ângulo atual.
Comportamentos (
Behaviors) já usam o dt
Todos os comportamentos de objetos do Construct 2 já usam o dt em seus cálculos internos de movimentação por padrão (com exceção do comportamento físico). Isso quer dizer que qualquer coisa movimentada por comportamentos como o de plataforma (Platform) e o de 8 direções (8 Direction) não necessitam de nenhum tratamento especial - eles já o fazem!
Note que o comportamento Físico (Physics) é uma exceção, e por padrão não usa o dt, portanto, depende da taxa de quadros. Isso, porque o dt conta com pequenas variações aleatórias, essas variações podem causar em jogos com a mesma configuração física, resultados diferentes, mesmo que a mesma ação seja executada mais de uma vez. Isso, de forma frequente, é extremamente chato em jogos físicos, e é por isso que por padrão estes são dependentes da taxa de quadros. Contudo, você pode habilitar o uso do dt pelo uso da ação Set Stepping Mode (Definir modo de passos) em "start of layout" (na largada/início do plano), escolhendo a opção "Framerate independent" (Taxa de quadros independente). Detalhe, esse modo ainda limita o número máximo de ciclos de tempo no comportamento físico para até 1/30 (cerca de 33 ms), porque o uso de ciclos maiores pode causar instabilidade nas simulações físicas.
Escala temporal (
timescaling)
Uma função realmente legal no Construct 2 é a Escala temporal. Ela permite que se mude razão de tempo passada no jogo, também é conhecido como escala do tempo(time scale). Você pode mudar essa escala, na ação Set Time Scale (Definir Escala de Tempo). Uma escala temporal com valor 1 mantém o jogo em velocidade normal, 0.5 o faz cair para metade da velocidade, 2.0 dobra a velocidade. Caso vc decida diminuir a razão de tempo em 0.1 por exemplo, seu jogo ficará bastante lento, porém suavizado - um belo efeito "slow-motion" (câmera lenta)!
A escala temporal funciona mudando o valor que dt retorna. Consequentemente, qualquer comportamento que use o valor de dt em seus movimentos, é afetado. Caso você não use o valor de dt em seus movimentos (como no primeiro evento mostrado no começo do tutorial), o mesmo não sofre nenhuma alteração pela escala de tempo! Portanto, use a escala temporal, você simplesmente deve usar o dt corretamente em todos os movimentos.
Pausa
Simplesmente defina a escala temporal para 0. Ela vai "congelar" todos os movimentos. É o jeito mais simples de pausar o jogo. Defina para 1, e o jogo volta a velocidade normal.
Você talvez note que ainda conseguira fazer algumas coisas como atirar usando os controles. Para contornar isso, você pode agrupar todos os eventos principais do jogo, e desativa-los quando o mesmo estiver pausado, retornando a atividade assim que o mesmo seja despausado.
Essa também é uma boa maneira de saber se você usou o dt corretamente. Caso o tenha feito, ao alterar a escala temporal para 0, tudo irá parar. Caso o contrário, alguns objetos ainda continuarão a se movimentar na tela, algo esquisito para um jogo que se diz pausado! Nesse caso, é a chance de verificar como esse objetos são movidos, e ter certeza de usar o dt da forma apropriada.
Outros tipos de movimentação
É importante ressaltar que o dt pode(e deve) ser usado por todos os tipos de movimento. Isso inclui rotação e aceleração.
Rotação
Similar ao evento mostrado no começo do tutorial, o evento rotacionará o porquinho 1 grau a cada ciclo, sem o uso do dt:
Logo, são 30 graus por segundo a 30 FPS, e 60 graus por segundo a 60 FPS - novamente, velocidades diferentes para taxa de quadros diferentes. Usando o dt da mesma forma, resolveremos o problema novamente. Dessa forma, o porquinho rotacionará 60 graus por segundo independentemente da taxa de quadros:
Aceleração
A Aceleração é bastante simples. Usualmente é apenas aplicar o que você aprendeu em seu movimento personalizado via eventos.
Se você possuir uma variável "Velocidade", seu objeto se moverá "Objeto.Velocidade [] dt"* pixels por ciclo. A variável relacionada a velocidade do seu objeto, portanto, contém a velocidade em pixels por segundo.
Suponhamos que você queira acelerar seu objeto a 100 pixels por segundo durante um segundo. Você simplesmente precisa adicionar 100 [] dt à variável "Velocidade" a cada ciclo, e seu objeto, a partir de então, vai acelerar de forma independente da taxa de quadros. Trocando em miúdos, você usa o dt* tanto para ajustar a posição de um objeto, quanto para ajustar sua velocidade.
Equívocos comuns
Nunca use o dt no evento "Every X seconds" (Cada X segundos)! Um evento como "Cada 1 segundo"(Every 1 second) sempre contarão de 1 em um 1 segundo, independentemente da taxa de quadros, logo, já possuem independência quanto a taxa de quadros. O evento serve pra mensurar o tempo, e não quadros. Se você criar um evento como "Every 60 * dt seconds", você, inadvertidamente, criou uma condição dependente da taxa de quadros - o oposto do que busca! Será uma condição que rodará a cada 6 segundos a 10 FPS (quando dt = 0.1), ou a cada 0.6 segundos quando a 100 FPS (quando dt = 0.01); Se você, apenas usar o evento "Every 6 seconds", ele já estará contando a cada 6 segundos, independente da taxa de quadros.
Considerações avançadas
Taxa de quadros mínima
Em casos onde a taxa de quadros é muito baixa, o dt pode se tornar muito grande. Por exemplo, a 5 FPS, dt é de 0.2. Um objeto movendo-se a 500 pixels por segundo é portanto, movido a 100 pixels por ciclo. Isso pode causar uma espécie de "teleporte" através de paredes e perder colisões com outros objetos.
Jogos geralmente não são jogáveis a baixos FPS, mas seria ainda pior se eles se tornassem instáveis assim. Para ajudar os jogos a se adaptarem a baixos FPS, o Construct 2 não deixa que o dt passe de 0.1. Em outras palavras, abaixo de 10 FPS, o dt não passará de 0.1. Isso significa que abaixo de 10 FPS, seu jogo passará a estar em "câmera lenta" (descrito antes, como um dos problemas da dependência da taxa de quadros), No entanto, esse resultado, é melhor que com o problema dos "objetos teleportadores".
Variações aleatórias
Como mencionado com a física, o dt geralmente possuí pequenas e aleatórias variações, e às vezes, causa contagens\medições imperfeitas no computador. Assim, com a física, isso pode ser a causa de leves variações no seu jogo. Mas, esse problema é negligenciável e pouquíssimo notado com a física. É recomendável que você sempre use o dt em seus jogos, a não ser que você não queira eles sejam precisos (o que é raro).
Escala temporal para objetos individuais
Você pode definir a escala temporal para um objeto individual, através da ação do sistema Set object time scale(Definir escala temporal do objeto). Isso pode ser aplicado em uma situação, onde mesmo durante o "slow-motion" (time scale de 0.3, por exemplo), seu personagem se mova na velocidade normal (time scale definido para 1). Isso pode ser alcançado, definindo a escala do tempo para 0.3, então usando o Set object time scale para definir a velocidade do jogador. A expressão dt do sistema é apenas afetada pela escala temporal do jogo. Objetos tem suas próprias variáveis dt (exemplo: Player.dt), que você precisa associar ao dt do sistema para todo o movimento que for realizar.
Desde que elas (as variáveis dt de objetos) retornem valores diferentes, diferentes partes do jogo podem progredir de formas diferentes.
Neste exemplo, para retornar o jogador ao tempo do sistema, é só usar a ação Restore object time scale(Restaurar escala de tempo do objeto).
Conclusão
É importante desenvolver seu jogo desde o início, usando o valor dt. Ele aperfeiçoa a jogabilidade, assegurando que os passos do jogador nunca percam a velocidade, em particular, nas partes mais intensas do seu jogo. De quebra, você ainda pode implementar seu jogos com funções de escala temporal, aonde é possível de forma simples pausar o jogo e ainda criar sensações diferentes de "passagem de tempo" para seus personagens e cenário, enriquecendo a jogabilidade.
Não esqueça que os behaviors já usam o dt (exceto com a física, aonde você precisa habilitar isso manualmente), se você usa comportamentos para controlar todos os movimentos do jogo, não precisa se preocupar com o dt, mas caso tenha customizações de movimento utilizando eventos, é imprescindível o uso adequado deste recurso para independência da taxa de quadros em seus jogos!