Como Fazer um Jogo Online 3d

terça-feira, 3 de agosto de 2010

GM3D 01 - Primeiros passos


Estou dando inicio as minhas aulas de 3D no Game Maker. Happy

Antes de começar

Lembrem-se que para estudar esse assunto é preciso saber o suficiente sobre jogos 2D então essas aulas são para usuários mais avançados pois na maioria das vezes não irei explicar os míninos detalhes.

O que pode ser feito em 3D no Game Maker




Você pensa tão alto como o Cérebro?
Então é melhor dar uma calmada e pensar melhor!

O 3D no Game Maker ainda é bem limitado e falta muitas ferramentas que por padrão deveriam estar disponíveis. Embora tudo isso possa ser contornado com uso de DLLs e scripts que tem aos montes feitos pelos próprios usuários do programa, você precisara sempre pesquisar para expandir o que pode ser feito em seu jogo.

O computador do programador deve ter pelo menos uma boa placa de vídeo pois o uso de muitas texturas de tamanho grande podem não funcionar causando o famoso erro “Unexpected error occurred when running the game” que na maioria das vezes é causado porque o jogo está usando um recurso da placa de vídeo que não existe ou usando mais memória do computador do que ele realmente tem.

Considerando também que não vamos fazer uma obra de arte que custará milhões e centenas de pessoas para ser feita em três anos... É como dizem: “O limite e a criatividade do criador.”

A terceira dimensão!

Existem muitos tipos de conceitos para explicar o 3D, mas falarei apenas do mais usado.

Você já conhece as duas primeiras dimensões não é? X e Y.
A visão da tela apenas com o X e Y aparenta estar de pé, onde X vai para os lados e Y para cima ou para baixo.



Mas ao inserir a terceira dimensão percebi-se que na verdade a visão esta deitada, pois Z representa a profundidade.



Quanto maior o Z mais alto o ponto vai estar e quanto menor mais baixo ou profundo o ponto.

A terceira dimensão não tem nada de complicado. Basta lembrar que ela é a profundidade, volume ou altura enquanto as outras duas são a largura e o comprimento.

Construindo uma Engine

Agora vamos iniciar uma engine. A cada aula diferente você ira completá-la cada vez mais até que o resultado final seja algo parecido com um jogo de corrida.
Por enquanto vamos só aprender a desenhar um cubo na tela.
Abra o GM e vamos começar!


Todo jogo precisa de algo que controle o que o jogador irá ver.
Crie um novo objeto e nomeio como objCamera.
Agora temos que ativar o modo 3D e definir as configurações iniciais.
No evento Create do objCamera coloque:

Código:
d3d_start();

Isso inicia o modo 3D do Game Maker


Código:
d3d_set_perspective(true);

Ativa a visualização em perspectiva. Isso realça a noção de profundidade dos objetos.


Código:
d3d_set_hidden(true);

Esse é muito útil pois fará com que todos as superfícies desenhadas atrás de outro objeto não apareçam.


Código:
d3d_set_fog(true,c_white,1,1200);

Fog é uma espécie de neblina. Todo que estiver a 1200 pixels da visão do jogador será escondido por ela.


Código:
d3d_set_culling(false);

O culling faz a parte de dentro dos objetos, ou seja, o lado de trás das textura não serem desenhados. Se a câmera olhar de dentro de um cubo, as paredes do cubo ficam invisíveis.


Código:
d3d_set_shading(false);

Shading significa sombreamento. Ele melhora a qualidade do efeito da luz nos objetos mas como não usar iluminação nesse exemplo deixe-o como false.


Código:
texture_set_interpolation(true);

Melhora a qualidade das texturas.


Código:
draw_set_color(c_white)

Defina a cor de desenho como branco (c_white).


Código:
z = 48

Todos os objetos tem uma variável x e y por padrão. Mas o z terá sempre que ser criado manualmente. Defina como 48 porque a câmera ficara um pouco acima do chão.

Agora é preciso definir como o jogo vai ser visualizado.
No evento Draw digite:

Código:
d3d_set_projection(x,y,z,obj1.x,obj1.y,obj1.z,0,0,1);

Vou explicar essa função de um jeito mais completo na próxima aula pois o objetivo aqui é apenas ensinar a desenhar um cubo na tela.

Agora crie um novo background. Ele vai ser a textura do objeto.
Desenhe qualquer coisa. Servira apenas para visualizar melhor os lados do cubo.
Eu fiz este:



Agora crie outro objeto. Ele será o cubo. Nomeio como obj1.
No evento Create defina:

Código:
z = 0;

A variável z.


Código:
tex = background_get_texture(background0);

Essa função retorna o lugar na memória do computador onde a textura do background0 está armazenada.

Agora no evento Draw:

Código:
d3d_draw_block(x-16,y-16,z-16,x+16,y+16,z+16,tex,1,1)

Com isso desenho cada lado com 32 pixels, defino a textura dele e os dois últimos valores são quantas vezes a textura vai se repetir nas paredes do cubo.

Pronto! Coloque o jogo pra rodar e veja o resultado.
Se algo saiu errado você pode baixar a engine desta aula clicando aqui.


GM3D 02 – Projeção e Movimento da Câmera



Assim como os olhos dos seres vivos projetam o ambiente para o cérebro, o jogo também deve ter algo que projete o espaço virtual ao jogador.
Nessa aula vou explicar como criar uma câmera que se movimenta com o mouse.
Essa parte é complicada tanto para aprender quanto para explicar.
Fiquem atentos.
A função d3d_set_projection_ext


Existem duas funções que usamos para definir como o mundo será visto:

Código:
d3d_set_projection(xfrom,yfrom,zfrom,xto,yto,zto,xup,yup,zup); d3d_set_projection_ext(xfrom,yfrom,zfrom,xto,yto,zto,xup,yup,zup,angle,aspect,znear,zfar);


A primeira á a forma simples e a segunda é a forma estendida da função. Iremos usar apenas a segunda.

Fazer com que entendam cada argumento será bem complicado, mas eu gosto que tudo seja explicado então prestem bastante atenção e façam testes com cada parâmetro..


Código:
d3d_set_projection_ext(xfrom,yfrom,zfrom,xto,yto,zto,xup,yup,zup,angle,aspect,znear,zfar)

xfrom,yfrom,zfrom,xto,yto,zto – from significa “de” e to “para”. Os três primeiros argumentos são os pontos da câmera, ou seja, o lugar de onde se olha e os três últimos o lugar para onde esta se olhando. Por exemplo:
Você esta no quarto olhando para o computador. A posição dos seus olhos representam “xfrom,yfrom,zfrom” e a posição do computador representa “xto,yto,zto”.

xup,yup,zup – esses argumentos são definidos apenas como false ou true. Neles você especifica o eixo em que a projeção gira. Exemplo:
Você ainda está olhando para o computador.
Levante ou abaixe a cabeça. Pronto! Você acabou de girar a visão em torno do eixo y.
Agora mexa a cabeça para direita ou para a esquerda. Pronto! Você acabou de girar a visão em torno do eixo z.
Girar em torno do eixo x seria como rodar a cabeça em torno de si mesma olhando sempre pra fentre.

Marque como true os eixos que a câmera vai gira.
O padrão usado é “0,0,1”, ou seja, ira girar em torno do eixo z.

angle – este não tem nada a ver com o ângulo da visão e muita gente se confundi com ele. Aqui você deve definir a largura da câmera em graus. Exemplo:
As lentes dos óculos são medidas em graus lembra? Quanto maior o grau, mas as imagens são aumentadas.
Você já viu aquelas câmera que quanto se gira a lente o zoom aumenta ou diminui? Então! O efeito desse argumento é o mesmo.

Quanto maior o valor de angle, maior vai ser o zoom “natural” da projeção.
Um padrão razoável é 45° mas eu estou usando 90° em meus projetos.

aspect – defini o aspecto da tela. Não há muito o que explicar...
Se você esta usando uma dimensão de tela padrão (640x480, 1024x768...), qualquer valor sem ser a divisão desses tamanho causara distorção na imagem.
Defini-o como 640/480 ou simplesmente 1.3.

znear,zfar – de znear a zfar é a região que será desenhada pela câmera. Por exemplo:
znear é 1 e zfar é 10000. Tudo que estiver entre o pixel 1 e o pixel 10000 da projeção será desenhado. Tudo que estiver fora dessas coordenadas não será desenhando.



Nessa figura a primeira linha vertical representa o znear e a segunda linha vertical o zfar. Tudo o que há na parte azul será desenhado e tudo o que há na parte cinza não será desenhando.

O padrão é znear = 1 e zfar = 32000.


A função d3d_set_projection_ortho


Se você quer desenhar coisas bidimensionais em modo 3D?
Então terá que usar isso.
Visão ortogonal nada mais é do que a projeção das imagens ignorando a profundidade delas, ou seja, o valor z.
Seria como desenhar um modelo 3D com todas as suas coordenadas z iguais a zero. Ele ficaria achatado. Sem profundidade.
Essa função é extremamente útil, pois podemos desenhar textos e imagens na tela sem dificuldades.


Código:
d3d_set_projection_ortho(x,y,w,h,angle)


x,y,w,h – essa é a região ou tamanho da tela. Note que se for diferente do tamanho da view o que for desenhado na tela vai se esticar em relação a view.

angle – gira o que for desenhado. O ponto de origem é por padrão o meio da area que você definiu.

Para desenhar um texto na tela você pode fazer da seguinte forma:

Código:
draw_set_color(c_black); d3d_set_projection_ortho(0,0,view_width,view_height,0); d3d_set_hidden(false); draw_text(10,10,'FPS: ' + string(fps)); d3d_set_hidden(true);


Essa função deve vir sempre antes de d3d_set_projection_ext ou a cena não será desenhada direito.


Movimentando a câmera


Agora é hora de aprender como movimentar a câmera no mundo.
Vamos adicionar um exemplo de visão em terceira pessoa onde o personagem é totalmente enquadrado pela câmera no projeto da aula anterior. Depois explicarei como adaptar isso para primeira pessoa que é ver o mundo como se estiver com os olhos do personagem.

Abra a engine que começamos na aula 1. Você também pode baixar o source clicando aqui.

Crie um novo background que será a textura do chão.



Abra o obj1 e defina outra variável para a textura do chão:

Código:
tex2 = tex2 = background_get_texture(background1);


No evento Draw:

Código:
 d3d_draw_floor(0,0,0-16,room_width,room_height,0-16,tex2,5,5);


Isso vai desenhar um chão do tamanho da room e embaixo do cubo.

Agora abra o objCamera e vamos definir todos as variáveis necessários para controlar o movimento no evento Create:

Código:
 // Movimento da camera z  = 0;   // ponto z dx = 0;   // armazena movimento x da câmera dy = 0;   // armazena movimento y da câmera dz = 0;   // armazena movimento z da câmera px = 0;   // ponto x para onde se esta olhando py = 0;   // ponto y para onde se esta olhando pz = 0;   // ponto z para onde se esta olhando zoom = 400; zang = 0;   // angulo horizontal da camera yang = 0;   // angulo vertical da camera // Definir posição do mouse window_mouse_set(200,200); // Sem cursor window_set_cursor(cr_none);


Agora crie um evento Step e:
Primeiro deve-se achar os ângulos da câmera. É o movimento do mouse que vai modar os ângulos então precisamos saber o quanto e para onde ele esta se movendo.


Código:
// Definindo angulos a partir do mouse zang-=(window_mouse_get_x()-200)/8; yang-=(window_mouse_get_y()-200)/8; window_mouse_set(200,200); yang=median(yang,-89,89);


zang é igual a posição x do mouse -200 dividido por 8. Isso retorna quantos pixels o mouse se moveu em x.
yang é igual a posição y do mouse -200 dividido por 8. Isso retorna quantos pixels o mouse se moveu em y.
O valor 8 é a sensibilidade do mouse. Quanto menor mais rápido o movimento vai ficar.
A função window_mouse_set defini a posição do mouse novamente depois de já achar os ângulos.
A ultima linha de código faz com que yang nunca passe de -89 a 89, ou seja, quanto a câmera estiver em cima ou embaixo do objeto seu ângulo vertical não mudará mais.

Já temos os ângulos vertical e horizontal definidos. Agora precisamos saber o quanto os pontos x,y e z da câmera teriam que mudar para que possa girar em torno do personagem.


Código:
// Animação da camera // calculando movimento dos vetores da camera dx=cos(degtorad(zang)); dy=-sin(degtorad(zang)); dz=tan(degtorad(yang)); // normalizar vetores // torna movimento da camera totalmente esferico. m=sqrt(sqr(dx)+sqr(dy)+sqr(dz)); dx/=m; dy/=m; dz/=m;


Coseno, seno e tangente são operações matemáticas para achar pontos de acordo com um ângulo.
Convertemos os graus em radianos com a função degtorad(ang).
cos retorna quantos pixels o x terá que se mover para estar naquele ângulo.
sin retorna quantos pixels o y terá que se mover para estar no mesmo ângulo que x (ângulo vertical).
E tan retorna quantos pixels o z terá que se mover para estar de acordo com o ângulo vertical.
Por ultimo em tenho que normalizar dx, dy e dz para que o movimento gire de forma perfeitamente esférica em torno do personagem.
Não tem muito o que explicar disso. Aquilo é uma operação para não deixar que o zoom mude automaticamente dependendo do ângulo.

A parte complicada já foi. Happy
Agora é só definir as novas coordenadas da câmera de acordo com dx, dy e dz.


Código:
// Calculando posição da camera no mundo x = obj1.x+dx*zoom; y = obj1.y+dy*zoom; z = obj1.z+dz*zoom; // Zoom if keyboard_check(vk_add) zoom += 4; if keyboard_check(vk_subtract) zoom -= 4;


Ao multiplicar os valores que acho com cos, sin e tan eu estou adicionando uma distancia ao movimento. Essa distancia é o zoom que vale 400 pixels. A câmera ficará a 400 pixels do personagem.
Adicionei também algo para aumentar e diminuir o zoom apertando “+” ou “-“ no teclado.

Finalize o código do evento Step definindo o lugar para onde o câmera vai olhar que no caso é a posição do obj1.

Código:
// Movimento no personagem px = obj1.x; py = obj1.y; pz = obj1.z;


Abra o evento Draw do objCamera, apague o código lá e defina uma nova projeção:


Código:
d3d_set_projection_ext(x,y,z,px,py,pz,0,0,1,90,1.3,1,32000);


Adicione um movimento simples para o obj1. Crie um evento Step nele:

Código:
if keyboard_check(ord('W')) y -= 4; if keyboard_check(ord('S')) y += 4; if keyboard_check(ord('A')) x -= 4; if keyboard_check(ord('D')) x += 4;


Pra finalizar defina a velocidade da room como 60.

Se você quiser fazer um visão em terceira pessoa é simples.
Basta inverter os pontos xfrom, yfrom, zfrom, xto, yto, zto

Código:
d3d_set_projection_ext(px,py,pz,x,y,z,0,0,1,90,1.3,1,32000);


Não se esqueça de tirar o código que desenha o cubo porque senão a visão vai ficar tampada pela parte de dentro do cubo.

Esse exemplo pronto pode ser baixado clicando aqui.
 
GM3D 03 – Usando Transformações


As transformações em D3D são usadas para rotacionar um modelo no jogo. Há também a possibilidade de escalar o objeto aumento ou diminuindo seu tamanho mas isso não é aconselhável se você usar iluminação no jogo porque tem um bug no GM que calcula as normais (projeção da textura) erradas fazendo o modelo escurecer cada vez que é aumentado.


Transformações


Para rotacionar ou escalar um objeto você precisara usar as transformação D3D. Todas as suas funções começam com a expressão “d3d_transform_”.

Primeiro você precisa definir o que será transformado senão os efeitos serão aplicados no mundo todo.
Para isso existe uma função que identifica o que esta sendo desenhado e aplica os efeitos apenas neles. Ela é a “d3d_transform_set_identity()”.

Outra coisa importante é que as transformação são aplicadas usando o ponto de origem da room (que é 0,0,0 para x,y,z) e não o do objeto, ou seja, quanto você rodar um modelo ele vai girar em torno do ponto 0 da room e não em torno de si mesmo (como a Terra girando em torno do Sol).

Para resolver isso alem de usar “d3d_transform_set_identity” você também usará a função “d3d_transform_add_translation(x,y,z);” que defini que a transformação será aplicada em torno dos argumentos x,y,z.

Veja um exemplo:

Código:
d3d_transform_set_identity(); d3d_transform_add_translation(x,y,z); d3d_draw_model(modelo,0,0,0,textura); d3d_transform_set_identity();


Explicando linha por linha:

d3d_transform_set_identity(); – identifica o que vai ser afetado. Note que há também um no final do código, isso quer dizer que todo que esta entre essas funções será alterado.

d3d_transform_add_translation(x,y,z) – defini o ponto de origem das transformações. Os modelos também serão desenhados a partir dele (é como se fossem os novos pontos x, y e z do objeto).

d3d_draw_model(modelo,0,0,0,textura) – desenha o modelo. Note que os pontos x,y,z são definidos como 0. Por que?
A resposta é simples. Quanto eu usei d3d_transform_add_translation(x,y,z) os argumentos x,y,z dele se tornaram os novos pontos de origem do modelo. Tudo que for desenhado entre os identificadores será relativo a esta nova origem.
Se o modelo fosse desenhado em 10,15,8:

Código:
d3d_transform_add_translation(x,y,z); d3d_draw_model(modelo,10,15,8,textura);

Seria o mesmo que desenhá-lo na posição x+10,y+15,z+8 na room.

d3d_transform_set_identity(); – por último outro identificador. Lembrando que tudo que estiver entre ele e o primeiro será afetado.


Uma coisa interessante é que as transformações funcionam em qualquer tipo de desenho do GM. Você também pode usá-las na hora de desenhar uma sprite, background ou texto!


Continuando a engine


Abra a engine que fez seguindo as outras aulas ou baixe por este link.

Antes de continuar com as explicações vamos resolver um bug nessa engine.
O movimenta da câmera não esta seguindo direito o movimento do personagem. Quanto nós movimentamos o objeto a câmera puxa pros lados.
Para resolver isso apague as linhas de código abaixo no evento Step e coloque-os no evento Draw antes da função de projeção no objCamera:

Código:
 // Calculando posição da camera no mundo x = obj1.x+dx*zoom; y = obj1.y+dy*zoom; z = obj1.z+dz*zoom; // Movimento no personagem px = obj1.x; py = obj1.y; pz = obj1.z;


O evento Step ficará assim:

Código:
 // Definindo angulos a partir do mouse zang-=(window_mouse_get_x()-200)/8; yang-=(window_mouse_get_y()-200)/8; window_mouse_set(200,200); yang=median(yang,-89,89); // Animação da camera // calculando movimento dos vetores da camera dx=cos(degtorad(zang)); dy=-sin(degtorad(zang)); dz=tan(degtorad(yang)); // normalizar vetores // torna movimento da camera totalmente esferico. m=sqrt(sqr(dx)+sqr(dy)+sqr(dz)); dx/=m; dy/=m; dz/=m; // Zoom if keyboard_check(vk_add) zoom += 4; if keyboard_check(vk_subtract) zoom -= 4;


E o evento Draw assim:

Código:
 // Calculando posição da camera no mundo x = obj1.x+dx*zoom; y = obj1.y+dy*zoom; z = obj1.z+dz*zoom; // Movimento no personagem px = obj1.x; py = obj1.y; pz = obj1.z; d3d_set_projection_ext(x,y,z,px,py,pz,0,0,1,90,1.3,1,32000);


Agora sim a camera está ótima.
Mude o desenho do cubo para uma forma mais achatada para dar uma impressão de ser um carro.
Troque os valores no código que desenha o cubo no evento Draw do obj1:

Código:
d3d_draw_block(-16,-32,-10,16,32,10,tex,1,1);


Agora está tudo pronto para a próxima explicação.


Rotação e Escalação


As funções seguintes podem ser usadas para rotacionar e escalar os objetos:

d3d_transform_add_rotation_x(angle) – Gira o objeto em torno do eixo x.

d3d_transform_add_rotation_y(angle) – Gira o objeto em torno do eixo y.

d3d_transform_add_rotation_z(angle) – Gira o objeto em torno do eixo z.

d3d_transform_add_scaling(xs,ys,zs) – Altera a escala nos eixos x, y e z.

Vamos fazer um teste na nossa engine para entender o funcionamento.
Crie variaveis rx, ry e rz no evento Create do obj1:

Código:
rx = 0; ry = 0; rz = 0;

Ela será o valor do ângulo das rotações.

No evento Step adicione isso para mudar os ângulos com os números do teclado numérico:


Código:
if keyboard_check(vk_numpad1) rx += 1; if keyboard_check(vk_numpad4) rx -= 1; if keyboard_check(vk_numpad2) ry += 1; if keyboard_check(vk_numpad5) ry -= 1; if keyboard_check(vk_numpad6) rz += 1; if keyboard_check(vk_numpad3) rz -= 1;


No evento Draw vamos adicionar a função de rotação dos eixos.
Elas devem estar sempre antes da função d3d_transform_add_translation.
A parte que desenha o cubo ficará assim:

Código:
d3d_transform_set_identity() d3d_transform_add_rotation_x(rx); d3d_transform_add_rotation_y(ry); d3d_transform_add_rotation_z(rz); d3d_transform_add_translation(x,y,z); d3d_draw_block(-16,-32,-10,16,32,10,tex,1,1); d3d_transform_set_identity()


Teste o jogo e aperte 1 e 4 para girar no eixo x, 2 e 5 para girar no eixo y e 3 e 6 para girar no eixo z.

Notou a diferença entre eixo x, y e z?

Bom, agora é hora de testar as escalas.
No evento Create do obj1 adicione mais variáveis para cada escala:

Código:
sx = 1; sy = 1; sz = 1;


E no Step o código para mudar as escalar:

Código:
if keyboard_check(vk_delete) sx += 0.1; if keyboard_check(vk_insert) sx -= 0.1; if keyboard_check(vk_end) sy += 0.1; if keyboard_check(vk_home) sy -= 0.1; if keyboard_check(vk_pagedown) sz += 0.1; if keyboard_check(vk_pageup) sz -= 0.1;


No evento Draw adicione a função para escalar o modelo também sempre antes de d3d_transform_add_translation.

Código:
d3d_transform_set_identity() d3d_transform_add_rotation_x(rx); d3d_transform_add_rotation_y(ry); d3d_transform_add_rotation_z(rz); d3d_transform_add_scaling(sx,sy,sz); d3d_transform_add_translation(x,y,z); d3d_draw_block(-16,-32,-10,16,32,10,tex,1,1); d3d_transform_set_identity()


Teste e use Delete e Insert para mudar o eixo x, End e Home para o eixo y, Page Up e Page Down para o eixo z.

Copie essa engine com outro nome se quiser para fazer testes mais tarde.
Agora vamos fazer nosso “carro” andar!


Movimentação


A movimentação dessa engine será da seguinte forma:
As teclas A e D giram o objeto, W e S acelera e desacelera, C muda o modo da câmera.

Apague as novas variáveis que criamos e faça apenas uma para a rotação r, y_speed para a velocidade e mude o valor z para 16.
Evento Create Completo:

Código:
z = 16; tex = background_get_texture(background0); tex2 = background_get_texture(background1); x = 96; y = 96; r = 0;        // rotação z y_speed = 10;  // velocidade


Apague todo o código no evento Step e coloque este para girar o carro para os lados:

Código:
if keyboard_check(ord('A')) r += 2; if keyboard_check(ord('D')) r -= 2;


O carro tem que andar na direção que esta virado então temos que usar cos e sin para achar x e y de acordo com o ângulo.
No evento Step adicione este código:

Código:
if keyboard_check(ord('W')) {     x += cos(degtorad(r+90))*y_speed;     y -= sin(degtorad(r+90))*y_speed; } if keyboard_check(ord('S')) {     x -= cos(degtorad(r+90))*y_speed;     y += sin(degtorad(r+90))*y_speed; }


O “r+90” ajeita o ângulo de calculo para faze-lo correr na direção certa.

Por ultimo deixe o evento Draw desta forma:

Código:
d3d_transform_set_identity() d3d_transform_add_rotation_z(r); d3d_transform_add_translation(x,y,z); d3d_draw_block(-16,-32,-10,16,32,10,tex,1,1); d3d_transform_set_identity() d3d_draw_floor(0,0,0,room_width*3,room_height*3,0,tex2,10,10);


Isso muda a rotação do eixo z e aumento o tamanho do chão.
Teste o jogo é veja o resultado!

A engine pronta pode ser baixada aqui.

0 comentários: