Buenas :)

Estaba probando un código cuando me di cuenta de uno de los comportamientos extraños de GameMaker, resulta que el estado de las teclas (a través de keyboard_check() por ejemplo) es completamente ignorado en el cambio de habitación.

La solución que he encontrado en todos lados es ocupar la función keyboard_check_direct(), sin embargo por el funcionamiento del runner en GameMaker para Mac ésta función devuelve exactamente el mismo valor que keyboard_check() por lo que es natural descartarla.

Para añadir insulto a la injuria, las funciones keyboard_key_press() y keyboard_key_release() no funcionan en lo absoluto, haciendo imposible hacer manualmente en código el cambio de estado en las teclas al comenzar cada room.

Lo he intentado todo, y hasta ahora sólo tengo una solución parche que funciona sólo durante un cambio de room. A continuación se los explico:

Prescindí por completo de chequear directamente el estado de las teclas en los objetos que reciben órdenes a través del teclado, y en su lugar programé un rudimentario sistema de condicionales con una variable global para las teclas más importantes del juego.


if (keyboard_check_pressed(vk_left) == true)
{
global.dpad[0] = true;
}
if (keyboard_check_released(vk_left) == true)
{
global.dpad[0] = false;
}
//ad infinitum con todas las teclas posteriores


y luego, en el código del objeto de actor que es controlado por el usuario, va el siguiente código:


event_inherited(); //Hereda todas las mecánicas de la "clase" actor

if (global.kbwait == false)
{
walking = (((global.dpad[0] == true) && (dir == -1)) || ((global.dpad[1] == true) && (dir == 1)));
//etcetera para todas las acciones que el actor puede realizar, como correr, saltar y todo eso.
}
else
{
global.kbwait = false;
}


ahora, la variable global kbwait apareció cuando me di cuenta que era muy fácil provocar un bug peor que detenerse en cada cambio de room, y sucedía cuando el jugador soltaba la tecla en medio de un cambio de habitación, haciendo que el juego no reconociese éste evento y siguiese actuando tal cual como si la tecla siguiese presionada.

Básicamente, cada vez que la condición del cambio de room se cumple, el juego activa el siguiente código antes de llamar a la función de cambio de room:


global.kbwait = true;
global.dpad[0] = keyboard_check_direct(vk_left);
//etcetera para el resto de las teclas


Y esto soluciona ése error, pero vuelve al bug original (no recordar las teclas presionadas entre rooms) sólo que en lugar de suceder en la room siguiente, sucede en la subsiguiente.

Ahora, un par de anotaciones que he hecho con respecto a éste comportamiento:


  • No tiene un resultado estable. Mientras más arreglos le hago al código, más estabilizo el error, pero originalmente a veces no sucedía en absoluto, otras veces el valor devuelto por la función era false durante un par de steps y luego volvía a ser 1, y en otras ocasiones el valor devuelto por la función era 0 hasta que se soltase la tecla y se volviese a presionar.
  • Es más probable que suceda al presionar una tecla mientras hay otra presionada, lo que me hace sospechar de la variable keyboard_key pues ésta vuelve a ser 0 cuando se presiona una tecla mientras hay otra ya presionada.
  • Éste es un error de GameMaker para Mac, pues el mismo código funciona a la perfección en su versión para Windows.
  • La función io_handle() parece prometedora, pero por más que intento ocuparla bien, no puedo encontrar ejemplos fuera de ocuparla dentro de ciclos while, donde funciona a la perfección.

¿A alguien se le ocurre una forma de solucionar un bug como éste? Muchas gracias de antemano :)

Haz intentado simplemente usar la funcion io_handle() justo despues de cambiar de room?. Si eso funciona probablemente no necesitaras el metodo de los arrays. La verdad no se me ocurre mucho más. Es dificíl conciderando que pocos tienen gm para mac.

Cita de: brunoxzx en Enero 11, 2013, 04:03:50 PM
Haz intentado simplemente usar la funcion io_handle() justo despues de cambiar de room?. Si eso funciona probablemente no necesitaras el metodo de los arrays. La verdad no se me ocurre mucho más. Es dificíl conciderando que pocos tienen gm para mac.

Sí, lo intenté y no funciona. Me da la sensación de que no estoy usando la función correctamente, pero en realidad no hay motivo para pensar eso.
Por lo demás, hice un thread en el subreddit de Game Maker de reddit, y un usuario me dio una solución bastante buena y razonable: deshacerme de los cambios de room por completo, y cargar dinámicamente el contenido de los niveles en una sola room.

#3 Enero 12, 2013, 02:51:49 AM Ultima modificación: Enero 12, 2013, 02:54:05 AM por brunoxzx
Pero, toda la carga dinamica depende del tipo de juego que sea, planeas hacer varios rooms y dinamicamente sin entrar a ellos cargar todos sus recursos en el room actual? porque si es así no se me ocurre como. O planeas tener rooms guardados en archivos externos?

Archivos externos parece ser la solución más adecuada, sin embargo no es tan difícil hacerlo directamente desde el código, utilizando estructuras de datos como grids :D

En fin, mi duda se resolvió de forma inesperada con una solución que nada tiene qué ver con lo que quería lograr, pero es una solución al fin y al cabo ._.

De todos modos, me gustaría saber si alguien tiene alguna idea sobre cómo arreglarlo.

#5 Enero 12, 2013, 04:15:09 AM Ultima modificación: Enero 12, 2013, 04:46:01 AM por brunoxzx
Cita de: calio en Enero 12, 2013, 03:26:31 AM
Archivos externos parece ser la solución más adecuada, sin embargo no es tan difícil hacerlo directamente desde el código, utilizando estructuras de datos como grids :D
EL problemas es el acceso a los datos de otro room sin necesidad de cambiar en ningun momento.

Por cierto no entendí muy bien lo que decías del room subsiguiente. cómo? se repara para el siguiente room, pero al ir al anterior vuelve a suceder?.

Claro, guardar los datos de otra forma es esencial para solucionar el problema, aunque sea una consecuencia colateral :p

Por cierto, desde que me resigné a no usar el sistema nativo de rooms de GameMaker (léase ayer en la noche xD), he estado trabajando en esto :3



jujuju.

Cita de: brunoxzx en Enero 12, 2013, 04:15:09 AM
Por cierto no entendí muy bien lo que decías del room subsiguiente. cómo? se repara para el siguiente room, pero al ir al anterior vuelve a suceder?.

Claro, mira, considéralo así:

Tenemos 3 rooms, los vamos a llamar scn_room0, scn_room1 y scn_room2. Estos están secuenciados de tal forma que al salir por la derecha de scn_room0, el juego traslada el objeto del personaje a la esquina izquierda de scn_room1.
Del mismo modo, tenemos una variable llamada global.dpad[1] que almacena el valor de la tecla del modo explicado anteriormente.

Al salir de scn_room0 presionando la tecla vk_right, los valores devueltos por las siguientes funciones y variables son los siguientes:

global.dpad[1] == true;
keyboard_check(vk_right) == true;
keyboard_check_direct(vk_right) == true;


Al salir de scn_room0, el objeto ejecuta un script que guarda el valor de keyboard_check(vk_right) (y el resto de las teclas, claro) antes de ejecutar la función room_goto(scn_room1). Esto lo hace para que el juego actualice el valor de las variables si es que el jugador ha soltado la tecla justo antes de entrar a la siguiente room, evitando así que el jugador siga avanzando incluso si no hay ninguna tecla presionada.
Al entrar a scn_room1, el valor de las funciones y las variables son las siguientes:

global.dpad[1] == true;
keyboard_check(vk_right) == false;
keyboard_check_direct(vk_right) == false;

¿Por qué keyboard_check(vk_right) pasó a ser false? No tengo idea, ése es mi problema xD
¿Por qué global.dpad[1] sigue siendo true pese a que keyboard_check(vk_right) pasó a ser false? Porque el script que maneja los valores de global.dpad[1] funciona con keyboard_check_pressed(vk_right) y keyboard_check_released(vk_right), y dado que el juego no ha recibido ninguna órden de soltar la tecla vk_right, el valor de ésta variable sigue siendo true hasta que se suelte la tecla.

Ahora viene la parte problemática, y es lo que sucede al seguir caminando hacia scn_room2 desde scn_room1. Los valores devueltos por las variables y las funciones siguen siendo los mismos

global.dpad[1] == true;
keyboard_check(vk_right) == false;
keyboard_check_direct(vk_right) == false;

Y al salir de la room ejecuta nuevamente el script, siiiin embargo ésta vez -y por el bug presente en GameMaker- el valor de keyboard_check(vk_right) es false, por lo que global.dpad[1] pasa a ser false y los valores al entrar a scn_room2 desde scn_room1 son

global.dpad[1] == false;
keyboard_check(vk_right) == false;
keyboard_check_direct(vk_right) == false;

Haciendo que el código falle en la habitación subsiguiente. :(

¿Y durante todo ese proceso la tecla no se suelta, y si se soltara después de salir del primer room, keyboard_check_released(vk_right) igual sería false?
Si es así, buena suerte con tu método de un solo room :P.
Y sobre eso: ¿Game Maker no tiene funciones para leer las instancias que se crearían al entrar a un room? Si tiene podrías implementar tu propio room_goto que las lea y las cree...
Vim.

No, si uno suelta la tecla y la vuelve a presionar, keyboard_check(vk_right) vuelve a ser true. Lo de la room subsiguiente es si el jugador mantiene presionada la tecla durante esas 3 rooms, caso bastante esperable a decir verdad ._.

Y de tener funciones para leer las instancias que hay en rooms a las que no ha accedido, no las conozco. Se me ocurre un método bastante chungo de hacer un ciclo llendo por cada room y guardando la posición de objetos en grids para cada room, como una suerte de precarga, pero si tuviese que hacer eso mejor corto por lo sano y hago un sistema personalizado de rooms xD y eso es básicamente lo que estoy haciendo...

En fin, no se qué más agregar ._. si alguien sabe exactamente cómo solucionar el problema del estado de las teclas entre room y room sin recurrir a keyboard_check_direct() ni a keyboard_key_press()/keyboard_key_release(), por favor avíseme ;A;

#9 Enero 13, 2013, 03:40:49 AM Ultima modificación: Enero 13, 2013, 03:44:51 AM por Wadk
Cita de: calio en Enero 13, 2013, 02:21:13 AM
No, si uno suelta la tecla y la vuelve a presionar, keyboard_check(vk_right) vuelve a ser true. Lo de la room subsiguiente es si el jugador mantiene presionada la tecla durante esas 3 rooms, caso bastante esperable a decir verdad ._.
Pregunté por keyboard_check_released...

EDIT: Por otro lado, ¿alguna vez probaste ENIGMA? No creo que tenga ese problema, aunque te aviso que todavía le queda para ser del todo compatible con GM (y no sé si lograrás hacerlo andar, si te interesa y tenés problemas pasate por #enigma en Freenode).
Vim.

Cita de: Wadk en Enero 13, 2013, 03:40:49 AM
Cita de: calio en Enero 13, 2013, 02:21:13 AM
No, si uno suelta la tecla y la vuelve a presionar, keyboard_check(vk_right) vuelve a ser true. Lo de la room subsiguiente es si el jugador mantiene presionada la tecla durante esas 3 rooms, caso bastante esperable a decir verdad ._.
Pregunté por keyboard_check_released...

Upa! Oh, qué error de mi parte ._.
Sí, keyboard_check_released() funciona.

Cita de: calio en Enero 13, 2013, 03:45:47 AM
Cita de: Wadk en Enero 13, 2013, 03:40:49 AM
Cita de: calio en Enero 13, 2013, 02:21:13 AM
No, si uno suelta la tecla y la vuelve a presionar, keyboard_check(vk_right) vuelve a ser true. Lo de la room subsiguiente es si el jugador mantiene presionada la tecla durante esas 3 rooms, caso bastante esperable a decir verdad ._.
Pregunté por keyboard_check_released...

Upa! Oh, qué error de mi parte ._.
Sí, keyboard_check_released() funciona.
Entonces podrías hacer:
[gml]global.dpad[1] = global.dpad[1] || keyboard_check(vk_right);[/gml]
antes de cambiar de room, y en step en algún lado:
[gml]global.dpad[1] = global.dpad[1] && !keyboard_check_released(vk_right);[/gml]
¿No?
Vim.


#13 Enero 15, 2013, 01:16:37 AM Ultima modificación: Enero 15, 2013, 05:37:15 AM por calio
Cita de: Wadk en Enero 13, 2013, 04:07:18 AM
Entonces podrías hacer:
[gml]global.dpad[1] = global.dpad[1] || keyboard_check(vk_right);[/gml]
antes de cambiar de room, y en step en algún lado:
[gml]global.dpad[1] = global.dpad[1] && !keyboard_check_released(vk_right);[/gml]
¿No?

Lamentablemente, eso resulta en el error que provoca que el jugador siga caminando si suelta la tecla justo entre cambio de rooms.
El error lo gatilla la primera línea de código, y sucede porque -oh, error mío el no haber comentado eso xD- antes de pasar a de room el juego activa una variable que se desactiva al llegar a la room siguiente, y la función de esa variable es suspender el control de las teclas a través del script aburrido de keyboard_check_pressed() y keyboard_check_released() para pasar al chequeo de las teclas directamente previo al cambio de room y mantenerlo hasta la siguiente room, donde la variable vuelve a su estado normal.
Dado que el valor de global.dpad[1] no puede ser actualizado durante éste proceso mediante el método habitual de chequeo, el valor de global.dpad[1] no va a cambiar nunca entre rooms hasta que la variable de control vuelva a su estado normal.

De todos modos, voy bien encaminado con el tema de hacerlo todo en una sola room xD hice un mapa de 256 pantallas y me ocupaba 2.5mb, lo cual encontré excesivo y ridículo así que estoy en el proceso de cambiarme de estructura de datos de una grid a un map con llaves numéricas :) el concepto es sencillo: si enumeramos cada espacio dentro de la cuadrícula de 16x16 de una pantalla de 320x240, (o lo que es lo mismo, 20x15) tenemos 300 cuadros, y haciendo un par de expresiones sencillas

x = (key mod 20);
y = ((key div 20)*20);

guardar y recuperar la posición de elementos guardados en una estructura unidimensional no es difícil :D

EDIT: Hace un rato terminé de hacer la mayoría de los cambios para guardar las pantallas en mapas. El peso de un archivo de 256 pantallas vacías bajó de 2.5mb a 9kb :D

Cita de: calio en Enero 15, 2013, 01:16:37 AM
Cita de: Wadk en Enero 13, 2013, 04:07:18 AM
Entonces podrías hacer:
[gml]global.dpad[1] = global.dpad[1] || keyboard_check(vk_right);[/gml]
antes de cambiar de room, y en step en algún lado:
[gml]global.dpad[1] = global.dpad[1] && !keyboard_check_released(vk_right);[/gml]
¿No?

Lamentablemente, eso resulta en el error que provoca que el jugador siga caminando si suelta la tecla justo entre cambio de rooms.
El error lo gatilla la primera línea de código, y sucede porque -oh, error mío el no haber comentado eso xD- antes de pasar a de room el juego activa una variable que se desactiva al llegar a la room siguiente, y la función de esa variable es suspender el control de las teclas a través del script aburrido de keyboard_check_pressed() y keyboard_check_released() para pasar al chequeo de las teclas directamente previo al cambio de room y mantenerlo hasta la siguiente room, donde la variable vuelve a su estado normal.
Dado que el valor de global.dpad[1] no puede ser actualizado durante éste proceso mediante el método habitual de chequeo, el valor de global.dpad[1] no va a cambiar nunca entre rooms hasta que la variable de control vuelva a su estado normal.

Lo dicho, suerte con tu sistema de un solo room :P.
Vim.