[Engine] Desenhar uma bandeira ondulando

Ver o tópico anterior Ver o tópico seguinte Ir em baixo

[Engine] Desenhar uma bandeira ondulando

Mensagem por saim em Dom 22 Jan 2012, 19:46

Nome: Desenhar uma bandeira ondulando
Descrição: Uma senóide e algumas primitives serão usadas pra animar uma bandeira.
Nível de dificuldade: Intermediário
Requerimentos: Pro
Pré-requisitos: Domínio em arrays e primitives, conhecimentos em trigonometria, algumas horas livres.

Donwload

Eu estava pra fazer um tutorial sobre primitives, quando percebi que já existe um. Então resolvi fazer uma engine que usa esses conceitos.
O resultado será uma imagem que é distorcida de modo a parecer que é uma bandeira ondulando.

Não espere algo assim:

O resultado está mais para:


Primeira parte - a ondulação

Antes de fazer qualquer coisa, precisamos criar uma ondulação. A primeira coisa que vem à cabeça quando se fala em ondulação é uma senóide (aquela curva em forma de onda), certo? Façamos uma curva senoidal, então.
Antes, deixa eu sequestrar alguma imagem da internet, aqui... Essa aqui serve!
Vamos definir um bocado de variáveis:
  • número de segmentos: quantos segmentos serão usados para interpolar a curva (quanto maior o número de pontos, mais suave fica a curva e mais memória ela consome)
  • velocidade angular: quantos graus serão somados ao primeiro ponto a cada step (ou: velocidade do vento)
  • comprimento da onda: o "tamanho", no eixo x, nossa onda (e depois, nossa bandeira) terá
  • amplitude da onda: altura máxima que a senóide atingirá
  • ângulo mínimo: qual o ângulo cujo primeiro ponto da curva corresponde
  • ângulo máximo: qual o ângulo cujo último ponto da curva corresponde
  • diferença entre pontos: o espaço, no eixo x, entre um ponto e outro
  • diferença angular: quantos graus serão somados em cada ponto
  • posição dos pontos: as coordenadas de cada ponto da curva a cada momento
  • número de imagens: quantas imagens terá a animação

Pode parecer estranho falar em "quantos graus tem cada ponto". O que eu quero dizer é o seguinte: nós teremos uma linha reta (a princípio) e cada ponto dessa linha será deslocado pra cima ou pra baixo, de forma diferente, formando uma onda. Esse deslocamento acontece em função de um ângulo, que não aparece na tela.

Bom, vou mostrar o código que usei pra definir essas variáveis, no create event:
Código:
nSeg = 10; //número de pontos

amplit = 10; //amplitude
angIni = 0; //ângulo inicial
angMax = 360; //ângulo máximo
dAng = (angMax - angIni) / nPontos; //diferença angular

xMin = 0; //posição inicial
xMax = 100; //posição final
largura = xMax - xMin; //tamanho da bandeira
dX = largura / nPontos; //distância entre os pontos

yBase = amplit; //seria um eixo central em torno do qual a onda oscila.
//Poderia ser 0, mas prefiro usarum valor igual à amplitude, pra que toda a onda fique visível na tela
var i;
for(i = 0; i <= nSeg; i += 1){ //pra cada ponto
   xx[i] = xMin + dX * i;    //atribui um valor no eixo "x", distribuídos uniformemente
   }
for(i = 0; i <= nSeg; i += 1){ //pra cada ponto
   yy[i] = yBase + amplit * sin(degtorad(angIni + i * dAng)); //atribui um valor no eixo "y",
   //distribuídos em função do ângulo do ponto
   }
Pronto. Com isso, você já tem uma onda de pontos. Para visualisá-la, vamos ao draw event e pedimos ao programa pra ligar cada ponto ao ponto anterior:
Código:
var i;
for(i = 1; i <= nSeg; i += 1){ //pra cada ponto (fora o primeiro)
   draw_line(xx[i - 1], yy[i - 1], xx[i], yy[i]); //liga com o anterior
   }

Pronto, temos uma onda. Mas é uma onda estática. Pra fazer ela se movimentar, precisamos de mais "imagens". Nós vamos acrescentar a variável correspondente à velocidade angular e variar o ângulo inicial em função dela. Quando o ângulo inicial chegar a 360, a senóide terá completado um ciclo. Podemos colocar esse ciclo num loop e a onda estará eternamente variando.
Determinaremos a posição de cada ponto em função de qual step o ponto deve aparecer, então teremos uma array 2D, onde o primeiro índice corresponde à imagem que queremos na tela e o segundo corresponde ao ponto propriamente dito. Vamos aos códigos.
Novo create:
Código:
nSeg = 10; //número de pontos
angSpeed = 10; // velocidade angular

amplit = 10; //amplitude
angIni = 0; //ângulo inicial
angMax = 360; //ângulo máximo
dAng = (angMax - angIni) / nSeg; //diferença angular

xMin = 0; //posição inicial
xMax = 100; //posição final
largura = xMax - xMin; //tamanho da bandeira
dX = largura / nSeg; //distância entre os pontos

yBase = amplit; //seria um eixo central em torno do qual a onda oscila.
//Poderia ser 0, mas prefiro usarum valor igual à amplitude, pra que toda a onda fique visível na tela
var i;
for(i = 0; i <= nSeg; i += 1){ //pra cada ponto
   xx[i] = xMin + dX * i;    //atribui um valor no eixo "x", distribuídos uniformemente
   }
var j;
j = 0; //inicia o contador da array
for (angIni = 0; angIni < 360; angIni += angSpeed){ //pra cada imagem
   for(i = 0; i <= nSeg; i += 1){ //pra cada ponto de cada imagem
      yy[j, i] = yBase + amplit * sin(degtorad(angIni + i * dAng)); //atribui um valor no eixo "y",
      //distribuídos em função do ângulo do ponto
      }
   j += 1;
}

imagem = 0; //um índice pra imagem atual
nImagens = j; //o número de pontos (que foi atualizado no loop "for")
Step (pra variar a imagem mostrada):
Código:
imagem = (imagem + 1) mod nImagens;
Draw:
Código:
var i;
for(i = 1; i <= nSeg; i += 1){ //pra cada ponto (fora o primeiro)
   draw_line(xx[i - 1], yy[imagem, i - 1], xx[i], yy[imagem, i]); //liga com o anterior
   }

E pronto, temos uma senóide oscilando em nossa tela. O efeito fica melhor quando a velocidade angular for um divisor de 360, mas qualquer valor é válido.
Brinque à vontade com os valores pra ter uma boa idéia do que significa cada variável. São esses valores que vão definir o movimento da onda.
As possibilidades de uma senóide bem utilizada em games são imensas!

Segunda parte - a bandeira

Agora que temos uma onda oscilando, podemos facilmente copiá-la pra algum ponto acima ou abaixo do ponto inicial, ligar o primeiro e último pontos e teremos uma bandeira. Não é esso o efeito que estamos procurando, mas já dá pra ilustrar como será o resultado final a partir disso.
O efeito que estamos procurando é distorcer uma imagem dentro dessa bandeira em branco que acabamos de desenhar.

Pra distorcer uma imagem, nós jogamos essa imagem numa textura e desenhamos essa textura usando primitives. No exemplo, eu vou carregar a textura usando uma sprite e a função sprite_get_texture, mas você pode usar também um background e background_get_texture. Fica a seu critério.
Lembre-se que, pra garantir que funcione, precisamos que a textura tenha um tamanho em potência de 2. Significa que ela deve ser um quadrado cujo lado mede 2 elevado a um inteiro. 2, 4, 8, 16, 32, 64, 128, 256, 512... Eu usei 128 pixels e já gostei do resultado.
Poxa, mas só me diz agora? Minha bandeira é retangular e nenhum dos lados é potência de 2!!!
Não tem problema. Aumente a imagem até a potência seguinte que tudo será resolvido. Você pode optar por esticar a imagem até os limites ou deixar o resto da imagem em branco, mesmo. Como deixar o resto da imagem em branco dá um resultado melhor, vou usar esse método no exemplo, mas posso comentar a respeito da outra opção, se quiserem.
Usei uma imagem de 200x140


Pare de rolar a tela, porque é agora que o bicho pega!
Então, agora, jogue essa bandeira pra um sprite ou background e carregue-a como textura. Nós vamos redesenhá-la usando primitives.
Como exemplo, vamos criar um objeto só pra desenhar a bandeira. No create, nós colocamos a sprite na textura e no draw, desenhamos um retângulo composto por triângulos usando pr_trianglestrip.
pr_trianglestrip é uma forma de desenhar primitives onde se define 3 pontos de um triângulo e o triângulo seguinte será definido por um novo ponto + os dois últimos pontos do triângulo anterior.
Assim:

colocamos no create:
Código:
Texture = sprite_get_texture(sprText, 0); //carrega a sprite numa textura
texture_set_blending(false); //não sei pra que serve, mas precisa

//agora, define-se quais os pontos serão mostrados na tela
var xInicial, yInicial, largura, altura;
xInicial = 0; yInicial = 0; //o ponto inicial do desenho
largura = 300; altura = 140; //as dimensões do desenho na tela
px1 = xInicial;          py1 = yInicial;
px2 = xInicial;          py2 = yInicial + altura;
px3 = xInicial + largura; py3 = yInicial;
px4 = xInicial + largura; py4 = yInicial + altura;
//finalmente, os pontos que serão buscados na textura
var larOri, altOri;
larOri = 200; altOri = 140; //largura e altura da imagem original
tx1 = 0;            ty1 = 0;
tx2 = 0;            ty2 = altOri / 256;
tx3 = larOri / 256; ty3 = 0;
tx4 = larOri / 256; ty4 = altOri / 256;
Note que os pontos da textura estão entre 0 e 1. "0" significa o começo da textura e "1" significa o ponto final dela, seja ela do tamanho que for. Nesse exemplo eu mostrei qual o cálculo deve ser feito pra encontrar o ponto desejado.

Agora colocamos no draw event:
Código:
draw_primitive_begin_texture(pr_trianglestrip, Texture); //começa a desenhar a textura
draw_vertex_texture(px1, py1, tx1, ty1); //define o ponto superior-esquerdo
draw_vertex_texture(px2, py2, tx2, ty2); //define o ponto inferior-esquerdo
draw_vertex_texture(px3, py3, tx3, ty3); //define o ponto superior-direito, fechando o primeiro triângulo
draw_vertex_texture(px4, py4, tx4, ty4); //define o ponto inferior-esquerdo, fechando o segundo triângulo
draw_primitive_end(); // fecha e desenha a primitive
Mas... não aconteceu nada! a bandeira está igualzinho estava antes!!!
É, mas você aprendeu a usar primitives.

Terceira parte - o vento

Agora chegou a hora de misturar a curva senoidal, que nós já sabemos como definir todos os pontos, com o uso de primitives, cujos pontos nós já sabemos buscar na textura.
Vamos fazer o seguinte, vamos buscar na textura tantos pontos quanto estivermos usando pra fazer a nossa onda e desenhá-los na posição que quisermos, deixando as distorções por conta do game maker.

create:
Código:
// Inicialmente, definimos os dados da onda

nSeg = 10; //número de pontos
angSpeed = 4; // velocidade angular

amplit = 10; //amplitude
angIni = 0; //ângulo inicial
angMax = 360; //ângulo máximo
dAng = (angMax - angIni) / nSeg; //diferença angular

xMin = 50; //posição inicial
xMax = 250; //posição final
largura = xMax - xMin; //tamanho da bandeira
dX = largura / nSeg; //distância entre os pontos

yBase = 0; //seria um eixo central em torno do qual a onda oscila.
//Poderia ser 0, mas prefiro usarum valor igual à amplitude, pra que toda a onda fique visível na tela
var i;
for(i = 0; i <= nSeg; i += 1){ //pra cada ponto
   xx[i] = xMin + dX * i;    //atribui um valor no eixo "x", distribuídos uniformemente
   }
var j;
j = 0; //inicia o contador da array
for (angIni = 0; angIni < 360; angIni += angSpeed){ //pra cada imagem
   for(i = 0; i <= nSeg; i += 1){ //pra cada ponto de cada imagem
      yy[j, i] = yBase + amplit * sin(degtorad(angIni + i * dAng)); //atribui um valor no eixo "y",
      //distribuídos em função do ângulo do ponto
      }
   j += 1;
   }

imagem = 0; //um índice pra imagem atual
nImagens = j; //o número de pontos (que foi atualizado no loop "for")
yDesenho = 20; alturaDesenho = 140;

// Em seguida, carregamos a textura

sprText = sprite_add(working_directory + "\aumentada.bmp", 1, 0, 0, 0, 0); //a sprite que será transformada em textura
Texture = sprite_get_texture(sprText, 0); //carrega a sprite numa textura
texture_set_blending(false); //não sei pra que serve, mas precisa

// Por último, definimos que pontos serão buscados dessa textura, em função de suas dimensões

var larOri, altOri;
larOri = 200; altOri = 140;
for(i = 0; i <= nSeg; i += 1){ //pra cada ponto
   xt[i] = (i / nSeg) * (larOri / 256);    //atribui um valor no eixo "x", distribuídos uniformemente
   }
altTextura = altOri / 256;
Step, pra permitir a oscilação:
Código:
imagem = (imagem + 1) mod nImagens;
E o draw, pra ver o resultado:
Código:
var i;
draw_primitive_begin_texture(pr_trianglestrip, Texture); //começa a desenhar a textura
for(i = 0; i <= nSeg; i += 1){ //pra cada ponto da curva
   draw_vertex_texture(xx[i], yy[imagem, i] + yDesenho, xt[i], 0); //desenha o vértice superior
   draw_vertex_texture(xx[i], yy[imagem, i] + yDesenho + alturaDesenho, xt[i], altTextura); //desenha o vértice inferior
   }
draw_primitive_end(); // fecha e desenha a primitive
Pronto! Ponha pra rodar e curta o resultado!

Quarta parte - a parte complicada

Bom, agora já era pro tutorial ter acabado. Mas as possibilidade com as senóides são muitas, pra deixar passar em branco. Se você souber como modular a curva.
Voce pode manter um lado da bandeira preso num mastro, por exemplo. Se fizer a bandeira ondular nos dois sentidos, Deus sabe qual será o resultado (talvez até algo como a primeira imagem do tópico?). Mas a coisa não pára por aí.
Se a posição de um objeto for baseada numa senóide e um ponto de âncora, ele ficará indo e voltando num movimento mais natural que simplesmente inverter a velocidade.
Se a quantidade de inimigos criados for baseada numa senóide, o jogador terá a nítida sensação de tempestade e calmaria.

saim

Ranking : Nota B
Número de Mensagens : 2964
Idade : 38
Data de inscrição : 14/01/2011
Notas recebidas : C-D-A-B
Reputação : 121
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 1
   : 0
   : 3

Voltar ao Topo Ir em baixo

Re: [Engine] Desenhar uma bandeira ondulando

Mensagem por Kaique_Staff em Seg 23 Jan 2012, 13:59

isso é um tutorial nao engine.

Kaique_Staff

Ranking : Nota E
Número de Mensagens : 206
Data de inscrição : 16/01/2012
Notas recebidas : E+E
Reputação : 3
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

http://forumshow.blogspot.com

Voltar ao Topo Ir em baixo

Re: [Engine] Desenhar uma bandeira ondulando

Mensagem por PedroX em Seg 23 Jan 2012, 14:13

isso é um tutorial nao engine
É uma engine, com uma explicação.

Caramba, que explicação detalhada hein...
Gostei do resultado, ficou bem legalzinho.
O interessante é mudar os valores em tempo real, para visualizar as diferentes possibilidades....

PedroX

Ranking : Nota C
Número de Mensagens : 6034
Idade : 21
Data de inscrição : 26/07/2008
Notas recebidas : C+B
Reputação : 286
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   :
   :
   :

Voltar ao Topo Ir em baixo

Re: [Engine] Desenhar uma bandeira ondulando

Mensagem por Kaique_Staff em Seg 23 Jan 2012, 17:54

ah entao me desculpem pelo comentario. você explicou bem a engine.(MUITO BEM)

Kaique_Staff

Ranking : Nota E
Número de Mensagens : 206
Data de inscrição : 16/01/2012
Notas recebidas : E+E
Reputação : 3
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

http://forumshow.blogspot.com

Voltar ao Topo Ir em baixo

Re: [Engine] Desenhar uma bandeira ondulando

Mensagem por GameMakerTutoriais em Ter 24 Jan 2012, 15:47

Show de bola! Efeito legal demais! Dá pra usar em água também que fica bem bacana... É só usar uma textura legal ou até uns blends que dá um resultado bem bonito, fica um "diferencial" no jogo.

Ficou bem explicado, mas se o cara não entender o primitive, também dá pra adaptar um draw_sprite_part() e ir desenhando linha por linha com essa distorção que também fica interessante também.

Falou! Òtimo tuto!

GameMakerTutoriais

Número de Mensagens : 800
Data de inscrição : 29/01/2011
Reputação : 26
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: [Engine] Desenhar uma bandeira ondulando

Mensagem por saim em Ter 24 Jan 2012, 16:35

Kaique_Staff escreveu:isso é um tutorial nao engine.
Pedrø escreveu:
isso é um tutorial nao engine
É uma engine, com uma explicação.
Era pra ser uma engine, mas achei que se eu não explicasse os códigos, pouca gente ia entender. Aí, a explicação acabou ficando maior que o próprio arquivo. Mas é uma engine assim mesmo.
Pedrø escreveu:
Caramba, que explicação detalhada hein...
Gostei do resultado, ficou bem legalzinho.
O interessante é mudar os valores em tempo real, para visualizar as diferentes possibilidades....
Interessante, mesmo, é mudar algumas fórmulas. Eu tinha cometido um erro, aqui, que deu um resultado tão bacana, tão aleatório que resolvi guardar.
Experimente usar
Código:
yy[i] = yBase + amplit * sin(degtorad(i * (angIni + dAng)));
ou
Código:
yy[i] = yBase + amplit * sin(degtorad(angIni + i * dAng)) * 1 / 10;

Ninja8086 escreveu:Show de bola! Efeito legal demais! Dá pra usar em água também que fica bem bacana... É só usar uma textura legal ou até uns blends que dá um resultado bem bonito, fica um "diferencial" no jogo.
Essa é a quarta parte da explicação. Tem MUITAS possibilidades. Uma vez, vi uma engine que inchava a parte da tela onde o mouse passava e desinchava depois que ele saía, fazendo parecer que havia água vista de cima! Eu ainda chego lá...
Ninja8086 escreveu:Ficou bem explicado, mas se o cara não entender o primitive, também dá pra adaptar um draw_sprite_part() e ir desenhando linha por linha com essa distorção que também fica interessante também.
É verdade, mas dá quase o mesmo trabalho e o efeito fica bem pior, porque não tem a distorção. Acho que vale a pena queimar um pouco de pestana com isso. As primitives são a parte fácil da engine.

saim

Ranking : Nota B
Número de Mensagens : 2964
Idade : 38
Data de inscrição : 14/01/2011
Notas recebidas : C-D-A-B
Reputação : 121
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 1
   : 0
   : 3

Voltar ao Topo Ir em baixo

Re: [Engine] Desenhar uma bandeira ondulando

Mensagem por GameMakerTutoriais em Qui 26 Jan 2012, 23:32

Essa é a quarta parte da explicação. Tem MUITAS possibilidades. Uma vez, vi uma engine que inchava a parte da tela onde o mouse passava e desinchava depois que ele saía, fazendo parecer que havia água vista de cima! Eu ainda chego lá...

Seria como esse mesmo efeito de senóide, porém ao invés de lateral é uma visão de cima né? Muito doido mesmo!

É verdade, mas dá quase o mesmo trabalho e o efeito fica bem pior, porque não tem a distorção. Acho que vale a pena queimar um pouco de pestana com isso. As primitives são a parte fácil da engine.

É sim, fica mais "pesado" também. Acho que com draw_sprite_part() só ia ficar legal se fosse um sprite pequeno. Esse recurso de primitives dá um pouco de trabalho, mas dá pra fazer muita coisa legal mesmo.

GameMakerTutoriais

Número de Mensagens : 800
Data de inscrição : 29/01/2011
Reputação : 26
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 0
   : 0
   : 0

Voltar ao Topo Ir em baixo

Re: [Engine] Desenhar uma bandeira ondulando

Mensagem por Lukbebalduke em Qui 26 Jan 2012, 23:49

Só uma dica uma parte da bandeira retia que ficar parada se não vira um tapete magico XD

Lukbebalduke

Ranking : Nota B
Número de Mensagens : 764
Data de inscrição : 06/07/2011
Notas recebidas : B - B - A - B
Reputação : 30
Insignia 1 x 1 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 1
   : 0
   : 2

Voltar ao Topo Ir em baixo

Re: [Engine] Desenhar uma bandeira ondulando

Mensagem por saim em Sex 27 Jan 2012, 08:26

pinpi escreveu:Só uma dica uma parte da bandeira retia que ficar parada se não vira um tapete magico XD
É só variar em cima do mesmo tema, modulando a onda. Experimente o segundo código que eu mostrei pro Pedrø, um pouco acima. Nele eu multiplico a amplitude de cada ponto por um valor que depende da distância do ponto ao mastro (i / 10). Com essa pequena alteração, a bandeira fica com um lado preso no mastro imaginário enquanto o outro lado oscila com bastante força.

Mas o legal não é fazer uma bandeira-BANDEIRA. O legal é pegar os conceitos e sair aplicando num monte de efeitos. Estou quebrando a cabeça, aqui, pra tentas desvendar algumas formas mais criativas de usar esses conceitos. Se surgir algo bacana, eu crio outra engine ou atualizo essa.

saim

Ranking : Nota B
Número de Mensagens : 2964
Idade : 38
Data de inscrição : 14/01/2011
Notas recebidas : C-D-A-B
Reputação : 121
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Prêmios
   : 1
   : 0
   : 3

Voltar ao Topo Ir em baixo

Re: [Engine] Desenhar uma bandeira ondulando

Mensagem por Conteúdo patrocinado Hoje à(s) 21:13


Conteúdo patrocinado


Voltar ao Topo Ir em baixo

Ver o tópico anterior Ver o tópico seguinte Voltar ao Topo

- Tópicos similares

 
Permissão deste fórum:
Você não pode responder aos tópicos neste fórum