miércoles, 19 de septiembre de 2012

Interacción con Voz y Texto

Esta semana la entrada trata de realizar una especie de juego/aplicación donde se pueda interactuar con ella mediante voz o texto.

Para el uso de la voz se puede realizar mediante comando ("predefinidos") y para el texto de igual forma, buscando siempre que el uso de estos "comandos" sean de una manera muy natural. Esto puede ayudar a minimizar la curva de aprendizaje de los usuarios, omitiendoles el paso en el que tienen que memorizar los "comandos" que se usan y que acción realiza cada uno.

Mediante el uso de la voz, a mi parecer resulta mas natural decir: "izquierda" y que cierto objeto en el juego se mueva hacia ese lado.
Mientras que en el texto, resulta mas comodo/natural escribir: "más rápido", que "aumentar la velocidad x veces".

Voz
Mi elección(la principal) fue la de mover cierto objeto, mediante el uso de la voz.

La parte del juego esta desarrollada con Pilas Engine (el motor que utilice en la entrada anterior) y prácticamente es lo mismo, solo cambia en la situación de que:
  • Los actores son diferentes, se agregaron un chango y platanos.
  • Ahora solo se mueve el chango.
  • Existen 2 tipos de colisiones, cuando choca con los platanos y cuando choca con las bombas.
  • Al chocar con los platanos, se los "come" y el chango se ríe.
  • Al chocar con las bombas, éstas "explotan" y el chango grita.

Para el apartado de la voz, me apoyé de la librería PySpeech, que hace uso del motor de reconocimiento de voz incluido en Windows (por lo que solo trabaja bajo Windows, una desventaja).
  • Por si les interesa probarla, les dejo el enlace para su descarga e instalación aquí. (en la página dice que solo funciona bajo python 2.4 o 2.5, pero yo estoy trabajando con python 2.6).
 
 Ya que se hace uso del motor de voz de Windows, tambien toca realizar la configuración de este para que trabaje de manera correcta, esto si no habían usado anteriormente esta herramienta.

Al parecer la combinación de esta librería y el motor de voz de Windows realizan un trabajo muy eficiente (tiene algunas deficiencias muy mínimas), tomando en cuenta que la mayoria de los programas/aplicaciones que usan apartados para el reconocimiento de voz estan entrenados o implementados para escuchar comandos en inglés, esta combinación resulto ser muy buena.


En el programa el uso que se le da a esta librería es:
  • Escuchar algún "comando" y pasarlo a texto, tambien hay la posibilidad de que la aplicación repita el comando que se dijo.
 
Ya después mediante condiciones, se valida el comando y la acción que se va a realizar, en este caso mover el chango en 4 direcciones por toda la ventana de la aplicación (actualizando los valores de las coordenadas de la posición del chango).

Aquí les dejo el código:

import pilas

import speech
import random

def comer(mono, banana):
 mono.sonreir()
 banana.eliminar()

def explotar(mono, bomba):
 mono.gritar()
 bomba.explotar()
 bomba.eliminar()

def comando_voz():
 comando = speech.input("Diga algo!!")
 print comando
 speech.say("You said, %s" % comando)
 return comando 

def movimiento(mono):
 comando = "nada"
 x = 0
     y = 0
 while (comando != "Salir"):
  comando = comando_voz()
  if (comando == "Uno"):
   mono.y = (y+5)
   y = mono.y
  elif (comando == "Dos"):
   mono.y = (y-5)
   y = mono.y
  elif (comando == "Izquierda"):
   mono.x = (x-5)
   x = mono.x
  elif (comando == "Derecha"):
   mono.x = (x+5)
   x = mono.x
  elif (comando == "Salir"):
   print "Saliendo.... "
   pilas.terminar()   
  else:
   print "Comando desconocido!!.."    

def main():

 bananas = []
 bombas = []

 pilas.iniciar()

 mono = pilas.actores.Mono()
 mono.x = 0
 mono.y = 0

 cantidad = 5
 for i in range(cantidad):
  bomba = pilas.actores.Bomba() #se crean las bombas
  bomba.x = random.randrange(-200, +200) #coordenadas aleatorias en x
  bomba.y = random.randrange(-200, +200) #coordenadas aleatorias en y
  bombas.append(bomba)  #se agregan las bombas al grupo 
  banana = pilas.actores.Banana() #se crean las bananas
  banana.x = random.randrange(-200, +200) #coordenadas aleatorias en x
  banana.y = random.randrange(-200, +200) #coordenadas aleatorias en y
  bananas.append(banana)  #se agregan las bananas al grupo
 
 mono.aprender(pilas.habilidades.Arrastrable)
 pilas.mundo.colisiones.agregar(mono, bananas, comer)
 pilas.mundo.colisiones.agregar(mono, bombas, explotar)

 movimiento(mono) 
 
 pilas.ejecutar()
 
main()



Un incoveniente con el que me tope (muy desagradable) fue el de que al tratar de usar la palabra/comando "arriba" o "abajo", el motor de voz lo interpreata como "activar la ventana que se encuentra por encima o por abajo de la ventana actual" por lo que la acción de mover al chango hacia arriba/abajo no se puede completar.

Debido a esto me vi en la necesidad de cambiar los comandos "Arriba/Abajo" por "Uno/Dos" respectivamente.

El video en esta parte va quedar pendiente, debido a que al ejecutar el juego y el programa de grabación la computadora se cuelga y no responde. (Más abajo pondre un video de esto mismo, pero ingresando los comandos por texto).

Texto
Esto lo realice antes de comenzar con la parte de la voz, lo hice a manera de checar como podía actualizar los valores de las coordenadas para la posición dle chango, ya después le agregue más cosillas.
Prácticamente esta versión hace lo mismo que la comentada arriba, solo con la diferencia de que los comandos se ingresan mediante texto (algo simple), tratandolo en cierta medida de hacerlo lo más natural posible.

Aquí esta el código:

import pilas
import random

def comer(mono, banana):
 mono.sonreir()
 banana.eliminar()

def explotar(mono, bomba):
 mono.gritar()
 bomba.explotar()
 bomba.eliminar()

def movimiento(mono):
 comando = "nada"
 comandos1 = ["arriba", "up", "y+", "subir", "lado1"]
 comandos2 = ["abajo", "down", "y-", "bajar", "lado2"]
 comandos3 = ["izquierda", "izq", "x-", "left", "lado3"]
 comandos4 = ["derecha", "der", "x+", "rigth", "lado4"]
 comandos5 = ["salir", "exit", "quit", "fin", "end"]

 x = 0
     y = 0
 while (comando != "Salir"):

  comando = raw_input("Diga algo!!  ") 

  if (comando in comandos1 ):
   mono.y = (y+5)
   y = mono.y
  elif (comando  in comandos2 ):
   mono.y = (y-5)
   y = mono.y
  elif (comando  in comandos3 ):
   mono.x = (x-5)
   x = mono.x
  elif (comando  in comandos4 ):
   mono.x = (x+5)
   x = mono.x
  elif (comando  in comandos5 ):
   print "Saliendo.... "
   pilas.terminar()   
  else:
   print "Comando desconocido!!.."    

def main():
 bananas = []
 bombas = []
 
        pilas.iniciar()
 
        mono = pilas.actores.Mono()
 mono.x = 0
 mono.y = 0

 cantidad = 5
 for i in range(cantidad):
  bomba = pilas.actores.Bomba() #se crean las bombas
  bomba.x = random.randrange(-200, +200) #coordenadas aleatorias en x
  bomba.y = random.randrange(-200, +200) #coordenadas aleatorias en y
  bombas.append(bomba)  #se agregan las bombas al grupo 
  banana = pilas.actores.Banana() #se crean las bananas
  banana.x = random.randrange(-200, +200) #coordenadas aleatorias en x
  banana.y = random.randrange(-200, +200) #coordenadas aleatorias en y
  bananas.append(banana)  #se agregan las bananas al grupo
 
 mono.aprender(pilas.habilidades.Arrastrable)
 pilas.mundo.colisiones.agregar(mono, bananas, comer)
 pilas.mundo.colisiones.agregar(mono, bombas, explotar)

 movimiento(mono) 
 
 pilas.ejecutar()

main()



En esta versión hay un poco más de libertad en los comando, estan almacenados en listas.
Una buena idea sería la de capturar el comando que ingrese el usuario y después dividirlo en palabras, posteriormente checar si una palabra de esas esta en las listas de los comandos soportados, pero eso no lo agregue ya que mi prioridad era la de la voz.

Aquí dejo el video, un poco lento y trabado, pero aquí esta:



__________________________________________________________________________________
Enlaces útiles:

jueves, 13 de septiembre de 2012

Colisiones (Interacción entre objetos)

En esta entrada realizaremos una pequeña demostración de la interación entre objetos dentro de nuestro juego.
El ejemplo no tiene nada relacionado con la temática del  videojuego, es solo una demostración.

A estas interaciónes, se les conoce como colisiones, debido a que se necesita que 2 o mas objetos choquen entre si para que se lance la acción a realizar.

Para esta entrada yo elegi un ejercicio donde se tiene una ventana con bombas, estas se pueden mover hacia cualquier lado dentro de la ventana, pero si llega a tocar una bomba con otra, las dos explotan, cuando esto sucede salen llamas, se escucha un sonido de explosión y obviamente las bombas que choquen desaparecen.



Este ejempo lo realice con ayuda del motor gráfico Pilas, que ya habia comentado en la primera entrada en este blog, donde se hablaba de las herramientas para creae videojuegos ( aquí la entrada ) .
Al realizarlo de esta manera el código es más simple, más supuesto mas entendible y por su puesto más fácil de implementar esto.

Pilas Engine, hace uso de diferentes librerías, basicamente junta todas éstas y facilita su
uso, ya que pilas se encarga de su administración y todo lo necesario, por lo que no es necesario que conoscas las funciones de todas la librerías. Las mas importante son:
  • Pygame: para la manipulación de los personajes, imagenes, etc, 
  • Pyqt: para el manejo de las interfaces gráficas(ventanas, botones, etc) 
  • PyBox2d: para el manejo de la física de los juegos(gravedad, colisiones, etc.).
Si quieren probar Pilas Engine ( esta todo en español) :
  • Una guía de como instalarlo en Windows 7 está aquí.
  • Una guía de como instalarlo en Ubuntu 12.04 está aquí.
 ahí explican todo y ahí mismo estan los links para las cosas que se necesitan.

Sin más que decir, aquí esta el código :

import pilas
import random

def choque_explosion(bomba1, bomba2): # funcion para realizar la explosion y
 bomba1.explotar()      # la eliminacion de las bombas
 bomba1.eliminar()
 bomba2.explotar()
 bomba2.eliminar() 
 

def main():
 pilas.iniciar() #inicia pilas
 cantidad = 10
 bombas = pilas.grupo.Grupo()  #se crea un grupo para las bombas
 for i in range(cantidad):
  bomba = pilas.actores.Bomba() #se crean las bombas
  bomba.x = random.randrange(-200, +200) #coordenadas aleatorias en x
  bomba.y = random.randrange(-200, +200) #coordenadas aleatorias en y
  bombas.append(bomba)  #se agregan las bombas al grupo
 bombas.aprender(pilas.habilidades.Arrastrable) #se le agrega la posibilidad de
                                                       #ser arrastrable
  
 pilas.mundo.colisiones.agregar(bombas, bombas, choque_explosion) #se agrega la accion a realizar al motor de colisiones
 
 pilas.ejecutar()

main()


Se hace uso de las opciones que Pilas nos ofrece, donde el principal es el uso del motor de colisiones (que es controlado por el motor de física).
Lo que hace el programa a grandes rasgos:
  • Se inicia Pilas
  • Se crea un grupo para las bombas, donde un grupo es como una lista de objetos del mismo tipo, esto se hace para facilitar la manipulación de sus propiedades o personalización de los objetos de una manera mas fácil (en este caso es para esto, puede usarse para otros fines) y no estas agregando o modificando propiedades de objeto por objeto.´Hay una opción parecida a esta en PyGame o tal ves sea una derivada de ésta.
  • Se van creando las bombas, se les asignan coordenadas aleatorios y se agregan al grupo.
  • Se agrega la posibilidad de que cada bomba se pueda arrastrar independiente de las demás y moverse libremente dentro de la ventana.
  • Se hace uso del motor de colisiones de Pilas, donde se le pasan los objetos que interactuaran y la acción a realizar cuando estos colisionen. La acción se define en una función que se encarga de explotar las bombas (una propiedad de estas dentro de Pilas) y posteriormente eliminarlas (otra propiedad para los objetos en Pilas).
Dejo un video del funcionamiento de esto:


El video esta medio lento debido a que se el script consume algo de recursos y el progragra para grabar la pantalla consume una cantidad considerable de recursos, y como mi computadora no es muy rapida que digamos, pues ocurre esto.

También dejo una captura del script corriendo, pero con losmodos de Física, Colisiones y Posicion habilitados.


Cabe aclarar que los circulos verdes (producidos por el modo de Colisión) alrededor de las bombas, son los radios de colisión de cada objeto (bombas en este caso)  y que pueden ser modificables según sea necesario.
El centro de la pantalla es el origen del plano, por lo que las coordenadas negativas estan perimitidas.
El modo de Física muestras actualiza los valores mostrados de la posición de cada bomba al moverla.


La parte correspondiente al proyecto, en mi caso, es aportar al desarrollo de la física del juego.

Para esto se puede hacer uso de la librería PyBoxd2, la misma utilizada para el motor de fisica de Pilas Engine, ya que nuestro juego se desarrollara en un ambiente 2D.
Esta misma, puede proporcionarnos lo necesario para manejar las colisiones o interacciones entre varios objetos que ocurran en nuestro juego y también para establecer una gravedad dentro del juego, aunque creo  que esta no es muy necesaria debido a que practicamentee el juego es 2D.

_________________________________________________________________________
Referencias:

jueves, 6 de septiembre de 2012

Planeación Lógica Adaptativa

Planeación Lógica Adaptativa es una serie de pasos que realizara un agente inteligente para alcanzar una meta y que van acompañado de ciertas condiciones. Cada paso o acción tiene sus respectivas precondiciones y postcondiciones.

De un curso anterior, recuerdo que se menciono que hay una universidad de Estados Unidos que creó un lenguaje de programación para el desarrollo de este tipo de  implementaciones, que a simple vista se podría confundir con el lenguaje de programación basado en el lógica Prolog.

Un ejemplo cotidiano donde se puede aplicar esto, es cuando un alumno se despierta por la mañana para acudir a la escuela. Donde:

El inicio sería:
  • Cuando se esta dormido
  
La meta sería :
  • Llegar a la escuela
Las acciones a realizar para llegar a esto pueden ser:
  • Levantarte/Despertar
  • Ducharse
  • Cambiar Ropa
  • Preparar libros p/llevar
  • Desayunar
  • Ir a la escuela
Las precondiciones podrían ser:
  • Para levantarte: (estar dormido)
  • Para ducharse: (estar sucio) (estar despierto)
  • Para cambiar ropa: (tener la ropa) (estar limpio)
  • Para los libros: (saber el horario) 
  • Para desayunar: (tener hambre) (tener que comer)
  • Para ir a la escuela: (traer los libros)
Las postcondiciones:
  • De levantarte: (estar despierto)
  • De ducharse: (estar limpio)
  • De cambiarse: (estar cambiado)
  • De los libros: (mochila preparada)
  • De desayunar: (no tener hambre)
  • De ir a la escuela (suponieno que no pasara nada en el transcurso de la casa a la escuela): (llegar a la escuela)
El siguiente diagrama explica un poco mejor las cosas (eso creo :D) y las opciones (lo adaptativo) que puede tener el alumno (agente) para poder alcanzar la meta (llegar a la escuela).


Algunas acciones, como se muestra, necesitan de que otras se realicen para poder ser llevadas a cabo mientras que otras pueden pasarse por alto y no son necesarias para nada.


Aplicación de esto a nuestro proyecto (el videojuego) 

La aplicación de esto (Planeación Lógica Adaptativa) a los videjuegos se puede dar en los segmentos en los cuales el usuario/jugador necesite tomar decisiones acerca del destino del personaje en el juego y estas decisiones a su vez pueden afectar en el progreso en el juego o pueden cambiar completamente la historia del juego.

Estas técnicas se estan haciendo muy populares dentro de los juegos de rol (RPG) donde se escoge un personaje al iniciar el juego y que la personalidad de este se verá afectada dependiendo de las acciones que se realicen dentro del juego.
Uno de los juegos que empezó con este sistema es:  
  • The Elder Scrolls IV: Oblivion


Se podría tomar en cuenta estas prácticas para implementarse dentro de nuestro juego.

Una manera donde se puede implementar (lo de PLA) en el proyecto es que dependiendo de las acciones, las armas y los caminos que se elijan al estar jugando se puede ver afectado el que enfrentes o no, a ciertos enemigos o jefes de nivel, es decir:
  • Para luchar contra (X) jefe es necesario: tener (a) escudo y (b) espada. 
  • Para obtener (a) escudo: hay que matar a (Y) jefe y quitarselo.
  • Para obtener (b) espada: es necesario tener (a) escudo y matar a (W) jefe y quitarle (b) espada.
A mi parecer esa seria una forma de aplicarlo/implementarlo en nuestro proyecto.


__________________________________________________________________
Referencias: