Autor: KlanPaia | Publicado: 2013-01-08T21:01:00-08:00

Nome:&nbsp;Partículas
Descrição:&nbsp;Esse tutorial tem a pretensão de mostrar como usar as partículas no game maker
Nível de dificuldade:&nbsp;Mediano. Conhecer alguns conceitos (ver abaixo) é desejável em alguns momentos.
Requerimentos:&nbsp;Versão PRO do game maker (usei o 8.0, mas acredito que em qualquer versão seja igual). É recomendável entender como funcionam as&nbsp;cores&nbsp;e os&nbsp;blend modes.

Antes de qualquer coisa: eu NÃO VOU traduzir o que o manual diz. Já temos uma versão traduzida do manual&nbsp;aqui. Uma vez traduzida a parte referente a partículas do manual, a coisa toda deveria ficar muito simples. Deveria.
Mas na prática, não é o que acontece. Surge um monte de dúvidas. A principal delas:
- Como organizar meu sistema de partículas? (Que valores usar em cada função?)
E, menos importantes:
- Se o manual diz que eu só preciso organizar meu sistema de partículas uma vez no jogo inteiro, isso significa que qualquer variável usada será automaticamente global?
- Quando usar partículas?
- Pra que fazer isso, afinal?
- O que eu posso fazer com elas, além de efeitos especiais?

Esse tutorial tem a pretensão de responder a todas essas dúvidas. Se alguma permanecer sem resposta ao final da leitura, por favor entre em contato (eu não vou respondê-las diretamente, isso fica como exercício pra você).
Entretanto, é impossível fazer isso sozinho. Uma particularidade das partículas é a forma que elas se comportam em conjunto, em movimento. Explicar ou mesmo ilustrar isso é impossível. Portanto, eu pretendo soltar alguns conceitos e códigos e espero que você teste cada um deles na sua máquina. A partir desses testes, suas perguntas serão respondidas sozinhas. Pense nesse tutorial mais como um estudo dirigido do que propriamente um tutorial.
Ah, volta-e-meia eu vou abrir um parêntese pra comentar um pouco do vocabulário usado. Eu não gosto dos nomes escolhidos.

Chega de introdução, vamos ao que interessa!


O que são partículas?

Partículas são objetos com uma série de códigos "built-in" e que não permitem a inserção de mais nenhum código. Isso torna as partículas muito leves, permitindo a inserção de centenas, se não milhares, de partículas no jogo ao mesmo tempo, um número muito maior que de objetos comuns.

Esses códigos "built-in" usam uma série de parâmetros que moldam as partículas, fazendo-as se comportarem das mais diversas formas. Basicamente, esses códigos interferem na forma, cor e movimentação. Tem um pouco mais, mas não muito. Pra uma informação geral, lembre-se que você pode interferir nisso: forma, cor e movimentação.
Elas têm um tempo de vida pré-determinado e são destruídas ao final desse tempo.

Quando a gente diz "partícula" (particles, no original), fica a impressão de que estamos falando de objetinhos pequenininhos, microscópicos, mas que numa quantidade muito grande acabam ficando visíveis na tela.

Embora isso até possa ser verdade, está longe de ser a regra. Uma única partícula pode ocupar a tela inteira, se preciso. Elas têm sprites que não são nem um pouco microscópicos (você pode até criar uma sprite de qualquer tamanho, pra não ficar só nas built-ins).



Eu preferiria o termo "objetos leves"

Agora, uma apresentação formal. Partículas, essa é a galera da GMBR. Galera, essas são as partículas:







Os nomes delas são:
0: pt_shape_pixel
1: pt_shape_disk
2: pt_shape_square
3: pt_shape_line
4: pt_shape_star
5: pt_shape_circle
6: pt_shape_ring
7: pt_shape_sphere
8: pt_shape_flare
9: pt_shape_spark
10: pt_shape_explosion
11: pt_shape_cloud
12: pt_shape_smoke
13: pt_shape_snow



Não se enganem, elas são mais elaboradas do que parecem. Têm pixels com alpha baixo pra todo lado, de forma que se juntarmos um monte delas no mesmo lugar, o visual fica diferente. Eis como ficam 100 delas empilhadas:







Como funcionam as partículas?

É o seguinte, as partículas têm que surgir de algum lugar. Pra determinar onde elas vão surgir, existe um "criador de partículas" chamado&nbsp;emitter. É ele quem vai, de fato, criar os objetos-partículas. Então, sim, você pode pensar num&nbsp;emitter&nbsp;como um objeto especial, sem sprite nem coordenadas, que cria as partículas determinadas nas coordenadas determinadas.





Quando a gente diz "emitter" (emissor), fica a impressão de que as partículas sairão com alguma velocidade, que estamos usando um canhão e LANÇANDO partículas pela tela.
Não, só estamos criando. Elas se lançam sozinhas, em função da direção e velocidade inicial. Se não tiverem velocidade nem gravidade, elas ficam quietinhas, onde foram criadas.
Eu preferiria o termo "criador de partículas"





Esses&nbsp;emitters&nbsp;podem ser entendidos como&nbsp;child&nbsp;de um objeto&nbsp;parent. Esse parent seria o sistema de partículas. A princípio, isso parece uma bobagem, afinal se temos os&nbsp;emitters, pra que criar um&nbsp;parent&nbsp;pra eles? Eles não podem se virar sozinhos?
Poder, podem, mas é que não existem comandos pras partículas - lembre-se, a idéia é mantê-las LEVES. Então suponha que você quer fazer uma transição de&nbsp;rooms&nbsp;e está com a tela entulhada de partículas. Suponha que essa transição não fique perfeita se essas partículas continuarem na&nbsp;room. O que você faz?


Você usa um comando "with (particles){ instance_destroy() }"
Errado, as partículas podem ser ENTENDIDAS como objetos, mas não são objetos "de verdade". Além disso, que raio de variável é essa, "particles"? De onde você tirou que ela foi declarada em algum momento? Mas a resposta é de um raciocínio parecido. Mais alguém?
Você cria um&nbsp;Destroyer&nbsp;que ocupa a&nbsp;room&nbsp;inteira!
É... Isso resolveria... Mas não é a resposta que eu esperava. Além disso, não falei nada a respeito de&nbsp;destroyers, ainda.


Existe um comando pra eliminar todas as partículas de um sistema. Esse comando ignora qual foi o&nbsp;emitter&nbsp;que criou a partícula, qual a posição dela ou quanto tempo de vida ela já percorreu. Você digita o comando (part_particles_clear, pra matar a curiosidade), coloca qual o sistema você quer "limpar" e pronto, as partículas não estão mais lá.
Na verdade, existem comandos de sistemas de partículas que até dispensam osemitters, mas eles acabam sendo úteis pra generalizar a distribuição de partículas, então resolvi apresentá-los primeiro.


Mas o fato é que além dos&nbsp;emitters, existem outros "objetos" que existem dentro de um sistema de partículas. São os Atratores (attractors), destruidores (destroyers), defletores (deflectors) e alteradores (changers). ESSES, têm nomes adequados. Os comandos desses "objetos" levam em conta o sistema em que as partículas foram criadas, independente do&nbsp;emitter. Portanto, todas as partículas de um sistema, por mais heterogêneas que sejam, reagem a esses "objetos" de forma igual.


Como eu disse no começo do tutorial, não vou traduzir o manual. Os comandos são bem simples, apesar da quantidade. Leiam e saibam como usar cada um desses objetos.




Pra que servem as partículas?

É uma boa pergunta, apesar da resposta ser meio óbvia. Mas deixa eu enrolar um pouco aqui: um "objeto" que não aceita comandos diretos, não têm origem nem eventos (nem de colisão), não faz p*rra nenhuma, serve pra que, afinal?
Bom, ele faz algumas coisas.
Ele nasce. Ele aparece na tela. Ele morre.
E, se você fizer isso direitinho, ele deixa seu jogo bonito.
Essa complicação toda é só pra deixar o jogo bonitinho? Que frescura!
Não é só "bonitinho". É um acabamento que dá um toque de profissionalismo que nenhuma sprite (sozinha) é capaz de fazer. Aliás, pra quem não sabe desenhar, muitas vezes, é uma mão na roda!
E partículas não são complicadas. É só um monte de comandos.
Enfim, elas servem pra efeitos especiais, substituindo objetos com comportamentos erráticos que não precisam ficar com a imagem muito bem definida. Por exemplo, fogo, areia, fumaça, neblina, energias etéreas. Um pouco de experimentação e você vai imaginar mais e mais utilidades.

Foguetes têm uma cauda de fogo e fumaça. Por falar em cauda, cometas têm caudas cintilantes. Que jogo de luta tem impacto se os golpes não causam liberação de energia das mais diversas origens? Pântanos estão sempre cheios de vapores em toda a sua área. Magia sempre causa a liberação de forças etéreas.
Nem todo jogo precisa de partículas. Aliás, NENHUM jogo precisa de partículas. Mas todo jogo pode ter seu visual melhorado com elas.

Como, então, usar as partículas?

Aí, meu amigo, é por sua conta. Você viu a quantidade de comandos que existem. Vai querer que eu te mostre como organizar CADA UM deles? Tenha dó.
O negócio é sair experimentando.
Você quer uma atmosfera mística? Que tal um pouco de fumaça com vários tons de roxo?
Quer um fogo frio? Que tal criar partículas com arestas irregulares (como pt_shape_square, pt_shape_snow ou pt_shape_spark), usando uma colorização azul aditiva, lançadas em todas as direções em baixa velocidade e com uma gravidade leve para cima?

Poxa, então esse texto todo é inútil?&nbsp;ou:&nbsp;Finalmente algum código!!!

Tá bom, tá bom, vou dar umas dicas!

Primeira dica: use o create event pra quase tudo. Os emissores podem ficar emitindo partículas indefinidamente, então não tem motivo pra usar outros eventos - a menos que você queira as partículas só em momentos específicos. Claro que, pra economizar memória, você vai querer limpar tudo depois de usar. Portanto, use um destroy_event, game_end_event, room_exit ou coisa do tipo pra destruir o sistema, os tipos de partículas, os emissores, defletores, alteradores e destruidores. Mas fora essas exceções, ponha tudo num evento que não se repita. Ah, você pode usar eventos comuns pra mudar a área de atuação dos&nbsp;emitters, também.

Primeira coisa a fazer: crie o(s) sistema(s) de partículas. Tudo mais depende do sistema, então crie logo o sistema pra poder seguir em frente. Dê a ele um nome que você vai se lembrar que é um SISTEMA (e de pra que ele serve). Tipo "part_syst_caudaDoCometa" ou "sistema_poeira".

Segunda dica: Experimente MUITO. A menos que você seja um designer gráfico, não vai conseguir visualizar o sistema antes dele ser implantado. Então tente cores claras e escuras, experimente as mais diversas shapes, orientações, escalar, velocidades, gravidades... tem muitas variáveis pra serem levadas em conta.
Uma coisa que eu sempre testo é o&nbsp;additive_blend&nbsp;com alpha menor que um. Oadditive_blend&nbsp;é ótimo com muitas instâncias sobrepostas e fundos diferentes do branco (porque qualquer coisa com&nbsp;additive&nbsp;some num fundo branco). Por falar emadditive_blend, se for usá-lo, experimente várias cores de&nbsp;background&nbsp;e/oubackgrounds irregulares.
Quer um códigozinho pra testar muita coisa? Deixa eu te mostrar como fiz aquelas imagens no começo... Muita coisa aí está escrita só pra servir de copy-paste. Eu fiz um monte de definições que são default. Como é um código comprido, vou esconder ele num spoiler.


CÓDIGO PADRÃO:


//primeiro, define um índice pra cada shapevar&nbsp;i; i =&nbsp;0;
shape[i] =&nbsp;pt_shape_pixel;&nbsp; &nbsp; i +=&nbsp;1;// 0shape[i] =&nbsp;pt_shape_disk;&nbsp; &nbsp; &nbsp; i +=&nbsp;1;// 1shape[i] =&nbsp;pt_shape_square;&nbsp; &nbsp; i +=&nbsp;1;// 2shape[i] =&nbsp;pt_shape_line;&nbsp; &nbsp; &nbsp; i +=&nbsp;1;// 3shape[i] =&nbsp;pt_shape_star;&nbsp; &nbsp; &nbsp; i +=&nbsp;1;// 4shape[i] =&nbsp;pt_shape_circle;&nbsp; &nbsp; i +=&nbsp;1;// 5shape[i] =&nbsp;pt_shape_ring;&nbsp; &nbsp; &nbsp; i +=&nbsp;1;// 6shape[i] =&nbsp;pt_shape_sphere;&nbsp; &nbsp; i +=&nbsp;1;// 7shape[i] =&nbsp;pt_shape_flare;&nbsp; &nbsp; i +=&nbsp;1;// 8shape[i] =&nbsp;pt_shape_spark;&nbsp; &nbsp; i +=&nbsp;1;// 9shape[i] =&nbsp;pt_shape_explosion; i +=&nbsp;1;// 10shape[i] =&nbsp;pt_shape_cloud;&nbsp; &nbsp; i +=&nbsp;1;// 11shape[i] =&nbsp;pt_shape_smoke;&nbsp; &nbsp; i +=&nbsp;1;// 12shape[i] =&nbsp;pt_shape_snow;&nbsp; &nbsp; &nbsp; i +=&nbsp;1;// 13
nShapes = i;&nbsp;//13
// cria umas variáveis pras posições na teladx = (room_width&nbsp;/ (nShapes +&nbsp;1)) *&nbsp;2;&nbsp;// distância entre emitters
// organiza o sistema de partículas// cria o sistemapartSys =&nbsp;part_system_create();

for(i =&nbsp;0; i &lt; nShapes; i +=&nbsp;1){
&nbsp; &nbsp;// cria a partícula&nbsp; &nbsp;partType[i] =&nbsp;part_type_create();&nbsp;// um tipo pra cada shape&nbsp; &nbsp;part_type_shape(partType[i], shape[i]);&nbsp;// coloca a shape no tipo (equivalente a sprite_index)&nbsp; &nbsp;part_type_size(partType[i],&nbsp;1,&nbsp;1,&nbsp;0,&nbsp;0);&nbsp;// tamanho real, sem escala (ou: escala=1)&nbsp; &nbsp;part_type_scale(partType[i],&nbsp;1,&nbsp;1);&nbsp;// proporção (x, y) (equivalente a image_xscale E image_yscale)&nbsp; &nbsp;part_type_orientation(partType[i],&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0);&nbsp;// as partículas sempre saem giradas no ângulo=0 (equivalente a image_angle)&nbsp; &nbsp;part_type_color1(partType[i], $ffffff);&nbsp;// a cor a mesclar a sprite da partícula (branco resulta na cor original) (equivalente a image_blend)&nbsp; &nbsp;part_type_alpha1(partType[i],&nbsp;1);&nbsp;// opacidade exatamente igual a da sprite (equivalente a image_alpha)&nbsp; &nbsp;part_type_blend(partType[i],&nbsp;0);&nbsp;// blend não é aditivo (equivalente a usar draw_set_blend_mode)&nbsp; &nbsp;part_type_life(partType[i],&nbsp;30,&nbsp;30);&nbsp;// um segundo de vida (suficiente pro F9) (equivalente a usar um alarme com instance_destroy())&nbsp; &nbsp;// não usarei part_type_step(ind,step_number,step_type) ou part_type_death(ind,death_number,death_type)&nbsp; &nbsp;// não faz sentido usá-los pra mostrar cada partícula em separado&nbsp; &nbsp;part_type_speed(partType[i],&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0);&nbsp;// velocidade nula (equivalente a definir speed)&nbsp; &nbsp;part_type_direction(partType[i],&nbsp;0,&nbsp;0,&nbsp;0,&nbsp;0);&nbsp;// direção igual a 0 graus, sempre (equivalente a definir direction)&nbsp; &nbsp;part_type_gravity(partType[i],&nbsp;0,&nbsp;0);&nbsp;// aceleração zero (equivalente a definir gravity E gravity_angle)
&nbsp; &nbsp;// cria o emissor&nbsp; &nbsp;xi[i] = ((1&nbsp;+ i) * dx)&nbsp;mod&nbsp;room_width;&nbsp;//posição inicial (X) de cada emissor&nbsp; &nbsp;yi[i] = (room_height&nbsp;/&nbsp;3) * (1&nbsp;+ ((1&nbsp;+ i) * dx)&nbsp;div&nbsp;room_width);//posição inicial (Y) de cada emissor&nbsp; &nbsp;partEmit[i] =&nbsp;part_emitter_create(partSys);&nbsp;//cria o emissor&nbsp; &nbsp;part_emitter_region(partSys, partEmit[i], xi[i], xi[i], yi[i], yi[i],ps_shape_ellipse,&nbsp;ps_distr_linear);&nbsp;// define a região do emissor (um ponto só)&nbsp; &nbsp;part_emitter_stream(partSys, partEmit[i], partType[i],&nbsp;1);&nbsp;// faz o emissor lançar as partículas pra sempre (se eliminada essa linha, ele não lançará nada)&nbsp; &nbsp;}

Últimas dicas: Bom, obviamente, você pode renomear as variáveis à vontade, e principalmente retirar esse uso de arrays - dificilmente você vai precisar de todas as shapes ao mesmo tempo. Brinque com os valores de cor, direção, gravidade, tamanho (usar escalas diferentes em x e y pode ser interessante), etc.
Sabe aqueles comandos que eu não usei? Eles servem pra criar novas partículas de outro tipo, ou no equivalente ao "step_event" ou no equivalente ao "destroy_event" (Não TEM que ser de outro tipo, mas imagine o que acontece se uma partícula cria outra(s) igualzinha a cada step). Tem situações em que é interessante ter partículas criadas no ponto em que tem outra partícula. Por exemplo, uma chuva de meteoros em que os meteoros são partículas e as caudas são formadas por outras partículas. Ou fogos de artifício, como no código que vem com o manual. Ou um poder tão poderoso que suas partículas soltam faíscas! Sei lá, sempre tem alguma coisa.
Existem outros tipos de definição pra cores e alpha (recomendo brincar com opart_type_color_hsv()&nbsp;ou&nbsp;part_type_color_mix()&nbsp;pra vários tons da mesma cor, fica legal), mas não dá pra usar todos eles no mesmo sistema: você escolhe um tipo e pronto. Eu escolhi os mais simples.
Existem mais opções, mais comandos. Você pode escolher a depth do sistema como um todo, pode criar as partículas mais novas ATRÁS das já existentes (quando a cor muda, isso faz MUITA diferença), pode definir o quanto um defletor tira domomentum&nbsp;da partícula, pode fazer um monte de coisas.
Fonte:&nbsp;http://gmbr.forumeiros.com/t23691-tutorial-particulasPor: Saim