GMBR
Gostaria de reagir a esta mensagem? Crie uma conta em poucos cliques ou inicie sessão para continuar.
Entrar

Esqueci-me da senha

Últimos assuntos
» player não consegue andar
por lovn7 Qui 21 Nov 2024, 13:33

» É possível fazer istó no game maker
por William Lima Qui 21 Nov 2024, 10:56

» Rio Rise - novo launcher do Gta San Andreas SAMP Brasil
por Lua Sáb 16 Nov 2024, 20:22

» (Resolvido) Cenario longo x Texture Pages
por josuedemoraes Sáb 16 Nov 2024, 15:31

» Kids' band
por Adilson Lucindo Santos Sex 15 Nov 2024, 12:23

» (RESOLVIDO) Engasgos-Troca de Sprites/animações
por josuedemoraes Ter 12 Nov 2024, 01:49

» Block Room - DEMO
por Joton Qua 06 Nov 2024, 22:58

» Game Infinito vertical (subindo)
por macmilam Sáb 26 Out 2024, 12:36

» Retorno da GMBR!!!
por Dancity Ter 22 Out 2024, 16:36

» Máquina de estados
por aminaro Qui 10 Out 2024, 13:33

» como faço pra um objeto colidir com o outro e diminuir a vida do player ?
por josuedemoraes Qui 03 Out 2024, 16:51

» RESOLVIDO: Colisão com objetos moveis
por josuedemoraes Qua 02 Out 2024, 20:28

» Crypt of the Blood Moon
por divin sphere Qua 11 Set 2024, 18:18

» como fazer um objeto seguir?
por divin sphere Dom 18 Ago 2024, 18:08

» Procuro de alguém para Modelar/Texturizar/Animar objetos 3D
por un00brn Dom 11 Ago 2024, 11:10

» Destruição de cenário (estilo DD Tank)
por CoronelZeg Sex 09 Ago 2024, 17:16

» RESOLVIDO-Como destruir uma instancia especifica de um objeto
por josuedemoraes Ter 23 Jul 2024, 00:40

» Automatizar a coleta de id
por GabrielXavier Seg 22 Jul 2024, 18:01

» Preciso de ajuda para concluir um pequeno projeto
por lmoura Qui 27 Jun 2024, 15:45

» ANGULO ACOMPANHAR O OBJETO
por Klinton Rodrigues Qui 27 Jun 2024, 08:34

» Musica reinicia quando sala reinicia
por GabrielXavier Ter 18 Jun 2024, 07:28

» como fazer uma copia de gd
por generico_cube Sex 14 Jun 2024, 15:48

» Square Adventure
por guilherme551 Ter 11 Jun 2024, 09:54

» como posso definir limite de uma variavel
por GabrielXavier Sex 07 Jun 2024, 14:14

» [Resolvido] Dúvida, colisão única de objeto
por vdm842 Sex 24 maio 2024, 09:50


[Tutorial] Com lasers!!!

5 participantes

Ir para baixo

[Tutorial] Com lasers!!! Empty [Tutorial] Com lasers!!!

Mensagem por saim Qua 21 Dez 2011, 14:27

Título: Lasers simples
Nível: Intermediário
Pré-requisitos: Domínio das funções lengthdir_x e lengthdir_y. Domínio dos loops (laços).
Descrição: Esse tutorial mostra uma maneira de lidar com lasers. Não é parte do escopo desse tutorial ensinar a desenhar o laser, embora seja mostrada uma forma de se fazer isso.

Introdução

Você quer fazer um raio laser. Primeiro, vou deixar aqui a minha definição de raio laser em games.
Raio laser é uma linha que parte de um ponto e vai até outro ponto e é interrompida quando colide com um objeto, no ponto em que ocorre essa colisão, podendo ou não alterar alguma variável do objeto colidido
Saim, sua definição é diferente da minha! Eu quero que meu laser vá até o infinito!
Azar o seu. Eu só sei fazer de um ponto a outro. Normalmente é possível substituir "até o infinito" por "muito longe". Espero que sirva.

Uma vez que se defina esses dois pontos, verifica-se se existe algum objeto entre eles. Se houver, defina o objeto como "o objeto que o laser acertou" e defina o ponto final como o ponto imediatamente antes da colisão ocorrer. Repita esse procedimento até que não exista nenhum objeto entre o ponto inicial e o (novo) ponto final. Faça o que tiver que fazer com o objeto colidido e desenhe o laser de um ponto a outro.

Fim

Como fazer isso

Ah, você já sabia que o tutorial não acabava ali? Droga, eu queria fazer surpresa...
Bom, tem duas perguntas principais, aqui.

Como saber se há um objeto colidindo com a linha?
Como saber o ponto imediatamente anterior à colisão?


Pra primeira pergunta, existe uma função específica no game maker. É a collision_line. Ela te retorna a id da instância que colide com a linha definida pelos pontos (que o usuário define nos argumentos). Não havendo nenhuma instância, ela retorna o objeto especial noone. Deixa eu te apresentar os argumentos dela:
collision_line(x1, y1, x2, y2, obj, prec, notme)
x1,y1,x2,y2: Os valores de x e y dos pontos que definem a linha
obj: O objeto a ser verificada a colisão. Pode ser um objeto, um parent ou uma id.
prec: Se a colisão é precisa (true) ou não (false).
notme: Se o objeto que chama a função deve ser ignorado (true) ou levado em conta (false)
Assim, jogamos os pontos do laser nos argumentos da função e pronto, não apenas sabemos se alguém colide com a linha como também sabemos quem é.

A segunda pergunta só faz sentido se houver alguma colisão. Aí, a coisa complica um pouco. Se o laser for prefeitamente ortogonal, podemos simplificar usando os valores de bbox_(left, right, top, bottom) da instância colidida. Mas e se não for? Só temos a id da instância colidida e, por consequencia, sua posição (que, conforme veremos, não será utilizada). O que faremos é ir testando novos comprimentos de laser até chegar naquele que NÃO colide com nada, mas que se aumentar um pouquinho só, já colide. Isso significa ir tentando uma aproximação, ir reduzindo uma variação até um valor de precisão pré-definido. Quando a variação for menor que a precisão, saberemos que encontramos o ponto.
Podemos definir essa variação como um pixel, por exemplo, ou um valor que o jogador não vai notar que o ponto não está exato.

Existem diversas formas de se aproximar, mas a que me parece mais rápida é também a mais simples: soma-se ou subtrai-se (conforme a colisão aconteça ou não) um valor variável, que começa sendo a metade do comprimento do laser e vai sendo dividido por 2. Com poucas iterações reduz-se esse valor a menos que um. Obviamente, quanto maior o valor inicial, mais iterações serão necessárias, o que pesa no processamento.
Melhore esse tutorial:
Assim, estabelece-se a rotina:
0- Verifica-se se é necessária alguma iteração. Se não, desenha-se o laser e fim. Se sim, continua-se com a rotina.
1- Divide-se o comprimento do laser pela metade e define-se o valor inicial da variável a ser somada/subtraída ao comprimento do laser como o novo comprimento total do laser.
2- Verifica-se se há colisão do laser com o objeto a ser colidido (já que o comprimento do laser mudou). Divide-se o valor da variável por 2.
3- Se houve colisão, subtrai-se do comprimento total do laser, o valor da variável. Se não, soma-se esse valor.
4- Verifica-se se o valor da variável ficou menor que a precisão requerida.
5- Se sim, podemos parar com o procedimento, já chegamos à precisão requerida. Se não, voltamos ao passo 2.
Note que seria possível já entrar direto na rotina, ignorando o passo 0, mas acho mais eficiente entrarmos nela apenas nos casos em que ela for necessária. Afinal, mesmo que se chegue no resultado com poucas iterações, podemos ter muitos lasers operando ao mesmo tempo.
Note, também, que havendo mais de uma instância colidida, essa rotina encontrará aquela mais próxima do início do laser.
No passo 2, podemos também armazenar a id colidida em uma outra variável pra alterarmos alguma variável dela, como a vida, por exemplo.

Fim (mesmo) da teoria.

Script

O que eu vou apresentar a seguir é um script que não retorna nada, mas define o valor de algumas variáveis. Ele não retorna nada porque eu precisaria de 3 retornos: o valor em x do fim do laser, o valor em y do fim do laser e a id da instância colidida.
Então, se você quiser usar outros valores de variáveis, fique à vontade pra alterar o script.
Código:
/* uso do script:
** laser(x1, y1, x2, y2, obj, precisão)
** o script verifica se há colisão do laser com uma instancia do objeto "obj".
** havendo, ele aproxima o laser até o ponto de colisão com o objeto
** a id colidida é armazenada na variável "vitima"
** o ponto de colisão é armazenado nas variáveis "xL" e "yL"
*/
var x1, y1, alvo;
x1 = argument0; y1 = argument1; //posição inicial do laser
xL = argument2; yL = argument3; //posição final do laser
alvo = argument4;              //quem o laser vai procurar

//primeiro, verifica-se se o script é mesmo necessário
vitima = collision_line(x1, y1, xL, yL, alvo, 1, 1);
if (vitima == noone){ //se não há colisão
   exit;            //acaba com o script aqui mesmo
   }
//Se ainda estamos aqui, é porque HOUVE a colisão. Precisaremos de mais algumas variáveis.
var prec, comp, ang, soma, novaVitima;
prec = argument5;
comp = point_distance(x1, y1, xL, yL) / 2; //tamanho do laser, já pela metade pra acelerar o processo
ang = point_direction(x1, y1, xL, yL); //ângulo do laser
soma = comp; //valor a ser somado/subtraído no tamanho do laser, até achar o ponto

while (soma >= prec){
   xL = x1 + lengthdir_x(comp, ang); yL = y1 + lengthdir_y(comp, ang);
   novaVitima = collision_line(x1, y1, xL, yL, alvo, 1, 1);
   soma *= 0.5; //diminui o tamanho a ser somado/subtraído
   if (novaVitima == noone){ //se o comprimento atual é menor do que o que dá colisão
      comp += soma;        //aumenta o comprimento
      }
      else {                //se o comprimento atual é maior ou igual ao que dá colisão
         comp -= soma;    //diminui o comprimento
         vitima = novaVitima; //atualiza a instância mais próxima (podendo repetir o valor)
         }
   }

Não acabou ainda???

Bom, isso nos dá o ponto final pra desenharmos o laser e a instância que o laser colide. O que mais precisamos? Precisamos achar uma utilidade pra isso!
A seguir, alguns exemplos.

Desenhar o laser como uma linha
Coloque, no draw event:
Código:
draw_line(x1, y1, xL, yL);
Pode ser substituído por draw_line_color. Recomendo usar vermelho. Fica meio chocho, mas dá pro gasto.

Acertar o objPlayer, diminuir sua vida e permitir que ele se esconda atrás do objParede
Coloque, no step event
Código:
//sendo que x2, y2 é o ponto máximo do laser:
xL = x2; yL = y2;
laser(x1, y1, xL, yL, objParede, 1);
//agora, (xL, yL) é o ponto de colisão com a parede (se houver colisão) ou o ponto máximo, se não houver colisão
laser(x1, y1, xL, yL, objPlayer, 1);
//agora, (xL, yL) é o ponto de colisão com o player (se houver colisão) e não temos certeza do valor de "vitima"
if (vitima != noone){ //se há uma vítima
   if (vitima. object_index == objPlayer){ //se a vítima for o player (ou algum personagem que se machuque)
      vitima. vida -= 1; //faz algo com a vítima
      }
   }
Esse raciocínio serve pra inimigos também, obviamente. Se você usar um parent, pode facilitar alguma coisa.
Uma outra forma de verificar isso seria verificar a colisão diretamente no player e, depois, usar só um collision_line pras paredes, mas aí, você não teria o ponto de colisão na parede. Além do mais, espera-se que seja mais comum o laser acertar paredes do que acertar o player.

Uma bala rápida como uma bala
Jogos de tiro não são muito realistas porque normalmente é possível var a bala, sendo que na vida real, só com uma câmera muito rápida. Se você usar o raciocínio acima no evento de atirar (ao invés de usar o step) e NÃO desenhar o laser, o efeito será o de uma bala atingindo o alvo instantaneamente. O resto do realismo fica por sua conta. Você ainda pode usar o ponto (xL, yL) pra criar um efeito de tijolos sendo estilhaçados ou sangue, pra mostrar pro jogador o ponto que ele atingiu.

laser articulado
Você ainda pode fazer o laser rodar, ficar num canhão que se move, oscilar entra "atirando" e "sem atirar", etc, usando sua criatividade. Mude o valor das coordenadas do ponto final pra fazer o laser mudar de direção. Faça o ponto inicial depender da posição do objeto e você pode prender o laser num canhão que se move. Rode o script apenas quando uma variável for verdadeira e prenda essa variável num alarm e - voilà, você tem um laser intermitente. Use tudo isso num monte de objetos e ao invés de tirar a vida do player, acione um som ao colidir o laser e - pimba! - um jogo de espionagem.

Brinque bastante e, se descobrir novas utilidades para os lasers, comente aqui!

Abraços,

saim

Update!!! (22/12/2011)
Com a dica do Pedrø (logo abaixo), é possível reduzir drasticamente o comprimento inicial do laser e ainda mais drasticamente o tamanho da veriável que será reduzida até o valor da precisão, o que reduz drasticamente o numero de iterações e, consequentemente, aumenta a eficiência do script. Alterei uma coisinha aqui e outra ali, o script ficou com essa cara:
Código:
/* uso do script:
** laser(x1, y1, x2, y2, obj, precisão)
** o script verifica se há colisão do laser com uma instancia do objeto "obj".
** havendo, ele aproxima o laser até o ponto de colisão com o objeto
** a id colidida é armazenada na variável "vitima"
** o ponto de colisão é armazenado nas variáveis "xL" e "yL"
*/
var x1, y1, alvo;
x1 = argument0; y1 = argument1; //posição inicial do laser
xL = argument2; yL = argument3; //posição final do laser
alvo = argument4;              //quem o laser vai procurar (pode ser um objeto, um parent ou uma id)

//primeiro, verifica-se se o script é mesmo necessário
vitima = collision_line(x1, y1, xL, yL, alvo, 1, 1);
if (vitima == noone){ //se não há colisão
   exit;            //acaba com o script aqui mesmo
   }
// Se ainda estamos aqui, é porque HOUVE a colisão. Precisaremos de mais algumas variáveis.
// Mas antes, veremos qual é a vítima mais próxima e usaremos a distância até ela
// pra reduzir o número de iterações.
var vitPrio, prec, comp, ang, soma, novaVitima;
vitPrio = ds_priority_create(); //cria uma lista de prioridades
with(alvo){ //para todas as instâncias de "alvo"
   if (collision_line(x1, y1, other . xL, other . yL, id, 1, 0)) { //se está no caminho do laser
      //entra na lista, com a prioridade sendo a distância até o ponto de origem do laser
      ds_priority_add(vitPrio, id, point_distance(x, y, x1, y1));
      }
   }
vitima = ds_priority_find_min(vitPrio); //vitima passa a ser a instância mais próxima do laser

ds_priority_destroy(vitPrio); //me livro da lista, liberando memória
   
prec = argument5;
comp = point_distance(x1, y1, vitima . x, vitima . y); //tamanho do laser
ang = point_direction(x1, y1, xL, yL); //ângulo do laser
soma = point_distance(0, 0, vitima . sprite_width, vitima . sprite_height); //valor a ser somado/subtraído no tamanho do laser, até achar o ponto

while (soma >= prec){
   xL = x1 + lengthdir_x(comp, ang); yL = y1 + lengthdir_y(comp, ang);
   novaVitima = collision_line(x1, y1, xL, yL, alvo, 1, 1);
   soma *= 0.5; //diminui o tamanho a ser somado/subtraído
   if (novaVitima == noone){ //se o comprimento atual é menor do que o que dá colisão
      comp += soma;        //aumenta o comprimento
      }
      else {                //se o comprimento atual é maior ou igual ao que dá colisão
         comp -= soma;    //diminui o comprimento
         vitima = novaVitima; //só é util pra alguns casos muito específicos
         }
   }
Fiz uns testes de performance e, com esse novo script, coloco 300 canhões na room e consigo uma velocidade de 56 fps, ao passo que com o script anterior, consigo apenas 52.


Última edição por saim em Qui 22 Dez 2011, 16:27, editado 3 vez(es)
saim
saim

Games Ranking : Nota B

Notas recebidas : C-D-A-B
Data de inscrição : 14/01/2011
Reputação : 136
Número de Mensagens : 3033
Prêmios : [Tutorial] Com lasers!!! Empty

Medalhas x 1 Tutoriais x 6 Moedas x 0

Ouro x 1 Prata x 0 Bronze x 3

Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0

Ir para o topo Ir para baixo

[Tutorial] Com lasers!!! Empty Re: [Tutorial] Com lasers!!!

Mensagem por PedroX Qua 21 Dez 2011, 23:52

Alternativas:

Achei o tutorial bem interessante.
Mais um bom tutorial para o fórum.
Parabéns.

Até mais!

_________________


Leia o Manual do Iniciante e a Lista de Tutoriais, para aprender bastante sobre o GM.
Recomendo o Manual completo das colisões, bem útil.
O exemplo Criar um chat (banir, kickar, etc) é interessante.

Para seu jogo ficar rápido e legal, aprenda a Aumentar o desempenho do seu jogo.

Aprenda a calcular a velocidade de suas animações


Entre para o Clube do Inglês:
PedroX
PedroX

Games Ranking : Nota B

Notas recebidas : C+B
Data de inscrição : 26/07/2008
Reputação : 311
Número de Mensagens : 6087
Prêmios : [Tutorial] Com lasers!!! Empty

Medalhas x 0 Tutoriais x 17 Moedas x 0

Ouro x 0 Prata x 0 Bronze x 0

Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0

https://web.whatsapp.com/send?phone=5519995935953&text=Pedro

Ir para o topo Ir para baixo

[Tutorial] Com lasers!!! Empty Re: [Tutorial] Com lasers!!!

Mensagem por primz Qui 22 Dez 2011, 08:20

muito bom tutorial!

para a linha não ficar tão "chocho" pode usar a função:

Código:

draw_line_width(x1, y1, xL, yL,3);
em vez de só:
Código:

draw_line(x1, y1, xL, yL);

para ela ficar mais larga!

eu usei 3 mas podem trocar o valor.

como ficou o meu:

[Tutorial] Com lasers!!! Exeh

Razz
primz
primz

Games Ranking : Nota C

Notas recebidas : C
Data de inscrição : 09/02/2010
Reputação : 2
Número de Mensagens : 321
Prêmios : [Tutorial] Com lasers!!! Empty

Medalhas x 0 Tutoriais x 0 Moedas x 0

Ouro x 0 Prata x 0 Bronze x 1

Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0

http://www.primz.weebly.com

Ir para o topo Ir para baixo

[Tutorial] Com lasers!!! Empty Re: [Tutorial] Com lasers!!!

Mensagem por saim Qui 22 Dez 2011, 09:22

@Pedrø: Excelente dica! Nunca tinha visto utilidade pras Priority Queues, e essa utilidade estava aí, dançando na minha frente! No caso da vítima estar perto do final do laser, acaba sendo mais lento, mas essa é uma situação, por definição, difícil de acontecer (já que o final do laser é localizado no ponto "muito longe"). Só não vou mudar a definição da direção do laser, porque a origem (âncora) da vítima dificilmente estará num ponto da linha do laser.
Não fiz nenhum teste de performance ainda, mas se alguém fosse colocar dinheiro nisso, eu apostaria na sua alternativa.

@primz: Obrigado pela dica, mas usar width é quase tão chocho quanto usar uma linha simples. Já vi um tutorial (em outra comunidade) que usava um sprite especialmente elaborado e image_xscale, que dava um efeito sensacional - o laser ficava todo irregular, vibrante, potente. Parecia realmente um raio de energia nociva.
Não mencionei isso porque foge do escopo do tutorial. Meu objetivo, aqui, é só atender aquela definição do início. Deixei a parte gráfica por conta da criatividade do desenvolvedor.
saim
saim

Games Ranking : Nota B

Notas recebidas : C-D-A-B
Data de inscrição : 14/01/2011
Reputação : 136
Número de Mensagens : 3033
Prêmios : [Tutorial] Com lasers!!! Empty

Medalhas x 1 Tutoriais x 6 Moedas x 0

Ouro x 1 Prata x 0 Bronze x 3

Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0

Ir para o topo Ir para baixo

[Tutorial] Com lasers!!! Empty Re: [Tutorial] Com lasers!!!

Mensagem por Klior Qui 22 Dez 2011, 12:23

Parabéns saim, realmente um ótimo tutorial, tudo muito bem explicado e detalhado.
Klior
Klior

Data de inscrição : 07/03/2010
Reputação : 13
Número de Mensagens : 426
Prêmios : [Tutorial] Com lasers!!! Empty

Medalhas x 0 Tutoriais x 1 Moedas x 0

Ouro x 0 Prata x 0 Bronze x 0

Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0
Plataformas :
  • Game Maker 8.0 ou 8.1
  • C#
  • Javascript


Ir para o topo Ir para baixo

[Tutorial] Com lasers!!! Empty Re: [Tutorial] Com lasers!!!

Mensagem por Stryker Qui 22 Dez 2011, 12:37

Ja fiz um tutorial desse aki ja so que ta numa antiga conta ke foi excluida.
so ke vc feiz com codigos diferentes.

mais ta legal


Aki olha https://gmbr.forumeiros.com/t15799-tutorialtiro-em-top-down?highlight=tiro
sei ke fui banido na kela conta mais to tentando recomeçar


a e eu sei deixa lazer infinito
Stryker
Stryker

Data de inscrição : 15/12/2011
Reputação : 1
Número de Mensagens : 9
Prêmios : [Tutorial] Com lasers!!! Empty

Medalhas x 0 Tutoriais x 0 Moedas x 0

Ouro x 0 Prata x 0 Bronze x 0

Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0

Ir para o topo Ir para baixo

[Tutorial] Com lasers!!! Empty Re: [Tutorial] Com lasers!!!

Mensagem por saim Qui 22 Dez 2011, 17:04

Olhei lá o tutorial, ele repete 9999 vezes uma linha, ou até acertar o alvo. Em nenhuma das duas situações é muito eficiente. Veja bem, supondo que o alvo esteja numa distância de 200 pixels, você vai repetir essa linha 200 vezes. Com essa aproximação progressiva, um alvo a 800 pixels (que é a diagonal de uma room padrão) precisa repetir o código apenas 10 vezes. Com o adendo do Pedrø (e uma idéiazinha minha, que usa a sprite do alvo), apenas 6 vezes.

Realmente seu código funciona, mas recomendo mudar o paradigma usado na aproximação.
saim
saim

Games Ranking : Nota B

Notas recebidas : C-D-A-B
Data de inscrição : 14/01/2011
Reputação : 136
Número de Mensagens : 3033
Prêmios : [Tutorial] Com lasers!!! Empty

Medalhas x 1 Tutoriais x 6 Moedas x 0

Ouro x 1 Prata x 0 Bronze x 3

Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0

Ir para o topo Ir para baixo

[Tutorial] Com lasers!!! Empty Re: [Tutorial] Com lasers!!!

Mensagem por Conteúdo patrocinado


Conteúdo patrocinado


Ir para o topo Ir para baixo

Ir para o topo

- Tópicos semelhantes

 
Permissões neste sub-fórum
Não podes responder a tópicos