Como el titulo es muy genérico, mostrare un ejemplo de lo que quiero hacer.
(https://i.gyazo.com/b5ab2543486cbabfa96e407748e9c665.gif)
Como pueden el comportamiento de este enemigo/trampa, es moverse de forma lineal cuando detecta al jugador, en este caso es cuando lo detecta verticalmente hacia abajo, lo que vendría siendo una coordenada "Y" positiva, pero no solo va al ataque del jugador cuando pasa por lo que seria su rango de visión (abajo en este caso), sino que se mantiene quieto por unos segundos, después empieza a subir hasta llegar a su lugar de origen lentamente, mientras esta asiendo la recarga de volver a su lugar de origen no puede atacar, y solo se detiene cuando golpea el suelo, las posibilidades que tendría un objeto con estas cualidades no se encuentran en muchos juegos de este genero, ademas el código para crearlo no se muestra en algún tutorial, probablemente por que sea un elemento muy rebuscado, incluso en mi juego "Red Heart" ningún enemigo o jefe tiene cambios de estado, excepto el jefe final, pero utiliza patrones de movimiento de los "paths", por lo que este seria el enemigo con la IA mas compleja en comparación a los demás, como todo inicio tengo el sprite y el objeto que recibirá los códigos, probablemente tenga que usar los eventos "CREATE","STEP" y tal vez alguna "ALARMA" ya que este enemigo tiene cambios de estado que hacen un siclo una vez que cumplió su acción, aquí ya es la hora de la madrugada y creo que desvelarme no fue buena idea :-\.
Bueno para hacer ese tipo de programacion necesitarás una variable de estado, que controle la accion que está realizando tu enemigo.
Para ello se puede dividir en todos los estados que describes en el problema:
1) esperando
2) cayendo
3) quieto (o esperando N segundos)
4) subiendo
Entonces primero usarías una variable llamada estado
Ev_create
[gml]
estado = "vigilando" // vigilando que el player pase por debajo
/*
otros valores
"cayendo"
"esperando"
"subiendo"
*/
[/gml]
Luego los estados deberían ser checkeados según sea el caso. Por ejemplo si está vigilando, es logicamente necesario, que lo haga todo el tiempo, por lo que podría bien ir en el evento step.
Una vez, el enemigo encuentra al jugador debajo, automaticamente cambiaría su estado a "cayendo" por que quiere aplastar al protagonista.
EV_STEP
[gml]
if (estado == "vigilando")
{
if (place_meeting(x,y+64, o_player))
estado = "cayendo";
}
[/gml]
Una vez determinado cuándo debe caer, se debe animar al enemigo para que caiga. Eso depende de cómo quieres que se haga: usando gravedad, o bien solo moviendolo verticalmente con vspeed o sencillamente moverlo en la coordenada Y.
EV_STEP
[gml]
if (estado == "cayendo")
y = y + 1;
[/gml]
Entonces caerá infinitamente.
Ahora, podríamos colocar el evento más logico, de todos, que cuando toca el suelo, espere un momento. Entonces, puedes colocar esta lógica dentro del evento de colision del enemigo contra el objeto que haga de suelo.
EV_COLISION_CON_EL_SUELO
[gml]
if (estado == "cayendo") // si estaba cayendo hacia abajo, y choca con el suelo, esperar unos segundos
estado = "esperando"
[/gml]
Ahora como el estado cambió de "cayendo" a "esperando", el objeto dejará de moverse hacia abajo, sin mucho codigo.
Podemos implementar la espera dentro del codigo anterior ya que se ejecuta una única vez al colisionar si y solo si esta cayendo. Entonces lo apropiado sería llamar ahí la alarma.
[gml]
if (estado == "cayendo"){ // si estaba cayendo hacia abajo, y choca con el suelo, esperar unos segundos
estado = "esperando";
alarm[0] = room_speed * 3; //espera aproximadamente 3 segundos
}
[/gml]
Luego , nuevamente cambiamos el estado del enemigo para que suba. Claramente si se llama a la alarma0 hay que colocar el codigo allí.
Colocamos el estado del enemigo en "subiendo", para que suba...
EV_ALARM[0]
[gml]
estado = "subiendo";
[/gml]
Y en el evento step añadimos el codigo para que suba, claro XD
EV_STEP
[gml]
if (estado == "cayendo")
y = y + 1;
if (estado == "subiendo")
y = y - 1;
[/gml]
Luego con la misma logica de que al caer pare, cuando suba... bueno, es exactamente igual :P
EV_COLISION_CON_EL_SUELO
[gml]
if (estado == "subiendo") // si estaba subiendo, ahora dejarlo vigilando
estado = "vigilando"
[/gml]
Honestamente no sé si funcione XD no lo probé, pero debería.
Y sino, ahi tienes toda la lógica que necesitas
Cita de: FridaFlowers en Septiembre 14, 2017, 03:59:39 PM
Bueno para hacer ese tipo de programacion necesitarás una variable de estado, que controle la accion que está realizando tu enemigo.
Para ello se puede dividir en todos los estados que describes en el problema:
1) esperando
2) cayendo
3) quieto (o esperando N segundos)
4) subiendo
Entonces primero usarías una variable llamada estado
Ev_create
[gml]
estado = "vigilando" // vigilando que el player pase por debajo
/*
otros valores
"cayendo"
"esperando"
"subiendo"
*/
[/gml]
Luego los estados deberían ser checkeados según sea el caso. Por ejemplo si está vigilando, es logicamente necesario, que lo haga todo el tiempo, por lo que podría bien ir en el evento step.
Una vez, el enemigo encuentra al jugador debajo, automaticamente cambiaría su estado a "cayendo" por que quiere aplastar al protagonista.
EV_STEP
[gml]
if (estado == "vigilando")
{
if (place_meeting(x,y+64, o_player))
estado = "cayendo";
}
[/gml]
Una vez determinado cuándo debe caer, se debe animar al enemigo para que caiga. Eso depende de cómo quieres que se haga: usando gravedad, o bien solo moviendolo verticalmente con vspeed o sencillamente moverlo en la coordenada Y.
EV_STEP
[gml]
if (estado == "cayendo")
y = y + 1;
[/gml]
Entonces caerá infinitamente.
Ahora, podríamos colocar el evento más logico, de todos, que cuando toca el suelo, espere un momento. Entonces, puedes colocar esta lógica dentro del evento de colision del enemigo contra el objeto que haga de suelo.
EV_COLISION_CON_EL_SUELO
[gml]
if (estado == "cayendo") // si estaba cayendo hacia abajo, y choca con el suelo, esperar unos segundos
estado = "esperando"
[/gml]
Ahora como el estado cambió de "cayendo" a "esperando", el objeto dejará de moverse hacia abajo, sin mucho codigo.
Podemos implementar la espera dentro del codigo anterior ya que se ejecuta una única vez al colisionar si y solo si esta cayendo. Entonces lo apropiado sería llamar ahí la alarma.
[gml]
if (estado == "cayendo"){ // si estaba cayendo hacia abajo, y choca con el suelo, esperar unos segundos
estado = "esperando";
alarm[0] = room_speed * 3; //espera aproximadamente 3 segundos
}
[/gml]
Luego , nuevamente cambiamos el estado del enemigo para que suba. Claramente si se llama a la alarma0 hay que colocar el codigo allí.
Colocamos el estado del enemigo en "subiendo", para que suba...
EV_ALARM[0]
[gml]
estado = "subiendo";
[/gml]
Y en el evento step añadimos el codigo para que suba, claro XD
EV_STEP
[gml]
if (estado == "cayendo")
y = y + 1;
if (estado == "subiendo")
y = y - 1;
[/gml]
Luego con la misma logica de que al caer pare, cuando suba... bueno, es exactamente igual :P
EV_COLISION_CON_EL_SUELO
[gml]
if (estado == "subiendo") // si estaba subiendo, ahora dejarlo vigilando
estado = "vigilando"
[/gml]
Honestamente no sé si funcione XD no lo probé, pero debería.
Y sino, ahi tienes toda la lógica que necesitas
Funciona bien, lo arme de esta forma.
CREATE
///Variables
estado = "vigilando" // vigilando que el player pase por debajo
/*
otros valores
"cayendo"
"esperando"
"subiendo"
*/
ALARMA 0
///Retorno
estado = "subiendo";
STEP
///Estados de movimiento
if (estado == "vigilando")
{
if (place_meeting(x,y+64, obj_Jugador))
estado = "cayendo";
}
if (estado == "cayendo")
y = y + 3;
if (estado == "subiendo")
y = y - 3;
COLISION con pared
///Detener el movimiento
if (estado == "cayendo"){ // si estaba cayendo hacia abajo, y choca con el suelo, esperar unos segundos
estado = "esperando";
alarm[0] = room_speed * 3; //espera aproximadamente 3 segundos
}
if (estado == "subiendo") // si estaba subiendo, ahora dejarlo vigilando
estado = "vigilando"
(https://i.gyazo.com/7ea7622a82052842c0f7dd82b699eaa5.gif)
Pero hay un problema cuando detecta al jugador, y es que debe estar a una distancia "Y" precisa, es decir si paso verticalmente bajo el estando alejado no se moverá, por lo que no cubre la linea "Y" bajo el, sino que solo se activa cuando el jugador toca una coordenada ya establecida, en este caso es 64 correspondiente a este código.
if (estado == "vigilando")
{
if (place_meeting(x,y+64, obj_Jugador))
estado = "cayendo";
}
jugue con su valor cambiandolo, por ejemplo 164, simplemente le agregue un 1 por delante, descubri que si paso serca de el ahora no se movera verticalmente hacia abajo como deberia hacerlo, solo me atacara si paso por la coordenada "Y" 164, entonces el problema se resume asi.
El enemigo solo te ataca cuando pasas por la "Y" especificada y no por la linea "Y".
La idea es que su rango de vision sea toda la Y bajo el como se tenia especiicado desde un principio.
Para no haberlo probado, debo admitir que ni yo esperaba que funcionara a la primera :-[, pensé que seria mas complejo de programar, arreglando ese pequeño detalle cuando detecta al jugador, seria un enemigo terminado, y por ende este post, si algo se me ocurre es que hay unas funciones de rango que dicen que si algo se encuentra entre "X" números, se debe ejecutar la acción programada, probablemente se deba indicar un numero como 0 a un numero como 320, se esa forma cada que el jugador pase por esos 320 valores de "Y" se activara en vez de depender de 1 solo valor, bueno hasta el momento ha sido un buen avance :).
CitarPero hay un problema cuando detecta al jugador, y es que debe estar a una distancia "Y" precisa, es decir si paso verticalmente bajo el estando alejado no se moverá, por lo que no cubre la linea "Y" bajo el, sino que solo se activa cuando el jugador toca una coordenada ya establecida, en este caso es 64 correspondiente a este código.
Eso se puede arreglar con un [gml] collision_line [/gml]
Cita de: TheWood en Septiembre 15, 2017, 01:28:16 AM
CitarPero hay un problema cuando detecta al jugador, y es que debe estar a una distancia "Y" precisa, es decir si paso verticalmente bajo el estando alejado no se moverá, por lo que no cubre la linea "Y" bajo el, sino que solo se activa cuando el jugador toca una coordenada ya establecida, en este caso es 64 correspondiente a este código.
Eso se puede arreglar con un [gml] collision_line [/gml]
En mi poco mas de un año que llevo usando :GMS:, jamas he visto en guías y vídeos que alguien use funciones que se escriban con "collision", salvo el evento colisión, pero en funciones seria un elemento del que no tenia idea de su existencia, al parecer hay 5 tipos por lo que me da el buscador de :GMS:, pero mi conocimiento de como implementarlo en este código enemigo es nulo :-\.
http://docs.yoyogames.com/source/dadiospice/002_reference/movement%20and%20collisions/collisions/collision_line.html
La función devuelve la id de cualquiera de las instancias que estén dentro de la colisión.
Cita de: NiuWeb en Septiembre 15, 2017, 02:44:07 AM
http://docs.yoyogames.com/source/dadiospice/002_reference/movement%20and%20collisions/collisions/collision_line.html
La función devuelve la id de cualquiera de las instancias que estén dentro de la colisión.
Pues tendría esto.
///Estados de movimiento
if (estado == "vigilando")
{
//if (place_meeting(x,y+64, obj_Jugador))
if (place_meeting(x, collision_line (y (obj_Aplastador, obj_Aplastador, obj_Jugador, obj_Jugador, obj_Jugador, true, true))));
estado = "cayendo";
}
if (estado == "cayendo")
y = y + 3;
if (estado == "subiendo")
y = y - 3;
y logrado esto
(https://i.gyazo.com/ffcc8391df12b3062cbad3d1a872c5a0.gif)
claramente esta mal armado, como dice el error, pero no se como debería estructurarlo para que toda la linea vertical bajo el objeto se active cuando el jugador pase :-\, como ya e dicho es un elemento nuevo que nunca he usado.
Sería así:
[gml]
if (estado == "vigilando")
{
if(collision_line (x, y, x, y + 64, obj_jugador, true, true));
estado = "cayendo";
}
[/gml]
Cita de: NiuWeb en Septiembre 15, 2017, 06:32:37 AM
Sería así:
[gml]
if (estado == "vigilando")
{
if(collision_line (x, y, x, y + 64, obj_jugador, true, true));
estado = "cayendo";
}
[/gml]
///Estados de movimiento
if (estado == "vigilando")
{
if(collision_line (x, y, x, y + 64, obj_Jugador, true, true));
estado = "cayendo";
}
if (estado == "cayendo")
y = y + 3;
if (estado == "subiendo")
y = y - 3;
(https://i.gyazo.com/90be281f71798a6724867f1c464ff6bb.gif)
Esta malo, ademas el obj_Jugador esta escrito asi, pero sigue sin funcionar ¿ esta seguro que alguna función de rango no seria mejor que escribir esta función tan exótica? si nadie o pocos la usan es por algo.
Lo siento, mi error. Elimina el punto y coma en la condición.
[gml]
if (estado == "vigilando")
{
if(collision_line (x, y, x, y + 64, obj_jugador, true, true))
estado = "cayendo";
}
[/gml]
Las funciones de colisión avanzada son muy eficientes y usadas para desarrollar videojuegos, sólo hay que saber usarlas. Para lo que necesitas, collision_line es perfecta. Que te sea desconocida no la hace mala.
Cita de: NiuWeb en Septiembre 15, 2017, 06:57:48 AM
Lo siento, mi error. Elimina el punto y coma en la condición.
[gml]
if (estado == "vigilando")
{
if(collision_line (x, y, x, y + 64, obj_jugador, true, true))
estado = "cayendo";
}
[/gml]
Las funciones de colisión avanzada son muy eficientes y usadas para desarrollar videojuegos, sólo hay que saber usarlas. Para lo que necesitas, collision_line es perfecta. Que te sea desconocida no la hace mala.
El juego corre nuevamente, pero entre el código
if(collision_line (x, y, x, y + 64, obj_Jugador, true, true)) // el nuevo
if (place_meeting(x,y+64, obj_Jugador)) //el antiguo
No hay ningún cambio, el enemigo/trampa, solo se activa cuando el jugador toca la "Y" a 64 pixeles de el, pero me di cuenta que si ahora cambio su valor y + 64 --> y + 264 puede tener una vision que puedo personalizar, asiendo que se active entre 0 a "X" distancia, el resultado es...
(https://i.gyazo.com/1d903b20a9dbabee1751677cd5d3a2a6.gif)
Excelente :) ¿Quieres que te envié todos los códigos del objeto como agradecimiento? la verdad es que se me ocurre utilizar este modulo para hacer la vertical arriba, la horizontal derecha y la izquierda, incluso hasta hacer diagonales imaginate 8 tipos de direcciones en base a 1 ejemplo inicial, claro que tendré problema para las diagonales con el uso de "XY", pero cualquier duda preguntare ;D.
Citar(collision_line (x, y, x, y + 64, obj_jugador, true, true))
creo que debería ir así o por lo menos yo la uso así
[gml] (collision_line (x, y, x, y + 64, obj_jugador, true, true) != noone)[/gml]
Al momento de ejecutarse, la función devuelve la id de la instancia (que siempre será positiva) o noone (-4) en caso de no encontrar ninguna. De las dos formas funciona igual.