Game Development Tutorial – 01 – Sprites & Tiles

Antes de alguém tentar compreender como se criam videojogos, há que saber o que são sprites, e tiles.

Com a excepção de jogos 3D, em que vemos objectos desenhados em tempo real em 3D, tudo o que é jogos usam sprites, e grande parte deles usam tiles, em especial há umas décadas atrás, quando era necessário poupar memória ao máximo.

Vão ser usadas neste tutorial algumas imagens tiradas de um vídeo cujo link será disponibilizado no fim para quem queira ver a explicação de uma forma mais dinâmica.

Antes da matéria propriamente dita, fica aqui o vídeo associado a este post:

Como exemplo: o jogo Rick Dangerous:

Vamos começar com o exemplo do famosíssimo jogo Rick Dangerous, publicado em 1989, desenvolvido pela Core Design, e que foi o percursor da ainda mais famosa série de videojogos Tomb Raider, criada pela mesma empresa.

Pode dizer-se que foi o Tomb Raider original em 2D:

Game Development Tutorial - 01 - Sprites & Tiles - Rick Dangerous
Game Development Tutorial – 01 – Sprites & Tiles – Rick Dangerous

A versão que estudaremos será a versão para as máquinas Amiga (como Commodore Amiga):

Game Development Tutorial - 02 - Sprites & Tiles - Rick Dangerous Gameplay
Game Development Tutorial – 02 – Sprites & Tiles – Rick Dangerous Gameplay

Este jogo, poderão reparar que tem gráficos semelhantes do dos níveis de teste do Game Engine que iremos usar, desenvolvido do zero em C++ por Gonçalo Ferreira, e se estiverem a ler este texto neste preciso momento, serão muito grandes as probabilidades de estarem a ter formação com o autor deste Game Engine (ou em Português: “Motor de Jogos”), que será designado por mim abaixo como “o meu Game Engine”:

2022-06-13 - Animação com pequenos efeitos de relâmpagos no meu Game Engine em C++...
2022-06-13 – Animação com pequenos efeitos de relâmpagos no meu Game Engine em C++…

Quem conhece o Rick Dangerous original, poderá reparar que são usadas imagens e bonecos tirados do jogo Rick Dangerous, e acima podemos identificar tanto as paredes “douradas”, o fundo de pedra escuro, e os “inimigos” Egípcios.

Abaixo temos um mapa do jogo original, e por ele podemos ver que um nível apenas, tem perto de 60 ecrãs, e tínhamos 4 níveis no total, no jogo.

E podemos ver abaixo que cada um desses ecrãs, tinha bastante riqueza de conteúdos, tanto em gráficos, como inimigos, armadilhas, etc:

Game Development Tutorial - 03 - Rick Dangerous Level Directions
Game Development Tutorial – 03 – Rick Dangerous Level Directions

O foco do jogo:

Neste jogo, há um foco quase absoluto no ecrã actual em que o jogador estaria dentro do jogo, a cada momento.

Isto devia-se à necessidade de poupar ao máximo o CPU e a memória usada, em especial a memória neste caso específico.

Dá-se assim um ênfase grande ao ecrã actual em que o jogador está e não tanto ao mapa inteiro:

Game Development Tutorial - Sprites and Tiles - 04 - Level and CPU focus on Rick Dangerous
Game Development Tutorial – Sprites and Tiles – 04 – Level and CPU focus on Rick Dangerous

Este é um sistema diferente do usado por defeito no meu Game Engine que terão a oportunidade de experimentar, que é um sistema em que se usa uma câmera activa que centra o ecrã do jogo sempre no jogador, e não em ecrãs exibidos por inteiro um de cada vez:

2022-11-22 – Melhorias no sistema de cordas, e vídeo de demonstração a imitar o Tarzan ou Homem-Aranha, no meu Game Engine em C++…

A título de curiosidade, no vídeo acima, podemos ver que a personagem principal é representada pela personagem “Eric the Swift” do jogo Lost Vikings, e tem um sistema de cordas que em termos de aparência é igual ao do jogo Batman the Movie, publicado pela Ocean em 1989, pois foram alguns dos jogos que escolhi para criar um remake melhorado dos mesmos um dia.

Podem notar que o nível abaixo é totalmente diferente, pois foi retirado do tal jogo Batman the Movie, e deixo-vos abaixo um exemplo do mesmo para verem as semelhanças:

Mas antes de prosseguirmos, é importante, para entenderem a razão pela qual as técnicas de sprites e tiles são muito usadas, compreenderem o uso da memória e processador num computador.

Estudemos primeiro a memória, e como os gráficos a afectam…

O que é um Byte?

Antes de prosseguirmos com a explicação do porquê ser importante o uso de tiles e sprites, vamos ter de saber muito bem, o que é um byte.

Um byte, é um conjunto de 8 dígitos de 1 ou 0 (representando Verdadeiro ou Falso), que em binário corresponde a um intervalo de valores entre 0 a 255 no sistema decimal, ou seja, 256 possíveis valores (calculados por 2 elevado a 8, pois 2^8=256):

Game Development Tutorial - Sprites and Tiles - 05 - What is a Byte?
Game Development Tutorial – Sprites and Tiles – 05 – What is a Byte?

Sabemos assim que um byte pode representar 256 valores possíveis, de 0 a 255 (pois praticamente tudo nos computadores começa em 0 e não em 1), e é a unidade de memória mais importante num computador, junto com os bits (um byte é composto por 8 bits).

A relação entre Bytes e Cores e o RGBA:

Agora o que tem um Byte a ver com Cores?

Em primeiro lugar vamos ver que, o sistema de cores RGBA, significa: Red+Green+Blue+Alpha Opacity, ou seja, é um sistema onde temos 3 bytes, cada um representando uma cor.

Não são as cores primárias mas é parecido, são as que melhor podem ser usadas em ecrãs, em que substituímos o Amarelo pelo Verde, das cores primárias originais, por funcionar melhor para misturar cores num ecrã electrónico do que a cor primária Amarelo.

Por outro lado, a título de curiosidade, nas impressoras, não usamos as cores primárias também, mas algo ainda mais distinto: usamos as CMYK, que significam Cyan+Magenta+Yellow+Key, onde Key representa a cor negra, mas é usada para definir o resultado final da cor na impressora, daí o nome “Key”, de “Chave”.

Ou seja, no mundo dos computadores, construímos cores não com as cores primárias, mas com variantes, seja nos ecrãs, nas impressoras, etc.

Cada um dos bytes do RGB representam um número entre 0 e 255, e se multiplicarmos 256*256*256 teremos um resultado de 16.777.216 cores possíveis.

Podemos dizer assim que as cores RGB têm 16 milhões de cores possíveis para nosso uso, e dado que se estima que a maioria dos humanos só consegue identificar perto de um milhão de cores possíveis, nunca necessitaremos de mais do que RGB no Futuro para os nossos olhos.

Basicamente, se a cor R (Red/Vermelho) tiver 256 valores possíveis, cada um deles com 256 valores possíveis de G (Green/Verde), e cada uma dessas tiver 256 valores possíveis de B (Blue/Azul), temos nesses 3 bytes 256*256*256 cores, ou seja, as tais 16 milhões de cores.

Vejamos a combinação das cores RGB abaixo:

Game Development Tutorial - Sprites and Tiles - 06 - What are RGBA colors?
Game Development Tutorial – Sprites and Tiles – 06 – What are RGBA colors?

O Byte de Alpha Opacity:

A estas cores, juntamos o byte A, de Alpha Opacity, e com ele podemos definir 256 possíveis níveis de transparência, o que nos permite ter ainda mais cores no ecrã, ou fazer com que haja certas transparências no jogo, como por exemplo na água abaixo, no nível de testes do meu Game Engine:

2022-06-19 - Relâmpagos melhorados, e com efeitos sonoros também, bem como a Chuva, no meu Game Engine em C++...
2022-06-19 – Relâmpagos melhorados, e com efeitos sonoros também, bem como a Chuva, no meu Game Engine em C++…

Como podem ver acima, a água presente neste meu Game Engine, é transparente, mas não a 100%, tem um nível de opacidade entre 0 (que a faria ser totalmente transparente) e 255 (que a faria ser totalmente opaca e não deixar ver nada atrás).

Está obviamente num nível intermédio, que nos permite espreitar atrás da água em si, incluindo o próprio jogador.

Contas com bytes e imagens:

Vamos agora fazer algumas contas para compreender a importância dos bytes e memória na criação de videojogos, e com isso a importância dos tiles e sprites.

Vamos simular a quantidade de memória necessária caso quiséssemos guardar toda a informação visual de cada ecrã de cada nível, em 4 níveis diferentes (imaginando que fossem 60*4=240 ecrãs), usando cores RGBA (4 bytes por cada letra RGBA, ou seja, 4 bytes por píxel), em ecrãs de várias resoluções (256×192 e 1024×768), e numa imagem sem perda de qualidade:

Game Development Tutorial - Sprites and Tiles - 07 - The Amount of Memory the Rick Dangerous Videogame would require if made with RGBA colors and without the use of Tiles
Game Development Tutorial – Sprites and Tiles – 07 – The Amount of Memory the Rick Dangerous Videogame would require if made with RGBA colors and without the use of Tiles

Acima podemos ver que necessitaríamos de 45 MB (MegaBytes), ou mesmo 720 MB, de memória, só para poder armazenar tanta informação, e relembro que 1 MB são 1024 KB (KiloBytes), ou seja, 1024*1024=1.048.576 bytes.

E isto era proibitivo, em especial nos anos 80, em que a máquina para a qual a versão do jogo demonstrado, era o Commodore Amiga, uma máquina na altura muito à frente no seu tempo:

Viva Amiga Teaser Trailer Better Audio

Esta máquina acima tinha de memória apenas meio MB, ou seja, 512 KB, muito longe das memórias acima faladas, ou seja, não uns 16 GB como hoje em dia mas sim uns 0,00005 GB de memória o tal Commodore Amiga acima, e dez vezes menos para o ZX Spectrum 48K.

E isto era algo do outro mundo, e para que se compreenda isso, vejamos um jogo na época, numa máquina ainda mais cara (um PC com sistema operativo MS-DOS), com os seus gráficos CGA:

Ou o mais barato mas muito famoso ZX Spectrum 48K:

Agora espreitemos um jogo para o Commodore Amiga, nos anos 80, e poderão ver como era uma máquina muito à frente do seu tempo:

Como podem ver, o Amiga foi uma máquina muito à frente do seu tempo, mas mesmo ele, estava limitado a 512 KB de memória de origem que era partilhada entre vídeo, processador e som, tal como no ZX Spectrum, algo que falaremos mais adiante.

Mesmo hoje em dia em 2023, seria proibitivo e sem sentido desperdiçar centenas de MB (MegaBytes) de memória com níveis desenhados assim a desperdiçar memória desnecessariamente, pois todos sabemos que um programa de desenho pode bloquear se tentarmos abrir imagens de 720 MB de tamanho, agora imaginem um jogo ter de lidar com 720 MB de imagens em memória além de tudo o resto!

Como se resolvia esse enorme problema? Com os Tiles, por exemplo.

Onde entram os tiles?

Vejamos esta imagem abaixo tirada do meu Game Engine:

2022-09-11 - Terminada a funcionalidade das cordas e de subir para plataformas superiores do meu game engine em C++...
2022-09-11 – Terminada a funcionalidade das cordas e de subir para plataformas superiores do meu game engine em C++…

Se repararem acima, todo o fundo é composto por vários tiles repetidos, tanto as plataformas como o fundo do ecrã, sendo que um tile, que significa em Português “azulejo”, é mesmo como um azulejo numa parede, ele fica lá fixo, e não muda. e neste caso têm 32 píxeis de largura por 32 píxeis de altura, e é repetido, tal como os azulejos nas nossas paredes, para preencher o fundo do jogo, com aqueles tiles mais escuros.

Da mesma maneira podem ver os blocos de pedra, todos repetidos, com tamanho igual, já de cor mais dourada, que são usados para construir as plataformas onde o jogador se suporta, e ambos estão fixos.

No fundo, usamos tiles para tudo o que é fixo, sejam tiles de colisão (aqueles onde embatemos, e que servem de paredes ou plataformas), ou os tiles de fundo, que são aqueles que não interferem connosco e que só lá estão para enfeitar.

Poupando memória:

Faz todo o sentido assim, ao invés de colocarmos tiles inteiros de 32×32 píxeis, ocupando cada um 4096 bytes (1024 píxeis de largura por 768 de altura tendo 4 bytes cada um), usarmos um mapa onde cada tile é representado por exemplo por um único byte que poderá conter valores de 0 a 255.

Passaríamos assim de 1024*768*4=3.145.128 bytes, ou seja, 3,1 MB de tamanho, caso usássemos imagens reais, para 32 tiles de largura por 24 de altura, com um total de 768 bytes apenas para um ecrã inteiro.

Gastaríamos assim 3.145.128/768=4.096, perto de quatro mil vezes menos memória gasta, e com o mesmo resultado no ecrã! E isto significa um uso de memória 4.096 vezes menor, sendo que gastaríamos a mesma memória aqui com 4096 ecrãs, que usaríamos com imagens inteiras num único ecrã.

Os mapas de tiles:

Para criarmos um nível de um jogo, bastaria que dividíssemos assim o ecrã num determinado número de colunas e linhas, com os tais 32*24=768 blocos, e colocar um valor em cada um, armazenados numa matriz/array específica.

Vejamos um exemplo abaixo de um ecrã mais pequeno, com apenas 12 por 8 blocos, e onde ao invés de números vamos usar simplesmente uma letra por cada tipo de tile para facilitar a compreensão:

Game Development Tutorial – Sprites and Tiles – 08 – The use of Tiles on VideoGames

Na imagem acima podemos ver de forma simplificada como representamos o mapa de um jogo através de números, ou no exemplo acima, letras, em que o motor do jogo (o meu Game Engine), substituiria as letras “P” por paredes com as quais o jogador colidiria, as letras “C” por um chão que suportaria o jogador, a letra “A” por água, onde o jogador poderia perder a vida, ou mergulhar e poder fazer outras coisas, etc.

O “S” pelo desenho do Sol? As letras “N” por nuvens? Etc, etc…

Teríamos assim um mapa de um jogo desenhado com meia dúzia de letras que indicariam o que cada secção do ecrã iria conter.

Os programas geradores de mapas de tiles:

O uso de tiles faz-se de uma maneira mais gráfica hoje em dia.

Na altura pensei em construir um programa específico para gerar mapas, mas decidi usar as poucas horas livres que tinha para adicionar features ao meu Game Engine, ao invés de reinventar a roda, e decidi usar um programa gratuito de nome “Tiled”, que pode ser sacado em http://www.mapeditor.org/.

Vejamos um exemplo meu a adicionar tiles do jogo Batman the Movie (versão Amiga de 1989) ao seu nível de testes:

2022-12-03 – Como qualquer pessoa poderá criar jogos com o meu Game Engine um dia, gratuito, sem saber programar…
2022-12-03 – Como qualquer pessoa poderá criar jogos com o meu Game Engine um dia, gratuito, sem saber programar…

Acima podem ver como é fácil usar o programa Tiled, com ele gravamos um ficheiro de formato XML, com as informações do mapa de jogo.

No programa definimos os valores de cada tile, as suas posições, e gravamos o ficheiro que contém o mapa.

Cabe ao criador do meu Game Engine, neste caso eu, Gonçalo Ferreira, conseguir que o mesmo leia tudo o que o mapa tem, e monte o jogo em tempo real sempre que o iniciamos, para podermos jogar ao mesmo.

No fim, até podemos meter o Sonic e o viking de nome “Eric the Swift” (do jogo “Lost Vikings”), a passear pelos mapas de jogo do Batman the Movie, como nos apetecer, a partir da metade do vídeo abaixo:

2023-09-07 – Luzes e Sombras em locais específicos como quartos ou água, no meu Game Engine em C++…

Se não conhecerem o jogo “Batman the Movie”, da Ocean (1989), não reconhecerão o mapa abaixo, mas ele veio directamente do jogo Batman the Movie original.

O original, como podem ver abaixo, tinha o Batman e não os nossos personagens acima:

Dinâmica de cordas no jogo Batman the Movie, versão para Commodore Amiga...
Dinâmica de cordas no jogo Batman the Movie, versão para Commodore Amiga…

Já coloquei este vídeo acima, mas vou voltar a deixá-lo caso queiram espreitar o jogo em funcionamento:

Se repararem, as cordas usadas no meu Game Engine são as mesmas do jogo:

2022-11-22 – Melhorias no sistema de cordas, e vídeo de demonstração a imitar o Tarzan ou Homem-Aranha, no meu Game Engine em C++…

Poderão ver este sistema em funcionamento quando criarem o vosso primeiro jogo com o meu Game Engine.

O uso de blocos de tiles:

Nos anos 80 e 90, quando a memória era muito pouca e até 768 bytes faziam muita falta, usava-se outra técnica que deixa saudades: o uso de blocos de tiles.

Se pré-construirmos blocos, cada um deles com uma combinação de 4 tiles, podemos representar 4 blocos de cada vez no lugar de um, e aí, poupa-se muita memória.

No exemplo abaixo, do Rick Dangerous, temos um case-study curioso…

É que apesar de o autor poder usar a resolução de 320×200 permitida pelo Commodore Amiga, usou apenas 256×192, porque como queriam migrar o jogo também para a plataforma ZX Spectrum, que tinha essa resolução, para facilitar todo o processo, usou essa resolução em todas as versões, inclusive a do Commodore Amiga.

Assim, na versão do ZX Spectrum, em relação à do Amiga, ficou tudo 100% igual (mas com menos ecrãs por falta de memória no ZX Spectrum), mas com a diferença de que tinha umas barras a negro nos lados e topo e fundo do ecrã, para centrar o mesmo, porque tinha a resolução do ZX Spectrum e teve de ser centrada.

Mas tinha a diferença enorme de ter muito mais cores (qualidade gráfica), e som, e ecrãs de jogo.

Abaixo podemos ver algumas versões do jogo para a máquina ZX Spectrum, de 8 bits, que possuo na minha colecção de jogos dos anos 80 e 90:

Rick Dangerous ZX Spectrum versions owned by Gonzalo Ferreira, author of the C++ Game Engine...
Rick Dangerous ZX Spectrum versions owned by Gonzalo Ferreira, author of the C++ Game Engine…

Na imagem acima temos várias versões, sendo a mais rara a primeira em versão double jewel, mas podemos ver que todas usavam ecrãs da versão Amiga, e no canto inferior direito podemos ver um ecrã do mesmo jogo na versão ZX Spectrum, que não ficaria muito atrás, apesar de ter muito menos cores, isto porque em termos de imagem (sem contar com as cores), eram 100% iguais.

Agora, se dividirmos 256 e 192 por 32, teremos 8 blocos de largura por 6 de altura.

Cada bloco era composto por 16 tiles, 4 de largura, e 4 de altura, ambos com 8 píxeis de largura e altura cada um:

Game Development Tutorial - Sprites and Tiles - 09 - The use of Blocks of Tiles on VideoGames
Game Development Tutorial – Sprites and Tiles – 09 – The use of Blocks of Tiles on VideoGames

Desta maneira gastávamos 16 vezes menos memória por cada ecrã no mapa do jogo, e os 180 bytes por ecrã que se poupavam ao usarmos 180 e não 192 bytes por bloco, eram muitíssimo importantes na altura.

Tínhamos assim na realidade 16 pequenos tiles por cada bloco, o que parece não servir para poupar muita memória, mas se virmos bem, como fazíamos o mapa com blocos pré-definidos com essas combinações de tiles e não com tiles em si, a poupança era muito grande:

Game Development Tutorial - Sprites and Tiles - 10 - Blocks made of Tiles on VideoGames
Game Development Tutorial – Sprites and Tiles – 10 – Blocks made of Tiles on VideoGames

As tile sheets:

Os tile sheets eram (e são) imagens que contêm todos os tiles de um jogo, e o jogo ao arrancar vai ler esses tiles todos, e construir os blocos e níveis com base nos mesmos, ao arrancar cada nível:

Game Development Tutorial - Sprites and Tiles - 11 - The Use of Tiles on Videogames and Game Engines
Game Development Tutorial – Sprites and Tiles – 11 – The Use of Tiles on Videogames and Game Engines

Vou deixar um exemplo real de uma tile sheet, neste caso da versão PC do mesmo jogo, abaixo:

Rick Dangerous PC Version Tile Sheet
Rick Dangerous PC Version Tile Sheet

E bastava isto, uma tile sheet que na realidade ocupa uns meros 13 KB de tamanho (0,013 MB), para termos todas as imagens necessárias para criar os mapas de todos os níveis, do jogo inteiro.

As imagens ocupavam assim pouco tempo, e com mais algum espaço gasto, construíamos os mapas de jogos, com poucos bytes por ecrã, além das combinações de tiles que compunham cada bloco, etc.

Vamos ver quanto gastaríamos ao usar apenas blocos de 16 tiles ao invés de imagens de ecrãs inteiros em RGBA:

Game Development Tutorial - Sprites and Tiles - 12 - The Use of Tiles on Videogames
Game Development Tutorial – Sprites and Tiles – 12 – The Use of Tiles on Videogames

Sim, é verdade, acabamos assim por gastar 65.526 vezes menos memória!

Significa que um jogo que poderia ocupar 1 KB de memória ao invés de 65.526 KB (66 MB), com os mesmos níveis, mesmas imagens, mas eliminando a redundância de ter várias imagens iguais a ocupar memória, tudo graças ao uso de tiles.

Ou ocupar 1 MB de memória ao invés de 65.526 MB (66 GB, que quase ninguém tem hoje em dia).

Os Tiles de Colisões e os de BackGround:

Agora, já vimos o que são tiles, e como eles nos fazem poupar memória, e como os usamos para preencher mapas de jogos.

Mas ainda não foi explicado que usamos dois tipos principais de tiles: tiles com e sem colisão.

O que quero dizer com isto?

Não se devem usar “objectos” (em termos de “Programação Orientada a Objectos” que será discutida mais tarde), para criar paredes ou plataformas, pois ocupariam mais recursos, como CPU e memória.

Usam-se em vez disso outras técnicas, que não farão parte da criação deste mini-curso pois terão mais a ver com Programação em si, e usamos os tiles tanto para preencher o fundo do ecrã como as colisões.

Eu criei a dada altura, para melhor mostrar o funcionamento dos tiles, adicionei uma feature no meu Game Engine que mostra a divisão entre os diversos tiles:

2023-12-29 - Criação de uma grid de tiles para debugging, no meu Game Engine em C++...
2023-12-29 – Criação de uma grid de tiles para debugging, no meu Game Engine em C++…

As riscas mais claras, que desenham os quadrados na imagem acima, são as linhas que dividem os tiles do jogo.

Vejamos em vídeo tudo isto em funcionamento:

2023-12-29 – Criação de uma grid de tiles para debugging, no meu Game Engine em C++…

Se repararem acima, existem tiles que têm imagens, como os blocos de pedra mais dourados, que servem de plataformas, onde tanto o jogador como os inimigos aterram quando caem (quando é chão), ou embatem quando saltam (quando é tecto), ou onde param quando colidem com eles (quando são paredes).

São neste caso imagens fixas na mesma, só que ao contrário das que podem ver no fundo, como os das rochas que estão por detrás dos bonecos Egípcios e que não interferem com eles, estes interferem com os inimigos e o jogador, não os deixando passar por eles.

São tiles de colisão, e iremos ver mais tarde os mesmos em funcionamento, ou no vídeo que faz parte deste tutorial onde mostro os mesmos a serem aplicados.

Sprites:

Acabámos de saber que usávamos imagens com muitos tiles, que o jogo depois poderia organizar em blocos de tiles, e assim poupar muita memória:

Game Development Tutorial - Sprites and Tiles - 13 - Tile Sheet from Rick Dangerous as example
Game Development Tutorial – Sprites and Tiles – 13 – Tile Sheet from Rick Dangerous as example

Mas um tile, como o jogo mesmo indica (“Azulejo”), é fixo na “parede”, tal como os azulejos.

Ou seja, são as imagens que ficam fixas no ecrã, como os fundos do jogo.

Então que nome dávamos às imagens que se movem pelos nossos ecrãs quando jogamos algum VideoJogo?

Isso mesmo, são os sprites!

Game Development Tutorial - Sprites and Tiles - 14 - What are Sprites on VideoGames...
Game Development Tutorial – Sprites and Tiles – 14 – What are Sprites on VideoGames…

Se virem acima, temos em destaque tanto o jogador (na personagem do “Rick Dangerous”), como o inimigo “Goolu” (o nome dado pelo criador do jogo Simon Phipps aos indígenas), que vai ser atingido pela bala (também sprite) que se dirige contra ele, bem como o “Goolu” acima.

Tudo o que se move no ecrã normalmente costumam ser sprites, que acabam por ser imagens que fazemos andar pelo ecrã, com as devidas animações, que dão a vida ao jogo, seja através dos bonecos inimigos, balas, armas, animações por vezes como fogo, ou até o boneco do jogo.

Sprite Sheets:

Da mesma maneira que as imagens fixas de um jogo (os tiles), se guardam em “tile sheets”, as imagens móveis de um jogo, organizam-se em “sprite sheets”, que o jogo lê ao arrancar, para construir cada nível, e podem ver abaixo as imagens acima mencionadas destacadas no exemplo abaixo:

Game Development Tutorial - Sprites and Tiles - 15 - Example of Sprites on Rick Dangerous videogame
Game Development Tutorial – Sprites and Tiles – 15 – Example of Sprites on Rick Dangerous videogame

Vamos ver a Sprite Sheet real do jogo Rick Dangerous na sua versão para PC (MS-DOS) dos anos 90:

Rick Dangerous PC Version Sprite Sheet
Rick Dangerous PC Version Sprite Sheet

Com a Sprite Sheet acima, poderíamos popular o jogo com todo o tipo de inimigos, dos diversos níveis, sejam os indígenas do 1º nível do Rick Dangerous, os Egípcios do 2º, e construir as animações tanto do jogador como dos inimigos, as animações das explosões, a animação do jogador ao perder, etc…

Os tipos de imagens usados num videojogo:

Podemos resumir assim as imagens principais num videojogo de 2D, em Sprites e Tiles, sendo que podemos ter também Blocos compostos por vários Tiles, se quisermos poupar ainda mais memória:

Game Development Tutorial - Sprites and Tiles - 16 - Types of Images used on VideoGames
Game Development Tutorial – Sprites and Tiles – 16 – Types of Images used on VideoGames

A edição dos Sprites:

Estes sprites abaixo são editados num programa de desenho como o The Gimp (estilo do famoso Adobe Photoshop mas Open Source, que uso Linux no dia-a-dia enquanto crio o meu Game Engine), e podemos ver como numa altura de Natal até adicionei os barretes de Pai Natal aos sprites do jogador, que explica porque é que os vídeos de demonstração do Game Engine têm personagens com barretes do Pai Natal:

2023-10-30 - Editando a Sprite Sheet do jogador no nível de testes de plataformas do meu Game Engine...
2023-10-30 – Editando a Sprite Sheet do jogador no nível de testes de plataformas do meu Game Engine…

Por norma dividimos a Sprite Sheet em colunas e linhas, neste caso em 30 de largura por 50 de altura, que é o tamanho dos sprites, e o game engine tratará depois da animação dos mesmos.

Temos depois de dizer ao Game Engine que as imagens do jogador se encontram num ficheiro, por exemplo o ficheiro “player.png”, e que se divide em linhas de 50 píxeis de altura e cada uma dividida em colunas de 30 píxeis de largura (no exemplo acima), e a que linha corresponde cada animação.

Na imagem acima podemos ver que na primeira linha visível se encontra o jogador a respirar, parado, enquanto que na segunda linha temos as imagens da animação do jogador a correr.

Na imagem acima podemos ver também que a animação do boneco a respirar tem 3 imagens, e que a do boneco a correr tem 4 imagens.

Após tudo isso, o Game Engine ao arrancar, irá ler as imagens, para saber que imagens usar em cada animação, e assim, se colocarmos o jogador a correr, o Game Engine irá buscar as imagens correctas e criar a animação no ecrã, durante o jogo.

Cores de fundo nas Sprite Sheets

É importante referir que nem sempre se usa o transparente como cor de fundo nas sprite sheets, essa escolha foi minha no exemplo acima.

Por vezes os programadores usam outras cores:

As cores de fundo das sprite sheets, na criação de VideoJogos...
As cores de fundo das sprite sheets, na criação de VideoJogos…

O que acontece, é que basta que escolhamos uma cor específica que saibamos que não coincide com nenhuma cor usada em todas as imagens do jogo, e podemos durante o jogo transformá-la em píxeis vazios (invisíveis).

Eu prefiro ter as imagens sempre transparentes à partida, mas podem ver que muitos adoptam outras cores, e claro que nessas spritesheets com diferentes cores, temos de escolher a cor com a ferramenta de varinha mágica das aplicações de desenho, e apagá-la, transformando-as em píxeis vazios, para que o meu game engine as possa colocar bem no jogo.

Isto é demonstrado também no vídeo anexo.

As configurações dos sprites:

Os sprites são depois configurados nos ficheiros de configuração do jogo, ou no meu caso, do meu Game Engine, para que ele os saiba ler e como usar.

Abaixo podemos ver um ficheiro de configuração de sprites do meu Game Engine em 2023-12-31 cujos nomes de variáveis provavelmente se alterão posteriormente pelo que não se devem basear nesta imagem mas sim no vídeo e instruções noutra secção quando quiserem criar o vosso jogo com o meu Game Engine, mas vejamos a seguinte imagem:

Configuração dos sprites no meu Game Engine em 2023-12-31…

A configuração acima usa a linguagem que escolhi para a configuração de sprites no meu Game Engine, a XML (de “eXtensible Markup Language”), muito semelhante ao HTML e fácil de usar, em que envolvemos os valores por tags com os nomes das variáveis.

Reparem que nela podemos ver que a animação de meu nome interno “player-quietBreathing” é dita começar no píxel Y 0 (píxel vertical 0), indicando ter 3 frames (“numFrames=3”), ou seja, três imagens, com 30 píxeis de largura por 50 de altura, e podem ver que a animação correspondente ao jogador a correr (de nome “player-running”), é indicada começar no píxel vcertical 50 (“posY=50”) e não 0 como a anterior, por estar verticalmente logo a seguir à anterior que tinha 50 píxeis de altura. Ela tem também o x como 0 (“posX=0”), porque tanto ela como a outra começam no primeiro píxel da imagem.

Esta outra animação tem um “numFrames” de 4, indicando ter 4 imagens, vamos conferir se é verdade:

2023-10-30 - Editando a Sprite Sheet do jogador no nível de testes de plataformas do meu Game Engine...
2023-10-30 – Editando a Sprite Sheet do jogador no nível de testes de plataformas do meu Game Engine…

Como podemos ver pela imagem acima, bate tudo certo, não é verdade?

A animação do respirar acima, começando com X e Y como 0, com 3 imagens, e a de baixo com 4 imagens, começando no Y=50 (por o X terem 50 píxeis de altura e ser a segunda), e no X=0 também.

E podem ver que batem certo com a altura de 50 píxeis e largura de 30 píxeis.

E notem que têm ambas o ficheiro onde se podem encontrar os tais frames (imagens individuais que compõem as animações), de cada sprite.

Alterando a configuração dos sprites:

Se alterarem o nome da animação, dando o nome “player-running” à animação “player-quietBreathing”, e vice-versa, irão ver que o jogador passará a ter a animação de “dying” (morrendo, quando se transforma num esqueleto), quando corre, e a animação de correr, quando morre:

2023-12-31 – Exemplo do meu Game Engine com os sprites mal configurados, a morrer quando corre, e vice-versa…

E é assim que tudo funciona, terão a oportunidade de experimentar depois com o meu Game Engine.

A duração das animações, e tempo entre cada imagem:

Existem muitas outras configurações, as quais iremos falar mais tarde.

Mas uma delas é a da duração em milissegundos de cada frame de um sprite, ou seja, o tempo que cada frame vai ter para estar no ecrã-

Por exemplo, a animação “player-dying”, tem na sua configuração que cada frame/imagem estaria no ecrã por 150 ms, ou seja, praticamente um sexto de segundo, sendo que como tem 9 imagens/sprites, se alterarmos o tempo para 15 ms, passaremos a ter a animação a decorrer num total não de 150*9=1.350ms (ou seja 1,35 segundos), mas sim de 15*9=135 ms, ou seja, a animação decorreria em perto de um sétimo de segundo.

Vejamos como ficaria:

2023-12-31 – O meu Game Engine com o sprite do jogador configurado com animação de “dying” 10 vezes mais rápida…

Por isso é fácil de configurar, se queremos que uma animação, que tenha 10 imagens, leve um total de 1 segundo a acontecer, teremos de atribuir um tempo de 100 ms (milissegundos) a cada uma das imagens que compõem essa animação, e ela levará os 10 segundos a acontecer.

Existem tiles animados?

Sim! Por estranho que pareça, apesar de um tile (azulejo), estar fixo no fundo do ecrã, ele poderá conter animações.

Recomendo espreitarem o minuto 12:00 do vídeo que criei abaixo do R-Type, famoso jogo de arcada dos anos 80, para verem que este jogo está todo ele cheio de tiles animados nos ecrãs, o que dá outra vida e realismo a tudo:

Os sprites podem ser rodados?

Sim, é frequente termos de rodar imagens, pois faz mais sentido hoje em dia termos uma imagem que possamos rodar consoante nos dá jeito, do que estar a criar 360 cópias da mesma imagem e guardar tudo em memória, com uma imagem por cada ângulo de inclinação do objecto.

Vejam o exemplo abaixo retirado do meu Gamem Engine e reparem como a nave se inclina para a frente, sempre que acelera (sempre que se vê a nave a andar para a frente), e como ela faz o inverso ao andar para trás (reduzir velocidade):

2023-12-23 – As minhas armas de raios já a criar danos no nível de testes de Shoot’Em Ups do meu Game Engine em C++…

Distinguir sprites de animações geradas em tempo real:

Por exemplo, muitos jogos têm o fogo desenhado com animações, e daí ficar repetitivo, mas no meu game engine, o fogo é desenhado em tempo real, mas dá para notar pela forma como se move que é algo desenhado em tempo real e não uma animação.

Neste tipo de situações, primeiro cria-se o algoritmo que gera o fogo em si:

2021-03-23 – Fogo desenhado em tempo real, para o meu Game Engine em C++…

Depois de termos um algoritmo funcional, podemos aplicá-lo ao Game Engine, e ele passará a fazer parte do nosso pequeno mundo virtual:

2021-04-01 - Terminada a funcionalidade de fogos em tempo real no meu Game Engine em C/C++...
2021-04-01 – Terminada a funcionalidade de fogos em tempo real no meu Game Engine em C/C++…

No jogo talvez não se note muito, mas este tipo de algoritmos são muito realistas:

2021-04-15 - Fogo com velocidade fixa, gerado em tempo real para o meu Game Engine em C++...
2021-04-15 – Fogo com velocidade fixa, gerado em tempo real para o meu Game Engine em C++…

No exemplo acima, sprites poderiam ter sido usados e não foram, porque em tempo real temos algo mais realista e não repetitivo (atenção que acima estará repetitivo porque é uma imagem animada em repetição).

Mas há casos em que não compensaria mesmo usar, vejamos o exemplo da corda abaixo:

2022-09-28 - Sistema de cordas perfeito com Gravidade incluída, no meu game engine em C++...
2022-09-28 – Sistema de cordas perfeito com Gravidade incluída, no meu game engine em C++…

No Gif animado acima, podem ver que a personagem atira uma corda, como um Homem-Aranha ou Tarzan, e essa corda é desenhada em tempo real, e não poderia ser com sprites, porque se tentássemos inclinar uma imagem composta por essa corda, a própria corda ficaria distorcida e nunca teria também o efeito de riscos amarelos horizontais que tem agora, que quis que tivesse para ser igual ao Batman the Movie 1989 da Ocean.

Outro exemplo tem a ver com o uso de armas, que têm de ser desenhadas em tempo real.

Vejamos uma arma estilo “laser” (entre aspas pois um laser nunca se comportaria assim), criada no meu Game Engine em homenagem ao jogo R-Type dos anos 80:

2023-12-23 – As minhas armas de raios já a criar danos no nível de testes de plataformas do meu Game Engine em C++…

O exemplo acima é muito bom, porque podem ver como o raio pode ter todo o tipo de tamanhos, e pode até curvar numa parede por apenas um píxel, e seria impossível fazer isto com sprites, ou seja, com imagens de raios desenhados e curvados de 1001 maneiras o tempo todo. Não só não ficaria bem, como ocuparia mais CPU do que desenhá-los no ecrã directamente.

Outro exemplo perfeito é a armam de raios sinusoidais abaixo, onde podem ver que os raios ao embater nas paredes, andam píxel a píxel e até se sobrepõem aumentando de intensidade:

2023-12-13 - A nova arma de raio triplo com maior tamanho e mais potência...
2023-12-13 – A nova arma de raio triplo com maior tamanho e mais potência…

Ou vejam em vídeo, pois um vídeo vale por mil imagens:

2023-12-13 – Finalizadas as armas de raios de energia para o meu Game Engine em C++…

Concluindo:

Não só nem sempre compensa usar sprites ou tiles, como por vezes nem poderíamos usá-los, e é bem mais fácil identificar o que poderá ser uma imagem do que poderá ser algo desenhado em tempo real.

E os jogos 3D?

Os sprites são bidimensionais, pelo que não faz tanto sentido usar sprites em jogos 3D, onde os objectos 3D são desenhados e construídos pela junção de muitos polígonos em tempo real, daí necessitarem de placas aceleradoras poderosas para o efeito.

Contudo, há jogos 3D que usam sprites, em especial quando representam imagens bidimensionais no ecrã de jogo, mas a larga maioria não os usa.

Jogos 3D são normalmente desenhados em tempo real por placas aceleradoras criadas para o efeito e que aceleram o processo:

2021-06-16 - Game Engine já a renderizar em 3D, à pata (sem GPU ou OpenGL), em C/C++...
2021-06-16 – Game Engine já a renderizar em 3D, à pata (sem GPU ou OpenGL), em C/C++…

Acima podem ver como o meu Game Engine já renderizava objectos 3D no ecrã, com texturas e sombras.

No caso da imagem acima, não existiam sprites no ecrã, tudo era desenhado em tempo real, o que torna os jogos mais lentos, caso não possuamos placas aceleradoras e o jogo esteja preparado para isso.

E em jogos Isométricos?

Em jogos isométricos, conhecidos também por “fake 3D”, usamos tiles isométricos, que são desenhados de forma a encaixarem na perfeição num plano isométrico, o que dá um pequeno look de 3D, apesar de não o ser, já que podem identificar que as imagens lá ao longe no mapa, estão com o mesmo tamanho das que se encontram perto:

2023-10-07 – Efeitos mais giros nas vistas isométricas criadas para o meu Game Engine em C++…

Estes jogos dão um aspecto muito agradável aos jogos, e são muito úteis por exemplo em jogos de estratégia, jogos em que nos dê jeito ver todos os objectos do ecrã de jogo do mesmo tamanho.

Existem Tiles Isométricos?

Sim, claro. Os jogos isométricos não são desenhados em tempo real através de polígonos em 3D, como nos jogos 3D, eles têm imagens em 2D, que dão um aspecto de “3D” a tudo.

Quando criamos um Game Engine com funcionalidades Isométricas, normalmente criamos tiles isométricos muito básicos, como cubos:

2023-12-31 - Exemplo de blocos básicos isométricos que criei quando adicionei funcionalidades isométricas ao meu Game Engine, para criar superfícies e estruturas de teste...
2023-12-31 – Exemplo de blocos básicos isométricos que criei quando adicionei funcionalidades isométricas ao meu Game Engine, para criar superfícies e estruturas de teste…

Atenção que há outros formados do estilo isométrico mas com diferentes medidas, como o dimétrico, entre outros, mas poderão investigar mas não fará parte da matéria de momento.

Depois de criados níveis de teste, foi só testar andando dentro desses mundos com a nossa personagem.

Mas a dada altura temos de criar mundos com alguma complexidade.

No meu caso, não fui desenhar tiles do zero, fiz o que muitos fazem, procurei por Isometric Tile Sheets no Google, e escolhi um dos gratuitos que são fornecidos por pessoas que gostam de contribuir para o mundo dos videojogos, e usei:

2023-12-31 - Uma tile sheet Isométrica gratuita que saquei na Internet para testar com o meu Game Engine...
2023-12-31 – Uma tile sheet Isométrica gratuita que saquei na Internet para testar com o meu Game Engine…

Se virem bem, são os tiles usados no vídeo que partilhei antes.

Sprites Isométricos:

Da mesma maneira que usamos Tiles Isométricos, usamos também Sprites Isométricos para os objectos que se movem no ecrã, como o nosso jogador, ou inimigos.

Abaixo podem ver um dos que saquei gratuitos para testar, sendo a sprite sheet de um logo, nas suas várias posições e animações, pronto a funcionar num mundo isométrico:

2023-12-31 - Uma sprite sheet de um lobo Isométrica gratuita que saquei na Internet para testar com o meu Game Engine...
2023-12-31 – Uma sprite sheet de um lobo Isométrica gratuita que saquei na Internet para testar com o meu Game Engine…

Como distinguir um jogo Isométrico de um jogo 3D?

A maneira mais simples é reparar que as imagens que estão mais longe estarão sempre do mesmo tamanho das que estão mais próximas, espreitem as imagens no topo e no fundo do mapa abaixo:

2023-10-06 – Vistas diferentes Isométricas, e efeitos vários como ondas, no meu Game Engine em C++…

Escolhi o exemplo que criei acima mesmo de propósito, pois foi o primeiro mapa que criei na altura para testar, mas vejam como por muito distantes que estejam os tiles, são sempre do mesmo tamanho, daí serem tão úteis em jogos de estratégia.

Outra maneira de ver, é a de que estarão também sempre com o mesmo ponto de vista, porque como não são tiles em 3D, não os podemos rodar, pois iríamos ver que não teriam nada atrás, e por isso abaixo podem-me ver a rodar um mundo isométrico e ver que os tiles têm de estar na mesma posição, para que não desapareçam:

2023-10-10 – Funcionalidade de rotação do mapa, resizing e inclinação no estilo Isométrico do meu Game Engine em C++…

O meu Game Engine tem apenas um nível de testes Isométrico criado com imagens gratuitas, e estará assim longe de vos mostrar o seu potencial, pelo que deixo aqui um exemplo de como pode ser maravilhoso visualmente um jogo isométrico.

Deixo-vos o remake do antigo jogo Isométrico Espanhol “La Abadia del Crimen”, de 1987 (mas com o remake criado uns 30 anos depois), baseado no filme “O Nome da Rosa”:

O Remake de um dos mais famosos jogos Espanhóis da História, baseado no filme “O Nome da Rosa”

Deixo um exemplo em jogos de estratégia, reparem como os bonecos estão sempre do mesmo tamanho, quer estejam perto ou longe:

Warcraft 3 Reforged como exemplo das vantagens de vistas isométricas…

Conclusão:

Nesta secção, aprendemos a identificar tiles, sejam eles animados ou não, sejam eles de colisão ou de fundo, e distingui-los de sprites, bem como ver as diferenças entre tiles em jogos 2D e jogos Isométricos (ou dimétricos ou outros), e aprender que em jogos 3D não costumam ser usados.

Aprendemos também que podemos ir buscar alguns gratuitos à Internet para testar, ou usar tiles de jogos antigos como uso para experimentar se for o caso, ou desenhá-los nós mesmos, e que terão de ser bem configurados se queremos que o jogo corra na perfeição, pois aprendemos que temos de definir qual o conjunto de imagens certo para cada animação, quanto tempo cada imagem deverá estar no ecrã para a animação ser perfeita, etc.

Vídeo associado a este post:

Leave a Reply

Your email address will not be published. Required fields are marked *

RSS
Follow by Email
LinkedIn
LinkedIn
Share