Herramientas de usuario

Herramientas del sitio


manejo_de_eventos

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anterior Revisión previa
Próxima revisión
Revisión previa
Última revisión Ambos lados, revisión siguiente
manejo_de_eventos [2016/07/01 23:59]
al-khwarizmi
manejo_de_eventos [2016/07/08 13:47]
al-khwarizmi
Línea 100: Línea 100:
 Una particularidad muy característica de los juegos de texto es la posibilidad de que haya personajes en el mundo que conversen automáticamente con los jugadores. Esto se puede implementar de maneras muy diferentes, como pueden ser los menús o árboles de conversación,​ la conversación por temas seleccionables,​ o la conversación libre. Una particularidad muy característica de los juegos de texto es la posibilidad de que haya personajes en el mundo que conversen automáticamente con los jugadores. Esto se puede implementar de maneras muy diferentes, como pueden ser los menús o árboles de conversación,​ la conversación por temas seleccionables,​ o la conversación libre.
  
-El sistema AGE está diseñado para dar soporte sencillo a la conversación libre. Si bien otros modos de conversación (como puede ser por menús) se pueden implementar,​ no se proporcionan herramientas para ellos por defecto, al menos por el momento, teniendo que programar los menús el creador de la aventura. Sin embargo, AGE sí que tiene un sistema pensado para facilitar la conversación libre. Dicho sistema funciona de la siguiente manera:+El sistema AGE está diseñado para dar soporte sencillo a la conversación libre, es decir, aquélla donde un jugador o personaje puede decir cualquier cosa (sin estar restringido a seleccionar posibilidades de un menú, o a una estructura fija de pregunta). Si bien otros modos de conversación (como puede ser por menús) se pueden implementar,​ no se proporcionan herramientas para ellos por defecto, al menos por el momento, teniendo que programar los menús el creador de la aventura. Sin embargo, AGE sí que tiene un sistema pensado para facilitar la conversación libre. Dicho sistema funciona de la siguiente manera:
  
-  * En cualquier momento, un jugador o personaje puede decir algo. En el caso de los jugadores, esto sucede normalmente como consecuencia de una orden: por defecto, si un jugador teclea "decir "​hola""​ el resultado será que su personaje dice "​hola"​:+  * En cualquier momento, un jugador o personaje puede decir algo. En el caso de los jugadores, esto sucede normalmente como consecuencia de una orden: por defecto, si un jugador teclea "decir "​hola""​ el resultado será que su personaje dice "​hola"​. En el caso de los personajes no jugadores, podemos hacer que digan algo llamando a ''​personaje.say("​hola"​)''​. 
 + 
 +  * Cuando un personaje dice algo, se envía una notificación a la habitación que contiene el texto que ha dicho. Esto tiene como consecuencia que se muestra un mensaje a los personajes que estén allí, para informar de lo que ha pasado (por defecto, se mostrará ''​Dices "​hola"​.''​ si lo has dicho tú, o ''​Fulanito dice "​hola"​.'',​ si es otro personaje el que ha hablado).
  
 ''//>​ decir "​hola"//​\\ ''//>​ decir "​hola"//​\\
-Dices "​hola"​.\\''​+Dices "​hola"​.''​
  
-En el caso de los personajes ​no jugadores, podemos hacer que digan algo llamando ​a ''​personaje.say("​hola"​)''​.+  * El hecho de que alguien haya hablado en la habitación actual se puede capturar mediante un evento ''​onSay'',​ que toma como parámetros ​el personaje que ha hablado y el texto que ha dicho. 
 + 
 +Sabiendo esto, podemos programar personajes que conversen libremente a base de definir su evento ''​onSay''​. Para acceder a la plantilla de este evento, basta con ir al menú contextual en el panel de código del personaje, y seleccionar "​Insertar código - Definir eventos de personaje - Al oír a alguien decir algo". Entonces, aparecerá la siguiente plantilla:​ 
 + 
 +<code java> 
 +//evento que se ejecuta cuando alguien dice algo en la habitación 
 +//en la que está este personaje 
 + 
 +//subject: personaje que ha hablado 
 +//text: lo que ha dicho 
 + 
 +void onSay( Mobile subject , String text ) 
 +
 + 
 +
 +</​code>​ 
 + 
 +Supongamos que queremos mejorar a Manolo, el vendedor, para que sea capaz de responder a algunas preguntas de sus clientes. Para ello, definiremos este evento en el código de Manolo. Cada vez que alguien diga algo en su tienda, se invocará el método ''​onSay'',​ con el parámetro ''​subject''​ indicando quién ha hablado (esto no incluye al propio Manolo) y el parámetro ''​text'',​ que es una cadena, conteniendo lo que ha dicho. 
 + 
 +Para implementar la conversación,​ tendremos que analizar el contenido de la cadena ''​text'',​ mirando si podemos reconocer todo o parte de lo que dice. Para ello, podemos hacer uso de la rica funcionalidad de cadenas que nos proporciona Java, incluyendo todo tipo de métodos de comparación y de reconocimiento de patrones con expresiones regulares. 
 + 
 +Empecemos haciendo que responda si le dicen "​hola"​. La forma más elemental de hacerlo sería añadir esto al evento: 
 + 
 +<code java> 
 +if ( equals ( text , "​hola"​ ) ) 
 +    self.say("​Y hola a ti también, apreciado cliente"​);​ 
 +</​code>​ 
 + 
 +Efectivamente,​ con esto conseguiremos que el tendero responda a ese saludo: 
 + 
 +''//>​ decir "​hola"//​\\ 
 +Dices "​hola"​.\\ 
 +Manolo dice "Y hola a ti también, apreciado cliente"​.''​ 
 + 
 +Sin embargo, esta implementación no es muy flexible, porque sólo responderá si le dicen exactamente "​hola",​ y no si le dicen, por ejemplo, "hola Manolo"​. Podemos flexibilizarla haciendo algo como: 
 + 
 +<code java> 
 +if ( text.toLowerCase().startsWith("​hola"​) ) 
 +    self.say("​Y hola a ti también, apreciado cliente"​);​ 
 +</​code>​ 
 + 
 +''​startsWith''​ es un método de Java que nos dice si nuestra cadena empieza por el parámetro que se le pasa, en este caso "​hola"​. El método ''​toLowerCase()''​ es una función ​de paso del texto a minúsculas,​ que es necesaria aquí porque ​los métodos de manejo de cadenas de Java (como ''​startsWith''​),​ a diferencia de las funciones que vienen con AGE, son sensibles a mayúsculas y minúsculas. Por lo tanto, es conveniente pasar la cadena ''​text''​ a minúsculas antes de usar estos métodos sobre ella, al menos si queremos que Manolo sea capaz de entender tanto "​hola"​ como "​Hola"​ u "​HOLA"​. En la práctica, sobre todo si vamos a tener un evento ''​onSay''​ largo, lo más cómodo probablemente sea hacer esto al principio del evento, y luego desentendernos:​ 
 + 
 +<code java> 
 +//evento que se ejecuta cuando alguien dice algo en la habitación 
 +//en la que está este personaje 
 + 
 +//subject: personaje que ha hablado 
 +//text: lo que ha dicho 
 + 
 +void onSay( Mobile subject , String text ) 
 +
 +    text = text.toLowerCase();​ //a partir de aquí, sabemos que text ya está en minúsculas 
 +    if ( text.startsWith("​hola"​) ) 
 +        self.say("​Y hola a ti también, apreciado cliente"​);​ 
 +
 +</​code>​ 
 + 
 +Si en lugar de comprobar por qué empieza lo que se ha dicho en la habitación preferimos saber si contiene (no necesariamente al principio) una cadena dada, podemos utilizar otro método de las cadenas Java, en este caso ''​contains''​. Por ejemplo, podemos hacer que Manolo reaccione cuando se le menciona un perro en cualquier posición dentro de una oración: 
 + 
 +<code java> 
 +//evento que se ejecuta cuando alguien dice algo en la habitación 
 +//en la que está este personaje 
 + 
 +//subject: personaje que ha hablado 
 +//text: lo que ha dicho 
 + 
 +void onSay( Mobile subject , String text ) 
 +
 +    text = text.toLowerCase();​ //partir de aquí, sabemos que text ya está en minúsculas 
 +    if ( text.startsWith("​hola"​) ) 
 +        self.say("​Y hola a ti también, apreciado cliente"​);​ 
 +    else if ( text.contains("​perr"​) ) 
 +        self.say("​Perros. Puaj. No quiero a esas sucias criaturas cerca de mi pulcra tienda. ¡Ni las menciones!"​);​ 
 +
 +</​code>​ 
 + 
 +Como se puede ver, ''​contains''​ funciona de manera muy similar a ''​startsWith'':​ es un método booleano, tal que ''​a.contains(b)''​ devuelve ''​true''​ si la cadena ''​a''​ contiene en su interior a la cadena ''​b'',​ y ''​false''​ en caso contrario. 
 + 
 +Con métodos como ''​startsWith''​ y ''​contains''​ se puede implementar,​ con un poco de meticulosidad y cuidado, conversaciones muy completas, y son una buena opción para empezar. El lector que quiera profundizar en las capacidades de Java para operar con cadenas también puede consultar la [[http://​docs.oracle.com/​javase/​6/​docs/​api/​java/​lang/​String.html|documentación de la clase String]] de Java, donde aparecen muchos más métodos que se pueden utilizar con cadenas además de los que acabamos de ver. Muchos de ellos pueden ser de interés para implementar conversaciones más ricas. 
 + 
 +Adicionalmente,​ el programador avanzado hará bien en consultar la [[ 
 +https://​docs.oracle.com/​javase/​6/​docs/​api/​java/​util/​regex/​Pattern.html|documentación de la clase Pattern]], donde se especifica cómo crear y aplicar las expresiones regulares. Las expresiones regulares son una notación para expresar patrones de cadenas que nos permiten comprobar si una cadena cumple una condición de forma mucho más concisa y sencilla (una vez que uno las conoce) que con combinaciones de operaciones como ''​contains'',​ ''​toLowerCase'',​ ''​startsWith''​ y demás. Por ejemplo, si queremos comprobar si una cadena ''​s''​ es o bien "​perro"​ o bien "​perra",​ una manera de hacerlo sería 
 + 
 +<code java> 
 +if ( s.equals("​perro"​) || s.equals("​perra"​) ) ... 
 +</​code>​ 
 + 
 +Pero con expresiones regulares, podemos hacer esto en su lugar: 
 + 
 +<code java> 
 +if ( s.matches("​perr[oa]"​) ) ... 
 +</​code>​ 
 + 
 +Donde "​[oa]"​ significa que en esa posición hay o bien la letra "​o",​ o bien la letra "​a"​. De ese mismo modo, las expresiones regulares permiten expresar otras muchas cosas, no sólo sobre caracteres individuales sino también sobre rangos numéricos, alfanuméricos,​ límites entre palabras, etc. 
 + 
 +Por ejemplo, si nos fijamos bien en el último código para la conversación con Manolo, podemos darnos cuenta de que la forma de detectar si el interlocutor le habla de perros no es la más adecuada. Estamos mirando si el texto contiene "​perr",​ pero esto también va a suceder si alguien le dice a Manolo que está "​emperrado"​ en regatearle el precio de una alfombra, o que el dibujo de uno de sus productos es "​hiperrealista"​. Con expresiones regulares, podemos afinarlo mucho más, convirtiendo la línea en algo como: 
 + 
 +<code java> 
 +else if ( text.matches("​.*\\bperr[oa]s?​\\b.*"​) ) 
 +</​code>​ 
 + 
 +que mira si la cadena contiene las palabras "​perro",​ "​perra",​ "​perros"​ o "​perras":​ los ''​\\b''​ representan un límite de palabra (es decir, un carácter de espaciado, o bien el principio o final de la cadena), los ''​.*''​ representan que puede haber cualquier número de caracteres (incluyendo 0) antes o después de esa palabra, y la interrogación después de la letra "​s"​ quiere decir que ésta es opcional. 
 + 
 +En este documento no detallaremos toda la lista de posibles expresiones regulares que se pueden construir, ya que se pueden encontrar tanto en la documentación de Java (de la [[ 
 +https://​docs.oracle.com/​javase/​6/​docs/​api/​java/​util/​regex/​Pattern.html|clase Pattern]] antes mencionada) como en numerosos libros y tutoriales online. 
 + 
 +Con estos mimbres y lo visto en secciones anteriores de la documentación,​ se pueden construir personajes con conversaciones muy ricas y complejas. Por supuesto, lo que se puede hacer va mucho más allá de las secuencias de ''​if''​ como hemos visto en el caso de Manolo, ya que podemos aprovechar los estados y relaciones de AGE para hacer que la conversación de un personaje ​cambie según el contexto del diálogoA modo de ejemplo, el siguiente código de conversación pertenece a una mujer que se encuentra el héroe de la aventura "​Fuego"​ (se ha editado un poco para modernizar el código, ya que esa aventura se hizo en una versión antigua de AGE y de Java menos potente que la actual, donde conseguir algunas cosas era más engorroso):​ 
 + 
 +<code java> 
 +(...) 
 + 
 +else if ( lText.contains("​hola"​) ​|| lText.contains("​buenas"​)  
 + || lText.contains("​saludo"​) || lText.contains("​buenos d") ) 
 +
 + //nos están saludando, saludamos de vuelta 
 + self.say("​Hola..."​);​ 
 + //miramos si conocemos el nombre del interlocutor 
 + String nombre = get ( self , "​nombre",​ m ); 
 + if ( nombre == null || nombre.equals("​unknown"​) ) 
 +
 + //si no conocemos el nombre, se lo preguntamos. Esperamos que nos lo diga. 
 + self.say("​¿Cómo te llamas?"​);​ 
 + set ( self , "​esperandonombre"​ , m , true ); 
 +
 +
 + 
 +(...) 
 + 
 +else 
 +
 + //si estamos esperando nombre, asumiremos que lo que nos dice lo es. 
 + if ( get ( self , "​esperandonombre"​ , m ) ) 
 +
 + if ( !lText.matches("​(\\w*\\W*){1,​3}"​) ) 
 +
 + //si nos dice algo con más de tres palabras, probablemente no nos esté diciendo su nombre de verdad. Se ha ido por las ramas. 
 + self.say("​Uy,​ ese nombre es muy largo. Dudo que pueda recordarlo. ¿No te puedo llamar por algún nombre más corto?"​);​  
 +
 + else 
 +
 + //hacer que el nombre empiece por mayúscula 
 + if ( !Character.isUpperCase(lText.charAt(0)) ) 
 + lText = Character.toUpperCase(lText.charAt(0)) + lText.substring(1);​ 
 + //​actualizar las relaciones, reflejando que conocemos el nombre del personaje (y cuál es) 
 + set ( self , "​nombre"​ , m , (String)lText ); 
 + set ( self , "​esperandonombre"​ , m , false ); 
 + self.say("​Encantado,​ " + self.getRelationshipPropertyValueAsString ( m , "​nombre"​ ) + ". Yo soy María."​);​  
 +
 + }  
 + else 
 +
 + 
 +(...) 
 +</​code>​ 
 + 
 +En este ejemplo, cuando alguien saluda a María, ésta le pregunta su nombre. Cuando el interlocutor se lo dice, María se queda con el dato (almacenado en una relación con el interlocutor,​ llamada "​nombre"​) y lo usará a lo largo de la conversación:​ 
 + 
 +''​//> decir "​hola"//​\\ 
 +Dices "​hola"​.\\ 
 +La mujer dice "​Hola..."​.\\ 
 +La mujer dice "​¿Cómo te llamas?"​.\\ 
 +//> decir "​juan"//​\\ 
 +La mujer dice "​Encantado,​ Juan. Yo soy María."''​
manejo_de_eventos.txt · Última modificación: 2016/07/08 13:49 por al-khwarizmi