domingo, 22 de março de 2015

[Gamer Maker] Aula 0: aprendendo a lidar com GML!

Neste prefácio da Apostila de manuais de GML, eu irei passar um apanhadão sobre como funciona a GML e entregar á vocês noções básicas importantes que irão adiantar a sua vida.

A GML, ou Game Maker Language, é na verdade muito simples de primeira instância. Ela foi planejada para facilitar a vida de novos programadores, ao mesmo passado que adianta o trabalho de programadores experientes e da á eles liberdade ilimitada quando o assunto é 2D. O Game Maker na verdade foi foi para ser usado principalmente em seu sistema fácil de Drag'n'Drop, ou seja, você cria um jogo somente pegando e puxando ícones que executam determinadas funções e altera os valores de acordo com a sua vontade. É possível fazer um jogo perfeito e extremamente funcional sem ter a mínima noção sobre escrever códigos ou coisas do tipo, porém, é muito importante você se acostumar a criar seus jogos diretamente da GML, pois assim você não se limita e acelera em muito o seu trabalho.

Índice:
1. Termos comuns
2. Lógica e operações matemáticas
3. Constantes e variáveis pré-definidas
4. Finalização

1. Termos comuns:
O executável do GML funciona na base de objetos, rooms e scripts. O jogo só lê as instâncias que estão contidas na room atual, e as instâncias funcionam á base dos scripts. Aliás, você sabe o que é room e instância e quaisquer seja esses termos?

Vamos com calma então. Primeiro, deixe-me dar um pequeno "dicionário" de termos muito utilizados.

Instância: nome dado ao representante do objeto dentro da room, enquanto o jogo roda. Por exemplo, existe o objeto obj_inimigo_dragao. Dentro de uma room, chama rm_ninho_do_dragao, existem 10 instâncias do obj_inimigo_dragao. Você programa uma vez, e depois espalha pelas rooms.

Room: sala, ambiente, local. Pode ser um ambiente jogável, uma sala de menu, uma sala de cinemática, uma sala de opções do jogo, etc.

Views: são câmeras. Podem ser fixadas em algum lugar para manter o jogador com a tela fixa, ou podem ser atribuídas á um objeto, como o jogador, por exemplo, para fazer com que ela o siga durante a room.

Eventos: ocasiões em que os códigos são executados nos objetos. Exemplo: você atribui um código para quando o inimigo é criado, para definir as variáveis necessárias para o seu funcionamento, um outro código para ser executado constantemente enquanto ele existir para tornar sua I.A funcional, e um outro código para quando sua instância for destruída, para criar uma pilha de moedas.
Visible: torne o objeto visível ou invisível. Um objeto invisível ainda é completamente existente e possui funções de colisão. É muito útil para criar switches invisíveis e acionamento de armadilhas, por exemplo.

Mask: Área de colisão do objeto, independente do seu Sprite.

Parent: "Pai" do objeto, no qual ele irá copiar todos os eventos dos quais ele não tem nenhum código adicionado. Extremamente útil, pois com este recurso você pode, por exemplo, configurar apenas três tipos de inimigos e depois criar centenas de outros inimigos ligados apenas aos três iniciais, apenas com modificações em sua vida, dano, experiência, velocidade, sprites, habilidades... evitando assim ter que fazer as mesmas coisas diversas vezes. Isso é útil também para criar uma classe inteira de eventos em comuns, como por exemplo os "itens", os "npcs", os "obstáculos", nos quais todos se comportarão quase da mesma maneira, modificando uma coisa ou outra entre cada um.

Solid: variável pré-definida que interfere em ações de colisões. Um objeto solido é tratado pelo jogo como algo que não deve permitir a passagem de outros objetos no qual ele possui algum evento ou relação de colisão, porém isso não significa que ele seja impenetrável e que outros objetos que não possuem relação com ele passem por cima, ignorando-o.

Depth: profundidade visual do objeto. Quanto mais Depth, mais abaixo o objeto fica. Por exemplo: um objeto de depth 1 fica sempre, independente da situação, visualmente abaixo de qualquer objeto de depth 0 ou maior. Um objeto com depth -10000 fica sempre acima visualmente de qualquer objeto de depth -9999 ou menos. Isso é muito útil principalmente de criar enfeites que devem sempre permanecer abaixo de tudo, como flores, para impedir que itens fiquem encima das personagens ou que a interface do jogo fique com coisas atrapalhando.

Persistent: um objeto persistent é transferido de room em room, mantendo sua posição (X,Y). Você pode usar isso para transportar controladores invisíveis importantes ao seu jogo (como um objeto que segura dados de pontuação e desempenho do jogador) ou para transportar certos objetos á uma room específica e depois "desligar" seu Persistent, deixando-o lá. Com isso, me vem à mente trabalhar com personagens que Quests, por exemplo, que geralmente o seguem até o local designado e depois não aparecem mais. Uma room também por ser persistent, ou seja, ela se mantém da mesma maneira que foi deixada quando o jogador reentra, útil para salvar, por exemplo, uma casa pessoal personalizada do jogador ou coisas do tipo.

2. Lógica e operações matemáticas
A lógica da GML também é bem simplificada, não é a toa que é uma boa porta para a entrada de novos programadores. Ela utiliza-se da linguagem matemática simples para fazer suas atribuições, como > (maior), < (menor), == (igualdade comparativa), = (atribuição de um valor)... segue abaixo uma lista destas igualdades lógicas e operações matemáticas:

Comparações lógicas:Significado e uso:
Maior
<Menor
>=Maior ou Igual
<=Menor ou Igual
==Igualdade comparativa
=Atribuição de um valor (nova igualdade)
+=Soma
-=Subtração
*Multiplicação
/Divisão
|| (ou or)"ou"
&& (ou and)"e"
!Inversão. Transforma true em false e vice-versa, e igualdades em desigualdades.
sqrt(x)Raíz quadrada de X
sqr(x)X ao quadrado (X²)
random(x)Valor real aleatório entre 0 e X.
random(x,y)Valor real aleatório entre X e Y.
irandom(x)Valor real inteiro entre 0 e X.
irandom_range(x,y)Valor real inteiro entre X e Y.
round(x)Arredondamento para mais ou para menos.
ceil(x)Arredondamento para mais.
floor(x)Arredondamento para menos.
mean(x,y,z,w...)Moda entre os valores.
median(x,y,z,w...)Mediana entre os valores.
min(x,y,z,w...)Retorna o menor valor.
max(x,y,z,w...)Retorna o maior valor.
choose(x,y,z,w...)Retorna um valor aleatório dentre os especificados.
Entre outros mais, como sin, cos, tg, log...

3. Constantes e variáveis pré-definidas:
Já na parte das variáveis, saiba que todos os objetos do Game Maker são criados já com um pacote de variáveis pré-definidas (claro que o programador pode adicionar infinitas mais, quando quiser). Isso tem um lado bom e um ruim. O lado bom é que pode poupar ao programador de executar ações básicas desnecessárias, como atribuir uma variável de velocidade á um objeto toda vez que quiser que ele se mova, ou atribuir uma nova variável toda vez que o programador quiser que ele torne-se invisível ao jogador. O lado ruim é o custo de memória usada muitas vezes para nada, pois cada instância vem com um pacote de variáveis nas quais o programador sequer irá mexer alguma vez. 

Segue um listão então das variáveis e constantes pré-definidas:

Constante ou variável pré-definida:Significado e uso:
ifCondição"se".
Exemplo: if speed > 0 {image_speed = 1}
Se a velocidade for maior que 0, a velocidade da animação será de 1:1 ao framerate da room.
elseCondição "ou", "se não".
Exemplo: if speed < 0 {image_speed = 0} else {image_speed = 1}
Se a velocidade for menor que 0, a velocidade da animação será nula, se não, ela será de 1:1 ao framerate.
while e doCondição "enquanto... for... faça..."
Exemplos: while image_speed > 1 do {image_speed -= 0.1}
Enquanto a velocidade de imagem for maior que 1, faça ela diminuir 0.1.
switchConjunto compacto e de rápida execução de vários "if" e um "else" para uma única variável.
Exemplos: switch(var_nota)
{
case 8: var_message="Melhore um pouco!"; break;
case 9: var_message="Quase lá!"; break;
case 10: var_message="Excelente!"; break;
default: var_message="Estude mais!"; break;
}
trueEquivale á 1.
Exemplo: (var_teste = 1) é o mesmo que (var_teste = true)
falseEquivale á 0.
Exemplo: (var_ligado = 0) é o mesmo que (var_ligado = false)
repeat(x)Repete a próxima ação ou bloco X número de vezes.
Exemplos: repeat(3) instance_create(x,y,obj_enfeite);

repeat(global.var_numero_de_pontos)
{
obj_jogador.var_recurso_energia += 1;
obj_jogador.var_recurso_forca += 1;
obj_jogador.var_fome -= 10;
}
globalAlgo que engloba o próprio jogo, e não uma instância ou room específica.
Exemplos: global.var_numero_de_pontos += 100;
global.var_numero_da_fase = 3;
global.var_nome_do_jogador = "Felipe";
allTodas as instâncias da sala.
xCoordenada X (horizontal).
yCoordenada Y (vertical)
xstartValor inicial X da instância no momento que foi criado na room. Não pode ser alterado.
ystartValor inicial Y da instância no momento que foi criado na room. Não pode ser alterado.
selfA própria instância, retorna em forma do id dela.
Exemplo: uuu = instance_create(x, y, obj_magia_de_cura)
uuu.var_alvo_para_curar = (self);
idO número da instância. Funciona como "RG" dela. Nenhuma instância, nunca, tem o mesmo id igual, ou seja, é uma boa forma de acrescentar funções únicas á uma instância específica.
Use sempre entre ( ) parênteses. (id).
nooneNada e Ninguém. Normalmente usado para zerar uma variável, completamente.
Exemplo: var_meu_alvo = noone;
if var_tesouro_do_bau != noone {instance_create(x, y, var_tesouro_do_bau) }
otherUsado só em eventos de colisões, refere-se ao objeto em que você colidiu.
Exemplos: with other do instance_destroy();
other.var_recurso_vida -= 1;
orEquivale á ||, "ou".
andEquivale á &&, "e".
livesVariável global de "vida" pré-definido.
Exemplos: if lives == 0 {room = rm_gameover;}
lives += 4;
lives -= 1;
healthVariável global de "energia" pré-definido. Mínimo de 0 e máximo de 100.
Exemplos: if health == 0 {room_restart(); lives -=1; sound_play(snd_morte)}
scoreVariável global de "pontuação" pré-definido.
Exemplos: 
score += 50;
score += irandom(80);
score += irandom_range(20, 200);
mouse_xCoordenada X do mouse. 
mouse_yCoordenada Y do mouse. 
sprite_indexSprite no qual o objeto está usando no momento. Pode ser usado para definir um novo sprite existente naturalmente.
Exemplos: if sprite_index == spr_super_personagem {var_poder = 90;}
sprite_index = spr_item_poder;
sprite_index = 68;
mask_indexMask no qual o objeto está usando como área de colisão. Assim como o sprite, pode ser normalmente trocada.
Exemplos: if sprite_index = spr_objeto_grande {mask_index = spr_mask_grande;}
mask_index = spr_mask_circulo32x32;
image_indexNúmero da subimagem do sprite atual. Pode ser alterada normalmente.
Exemplos: if speed == 0 {image_speed = 0; image_index = 0;}
if image_index == 0 {var_velocidade += 1;}
image_speedNúmero que é constantemente adicionado ao image_index para ocorrer a animação.
Exemplos: image_speed = 0.25;
if image_speed <= 0 && speed > 0 {image_speed = 0.5;}
image_numberNúmero de subimagens total do sprite utilizado no momento. Embora uma "variável", não pode ser alterada durante o jogo.
image_angleÂngulo, em graus, da imagem. Lembre-se que 360º = volta completa = 0º.
Exemplos: image_angle += 10;
image_angle -= 36;
image_angle = 75;
image_angle = direction;
image_alphaTransparência da imagem, entre 0 (totalmente invisível) e 1 (totalmente opaco).
Exemplos: if var_morto == true 
{
image_alpha -= 0.1; 
if image_alpha <= 0 {instance_destroy();}
}

image_alpha = 0.5;
directionDireção, em graus, do objeto. Ou seja, para onde está virada a "cara" dele.
image_xscaleProporção de tamanho de imagem:tamanho original, horizontalmente.
Exemplos: image_xscale = 2;
image_xscale += 1;
image_yscaleProporção de tamanho de imagem:tamanho original, verticalmente.
Exemplos: image_yscale = 2;
image_yscale += 1;
solidVariável que interfere em eventos de colisão e de movimentação. Um objeto sólido é tratado diferentemente nessas ocasiões, mas não necessariamente é impenetrável.
alarm[0,1,2,3...11]Aciona o alarme selecionado, depois de um momento especificado.
Exemplos: alarm[0] = 30;
alarm[3] = 45;
alarm[7] = 300;
alarm[4] = 900;
alarm[11] = 5;
sleep(x)Congela o jogo totalmente por x milissegundos.
Existem centenas de outras variáveis pré-estabelecidas. Eu só listeis as mais importantes, as mais básicas e as mais utilizadas normalmente.

4. Finalização: 
Existem também algumas convenções matemáticas que são importantes lembrar. O valor de 30 unidades em alarms significa 1 segundo real, 60 unidades em alarms, 2 segundos, 450 unidades em alarms, 15 segundos reais para o alarm ativar.

Tudo (ou 99,9999%) pode ser alterado via código. Enfim... muita coisa ainda deve ser explicada! Use esta postagem como referência fácil enquanto você acompanha o restante da apostila. Espero que eu tenha ajudado em algo! Até a próxima!

2 comentários:

Dúvidas? Pergunte!
Ajudou? Agradece!
:]