Curso de Jogos em Basic
Criação do cenário
Você está em: MarMSX >> Cursos >> Jogos em Basic
O cenário é onde a história do jogo irá se desenvolver. É nele que o desenvolvedor irá contar a sua história e fazer com que o jogador mergulhe em seu "mundo imaginário".
Para o MSX 1, podemos criar o cenário tanto na screen 1 como na screen 2, utilizando diferentes formas de desenhar. Depende das habilidades do desenvolvedor e seus objetivos escolher a forma mais apropriada.
2.1. Formas de criar o cenário
Há diversas maneiras de se desenhar um cenário na tela do computador. Dentre essas maneiras, podemos destacar:
- Utilizar as formas geométricas como linhas e círculos como base.
- Criar a tela em um editor gráfico.
- Utilizar o recurso de "tiles".
Não há um método que seja o correto ou o mais certo. A escolha do método depende dos gostos, objetivos e habilidades do desenvolvedor.
2.1.1. Formas geométricas
Em alguns jogos mais simples, o desenvolvedor prefere desenhar o cenário utilizando as formas geométricas disponíveis na linguagem Basic, como os comandos LINE, CIRCLE e DRAW.
✓ Desenhar dessa forma garante uma maior liberdade de criação. Podemos ver exemplos de desenhos fantásticos criados dessa maneira.
✗ Entretanto, desenhar as formas geométricas utilizando um processador lento como o Z80 leva um bom tempo, mesmo em linguagem de máquina. Por exemplo, o desenho da figura anterior leva alguns minutos para ser completado. Assim, se o cenário for muito complexo, levará um bom tempo para ficar pronto, causando impaciência por parte do jogador.
Exemplos de jogos em Basic que desenham o cenário dessa maneira:
O exemplo a seguir desenha um dos cenários do jogo Guie o Balão utilizando as formas geométricas disponíveis no Basic do MSX.
10 SCREEN 2
20 A$="E10F10H5G5" : ' Desenho básico da pirâmide
30 LINE(0,100)-(100,108),6,BF:LINE(111,100)-(250,108),6,BF
40 LINE(111,0)-(119,30),6,BF:LINE(111,43)-(119,100),6,BF
50 DRAW"BM84,70C6XA$;"
60 FORT=30TO200STEP20
70 DRAW"BM=T;,165C6XA$;"
80 NEXTT
90 DRAW"BM70,50C6XA$;"
100 DRAW"BM18,68C6XA$;"
110 DRAW"BM18,60C6XA$;"
120 DRAW"BM220,40C6XA$;":DRAW"BM122,50C6XA$;":DRAW"BM142,40C6XA$;"
130 DRAW"BM165,33C6XA$;"
140 X=150:Y=130
150 GOTO 150
Saída:
Detalhes de como utilizar as formas geométricas podem ser encontrados capítulo de gráficos do curso de Basic.
2.1.2. Criar tela em editor gráfico
Aqui, o cenário é criado em um editor gráfico qualquer (até no PC) e a tela é salva como um arquivo a parte.
✓ Assim como na modalidade anterior, podemos criar belíssimos cenários. O desenvolvedor necessita ter mais habilidades artísticas do que saber programar, pois dispensa a necessidade de codificações complexas para gerar os cenários.
✗ O cenário deve ser armazenado a parte. Isto não é problema hoje. Porém, o uso de disk-drives nos anos 80 era restrito, onde o armazenamento mais comum era o cassete, onde levaria de 2 a 5 minutos para carregar uma tela no formato screen 2. Mesmo utilizando disquetes, o carregamento de telas pode ser demorado, principalmente os modos de tela das novas gerações de MSX (screens 5-12). Além disso, os jogos eram publicados em revistas ou livros, no qual o código binário de uma tela era imenso para ser publicado, além de dar um trabalho árduo e complexo para digitá-lo.
Exemplo de jogo em Basic que desenha o cenário dessa maneira:
2.1.3. Tiles
Nessa modalidade, a tela é desenhada a partir de pequenos blocos, ou tiles, que são os elementos constituintes do cenário. Assim, o cenário é um mosaico formado a partir desses elementos.
No MSX 2, os tiles padrão geralmente têm 8x8 pixels. Entretanto, alguns jogos podem utilizar blocos maiores, geralmente múltiplos de 8.
Na imagem a seguir, um bloco de 8x8 pixels contém um elemento constituinte do cenário que é um tijolo.
Ao combinarmos diversos tijolos, ou seja, blocos de 8x8, podemos formar um labirinto, conforme mostra a figura a seguir.
As screens 1 e 2 são formadas por caracteres de 8x8 pixels. Assim, a construção da tela é feita através do mapeamento de cada caractere da tela apontando para o elemento constituinte desejado. Seria, por exemplo, como se construíssemos um labirinto na screen 0 com a letra "A" fazendo o papel de tijolo.
10 SCREEN 0:WIDTH40
20 PRINT "AAAAAA"
30 PRINT "A A"
40 PRINT "A AA AAA"
50 PRINT "A AA"
60 PRINT "A AAAAAA"
70 PRINT "A A"
80 PRINT "AAAAAAAA"
Como na screen 0 não podemos ter elementos de cores distintas e nem sprites, vamos utilizar as screens 1 e 2. Nesse caso, substituímos o desenho da letra "A" pelo desenho do tijolo.
✓ Essa técnica permite a construção rápida de uma infinidade de cenários a partir de elementos constituintes simples como tijolos, portas, janelas, pontes, rios etc. Em Assembly dá para construir cenários num piscar de olhos, o que permite o deslocamento do cenário e, consequentemente, a construção de cenários maiores que a tela.
✗ Essa técnica pode limitar o cenário a somente ser construído dos elementos básicos. Entretanto, essa limitação pode ser superada utilizando-se outras técnicas de desenho, inclusive sprites.
Exemplos de jogos em Basic que desenham o cenário dessa maneira:
2.2. Modos de tela do MSX
Apesar de alguns jogos terem sido desenvolvidos para a screen 0, principalmente jogos criados para outras linhas de micros e portados para o MSX, esse modo de tela possui poucos recursos. Já as screens 1 e 2 permitem o uso de elementos coloridos, bem como os sprites.
A screen 2 comporta todas as técnicas apresentadas. Entretanto, a screen 1 só permite o uso da técnica de "tiles" para a composição de cenários, uma vez que essa tela é baseada totalmente em caracteres. Assim, nas seções seguintes iremos focar nessa técnica.
O leitor que não estiver familiarizado com os conceitos das tabelas de nomes, caracteres e cores do MSX 1 pode ler o capítulo 15 do curso de Basic antes de prosseguir.
2.2.1. Screen 1
Alguns bons jogos foram desenvolvidos na Holanda utilizando a screen 1 como pano de fundo, uma vez que esse modo de tela é capaz de utilizar sprites.
Devido à capacidade desse modo de tela mudar a cor de frente e fundo para grupos de 8 caracteres (vide este artigo, seção "tabela de cores"), os desenvolvedores de jogos modificavam a tabela ASCII, de forma a conter os tiles do cenário.
Os agrupamentos de caracteres podem ser vistos na figura a seguir.
Observe que quatro cores destacam os diferentes agrupamentos de 8 caracteres consecutivos. Por exemplo, um grupo é composto dos caracteres "pqrstuvw" (linha 7, colunas 0-7).
Na screen 1, temos a limitação de 2 cores para cada bloco de 8x8. A tabela de cores começa na posição 8192 da VRAM e possui 32 posições.
Os bits mais significativos de cada posição da tabela controlam a cor do caractere, enquanto os demais controlam a cor de fundo.
No exemplo a seguir iremos modificar a cor de frente e fundo dos caracteres "pqrstuvw". A cor de frente é o verde 2 (&h2) e cor de fundo magenta 13 (&hD). Esse grupo está 14 posições à frente do primeiro grupo.
10 SCREEN 1
20 VPOKE 8192+14,&H2D
30 PRINT""nopqrstuvwxyz"
Saída:
Assim como na screen 0, cada caractere possui 8 linhas para a definição do seu desenho dentro do bloco de 8x8 linhas. O bit 1 indica a cor de frente, enquanto que o bit 0 indica a cor de fundo.
Podemos alterar o caractere "p" para ter outro desenho. A tabela de caracteres da screen 1 começa na posição 0 da VRAM e o caractere "p" possui código ASCII 112. Assim, a posição dele na tabela é 112 x 8 = 896.
10 VPOKE 896,&B01111110
20 VPOKE 897,&B10000001
30 VPOKE 898,&B10011110
40 VPOKE 899,&B10100000
50 VPOKE 900,&B10100000
60 VPOKE 901,&B10100000
70 VPOKE 902,&B10100000
80 VPOKE 903,&B10100000
Saída:
Para construir o cenário, devemos reservar uma parte da tabela ASCII para desenhar os tiles. Lembre-se que eles estão limitados a duas cores em todo o bloco 8x8.
O jogo Break-man constrói o labirinto em cima dos caracteres de 128-143, uma área que não afeta as letras, números e símbolos de pontuação.
O código a seguir define os caracteres de 128 a 143 para os tiles que irão ser a base do desenho do labirinto.
10 SCREEN 1
20 FOR X=128 TO 143
30 FOR I=0 TO 7
40 READA$
50 VPOKE X*8+I,VAL("&H"+A$)
60 NEXT I,X
70 FOR X=128 TO 143
80 PRINT CHR$(X);
90 NEXT X
500 ' Tiles do labirinto (total=16)
510 DATA 00,00,18,24,24,18,00,00
520 DATA 24,24,24,24,24,18,00,00
530 DATA 00,00,1F,20,20,1F,00,00
540 DATA 24,24,23,20,20,1F,00,00
550 DATA 00,00,18,24,24,24,24,24
560 DATA 24,24,24,24,24,24,24,24
570 DATA 00,00,1F,20,20,23,24,24
580 DATA 24,24,23,20,20,23,24,24
590 DATA 00,00,F8,04,04,F8,00,00
600 DATA 24,24,C4,04,04,F8,00,00
610 DATA 00,00,FF,00,00,FF,00,00
620 DATA 24,24,C3,00,00,FF,00,00
630 DATA 00,00,F8,04,04,C4,24,24
640 DATA 24,24,C4,04,04,C4,24,24
650 DATA 00,00,FF,00,00,C3,24,24
660 DATA 24,24,C3,00,00,C3,24,24
Saída:
Que representa todos os tiles utilizados para construir o labirinto do jogo.
Uma vez pronta a tabela de caracteres, podemos utilizar os comandos VPOKE ou PRINT para desenhar o labirinto. Mas antes, vamos mudar a cor do elemento para verde 2 e preto de fundo (&h2 e &h0 = &h20 = 32). O caractere 128 começa no grupo 16 (128/8) e vai até o grupo 17 (143/8).
100 VPOKE 8192+16,32:VPOKE 8192+17,32
A seguir, o mapa do labirinto. Cada caractere corresponde a um tile da figura acima. Entretanto, há alguns elementos adicionais que não criamos nesse exemplo e que serão ignorados pelo programa.
O autor criou o mapa pela metade e o espelhou no eixo X.
1000 ' Mapa do labirinto (14 x 21)
1010 DATA gkkkkkkkkkkkok
1020 DATA fAQQQQQQQQQQdk
1030 DATA fQgkkkmQeQeQQQ
1040 DATA fQfQQQbQfQdkko
1050 DATA fQfQeQQQfQQQQf
1060 DATA fQbQdkkkpkkiQb
1070 DATA fQQQQQQQfQQQQQ
1080 DATA hkkiQgmQbQckkk
1090 DATA fQQQQdjQQQQQQQ
1100 DATA fQgiQQQQckkkiQ
1110 DATA fQfQQgmQQQQQQQ
1120 DATA fQfQcljQeQeQgk
1130 DATA fQbQQQQQbQbQbQ
1140 DATA fQQQeQQQQQQQQQ
1150 DATA hkiQfQckoiQciQ
1160 DATA fQQQfQQQfQQQQQ
1170 DATA fQcklkiQfQgkkk
1180 DATA fQQQQQQQfQfQQQ
1190 DATA fQckkkiQfQdkkk
1200 DATA fQQQQQQQfQQQQQ
1210 DATA dkkkkkkklkkkkk
O autor deslocou 31 posições para trás na tabela ASCII, para poder utilizar os caracteres comuns em vez dos especiais.
O código a seguir desenha o labirinto.
200 ' Imprime labirinto
210 FOR Y=0 TO 20:READA$
220 FOR X=1 TO 14
230 A=ASC(MID$(A$,X))+31
240 IF A>=128 THEN LOCATE X,Y,0:PRINT CHR$(A);
250 IF(A AND 10)<>10 THEN IF (A AND 2) THEN A=(A AND 253 OR 8) ELSE IF (A AND 8) THEN A=(A AND 247 OR 2)
260 IF A>=128 THEN LOCATE 28-X,Y,0:PRINTCHR$(A);
270 NEXT X,Y
Na linha 250, o autor faz a alteração de alguns tiles para imprimir corretamente na versão espelhada (linha 260).
Junte os 4 últimos códigos, removendo as linhas 70-90 e rode. O resultado pode ser visto na figura a seguir.
Nesse exemplo o autor desenha o cenário caractere a caractere. Entretanto, o resultado pode ser mais rápido se escrevermos uma linha ou até mais caracteres por vez. É o que os jogos Pucky e Q-Bert fazem.
Rode o teste a seguir e compare os tempos para escrever um caractere por vez, uma linha e tudo junto.
10 SCREEN 0
20 TIME=0
30 FOR I=1 TO 100
40 C=RND(-TIME)*30+40:PRINT CHR$(C);
50 NEXT I
60 T=TIME
70 PRINT:PRINT"Tempo para escrever um caractere por vez: ";T/60;"segundos."
80 TIME=0
90 PRINT"ABCDEFGHIJKLMNOPQESTUVWXYZ!()/"
100 PRINT"ABCDEFGHIJKLMNOPQESTUVWXYZ!()/"
110 PRINT"ABCDEFGHIJKLMNOPQESTUVWXYZ!()/"
120 PRINT"ABCDEFGHIJ"
130 T=TIME
140 PRINT"Tempo para escrever uma linha por vez: ";T/60;"segundos."
150 TIME=0
160 PRINT"ABCDEFGHIJKLMNOPQESTUVWXYZ!()/ABCDEFGHIJKLMNOPQESTUVWXYZ!()/ABCDEFGHIJKLMNOPQESTUVWXYZ!()/ABCDEFGHIJ"
170 T=TIME
180 PRINT"Tempo para escrever tudo de uma vez: ";T/60;"segundos."
Saída:
Tempo para escrever um caractere por vez: 1.98 segundos.
Tempo para escrever uma linha por vez: 0.05 segundos.
Tempo para escrever tudo de uma vez: 0.03 segundos.
2.2.2. Screen 2
Na screen 2, o processo é semelhante, Entretanto, temos alguns pontos a considerar:
- Podemos controlar 2 cores a cada grupo de 8x1 pixels em vez de 8x8.
- Podemos somente utilizar o VPOKE para construir o cenário.
- A tabela de nomes é dividida em três partes. Assim, devemos repetir os tiles nas três partes da tela.
Para exemplificar a construção de um cenário na screen 2, vamos nos basear no jogo Alcatraz.
Observe os desenhos dos tiles desse jogo. A qualidade melhora sensivelmente, em relação à screen 1.
O autor do jogo foi obrigado a repetir os tiles nas três partes da tela, sob pena de conseguir desenhar apenas na terça parte superior da tela. Cada terço da tela possui um valor que varia de 0 a 255 na tabela de nomes, que corresponde ao código do tile desejado.
Feitas essas cópias, basta utilizar o código correspondente ao tile em qualquer região da tela.
A seguir, vamos criar um exemplo simples de mapeamento na screen 2, onde utilizamos três tiles do jogo Alcatraz para formar o nome MSX.
10 COLOR 15,0,0:SCREEN 2
20 GOSUB 500: GOSUB 600
30 GOTO 30
500 ' Cria tiles
510 FOR I=1 TO 3 : FOR C=0 TO 7
520 READ A$ : VPOKE I*8+C,VAL("&H"+A$)
530 NEXT C,I
540 FOR I=1 TO 3 : FOR C=0 TO 7
550 READ A$ : VPOKE 8192+I*8+C,VAL("&H"+A$)
560 NEXT C,I
570 RETURN
600 ' Desenha a tela
610 E=6144
620 FOR L=1 TO 8
630 READ LN$
640 FOR C=1 TO 32
650 VPOKE E,VAL(MID$(LN$,C,1))
660 E=E+1:NEXT C,L
670 RETURN
1000 ' Tiles - Padrão
1010 DATA 01,01,01,FF,10,10,10,FF : ' Tijolo=1
1020 DATA 08,28,3A,7A,7E,7E,3E,1C : ' Fogo=2
1030 DATA 06,0E,1F,7D,DF,F6,BB,EF : ' Terra=3
1100 ' Tiles - Cores
1110 DATA E6,E6,E6,E6,E6,E6,E6,E6 : ' Tijolo=1
1120 DATA 60,60,60,80,80,80,90,90 : ' Fogo=2
1130 DATA 90,90,90,90,91,91,91,91 : ' Terra=3
2000 ' Mapa
2010 DATA 01000100022200030003000000000000
2020 DATA 01101100200020030003000000000000
2030 DATA 01010100200000003030000000000000
2040 DATA 01010100022200000300000000000000
2050 DATA 01000100000020003030000000000000
2060 DATA 01000100200020030003000000000000
2070 DATA 01000100022200030003000000000000
2080 DATA 00000000000000000000000000000000
Saída:
O mapeamento é feito linha a linha, onde o valor numérico indica o código do tile. O valor zero é um espaço em branco.
Há uma rotina pronta para testar que imprime todo o labirinto do jogo Alcatraz. O resultado pode ser visto a seguir.
Entretanto, o mapeamento desse jogo é feito de maneira diferente do apresentado até aqui. Em vez de considerar a posição absoluta dos elementos do cenário, ele toma como base a posição do jogador em um determinado local. Assim, para cada posição do personagem, o mapa contém a informação de desenho dos blocos ao norte, sul, leste e oeste.
Maiores detalhes de como é feito esse mapeamento pode ser conferido aqui.