Noticias

¡Ayuda a la Comunidad GM, haz una donación!

* Sponsor

Comunidad Game Maker

Bienvenid@ a la comunidad hispana de Game Maker. Nuestro objetivo es crear videojuegos y dar soporte en castellano de GM. Para mejorar nuestro servicio hemos implantado, como adicion al reglamento general, algunas normas especificas en los subforos más comunes. ¡No olvides informarte antes de participar!.

Autor Tema: Problema con ataques según la distancia  (Leído 715 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado Zhekken

en: Marzo 20, 2020, 09:46:07 am
disculpen el nombre de este titulo pero no sabia como ponerle  :'(

necesito ayuda con un juego de Tower Defense, para "resumir" mi duda, pasa que hice un cubo que tiene un rango de alcance de 60 pixeles, cada vez que el objeto enemigo pasa por este rango, se crea una bala que persigue al objeto enemigo con el codigo:

if distance_to_object(obj_enemigo) < obj_torre.rango_vision and object_exists(obj_enemigo){

    move_towards_point(obj_enemigo.x,obj_enemigo.y,5)
    }

quería que mientras la distancia entre el objeto enemigo sea menor que el rango de visión que tiene el objeto torre, y mientras exista el objeto enemigo(esta parte en la que si el objeto exista o no, me confunde y no se si lo puse bien pero sin esto la bala al no detectar un obj_enemigo y ya estar en "persecusion" cuando desaparece el obj_enemigo me manda un error) entonces se creara una bala (esto de que se cree la bala esta en otro código parecido al de arriba solo que esta es para que la bala siga al enemigo y el otro para crear la bala mientras este en el rango) que perseguirá al objeto enemigo.... no se si deba pasar el código completo, pero mi problema es que mi alarma crea la bala después de que el objeto enemigo sale del rango, no me llevo bien con las alarmas :( y también que mi idea era que mientras el objeto enemigo este dentro de este rango el objeto torre pueda disparar balas, y esta sigan al objeto enemigo (esto dando igual si ya salio del rango porque se supone que ya la bala fue disparada)... todo bien en esto pero el problema mas grave es que cuando la bala comienza a perseguir al objeto enemigo, asi haya otro objeto enemigo, la bala sigue persiguiendo al que va de primero, les dejo un gif para que entiendan el problema de forma visual...

Quiza sea muy confusa mi duda, pero quiero que cuando se cree el obj_bala esta persiga al obj_enemigo, pero no que todas las balas persigan a un mismo enemigo si no al que este dentro del rango solamente, osea el objetivo mas cercano en ese instante... mejor dejo los códigos por si acaso y así puedan ayudarme mejor:

obj_torre en Create:

poder = 0
rango_vision = 60
alarm[0] = -1

obj_torre en Step:

rango_vision = 60

if distance_to_object(obj_enemigo) < rango_vision {
    alarm[0] = 2
    }
obj_torre en alarm 0 :

if alarm[0] == 0 {
    instance_create(x,y,obj_bala)
}

obj_bala en step:

if place_meeting(x,y,obj_enemigo){
    instance_destroy()
}

if distance_to_object(obj_enemigo) < obj_torre.rango_vision and object_exists(obj_enemigo){

    move_towards_point(obj_enemigo.x,obj_enemigo.y,5)
    }

el enemigo solo tiene un evento de path para recorrer la ruta...

no espero que vengan a darme todo el código que me falta, si me dan algunas ideas de como razonar de forma lógica lo que hago mal igual me estarían ayudando... muchas gracias por tomarse su tiempo de leer  :)


 


No Tienes Permisos Para Dar Puntos
point 0 Puntos

Este tema no recibió puntos.


Desconectado correojon

  • El azote de los trollers y
  • Legendario
  • *
  • Puntos: 85
  • Mensajes: 4.112
  • Agradecido: 3 veces
  • No mercy
    • Ver Perfil
Respuesta #1 en: Marzo 20, 2020, 12:27:14 pm
Primero, esta parte:
obj_bala.step:
if place_meeting(x,y,obj_enemigo){
    instance_destroy()
}
if distance_to_object(obj_enemigo) < obj_torre.rango_vision and object_exists(obj_enemigo){
    move_towards_point(obj_enemigo.x,obj_enemigo.y,5)
    }
object_exists(obj_enemigo) lo usamos para comprobar si existe alguna instancia del objeto obj_enemigo, por lo que es más aconsejable ponerlo antes de intentar hacer nada con obj_enemigo, como chequear la distancia o ver si estamos chocando con una instancia. Además, si chocas con un enemigo destruyes la bala, por lo que no haría falta seguir comprobando la distancia para moverse. Así que el código quedaría así:
if instance_exists(obj_enemigo){//Ojo, hay que usar instance_exists, no objec_exists. Mira las diferencias en el manual.
    if place_meeting(x,y,obj_enemigo) instance_destroy();
    else if distance_to_object(obj_enemigo) < obj_torre.rango_vision move_towards_point(obj_enemigo.x,obj_enemigo.y,5);
}

Respecto a que las balas sigan sólo al primer enemigo eso es porque estás usando obj_enemigo, que es el nombre genérico del objeto, en vez de usar el id de la instancia concreta que quieres seguir. Si no tienes clara la diferencia entre un objeto y una instancia te recomiendo que compruebes el manual porque esto es algo básico que te va a hacer falta para muchas cosas.
Volviendo a tu duda, lo que tenemos que hacer es identificar al enemigo concreto que ve la torre y decirle a  cada bala que siga a ese enemigo.
obj_torre.step:
if enemigo_detectado==noone{//Si NO hemos detectado a ningún enemigo
    var vEnemy = instance_nearest(x, y, obj_enemigo);//Me guardo la id del enemigo más cercano en una variable temporal
    if point_distance(x, y, vEnemy.x, vEnemy.y) < rango_vision{//Compruebo a ver si hay está dentro del rango
        enemigo_detectado = vEnemy;//Me guardo la id en la variable enemigo_detectado.
       tiempoDisparo = tiempoDisparoMax;//Y pongo la cuenta atrás para el tiempo de disparo
    }
}else{//Ya tenemos un enemigo_detectado
    if tiempoDisparo > 0 tiempoDisparo--;//Reduzco la cuenta atrás para disparar
    else{//Cuando la cuenta atrás se complete
        with (instance_create(x, y, obj_bala)) enemigo = enemigo_detectado;//Creo una bala y le paso la id del enemigo que había detectado
        enemigo_detectado = noone;//Marco que ya no tengo enemigo, así puedo volver a intentar detectar a otro enemigo, o al mismo si sigue dentro del rango
    }
}
He definido unas cuantas variables nuevas que tendrás que inicializar en el evento create:
enemigo_detectado = noone;//id del enemigo detectado o noone si no hemos detectado nada
tiempoDisparo = 0;//Variable para hacer la cuenta atrás del disparo
tiempoDisparoMax = 2;//Valor máximo de la cuenta atrás

Ahora, a obj_bala le tenemos que decir que use la id del enemigo que le hemos pasado. Modificamos el código que teníamos antes y donde usábamos obj_enemigo ponemos enemigo, que es la variable que hemos usado al crear la bala para guardar la id del enemigo más cercano:
obj_bala.step:
if instance_exists(enemigo){
    if place_meeting(x,y,enemigo) instance_destroy();
    else if distance_to_object(enemigo) < obj_torre.rango_vision move_towards_point(enemigo.x,enemigo.y,5);
}
En el evento create de obj_bala tienes que inicializar la variable enemigo = noone;

Con esto te debería funcionar, aunque te va  a pasar que por ejemplo si el enemigo se aleja mucho de la bala la bala se queda parada. Creo que deberías hacer que en este caso la bala siguiera recta y se destruyera al salir de la pantalla, o algo así.

Bueno de todas formas, creo que es importante que consultes el manual y que entiendas bien la diferencia entre objeto e instancia. Mira también las funciones que tienen versiones de objeto e instancia para entender bien para qué sirve cada una.
 
Ánimo!

 
Los siguientes usuarios dieron las gracias a este tema: Zhekken


Desconectado Zhekken

Respuesta #2 en: Marzo 20, 2020, 06:39:20 pm
Primero, esta parte:
obj_bala.step:
if place_meeting(x,y,obj_enemigo){
    instance_destroy()
}
if distance_to_object(obj_enemigo) < obj_torre.rango_vision and object_exists(obj_enemigo){
    move_towards_point(obj_enemigo.x,obj_enemigo.y,5)
    }
object_exists(obj_enemigo) lo usamos para comprobar si existe alguna instancia del objeto obj_enemigo, por lo que es más aconsejable ponerlo antes de intentar hacer nada con obj_enemigo, como chequear la distancia o ver si estamos chocando con una instancia. Además, si chocas con un enemigo destruyes la bala, por lo que no haría falta seguir comprobando la distancia para moverse. Así que el código quedaría así:
if instance_exists(obj_enemigo){//Ojo, hay que usar instance_exists, no objec_exists. Mira las diferencias en el manual.
    if place_meeting(x,y,obj_enemigo) instance_destroy();
    else if distance_to_object(obj_enemigo) < obj_torre.rango_vision move_towards_point(obj_enemigo.x,obj_enemigo.y,5);
}

Respecto a que las balas sigan sólo al primer enemigo eso es porque estás usando obj_enemigo, que es el nombre genérico del objeto, en vez de usar el id de la instancia concreta que quieres seguir. Si no tienes clara la diferencia entre un objeto y una instancia te recomiendo que compruebes el manual porque esto es algo básico que te va a hacer falta para muchas cosas.
Volviendo a tu duda, lo que tenemos que hacer es identificar al enemigo concreto que ve la torre y decirle a  cada bala que siga a ese enemigo.
obj_torre.step:
if enemigo_detectado==noone{//Si NO hemos detectado a ningún enemigo
    var vEnemy = instance_nearest(x, y, obj_enemigo);//Me guardo la id del enemigo más cercano en una variable temporal
    if point_distance(x, y, vEnemy.x, vEnemy.y) < rango_vision{//Compruebo a ver si hay está dentro del rango
        enemigo_detectado = vEnemy;//Me guardo la id en la variable enemigo_detectado.
       tiempoDisparo = tiempoDisparoMax;//Y pongo la cuenta atrás para el tiempo de disparo
    }
}else{//Ya tenemos un enemigo_detectado
    if tiempoDisparo > 0 tiempoDisparo--;//Reduzco la cuenta atrás para disparar
    else{//Cuando la cuenta atrás se complete
        with (instance_create(x, y, obj_bala)) enemigo = enemigo_detectado;//Creo una bala y le paso la id del enemigo que había detectado
        enemigo_detectado = noone;//Marco que ya no tengo enemigo, así puedo volver a intentar detectar a otro enemigo, o al mismo si sigue dentro del rango
    }
}
He definido unas cuantas variables nuevas que tendrás que inicializar en el evento create:
enemigo_detectado = noone;//id del enemigo detectado o noone si no hemos detectado nada
tiempoDisparo = 0;//Variable para hacer la cuenta atrás del disparo
tiempoDisparoMax = 2;//Valor máximo de la cuenta atrás

Ahora, a obj_bala le tenemos que decir que use la id del enemigo que le hemos pasado. Modificamos el código que teníamos antes y donde usábamos obj_enemigo ponemos enemigo, que es la variable que hemos usado al crear la bala para guardar la id del enemigo más cercano:
obj_bala.step:
if instance_exists(enemigo){
    if place_meeting(x,y,enemigo) instance_destroy();
    else if distance_to_object(enemigo) < obj_torre.rango_vision move_towards_point(enemigo.x,enemigo.y,5);
}
En el evento create de obj_bala tienes que inicializar la variable enemigo = noone;

Con esto te debería funcionar, aunque te va  a pasar que por ejemplo si el enemigo se aleja mucho de la bala la bala se queda parada. Creo que deberías hacer que en este caso la bala siguiera recta y se destruyera al salir de la pantalla, o algo así.

Bueno de todas formas, creo que es importante que consultes el manual y que entiendas bien la diferencia entre objeto e instancia. Mira también las funciones que tienen versiones de objeto e instancia para entender bien para qué sirve cada una.
 
Ánimo!

al ver toda tu explicación y que prácticamente me corregiste todo mi código, solo me doy cuenta de que aun me falta mucho por delante para poder hacer a lo que a simple vista para mi era un juego "simple" y quizá exagero pero sin tu ayuda no me hubiera dado cuenta de la diferencia entre "object_exists y instance_exists" con el código que pasaste ya puedo saber mas o menos como detectar una instancia por separado, supongo que esa era la clave para la idea de este juego... la verdad hubiera sido mas "simple" hacer que las balas se disparen una sola vez en dirección al primer objeto(instancia) que detectan, pero así las balas tendrían probabilidad de fallar y eso no me gusta, por eso quería que las balas persigan...

´pase todo tu código al juego pero me da varios errores, y la verdad prefiero dejar el editable por si lo quieres revisar así se te haga mas cómodo...

por cierto si lo vas a revisar(o alguien mas) pido disculpas por el desastre que tengo en mis objetos, es que pensé que seria un proyecto pequeño donde solo haría pocas cosas y termine haciendo un desastre por no querer hacer un Background..


 


Desconectado BssString

Respuesta #3 en: Marzo 22, 2020, 04:36:38 am
Hola Zhekken

La manera en la que funcionan las alarmas, es que Game Maker gestiona una variable como cualquier otra (como X, sprite_index, vspeed, etc), pero que se llama alarm[0].
A esa variable le asignamos un valor para que se active, ej: "alarm[0] = 2". Entonces Game Maker automáticamente al final de cada STEP, le quitará uno al contador (y tendrá valor 1) y cuando llega a cero, se activa el evento "Alarm0".
if distance_to_object(obj_enemigo) < rango_vision {
    alarm[0] = 2
    }
El problema de tu código, es que mientras el enemigo se encuentra en rango, recargas el contador de la alarma a 2, entonces nunca dejas que baje a cero.
La alarma baja de 2 a 1, pero como el enemigo sigue en rango, la alarma se recarga y vuelve a quedar en 2 Steps y nunca se activa sino hasta que el enemigo sale del rango y ahí recién la alarma se puede activar.

Se debe añadir una condición para detectar si la alarma no está contando. Ejemplo:
&& alarm[0] = -1 { hace algo }

El otro problema que veo, es que cuando usas el código:
move_towards_point(obj_enemigo.x,obj_enemigo.y,5)
Le estás diciendo a la bala que se mueva hacia el enemigo, pero no le estás diciendo hacia cuál enemigo.


Solución propuesta:
Mi consejo es que en vez de hacer que la bala siga a un enemigo en específico, siga las coordenadas de ese enemigo.
Así cuando el enemigo muera, las balas que ya fueron creadas e iban en camino, seguirán su trayectoria hacia el cadaver.

Para ello te sugiero dejar lo siguiente
Create Event de la torre:
enemigo = noone //El ID único del objeto más cercano al que quiero que mi bala ataque

Create Event de la bala:
enemigo = noone //Es el enemigo del que tomará las coordenadas la bala. Use el mismo nombre de variable anterior, pero significan cosas diferentes, no las vayas a confundir.
enemigo_x = 0 //Las coordenadas a las que irá mi bala.
enemigo_y = 0
velocidad = 12 //La cantidad de pixeles que avanzará la bala por cada STEP (ponle el valor que gustes, yo usé 12 como ejemplo)

Primero la programación de la torre:

Step Event de la torre:
if alarm[0] = -1 && instance_exists(obj_enemigo) { //Revisa el cooldown de la torreta para ver si NO está disparando actualmente.
var inst = instance_nearest(x, y, obj_enemigo) //Esto trae el ID del enemigo más cercano
if distance_to_object(inst) < rango_vision { //Revisa si el enemigo más cercano está a rango de la torreta
    alarm[0] = 2 //Dispara y pone un cooldown de 2 STEPS en la torreta para que no dispare de nuevo
enemigo = inst //Esto guarda el ID del enemigo más cercano en la variable "enemigo". La necesitará la BALA después para perseguirlo
    }
}

Alarm0 de la torre:
if instance_exists(enemigo) { //Primero revisa que el enemigo no haya muerto durante estos 2 Steps de Cooldown. Si el enemigo murió, entonces no dispara nada.
var inst = instance_create(x,y,obj_bala) //Después de los 2 Steps, crea la bala y guarda el ID de la nueva bala creada en la variable "inst".
inst.enemigo = enemigo //Le dice a la bala a qué enemigo debo perseguir. La variable "enemigo" del objeto "inst" (o sea la bala), toma ahora el valor de la variable "enemigo" de la torre.
inst.enemigo_x = enemigo.x //Le dice a la bala a qué coordenada debe ir
inst.enemigo_y = enemigo.y //Le dice a la bala a qué coordenada debe ir
}

Step de la Bala
if instance_exists(obj_enemigo) { //Si aún existe el enemigo, recarga las coordenadas a las que me debo dirigir.
enemigo_x = enemigo.x
enemigo_y = enemigo.y
}
//Ahora me muevo
if point_distance(x,y,enemigo_x,enemigo_y) <= velocidad { //Si la velocidad de la bala es menor a la distancia que tiene contra el enemigo, teletransporta la bala hacia el enemigo.
x = enemigo_x;
y = enemigo_y
if instance_exists(enemigo) enemigo.vida -= 4 //Un ejemplo de cómo quitarle vida al enemigo, esto reduce en 4 puntos el valor de la variable "Vida" del objeto enemigo que recibió la bala.
instance_destroy() //Destruye la bala porque ya llegó a su objetivo
}
else {
x += lengthdir_x(velocidad,point_direction(x,y,enemigo_x,enemigo_y)) //Esto hace que la bala avance hacia las últimas coordenadas registradas por el enemigo
y += lengthdir_y(velocidad,point_direction(x,y,enemigo_x,enemigo_y))
}

Saludos

 


Desconectado Zhekken

Respuesta #4 en: Marzo 22, 2020, 07:05:31 am

if instance_exists(enemigo) { //Primero revisa que el enemigo no haya muerto durante estos 2 Steps de Cooldown. Si el enemigo murió, entonces no dispara nada.
var inst = instance_create(x,y,obj_bala) //Después de los 2 Steps, crea la bala y guarda el ID de la nueva bala creada en la variable "inst".
inst.enemigo = enemigo //Le dice a la bala a qué enemigo debo perseguir. La variable "enemigo" del objeto "inst" (o sea la bala), toma ahora el valor de la variable "enemigo" de la torre.
inst.enemigo_x = enemigo.x //Le dice a la bala a qué coordenada debe ir
inst.enemigo_y = enemigo.y //Le dice a la bala a qué coordenada debe ir
}


Hola BssString muchas gracias por tomarte tu tiempo en tratar de ayudarme con tu conocimiento  :D ... entiendo lo de la alarma gracias a tu explicación, yo lo solucione pero sin usar las alarmas con este código:

evento create de la torre:
contador = 0

evento step de la  :

if instance_exists(obj_enemigo)and distance_to_object(obj_enemigo) <= rango_vision {
    contador += 1
    if contador == 20 {
        instance_create(x,y,obj_bala)
        contador = 0
}

pero aun así con tu código esta mucho mejor... la verdad tenia muchas ganas de probarlo pero hay un error en la alarma de la torre:

inst.enemigo = enemigo

por el punto me dice: Unexpected symbol in expression.

y ahora eso me confundió porque no se exactamente que quisiste colocar ahí.


 


Desconectado BssString

Respuesta #5 en: Marzo 22, 2020, 01:31:30 pm
Hola Zhekken

El error que ves: "Unexpected symbol in expression" es en realidad un bug de Game Maker. Cuando se usa la palabra "var" y se termina la línea en una función o script (como el instance_create) Game Maker no cierra la línea y arroja ese bug.

Para corregirlo, a la fila anterior, cierra la línea manualmente con un signo punto y coma " ; " y se arregla:
var inst = instance_create(x,y,obj_bala); //Con el signo al final ya funciona bien

El problema fue que cree el código sin probarlo en mi GM antes y me olvidé de ese bug.

Citar
y ahora eso me confundió porque no se exactamente que quisiste colocar ahí.
Lo que hice fue crear una variable local en el evento alarma llamada "inst". Esa variable local existe sólo en el evento que se llama y luego se borra, eso quiere decir que la puedes editar y leer sólo durante la ejecución de ese evento alarm. Si la intentas leer en el evento STEP, te dirá que la variable ya no existe.
Hacer que la torre cree una bala, luego guarda el ID único que la bala que acaba de crear en la variable "inst" para poder manipular las variables de sólo esa nueva bala sin afectar a las balas que ya existen.

Luego la expresión:
inst.enemigo = enemigo
El problema fue que a la bala y a la torre les cree la misma variable "enemigo", por eso es un poco confuso, pero significan cosas diferentes.
La variable "enemigo" de la bala es el Instance ID del enemigo que tengo que perseguir hasta la muerte.
La variable "enemigo" de la torre es el Instance ID del objeto enemigo más cercano a la torre.

Entonces le estoy diciendo a la bala que acabo de crear, que persiga a muerte al enemigo que actualmente está más cercano a la torre.
Es como hacer:
obj_bala.x = 12, pero si hiciera eso, se cambiaría la variable de todos los "obj_bala" del juego y eso no es lo que se quiere.

Saludos

 
Los siguientes usuarios dieron las gracias a este tema: Zhekken


Desconectado Zhekken

Respuesta #6 en: Marzo 23, 2020, 12:23:16 pm
BssString no sabia que era un bug de GM, como este no utiliza los puntos y comas, no me imagine que seria por eso, supongo que sera lo único que usa ; ... ""y ahora eso me confundió porque no se exactamente que quisiste colocar ahí."" esto lo había dicho porque como en:
inst.enemigo = enemigo 
  el punto que me daba error, creí que debía ir otra cosa, osea algo así como un _ para una variable, por eso no sabia si querías colocar algo que no fuera el punto pero ya con lo del bug quedo aclarado  :) ... ya el código no da ningún error y funciona perfecto, muchas gracias por tu ayuda, aunque de hecho debería agradecerte por todo el código  :( ya que lo corregiste completo y yo prácticamente no hice nada, creí que iba por buen camino y solo me iban a corregir algunas cosas x.x pero fue todo... aun me falta mucho por delante, creí  que hacer esto no seria tan difícil pero se me complico mas de lo que pensaba.

también gracias a correojon porque ambos códigos eran parecidos y iban por una misma idea.

Muchas gracias.

por cierto así quedo el "juego" con el código de BssString y se nota demasiado la diferencia con el primer Gif que pase:


 


 


Warning: Parameter 1 to spoiler_buffer() expected to be a reference, value given in Unknown on line 0

Warning: Parameter 1 to custom_report_ob() expected to be a reference, value given in Unknown on line 0