OTIMIZAÇÃO E TLE (EVENTOS DE NÍVEL SUPERIOR)

3

Stats

492 visits, 700 views

Tools

License

This tutorial is licensed under CC BY 4.0. Please refer to the license text if you wish to reuse, share or remix the content contained within this tutorial.

Published on 22 Sep, 2023.

Em primeiro lugar, gostaria de expressar minha sincera gratidão a Guizmus por disponibilizar este tutorial incrível que tem sido de grande ajuda na otimização dos jogos que desenvolvo no Construct. Espero que esta tradução para o português também seja útil para todos vocês!

O QUE SIGNIFCA A SIGLA 'TLE'?

TLE significa "Eventos de Nível Superior" em português. No contexto do Construct 2, Construct 3 ou de programação de jogos em geral, os eventos de nível superior geralmente se referem a eventos ou ações que ocorrem no nível mais alto ou principal do jogo. Esses eventos podem incluir coisas como iniciar ou encerrar o jogo, mostrar telas de menu, controlar a progressão do jogo, entre outros. Eles são essenciais para o funcionamento adequado de um jogo e muitas vezes envolvem a lógica principal que direciona o fluxo do jogo.

Aqui estão alguns exemplos de TLEs (Eventos de Nível Superior) em jogos:

  1. Iniciar o Jogo: Um TLE que começa o jogo quando você pressiona "Iniciar" no menu principal. Ele configura o cenário inicial, carrega recursos e coloca o jogador no mundo do jogo.
  2. Fim de Jogo: Este TLE é ativado quando você conclui um jogo ou perde. Ele exibe uma mensagem de vitória ou derrota e pode redirecionar você para o menu principal ou permitir que você reinicie o jogo.
  3. Menu Principal: Quando você entra no jogo, é recebido pelo Menu Principal. Esse TLE controla a navegação pelos diferentes modos de jogo, como "Novo Jogo", "Carregar Jogo" e "Opções".
  4. Trocar de Nível: Em jogos com vários níveis, um TLE é usado para carregar o próximo nível quando você completa o atual. Ele pode ajustar a dificuldade, carregar novos recursos e definir os objetivos do próximo nível.
  5. Sistema de Pontuação: Um TLE acompanha e atualiza sua pontuação à medida que você joga. Ele pode exibir sua pontuação na tela e verificar se você atingiu um recorde pessoal.
  6. Gestão de Vida: Em muitos jogos, você tem uma quantidade limitada de vidas. Um TLE monitora suas vidas, atualiza a exibição na tela e verifica se você perdeu todas as vidas.

Esses são exemplos de TLEs que ajudam a controlar o fluxo principal de um jogo, tornando-o mais envolvente e divertido para os jogadores.

Agora iremos para a tradução!

PREFÁCIO

Como um novo programador C2, dediquei algum tempo para compreender o que está por trás das cenas, como funcionam os eventos e como utilizá-los de maneira eficiente.

Aqui, reuni as informações que encontrei sobre como otimizar eventos, especialmente ao desenvolver um jogo de médio a grande porte. Não estou isento de erros e ficaria contente em corrigir qualquer informação incorreta. Não hesite em deixar seu feedback!

INTRODUÇÃO

Cada exemplo individual no C2 é belo, divertido de jogar e funciona bastante suavemente. Por si só, eles não são realmente um jogo, mas quando combinados, podem criar praticamente o que você desejar.

No entanto, você pode ter certeza de que, ao adicionar muitos recursos ao seu jogo, a taxa de quadros por segundo (FPS) começa a cair, e em breve o jogo fica muito lento para ser jogado.

Isso pode ter várias causas (muita RAM sendo usada por animações, muitos comportamentos custosos como o SINE

, ... tudo explicado neste tutorial de dicas de desempenho), mas vamos nos concentrar em uma que requer um entendimento um pouco mais profundo de como os eventos funcionam e que pode ser crucial à medida que seu jogo começa a crescer até um certo ponto.

Após escrever a especificação: Como mencionado neste artigo de blog por Ashley, otimizar não é sua prioridade absoluta, e existem outras verificações-chave a serem feitas antes de entrar nisso.

ANTES DE COMEÇAR

Para compreender totalmente este tutorial, eu sugiro fortemente que você reserve um tempo para ler este manual e este tutorial.

A referência final será o tutorial mais lido, o Guia para Iniciantes.

Agora, temos tudo o que precisamos. Vamos abrir o exemplo do tutorial "GhostShooter (versão de tutorial).capx", disponível na pasta de exemplos.

Este exemplo funciona sem problemas, mas veremos como podemos torná-lo ainda mais amigável para a CPU e aberto a ainda mais recursos.

EVENTOS DE NÍVEL SUPERIOR (TLE)

O QUE É UM TLE?

Como qualquer mecanismo de jogo em tempo real, o C2 precisa atualizar seu estado 60 vezes a cada segundo para manter sua taxa de quadros de 60 FPS. Portanto, a cada vez, ele tem 160 milissegundos para executar todos os eventos e redesenhar-se. TLE são todos os eventos no "nível superior", diretamente na raiz de uma das folhas de eventos abertas ou em um grupo ativo, avaliados a cada quadro.

No entanto, pode haver diferentes tipos de eventos, e alguns não custam nada ao sistema, então vamos examiná-los um pouco mais detalhadamente.

DIFERENTES TIPOS DE EVENTOS

O primeiro tipo de evento é o gatilho, um evento chamado em um momento específico, nunca avaliado por si só.

Você pode identificar facilmente esses eventos olhando para a seta verde na frente da linha deles no C2/C3:

Um gatilho não é avaliado a cada quadro. Em vez disso, ele é chamado em um momento específico (aqui, no início do layout) e simplesmente ignorado pelo resto do tempo, efetivamente não consumindo CPU de forma alguma.

Conclusão: você pode ter todos os gatilhos que desejar, não afetará sua taxa de quadros.

O segundo tipo de eventos são as avaliações, os eventos estáticos que são avaliados a cada quadro:

E aqui você pode estar pensando: Sim, eu sei, há um "a cada quadro" para uma condição!

Você não estaria errado, esse é o comportamento pretendido, fazer com que este evento seja executado a cada quadro. Mas também é o caso de todas as avaliações individuais, como esses dois eventos:

O que deve ser compreendido aqui é o seguinte:

  • Primeiro evento: a cada quadro, para cada monstro atualmente existente, vamos comparar a vida dele com 0 e executar as ações nele se for inferior ou igual.
  • Segundo evento: a cada quadro, vamos executar as outras ações.

Sim, uma condição vazia é exatamente a mesma coisa que um "a cada quadro" em relação à avaliação do evento.

O último tipo de evento são os gatilhos falsos. Esses, que são exibidos com uma seta verde assim como os gatilhos, não funcionam da mesma forma. Eles são avaliados a cada quadro para verificar se a condição é atendida e são executados quando encontram as condições pela primeira vez, atuando na maioria dos casos como uma avaliação mais uma condição "uma vez enquanto verdadeiro".

O quarto evento do tutorial é um bom exemplo disso:

Na prática, este evento é relativamente custoso, pois a cada quadro ele verificará se cada par de (bala, monstro) está se sobrepondo e acionará o evento se eles não estiverem sobrepostos no quadro anterior.

Não é simples distinguir entre um gatilho e um gatilho falso, já que eles têm a mesma aparência. Somente com lógica, um bom conhecimento de JavaScript ou olhando o código do C2 você poderá deduzir a natureza de um evento.

Detecção de colisão? Precisa ser verificado.

Ao clicar? Iniciado por uma entrada do usuário, pode ser acionado.

Agora que vimos o básico, vamos ver como podemos reduzir a quantidade de TLE neste exemplo com algumas técnicas.

Saber o que custa o quê é uma coisa. Mas não podemos simplesmente ignorar os eventos custosos, então agora vamos nos concentrar em como minimizar o custo do programa. Dessa forma, poderemos incluir mais coisas nele antes que comece a ficar mais lento.

OTIMIZAÇÃO DE EVENTOS DE COMPARAÇÃO

CHAMANDO UMA FUNÇÃO EM VEZ DISSO

Eventos de comparação (estáticos), como o quinto evento do exemplo, "Monstro: vida <= 0", em qualquer estado dado do jogo, serão acionados ou não. A ativação não mudará de um quadro para o outro, a menos que as variáveis utilizadas mudem. Neste caso simples, se a vida do monstro for superior a 0, o evento será reavaliado a cada quadro, mas nunca será acionado até que o monstro perca vida. Portanto, a melhor maneira de otimizar isso é colocar essa avaliação logo após a mudança na vida do monstro, assim:

Com essa simples mudança, a avaliação será feita apenas uma vez toda vez que um monstro perder vida, economizando muitas avaliações desnecessárias.

O problema é que na maioria das vezes, quando você coloca uma avaliação desse tipo no meio da folha de eventos, você precisa dela em mais de um evento. Nesse caso, eu recomendaria simplesmente criar uma função que seja usada sempre que você alterar a variável verificada. No exemplo, isso significa usar uma função para subtrair a vida de um monstro:

Para explicar a função em duas palavras, os dois comentários (@0: e @1:) estão aqui para descrever quais parâmetros a função utiliza, o primeiro sendo o monster.UID para que possamos selecioná-lo e o segundo sendo o dano que ele sofre. A função lidará com a morte do monstro também.

Como uma função é chamada com um verdadeiro gatilho, isso não custará nada ao sistema.

ATIVANDO UM GRUPO

Às vezes, a avaliação a cada quadro é útil e não pode ser substituída. Pode então ser ativada/desativada dependendo do estado do sistema.

O melhor exemplo para isso são as entradas do usuário para movimentos. Geralmente, é algo assim:

Usei a ação "simular controle" para simplificar, mas certamente existem funções mais complexas para chamar se você fizer os movimentos manualmente. De qualquer forma, essa não é a parte que nos interessa.

Se pensarmos como se fôssemos o C2, primeiro verificaríamos se a tecla certa está pressionada e faríamos algo, depois verificaríamos se a tecla esquerda está pressionada, ... resultando em 4 avaliações por quadro.

Para reduzir essa quantidade, poderíamos adicionar uma condição geral, verificando se alguma tecla está pressionada e, em seguida, fazendo as 4 verificações anteriores, se necessário. Isso reduziria a contagem de verificações para 1 por quadro se nenhuma tecla estiver pressionada e adicionaria 1 avaliação se pelo menos uma tecla estiver pressionada.

No entanto, o que buscamos é 0 avaliação por quadro quando nenhuma tecla estiver pressionada e 4 avaliações quando pelo menos 1 tecla estiver pressionada. Para isso, vamos precisar de grupos:

Neste exemplo, você pode ver que temos:

Eventos 17-20: detecção de entrada (a cada quadro, mas somente se o grupo estiver ativo)

Evento 16: um grupo, não ativado no início

Eventos 12-13: ativação do grupo quando qualquer tecla de direção for pressionada (evento acionado, sem avaliação a cada quadro, apenas quando chamado)

Eventos 14-15: desativação do grupo quando nenhuma tecla de direção estiver mais sendo pressionada (evento acionado, sem avaliação a cada quadro, apenas quando chamado)

Essa técnica também pode ser usada para muitas outras coisas, como rastreamento suave da câmera, por exemplo (cálculo de interpolação linear a cada quadro e, em seguida, rolagem; o grupo "câmera" pode ser desativado quando a posição de rolagem atingir a do jogador e reativado quando qualquer movimento for acionado):

Observe a ativação do grupo da Câmera no evento 13, quando o jogador começa a se mover, e a desativação apenas quando o jogador para de se mover e a câmera quase alcança a posição do jogador. O rastreamento da câmera tem um custo de 2 interpolações lineares (lerp) e pelo menos 3 comparações, então é melhor desativá-lo quando não está em uso.

DETECÇÃO DE COLISÃO, NÃO REALMENTE NECESSÁRIA

Com a detecção de colisão sendo um gatilho falso avaliado a cada quadro, pode parecer uma boa ideia tentar colocá-la em um grupo e ativá-la/desativá-la conforme necessário. Por exemplo, você poderia tentar verificar a colisão da bala com os monstros apenas quando uma bala e um monstro forem gerados. No entanto, assim como está, não seria útil. A detecção de colisão verifica apenas os pares bala-monstro, então, se não houver nenhum, já está feito.

Informação importante que comprova a existência desse gatilho falso, o próprio Ashley confirmou a existência:

Se você deseja otimizar a detecção de colisão, tente reduzir o número de objetos a serem verificados. Por exemplo, use uma "área de carregamento" ao redor do seu jogador, maior do que a tela, é claro. Todos os monstros dentro da área devem ter suas colisões ativadas. Da mesma forma, qualquer coisa fora da área deve ter suas colisões desativadas.

CONCLUSÃO

Os TLE, em minha opinião, são algo crucial para otimizar, porque são avaliados a cada quadro. O que mostrei aqui foi como reduzir drasticamente os gatilhos e avaliações falsos desses eventos.

Dentre essas técnicas, é mais ou menos fácil reescrever qualquer evento em gatilhos e grupos. No entanto, essa não é uma técnica para usar muito cedo em um projeto, pois o código pode começar a ficar confuso e menos fácil de ler/escrever, dependendo dos seus hábitos. Em uma escala maior, em um projeto com muitos recursos distintos, isso pode economizar muita CPU, permitindo ainda mais recursos ou efeitos.

  • 0 Comments

Want to leave a comment? Login or Register an account!