[Ajuda] Tiro seguir objeto dentro do alcance

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

[Ajuda] Tiro seguir objeto dentro do alcance

Mensagem por Harus em Sab 01 Jun 2013, 12:11

Olá a todos!

Venho acompanhando o fórum há algum tempo e estou tentando aprender a usar o GM por conta, acompanhando os tutoriais e as dúvidas do pessoal.
Sou iniciante e ainda um pouco dependente de D&D.

Estou tentando criar um game no estilo tower defense e me deparei com o seguinte problema:

[img][/img][img][/img]

Preciso atender os seguintes aspectos:

- A torre começa a atacar quando um inimigo entra no alcance dela.
- Quando não tem inimigos dentro do alcance, a torre não ataca.
- No caso de dois ou mais inimigos dentro do alcance, a torre da preferência para atacar o que estiver mais próximo do final do caminho.

Para saber quando o inimigo está próximo eu uso o comando: if (distance_to_object (obj_enemy) < range)
Isso ativa um alarm que cria a instância do tiro.
No Step do tiro eu uso: move_towards_point(instance_nearest(x,y,obj_enemy).x,instance_nearest(x,y,obj_enemy).y, 8 )

O problema de usar esse comando é que ele ataca o inimigo mais próximo da torre, e não o mais próximo do final do caminho.

Tentei criar um objeto do final do caminho chamado de obj_end_line e trocar o Step do tiro pelo seguinte movimento:
move_towards_point(instance_nearest(obj_end_line.x,obj_end_line.y,obj_enemy).x,instance_nearest(obj_end_line.x,obj_end_line.y,obj_enemy).y, 8 );

Agora ele ataca o inimigo que está a frente, mas tem um problema crucial: O tiro não segue necessáriamente o inimigo que está dentro do alcance da torre, ele segue livremente pelo mapa o inimigo mais próximo do obj_end_line. Por exemplo, na imagem acima, como tem inimigos próximos da primeira torre, os tiros desta primeira torre estariam seguindo o primeiro inimigo lá da frente.

Eu preciso de um comando que escolha dentre os inimigos no alcance da torre, o que está mais próximo do final do caminho.

Não sei se fui bem claro, hehe.

Estou quebrando a cabeça aqui...

Desde já agradeço pela atenção;

Harus

Número de Mensagens : 2
Data de inscrição : 19/01/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0

Voltar ao Topo Ir em baixo

Re: [Ajuda] Tiro seguir objeto dentro do alcance

Mensagem por PedroX em Sab 01 Jun 2013, 13:53

Você precisa de uma condição composta (dupla). O problema é um pouco complexo, mas podemos usar uma ds_priority...

Primeiro, vamos criar uma lista das instâncias ao alcance da torre, em ordem crescente de distância (prioridade). Esse código deve ser posto dentro do evento (ou código) que dispara o tiro da torre.

Código:
ao_alcance = ds_priority_create();
with (OBJ_INIMIGO) {
 if (distance_to_object(OBJ_TORRE)<other.alcance) {
    ds_priority_add(other.ao_alcance, id, distance_to_object(other));
}
}
if (!ds_priority_empty(ao_alcance))
{
mais_proximo = ds_priority_find_min(ao_alcance);
while(mais_proximo != instance_nearest(obj_end_line.x, obj_end_line.y) && !ds_priority_empty(ao_alcance))
{
mais_proximo = ds_priority_delete_min(ao_alcance); //deleta, mas pega o valor também (embora o nome da função não indique isso)
}
}
ds_priority_destroy(ao_alcance);
move_towards_point(mais_proximo.x, mais_proximo.y, VELOCIDADE );

Espero que sirva, pois pesquisei bastante as funções.

Ver:
http://wiki.yoyogames.com/index.php/GML_Functions:_Priority_Queues
http://gmbr.forumeiros.com/t19975-tutorial-com-lasers#149793

Até mais!

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: [Ajuda] Tiro seguir objeto dentro do alcance

Mensagem por saim em Sab 01 Jun 2013, 18:36

Calma, PedroX, coisas simples primeiro. É o primeiro post do cara, ele ainda está usando DND.

MAS você não está errado em nada. Sua solução é ótima. Só que data structures são um tanto complexas pra um primeiro contato com a comunidade. Vou tentar uma aproximação mais simples. Embora você esteja certo também em afirmar que NÃO É um problema simples.

Primeiro, aquilo que não aparece na pergunta: como os inimigos fazem a curva? É um path? Se for, tem uma variável neles que você pode usar pra saber quem está mais à frente: path_position. Ela te armazena um número de 0 a 1 que representa o quanto do path o objeto já percorreu. Nem preciso dizer qual número representa um objeto mais perto do final, certo?

Agora, pra torre atirar, ela primeiro vai verificar se tem ALGUÉM ao alcance. Tendo, atira. Mas, se for atirar, que seja no que está mais adiantado. Certo?
Vou começar usando seu código:

Código:
if (distance_to_object (obj_enemy) < range){
// vai sair tiro
}

Aí, dentro do código genérico que eu coloquei como comentário, você vai:
- escolher o inimigo com a maior path_position (e que esteja na linha de tiro)
- criar um tiro
- explicar pro tiro quem é a vítima

O maior problema é escolher a vítima, mas com um pouco de gml e geometria, fica mole. Com gml você vai aprender a usar a declaração "with" e as variáveis "var".
Variáveis "var" só existem dentro de um código. Elas não estão em nenhum objeto, estão naquele código e morrem no final dele. Além de serem leves na memória, você pode entrar numa declaração "with" com elas sem se preocupar com o objeto em que elas estão.
Uma declaração "with" é percorrida em todos os objetos que atendem pelo nome dado no argumento. Isso pode ficar bem complexo, mas por enquanto vamos considerar que "with(obj_inimigo)" vai rodar um código em todos os obj_inimigo.

Vamos melhorar um pouco nosso código, então.
Código:
if (distance_to_object (obj_enemy) < range){ // se é pra atirar
var vitima;
with(obj_enemy){ // aqui eu começo a rodar códigos dentro dos inimigos
// se está na linha de tiro e é o mais próximo do final
vitima = self; // note que "vitima" é uma variável "var".
} // aqui eu volto pra torre
with(instance_create(x, y, obj_tiro){ // aqui eu crio um tiro - e já começo a escrever nele
alvo = vitima;
} // aqui eu paro de escrever no tiro
}
O que é "self"? É a id do objeto. Pode ser substituído por "id". Na verdade, ninguem usa "self". E o que é id? É um número gerado automaticamente pra cada instância, que a representa. Cada instância sabe sua id e tem um monte de funções que te retornam o número das ids. Informação valiosa, anote isso num papel.

A segunda parte do código já está explicada. Já verifiquei se é pra criar um tiro e já sei quem é a vítima. Falo pro tiro quem é o coitado que o tiro vai saber quem seguir.
A parte da geometria. Basicamente, o conceito envolvido é: se a sua distância até a mim é x, a minha distância até você é x. Bem básico.
É o seguinte, eu só usei o range da torre pra saber se é pra criar um tiro, ainda não expliquei que o tiro deve seguir um inimigo que está na linha de tiro.
Quem vai explicar isso é o inimigo. Não conta isso pro jogador, tá?

penúltima parte:
Código:
if (distance_to_object (obj_enemy) < range){ // se é pra atirar
var vitima;
with(obj_enemy){ // aqui eu começo a rodar códigos dentro dos inimigos
if (distance_to_object(other) < other . range) // && tá mais perto do fim
vitima = self; // note que "vitima" é uma variável "var".
} // aqui eu volto pra torre
with(instance_create(x, y, obj_tiro){ // aqui eu crio um tiro - e já começo a escrever nele
alvo = vitima;
} // aqui eu paro de escrever no tiro
}

Tem esse "other". Ele não servia só pra colisão?
Não, ele é útil pra caramba. De fato, num evento de colisão, "other" significa "o objeto colidido". Mas dentro de um "with", significa "a instância que chamou o código". Eu gosto de chamar de "a instância original".

Mole? É, eu sei. Mas o difícil já ficou pra trás. De agora pra frente fica mole, mesmo.
Também, já está quase acabando.

Código:
if (distance_to_object (obj_enemy) < range){ // se é pra atirar
var vitima, dist;
dist = 0; // o path_position da vitima (começa com 0)
with(obj_enemy){ // aqui eu começo a rodar códigos dentro dos inimigos
if (distance_to_object(other) < other . range && path_position > dist)
vitima = self; // note que "vitima" é uma variável "var".
dist = path_position; // pulo do gato
} // aqui eu volto pra torre
with(instance_create(x, y, obj_tiro){ // aqui eu crio um tiro - e já começo a escrever nele
alvo = vitima;
} // aqui eu paro de escrever no tiro
}
"Não saquei o pulo do gato..."
seguinte... "dist" começa como zero. não tem como ser menor que isso. Qualquer inimigo que não esteja fora do alcance vai ter um path_position maior que esse. Aí, se ele atender aos requisitos do "if" (estar ao alcance da torre E ter um valor de path_position maior que "dist"), ele vai ser a próxima vítima. Antes de criar o tiro, eu vou correr esse código em todos os obj_enemy, garantindo que o valor de "vitima" vai ser a id da instância DENTRO da linha de tiro e que esteja mais perto do final.

UFA! E eu comecei a escrever porque achei a resposta do PedroX complexa... Foi mal, PedroX, eu acabei complicando a coisa, também. Mas agora já escrevi pra caramba, não vou jogar fora.
Espero que o código esteja bem explicado, de qualquer forma.

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: [Ajuda] Tiro seguir objeto dentro do alcance

Mensagem por PedroX em Sab 01 Jun 2013, 18:44

Gostei da sua solução. Na verdade, eu fiquei pensando um bom tempo antes de responder e só pensei em ds_priority. O motivo foi que um usuário me perguntou recentemente, por MP, qual era o tópico que você [saim] fez e que eu [Pedro] tinha postado uma solução com ds_priority. É o tutorial "Com lasers", do link que coloquei num post acima.

De qualquer forma, são 2 maneiras de chegar ao mesmo resultado. Ambas parecem funcionar sem bugs (não testei nenhuma). O que não faltou foi solução.

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: [Ajuda] Tiro seguir objeto dentro do alcance

Mensagem por Harus em Sab 01 Jun 2013, 23:53

Primeiramente, agradeço pela ajuda!

Consegui adaptar no game o método do Saim. Aliás, está muito bem explicado. Já deu pra perceber que sou obrigado a desenvolver cada vez mais o gml, porque D&D é bem limitado, o que eu já esperava... O fato de conseguir manipular diferentes objetos, escrever neles, tomar ações com o other, e tudo isso dentro de um único script facilita demais a vida!

Só deu certo usando "id" ao invés de "self"... Não sei se tem a ver com o fato de estar usando GM Studio.

Também tentei o método do PedroX. Tentei, tentei mesmo fazer funcionar, mas não consigo...É bem provável que eu esteja fazendo algo errado, mas ele da erro quando um inimigo morre, ou o tiro se perde e fica indeciso em qual inimigo atacar, enfim, aconteceu variados problemas conforme eu tentava adaptar.

De qualquer modo foi tudo muito útil e aprendi mais um pouco.

Muito obrigado!

Harus

Número de Mensagens : 2
Data de inscrição : 19/01/2013
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0

Voltar ao Topo Ir em baixo

Re: [Ajuda] Tiro seguir objeto dentro do alcance

Mensagem por saim em Seg 03 Jun 2013, 06:27

Harus escreveu:Só deu certo usando "id" ao invés de "self"... Não sei se tem a ver com o fato de estar usando GM Studio.
Não sei o que pode ser. Não acho que seja o GMS, nunca ouvi falar de eliminarem essa variável (que, de fato, não faria falta).
Harus escreveu:da erro quando um inimigo morre, ou o tiro se perde e fica indeciso em qual inimigo atacar, enfim, aconteceu variados problemas conforme eu tentava adaptar.
Ehm... meu método também não prevê o que fazer quando o inimigo morre e o tiro já foi criado...
Bom, eu presumi (corretamente) que você soubesse o que fazer com o tiro, uma vez que ele estivesse criado. Ele deve se mover towards alvo.x, alvo.y. Mas "alvo" é a id de uma instância que pode ser eliminada antes do tiro chegar lá. Por exemplo, duas torres podem atirar ao mesmo tempo e o tiro de uma pode matar o obj_enemy antes do tiro da outra chegar. Nesse caso, o tiro restante vai tentar se mover em direção a uma instância que não existe mais. O resultado é o erro "unknown variable 'x'".

Solução? Mover-se em direção a uma instância que exista. No step, mova-se apenas se a instância existir.
Código:
if (instance_exists(alvo)){
move_towards(alvo.x, alvo.y, velocidade); // não sei se é desse jeito
}
else{
// elimina o tiro (o jogador não deve notar), escolhe outro alvo, sei lá
}

Você também pode gravar a posição que o alvo está e, se ele não existir mais, rumar pra essa posição e explodir quando a distância até ela for menor que a velocidade (dificilmente o tiro vai ficar EXATAMENTE na posição correta). Ou, dentro do inimigo, quando ele estiver morrendo, rodar um
Código:
with(obj_tiro){ // roda em todos os tiros existentes
if (alvo == other){ // escolhe apenas aqueles que me acertariam
// fazer alguma coisa
}
}
// pronto, já pode morrer
Seja qual for a solução, é importante prever esse caso do alvo simplesmente sumir e o tiro ficar sem saber o que fazer.

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: [Ajuda] Tiro seguir objeto dentro do alcance

Mensagem por Conteúdo patrocinado Hoje à(s) 18:15


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