hugoruscitti/pilas

View on GitHub
data/manual/como_funciona_pilas_por_dentro/index.json

Summary

Maintainability
Test Coverage
{
    "content": "<h1 id=\"como-funciona-pilas-por-dentro\">\u00bfC\u00f3mo funciona pilas por dentro?</h1>\n<p>Pilas es un proyecto con una arquitectura de objetos\ngrande. Tiene mucha funcionalidad, incluye un\nmotor de f\u00edsica, muchos personaje pre-dise\u00f1ados, eventos, escenas\ny un enlace al motor multimedia Qt.</p>\n<p>Mediante este cap\u00edtulo quisiera explicar a grandes\nrasgos los componentes de pilas. C\u00f3mo est\u00e1n estructurados\nlos m\u00f3dulos, y qu\u00e9 hacen las clases mas importantes.</p>\n<p>El objetivo es orientar a los programadores mas\navanzados para que puedan investigar pilas\npor dentro.</p>\n<h2 id=\"filosofia-de-desarrollo\">Filosof\u00eda de desarrollo</h2>\n<p>Pilas es un proyecto de software libre, orientado a facilitar\nel desarrollo de videojuegos a personas que generalmente no\nhacen juegos... Por ese motivo que gran parte de las decisiones de\ndesarrollo se tomaron reflexionando sobre c\u00f3mo\ndise\u00f1ar una interfaz de programaci\u00f3n simple y f\u00e1cil\nde utilizar.</p>\n<p>Un ejemplo de ello, es que elegimos el lenguaje de\nprogramaci\u00f3n python, y tratamos de aprovechar al m\u00e1ximo\nsu modo interactivo.</p>\n<h2 id=\"api-en-espanol\">API en espa\u00f1ol</h2>\n<p>Dado que pilas est\u00e1 orientado a principiantes, docentes y\nprogramadores de habla hispana. Preferimos hacer el motor\nen espa\u00f1ol, permitirle a los mas chicos usar su idioma\npara hacer juegos es alentador, tanto para ellos que\nobservan que el idioma no es una barrera, como\npara los que ense\u00f1amos y queremos entusiasmar.</p>\n<p>Esta es una decisi\u00f3n de dise\u00f1o importante, porque al\nmismo tiempo que incluye a muchas personas, no coincide\ncon lo que acostumbran muchos programadores (escribir\nen ingl\u00e9s).</p>\n<p>Posiblemente en el futuro podamos ofrecer una\nversi\u00f3n de pilas alternativa en ingl\u00e9s, pero\nactualmente no es una prioridad.</p>\n<h2 id=\"bibliotecas-que-usa-pilas\">Bibliotecas que usa pilas</h2>\n<p>Hay tres grandes bibliotecas que se utilizan dentro de pilas:</p>\n<ul>\n<li>Box2D</li>\n<li>Qt4</li>\n</ul>\n<p>.. image:: images/logos/box2d.png</p>\n<p>.. image:: images/logos/qt-logo.jpg</p>\n<p>Box2D se utiliza c\u00f3mo motor de f\u00edsica, mientras que Qt es un\nmotor multimedia utilizado para dibujar, reproducir sonidos y\nmanejar eventos.</p>\n<h2 id=\"objetos-y-modulos\">Objetos y m\u00f3dulos</h2>\n<p>Pilas incluye muchos objetos y es un sistema complejo. Pero\nhay una forma sencilla de abordarlo, porque hay solamente\n3 componentes que son indispensables, y han\nsido los pilares desde las primeras versiones de pilas\nhasta la fecha:</p>\n<ul>\n<li>Mundo</li>\n<li>Actor</li>\n<li>Motor</li>\n</ul>\n<p>Si puedes comprender el rol y las caracter\u00edsticas\nde estos 3 componentes el resto del motor es mas\nf\u00e1cil de analizar.</p>\n<p>Veamos los 3 componentes r\u00e1pidamente:</p>\n<p><code>Mundo</code> es un objeto <code>singleton</code>, hay una sola instancia\nde esta clase en todo el sistema y se encarga de\nmantener el juego en funcionamiento e interactuando con\nel usuario.</p>\n<p>Los actores (clase <code>Actor</code>) representan a los personajes de los\njuegos, la clase se encarga de representar todos sus atributos\ncomo la posici\u00f3n y comportamiento como \"dibujarse en la ventana\". Si\nhas usado otras herramientas para hacer juegos, habr\u00e1s notado\nque se los denomina <code>Sprites</code>.</p>\n<p>Luego, el <code>Motor</code>, permite que pilas sea un motor\nmultimedia portable y multiplaforma. B\u00e1sicamente\npilas delega la tarea de dibujar, emitir sonidos y controlar\neventos a una biblioteca externa. Actualmente esa biblioteca\nes Qt, pero en versiones anteriores ha sido implementada en pygame y\nsfml.</p>\n<p>Ahora que lo he mencionado, veamos con un poco mas\nde profundidad lo que hace cada uno.</p>\n<h3 id=\"inspeccionando-mundo\">Inspeccionando: Mundo</h3>\n<p>El objeto de la clase Mundo se construye cuando se invoca a la\nfunci\u00f3n <code>pilas.iniciar</code>. Su implementaci\u00f3n est\u00e1 en el\narchivo <code>mundo.py</code>:</p>\n<p><img alt=\"\" src=\"../imagenes/comofunciona/mundo.png\" /></p>\n<p>Su responsabilidad es inicializar varios componentes de pilas, como\nel sistema de controles, la ventana, etc.</p>\n<p>Uno de sus m\u00e9todos mas importantes es <code>ejecutar_bucle_principal</code>. Un\nm\u00e9todo que se invoca directamente cuando alguien escribe\nla sentencia <code>pilas.ejecutar()</code>.</p>\n<p>Si observas el c\u00f3digo, notar\u00e1s que es el responsable de mantener a todo\nel motor en funcionamiento.</p>\n<p>Esta es una versi\u00f3n muy simplificada del\nm\u00e9todo <code>ejecutar_bucle_principal</code>:</p>\n<pre><code>def ejecutar_bucle_principal(self, ignorar_errores=False):\n\n    while not self.salir:\n        pilas.motor.procesar_y_emitir_eventos()\n\n        if not self.pausa_habilitada:\n            self._realizar_actualizacion_logica(ignorar_errores)\n\n        self._realizar_actualizacion_grafica()\n</code></pre>\n<p>Lo primero que debemos tener en cuenta es que este m\u00e9todo contiene\nun bucle <code>while</code> que lo mantendr\u00e1 en ejecuci\u00f3n. Este bucle\nsolo se detendr\u00e1 cuando alguien llame al m\u00e9todo <code>terminar</code> (que\ncambia el valor de la variable <code>salir</code> a <code>True</code>).</p>\n<p>Luego hay tres m\u00e9todos importantes:</p>\n<ul>\n<li><code>procesar_y_emitir_eventos</code> analiza el estado de los controles y avisa al resto del sistema si ocurre algo externo, como el movimiento del mouse..</li>\n<li><code>_realizar_actualizacion_logica</code> le permite a los personajes realizar una fracci\u00f3n muy peque\u00f1a de movimiento, poder leer el estado de los controles o hacer otro tipo de acciones.</li>\n<li><code>_realizar_actualizacion_logica</code> simplemente vuelca sobre la pantalla a todos los actores y muestra el resultado del dibujo al usuario.</li>\n</ul>\n<p>Otra tarea que sabe hacer el objeto <code>Mundo</code>, es administrar\nescenas. Las escenas son objetos que representan una\nparte individual del juego: un men\u00fa, una pantalla de opciones, el\nmomento de acci\u00f3n del juego etc...</p>\n<h2 id=\"modo-interactivo\">Modo interactivo</h2>\n<p>Pilas soporta dos modos de funcionamiento, que t\u00e9cnicamente son\nmuy similares, pero que a la hora de programar hacen una gran\ndiferencia.</p>\n<ul>\n<li>\n<p><strong>modo normal</strong>: si est\u00e1s haciendo un archivo <code>.py</code> con el c\u00f3digo de tu juego usar\u00e1s este modo, tu programa comienza con una sentencia como <code>iniciar</code> y la simulaci\u00f3n se inicia cuando llamas a <code>pilas.ejecutar</code> (que se encarga de llamar a <code>ejecutar_bucle_principal</code> del objeto mundo).</p>\n</li>\n<li>\n<p><strong>modo interactivo</strong>: el modo que generalmente se usa en las demostraciones o cursos es el modo interactivo. Este modo funciona gracias a una estructura de hilos, que se encargan de ejecutar la simulaci\u00f3n pero a la vez no interrumpe al programador y le permite ir escribiendo c\u00f3digo mientras la simulaci\u00f3n est\u00e1 en funcionamiento.</p>\n</li>\n</ul>\n<h2 id=\"motores-multimedia\">Motores multimedia</h2>\n<p>Al principio pilas delegaba todo el manejo multimedia a una\nbiblioteca llamada SFML. Pero esta biblioteca requer\u00eda que todos los equipos\nen donde funcionan tengan aceleradoras gr\u00e1ficas (al menos con\nsoporte OpenGL b\u00e1sico).</p>\n<p>Pero como queremos que pilas funcione en la mayor cantidad\nde equipos, incluso en los equipos antiguos de algunas\nescuelas, reemplazamos el soporte multimedia con la biblioteca Qt. Que sabe\nacceder a las funciones de aceleraci\u00f3n de gr\u00e1ficos (si est\u00e1n disponibles), o\nbrinda una capa de compatibilidad con equipos antiguos.</p>\n<p>La funci\u00f3n que permite iniciar y seleccionar el motor es <code>pilas.iniciar</code>.</p>\n<pre><code>pilas.iniciar(usar_motor='qt')\n</code></pre>\n<p>Ahora bien, \u00bfc\u00f3mo funciona?. Dado que pilas est\u00e1 realizado\nusando orientaci\u00f3n a objetos, usamos un concepto llamado\npolimorfismo:</p>\n<p>El objeto motor sabe que tiene que delegar el manejo multimedia\na una instancia (o derivada) de la clase <code>Motor</code> (ver directorio\n<code>pilas/motores/</code>:</p>\n<p><img alt=\"\" src=\"../imagenes/comofunciona/motores.png\" /></p>\n<p>El motor expone toda la funcionalidad que se necesita para\nhace un juego: sabe crear una ventana, pintar una imagen o\nreproducir sonidos, entre tantas otras cosas.</p>\n<p>El objeto mundo no sabe exactamente que motor est\u00e1 utilizando, solo\ntiene una referencia a un motor y delega en \u00e9l todas las\ntareas multimedia.</p>\n<p>Solo puede haber una instancia de motor en funcionamiento, y\nse define cuando se inicia el motor.</p>\n<h2 id=\"sistema-de-actores\">Sistema de actores</h2>\n<p>Los actores permiten que los juegos cobren atractivo, porque\nun actor puede representarse con una imagen en pantalla.</p>\n<p>La implementaci\u00f3n de todos los actores est\u00e1n en\nel directorio <code>pilas/actores</code>.</p>\n<p>Todos los actores heredan de la clase <code>Actor</code>, que define\nel comportamiento com\u00fan de todos los actores.</p>\n<p>Por ejemplo, esta ser\u00eda una versi\u00f3n reducida de la\njerarqu\u00eda de clases de los actores Mono, Pingu y Tortuga:</p>\n<p><img alt=\"\" src=\"../imagenes/comofunciona/actores.png\" /></p>\n<p>Hay dos m\u00e9todos en los actores que se invocar\u00e1n en\ntodo momento: el m\u00e9todo <code>actualizar</code> se invocar\u00e1\ncuando el bucle de juego del mundo llame al m\u00e9todo\n<code>_realizar_actualizacion_logica</code>, esto ocurre unas\n60 veces por segundo. Y el otro m\u00e9todo es <code>dibujar</code>, que\nse tambi\u00e9n se invoca desde el objeto mundo, pero esta\nvez en el m\u00e9todo <code>_realizar_actualizacion_grafica</code>.</p>\n<h2 id=\"modo-depuracion\">Modo depuraci\u00f3n</h2>\n<p>Cuando pulsas teclas como F8, F9, F10, F11 o F12 durante\nla ejecuci\u00f3n de pilas, vas a ver que la pantalla comienza\na mostrar informaci\u00f3n valiosa para los desarrolladores.</p>\n<p>Esta modalidad de dibujo la llamamos <strong>modo depuraci\u00f3n</strong>, y\nayuda mucho a la hora de encontrar errores o ajustar detalles.</p>\n<p>El objeto <code>Mundo</code>, que mantiene en ejecuci\u00f3n al juego, tiene\nuna instancia de objeto <code>Depurador</code> que se encarga de\nhacer estos dibujos.</p>\n<p>Las clases mas importantes a la hora de investigar el depurador\nest\u00e1n en el archivo <code>depurador.py</code>:</p>\n<p><img alt=\"\" src=\"../imagenes/comofunciona/depurador.png\" /></p>\n<p>El Depurador tiene dos atributos, tiene una pizarra para dibujar y\nuna lista de modos. Los modos pueden ser cualquiera de los que est\u00e1n\nen la jerarqu\u00eda de ModoDepuracion, por ejemplo, podr\u00eda tener\ninstancias de ModoArea y ModoPuntoDeControl.</p>\n<h2 id=\"sistema-de-eventos\">Sistema de eventos</h2>\n<p>Hay varios enfoques para resolver el manejo de eventos\nen los videojuegos.</p>\n<p>Pilas usa un modelo conocido y elaborado\nllamado <code>Observator</code>, un patr\u00f3n de dise\u00f1o. Pero que\nlamentablemente no es muy intuitivo a primera vista.</p>\n<p>En esta secci\u00f3n intentar\u00e9 mostrar por qu\u00e9 usamos\nesa soluci\u00f3n y qu\u00e9 problemas nos ayuda a resolver.</p>\n<p>Comenzar\u00e9 explicando sobre el problema de gestionar eventos\ny luego c\u00f3mo el modelo <code>Observator</code> se volvi\u00f3\nuna buena soluci\u00f3n para el manejo de eventos.</p>\n<h3 id=\"el-problema-pooling-de-eventos\">El problema: pooling de eventos</h3>\n<p>Originalmente, en un modelo muy simple de aplicaci\u00f3n multimedia,\nmanejar eventos de usuario es algo sencillo, pero con el\ntiempo comienza a crecer y se hace cada vez mas dif\u00edcil de\nmantener.</p>\n<p>Resulta que las bibliotecas multimedia suelen entregar un\nobjeto <code>evento</code> cada vez que ocurre algo y tu responsabilidad\nes consultar sobre ese objeto en b\u00fasqueda de datos.</p>\n<p>Imagina que quieres crear un actor <code>Bomba</code> cada\nvez que el usuario hace click en la pantalla. El\nc\u00f3digo podr\u00eda ser algo as\u00ed:</p>\n<pre><code>evento = obtener_evento_actual()\n\nif evento.tipo == 'click_de_mouse':\n    crear_bomba(evento.x)\n    crear_bomba(evento.x)\nelse:\n    # el evento de otro tipo (teclado, ventana ...)\n    # lo descartamos.\n</code></pre>\n<p>A esta soluci\u00f3n podr\u00edamos llamarla <strong>preguntar</strong> y <strong>responder</strong>,\nporque efectivamente as\u00ed funciona el c\u00f3digo, primero\nnos aseguramos de que el evento nos importa y luego\nhacemos algo. En algunos sitios suelen llamar a esta\nestrategia <em>pooling</em>.</p>\n<p>Pero este enfoque tiene varios problemas, y cuando hacemos\njuegos o bibliotecas se hace mas evidente. El c\u00f3digo, a medida\nque crece, comienza a mezclar manejo de eventos y l\u00f3gica\ndel juego.</p>\n<p>Para ver el problema de cerca, imagina que en determinadas\nocasiones quieres deshabilitar la creaci\u00f3n de bombas, \u00bfc\u00f3mo\nhar\u00edas?. \u00bfY si quieres que las bombas creadas se puedan\nmover con el teclado?.</p>\n<h3 id=\"otro-enfoque-en-pilas-usamos-observator\">Otro enfoque, en pilas usamos 'Observator'</h3>\n<p>Hay otro enfoque para el manejo de eventos que me parece\nmas interesante, y lo he seleccionado para el motor\n<code>pilas</code>:</p>\n<p>En lugar de administrar los eventos uno a uno por\n<strong>consultas</strong>, delegamos esa tarea a un sistema que nos\npermite <strong>suscribir</strong> y <strong>ser notificado</strong>.</p>\n<p>Aqu\u00ed no mezclamos nuestro c\u00f3digo con el sistema de eventos, si\nqueremos hacer algo relacionado con un evento, escribimos\nuna funci\u00f3n y le pedimos al evento que llame a nuestra\nfunci\u00f3n cuando sea necesario.</p>\n<p>Veamos el ejemplo anterior pero usando este enfoque, se\ncrear\u00e1 una <code>Bomba</code> cada vez que el usuario\nhace <code>click</code> en la pantalla:</p>\n<pre><code>def crear_bomba(evento):\n    pilas.actores.Bomba(x=evento.x, y=evento.y)\n    return true\n\npilas.eventos.click_de_mouse.conectar(crear_bomba)\n</code></pre>\n<p>Si queremos que el mouse deje de crear bombas, podemos\nejecutar la funci\u00f3n <code>desconectar</code>:</p>\n<pre><code>pilas.eventos.click_de_mouse.conectar(crear_bomba)\n</code></pre>\n<p>o simplemente retornar <code>False</code> en la funci\u00f3n <code>crear_bomba</code>.</p>\n<p>Nuestro c\u00f3digo tendr\u00e1 <em>bajo acoplamiento</em> con los eventos\ndel motor, y no se nos mezclar\u00e1n.</p>\n<p>De hecho, cada vez que tengas dudas sobre las funciones\nsuscritas a eventos pulsa F7 y se imprimir\u00e1n en pantalla.</p>\n<h3 id=\"como-funciona\">\u00bfC\u00f3mo funciona?</h3>\n<p>Ahora bien, \u00bfc\u00f3mo funciona el sistema de eventos por dentro?:</p>\n<p>El sistema de eventos que usamos es una ligera adaptaci\u00f3n\ndel sistema de se\u00f1ales de django (un framework para desarrollo\nde sitios web) d\u00f3nde cada evento es un objeto que puede\nhacer dos cosas:</p>\n<ul>\n<li>suscribir funciones.</li>\n<li>invocar a las funciones que se han suscrito.</li>\n</ul>\n<p><strong>1 Suscribir</strong></p>\n<p>Por ejemplo, el evento <code>mueve_mouse</code> es un objeto, y cuando\ninvocamos la sentencia <code>pilas.eventos.mueve_mouse.conectar(mi_funcion)</code>,\nle estamos diciendo al objeto \"quiero que guardes una referencia\na <code>mi_funcion</code>\".</p>\n<p>Puedes imaginar al evento como un objeto contenedor (similar\na una lista), que guarda cada una de las funciones que le enviamos\ncon el m\u00e9todo <code>conectar</code>.</p>\n<p><strong>2 Notificar</strong></p>\n<p>La segunda tarea del evento es notificar a todas\nlas funciones que se suscribieron.</p>\n<p>Esto se hace, retomando el ejemplo anterior, cuando el usuario\nhace click con el mouse.</p>\n<p>Los eventos son objetos <code>Signal</code> y se inicializan en el\narchivo <code>eventos.py</code>, cada uno con sus respectivos\nargumentos o detalles:</p>\n<pre><code>click_de_mouse = Evento(\"click_de_mouse\")\npulsa_tecla = Evento(\"pulsa_tecla\")\n[ etc...]\n</code></pre>\n<p>Los argumentos indican informaci\u00f3n adicional del evento, en\nel caso del click, observar\u00e1s que los argumentos son el bot\u00f3n pulsado\ny la coordenada del puntero.</p>\n<p>Cuando se quiere notificar a las funciones conectadas a\nun evento simplemente se tiene que invocar al m\u00e9todo <code>emitir</code>\ndel evento y proveer los argumentos que necesita:</p>\n<pre><code>click_de_mouse.emitir(button=1, x=30, y=50)\n</code></pre>\n<p>Eso har\u00e1 que todas las funciones suscritas al evento <code>click_de_mouse</code>\nse invoquen con el argumento <code>evento</code> representando esos detalles:</p>\n<pre><code>def crear_bomba(evento):\n\n    print evento.x\n    # imprimir\u00e1 30\n\n    print evento.y\n    # imprimir\u00e1 50\n\n    [ etc...]\n</code></pre>\n<p>La parte de pilas que se encarga de llamar a los m\u00e9todos <code>emitir</code>\nes el m\u00e9todo <code>procesar_y_emitir_eventos</code> del motor.</p>\n<h2 id=\"habilidades\">Habilidades</h2>\n<p>Los actores de pilas tienen la cualidad de poder\nir obteniendo comportamiento desde otras clases.</p>\n<p>Esto te permite lograr resultados de forma r\u00e1pida, y\na la vez, es un modelo tan flexible que podr\u00edas\nhacer muchos juegos distintos combinando los mismos\nactores pero con distintas habilidades.</p>\n<p>Veamos un ejemplo, un actor sencillo como <code>Mono</code> no\nhace muchas cosas. Pero si escribimos lo siguiente, podremos\ncontrolarlo con el mouse:</p>\n<pre><code>mono = pilas.actores.Mono()\nmono.aprender(pilas.habilidades.Arrastrable)\n</code></pre>\n<p>Lo que en realidad estamos haciendo, es vincular dos objetos\nen tiempo de ejecuci\u00f3n. <code>mono</code> es un objeto <code>Actor</code>, y tiene una\nlista de habilidades que puede aumentar usando el m\u00e9todo <code>aprender</code>.</p>\n<p>El m\u00e9todo <code>aprender</code> toma la clase que le enviamos como\nargumento, construye un objeto y lo guarda en su lista de habilidades.</p>\n<p>Este es un modelo de c\u00f3mo se conocen las clases entre\ns\u00ed:</p>\n<p><img alt=\"\" src=\"../imagenes/comofunciona/habilidades.png\" /></p>\n<p>Entonces, una vez que invocamos a la sentencia, nuestro actor\ntendr\u00e1 un nuevo objeto en su lista de habilidades, listo para\nejecutarse en cada cuadro de animaci\u00f3n.</p>\n<h3 id=\"como-se-ejecutan-las-habilidades\">\u00bfC\u00f3mo se ejecutan las habilidades?</h3>\n<p>Retomando un poco lo que vimos al principio de este cap\u00edtulo, lo\nque mantiene con <em>vida</em> al juego es el bucle principal, la clase\n<code>Mundo</code> tiene un bucle que recorre la lista de actores en pantalla\ny por cada uno llama al m\u00e9todo actualizar.</p>\n<p>Bien, las habilidades se mantienen en ejecuci\u00f3n desde ah\u00ed tambi\u00e9n. Esta\nes una versi\u00f3n muy simplificada del bucle que encontrar\u00e1s en el\narchivo ``mundo.py```:</p>\n<pre><code>def ejecutar_bucle_principal(self, ignorar_errores=False):\n\n    while not self.salir:\n        self.actualizar_actores()\n\n        [ etc ...]\n\ndef actualizar_actores(self):\n    for actor in pilas.actores.todos:\n        actor.actualizar()\n        actor.actualizar_habilidades()\n</code></pre>\n<p>Aqu\u00ed puedes ver dos llamadas a m\u00e9todos del actor, el m\u00e9todo\n<code>actualizar</code> se cre\u00f3 para que cada programador escriba\nah\u00ed lo que quiera que el personaje haga (leer el teclado,\nhacer validaciones, moverse etc). Y el m\u00e9todo <code>actualizar_habilidades</code>\nes el encargado de <em>dar vida</em> a las habilidades.</p>\n<p>T\u00e9cnicamente hablando, el m\u00e9todo <code>actualizar_habilidades</code> es\nmuy simple, solamente toma la lista de objetos habilidades y\nlos actualiza, al <code>Actor</code> no le preocupa en lo mas m\u00ednimo\n\"qu\u00e9\" hace cada habilidad, solamente les permite ejecutar c\u00f3digo\n(ver c\u00f3digo <code>estudiante.py</code>, una superclase de <code>Actor</code>):</p>\n<pre><code>def actualizar_habilidades(self):\n    for h in self.habilidades:\n        h.actualizar()\n</code></pre>\n<p>Entonces, si queremos que un actor haga muchas cosas, podemos\ncrear un objeto habilidad y vincularlo con el actor. Esto\npermite generar \"comportamientos\" re-utilizables, la habilidad\nse codifica una vez, y se puede usar muchas veces.</p>\n<h3 id=\"objetos-habilidad\">Objetos habilidad</h3>\n<p>Las habilidades interact\u00faan con los actores, y por ese motivo\ntienen que tener una interfaz en com\u00fan, de modo tal que\ndesde cualquier parte de pilas puedas tratar a una habilidad\ncomo a cualquier otra.</p>\n<p>La interfaz que toda habilidad debe tener es la que define\nla clase <code>Habilidad</code> del archivo <code>habilidades.py</code>:</p>\n<pre><code>class Habilidad:\n\n    def __init__(self, receptor):\n        self.receptor = receptor\n\n    def actualizar(self):\n        pass\n\n    def eliminar(self):\n        pass\n</code></pre>\n<p>Tiene que tener tres m\u00e9todos, uno que se ejecuta al producirle\nla relaci\u00f3n con un actor, un m\u00e9todo que se ejecutar\u00e1 en\ncada iteraci\u00f3n del bucle de juego (<code>actualizar</code>) y un\n\u00faltimo m\u00e9todo para ejecutar cuando la habilidad se desconecta\ndel actor. Este m\u00e9todo <code>eliminar</code> suele ser el que desconecta\neventos o cualquier otra cosa creada temporalmente.</p>\n<p>Ten en cuenta que el m\u00e9todo <code>__init__</code>, que construye\nal objeto, lo invoca el propio actor desde su m\u00e9todo <code>aprender</code>. Y\nel argumento <code>receptor</code> ser\u00e1 una referencia al actor que\n<em>aprende</em> la habilidad.</p>\n<p>Veamos un ejemplo muy b\u00e1sico, imagina que quieres hacer\nuna habilidad muy simple, que gire al personaje todo el\ntiempo, c\u00f3mo una aguja de reloj. Podr\u00edas hacer\nalgo as\u00ed:</p>\n<pre><code>class GirarPorSiempre(pilas.habilidades.Habilidad):\n\n    def __init__(self, receptor):\n        self.receptor = receptor\n\n    def actualizar(self):\n        self.receptor.rotacion += 1\n\nmono = pilas.actores.Mono()\nmono.aprender(GirarPorSiempre)\n</code></pre>\n<p>La sentencia <code>aprender</code> construir\u00e1 un objeto de la\nclase que le indiquemos, y el bucle de pilas (en <code>mundo.py</code>)\ndar\u00e1 la orden para ejecutar los m\u00e9todos actualizar de\ncada habilidad conocida por los actores.</p>\n<h3 id=\"argumentos-de-las-habilidades\">Argumentos de las habilidades</h3>\n<p>En el ejemplo anterior podr\u00edamos encontrar una\nlimitaci\u00f3n. El actor siempre girar\u00e1 a la misma velocidad.</p>\n<p>Si queremos que los personajes puedan girar a diferentes\nvelocidades tendr\u00edamos que agregarle argumentos\na la habilidad, esto es simple: solo tienes que llamar\nal m\u00e9todo <code>aprender</code> con los argumentos que quieras\ny asegurarte de que la habilidad los tenga definidos en\nsu m\u00e9todo <code>__init__</code>.</p>\n<p>Este es un ejemplo de la habilidad pero que permite\ndefinir la velocidad de giro:</p>\n<pre><code>class GirarPorSiempre(pilas.habilidades.Habilidad):\n\n    def __init__(self, receptor, velocidad=1):\n        self.receptor = receptor\n        self.velocidad = velocidad\n\n    def actualizar(self):\n        self.receptor.rotacion += self.velocidad\n\na = pilas.actores.Mono()\na.aprender(GirarPorSiempre, 20)\n</code></pre>\n<p>Listo, es casi id\u00e9ntico al anterior, si llamas a <code>aprender</code> con un\nargumento como <code>20</code>, el actor girar\u00e1 mucho mas r\u00e1pido que\nantes. Y si no especificas la velocidad, se asumir\u00e1 que la\nvelocidad es <code>1</code>, porque as\u00ed lo indica el m\u00e9todo <code>__init__</code>.</p>\n<h2 id=\"documentacion\">Documentaci\u00f3n</h2>\n<p>El sistema de documentaci\u00f3n que usamos en pilas\nes Sphinx, un sistema muy interesante porque nos\npermite gestionar todo el contenido del manual\nen texto plano, y gracias a varias herramientas\nde conversi\u00f3n c\u00f3mo restructuredText y latex, se\nproducen muchos formatos de salida c\u00f3mo HTML y PDF.</p>\n<p>Toda la documentaci\u00f3n del proyecto est\u00e1 en el\ndirectorio <code>doc</code>. El directorio <code>doc/sources</code> contiene\ntodos los archivos que modificamos para escribir contenido\nen la documentaci\u00f3n.</p>\n<p>Para generar los archivos PDF o HTML usamos el comando\n<code>make</code> dentro del directorio <code>doc</code>. El archivo que\ndispara todas las acciones que sphinx sabe hacer est\u00e1n\ndefinidas en el archivo <code>Makefile</code>.</p>\n", 
    "url": "/como_funciona_pilas_por_dentro/", 
    "language": "en", 
    "title": "\u00bfC\u00f3mo funciona pilas por dentro?"
}