|
| 1 | +..include::common.txt |
| 2 | + |
| 3 | +************************** |
| 4 | +Clases de objetos de juego |
| 5 | +************************** |
| 6 | + |
| 7 | +..role::firstterm(emphasis) |
| 8 | + |
| 9 | +.. _makegames-4: |
| 10 | + |
| 11 | +4. Clases de objetos de juego |
| 12 | +============================= |
| 13 | + |
| 14 | +Una vez que hayas cargado tus módulos y escrito tus funciones de manejo de recursos, querrás pasar a escribir algunos |
| 15 | +objetos de juego. La forma en que esto se realiza es bastante simple, sin embargo puede parecer complejo al principio. |
| 16 | +Escribirás una clase para cada tipo de objeto en el juego y después crearás una instancia de esas clases para los objetos. |
| 17 | +Luego podés usar los métodos de esas clases para manipular los objetos, dándoles algún tipo de movimiento y capacidades |
| 18 | +interactivas. Entonces tu juego, en pseudo-código, se verá así:: |
| 19 | + |
| 20 | + |
| 21 | + #!/usr/bin/python |
| 22 | + |
| 23 | + # [load modules here] |
| 24 | + |
| 25 | + # [resource handling functions here] |
| 26 | + |
| 27 | + class Ball: |
| 28 | + # [ball functions (methods) here] |
| 29 | + # [e.g. a function to calculate new position] |
| 30 | + # [and a function to check if it hits the side] |
| 31 | + |
| 32 | + def main: |
| 33 | + # [initiate game environment here] |
| 34 | + |
| 35 | + # [create new object as instance of ball class] |
| 36 | + ball = Ball() |
| 37 | + |
| 38 | + while True: |
| 39 | + # [check for user input] |
| 40 | + |
| 41 | + # [call ball's update function] |
| 42 | + ball.update() |
| 43 | + |
| 44 | +Por supuesto, esto es un ejemplo muy simple, y tendrías que agregar todo el código en lugar de esos pequeños comentarios entre |
| 45 | +corchetes. Pero deberías entender la idea básica. Creás una clase, en la cual colocás todas las funciones de la pelota, incluyendo |
| 46 | +``__init__``,que crearía todos los atributos de la pelota, y ``update``, que movería la pelota a su nueva posición antes de |
| 47 | +blittearla en la pantalla en esta posición. |
| 48 | + |
| 49 | +Luego podés crear más clases para todos tus otros objetos de juego, y luego crear instancias de los mismos para que puedas manejarlos |
| 50 | +fácilmente en la función ``main`` y en el bucle principal del programa. En contraste con iniciar la pelota en la función ``main``, |
| 51 | +y luego tener muchas funciones sin clase para manipular un objeto de pelota establecido, y espero que puedas ver por qué usar clases |
| 52 | +es una ventaja: te permite poner todo el código perteneciente a cada objeto en un único lugar; hace que sea más fácil usar objetos; |
| 53 | +hace que agregar nuevos objetos y manipularlos sea más flexible. En lugar de agregar más código para cada nuevo objeto de pelota, |
| 54 | +podés simplemente crear instancias de la clase ``Ball`` para cada nuevo objeto de pelota. ¡Mágia! |
| 55 | + |
| 56 | + |
| 57 | +.. _makegames-4-1: |
| 58 | + |
| 59 | +4.1. Una clase simple de pelota |
| 60 | +------------------------------- |
| 61 | + |
| 62 | +Aquí hay una clase simple con el código necesario para crear un objeto pelota que se moverá a través de la pantalla, si la |
| 63 | +función ``update`` esa llamada en el bucleo principal:: |
| 64 | + |
| 65 | + class Ball(pygame.sprite.Sprite): |
| 66 | + """A ball that will move across the screen (Una peleota se moverá a través de la pantalla) |
| 67 | + Returns: ball object |
| 68 | + Functions: update, calcnewpos |
| 69 | + Attributes: area, vector""" |
| 70 | + |
| 71 | + def __init__(self, vector): |
| 72 | + pygame.sprite.Sprite.__init__(self) |
| 73 | + self.image, self.rect = load_png('ball.png') |
| 74 | + screen = pygame.display.get_surface() |
| 75 | + self.area = screen.get_rect() |
| 76 | + self.vector = vector |
| 77 | + |
| 78 | + def update(self): |
| 79 | + newpos = self.calcnewpos(self.rect,self.vector) |
| 80 | + self.rect = newpos |
| 81 | + |
| 82 | + def calcnewpos(self,rect,vector): |
| 83 | + (angle,z) = vector |
| 84 | + (dx,dy) = (z*math.cos(angle),z*math.sin(angle)) |
| 85 | + return rect.move(dx,dy) |
| 86 | + |
| 87 | +Aquí tenemos la clase ``Ball`` con una función ``__init__`` que configura la pelota, una función ``update`` que cambia el |
| 88 | +rectángulo de la pelota para que esté en la nueva posición, y una función ``calcnewpos`` para calcular la nueva posición de |
| 89 | +la pelota basada en su posición actual, y el vector por el cual se está moviendo. Explicaré la física en un momento. |
| 90 | +Lo único más a destacar es la cadena de documentación, que es un poco más larga esta vez, y explica los conceptos básicos |
| 91 | +del la clase. Estas cadenas son útiles no solo para ti mismo y otros programadores que revisen el código, sino también para |
| 92 | +las herramientas que analicen y documenten tu código. No harán mucha diferencia en programas pequeños, pero en los grandes |
| 93 | +son invaluables, así que es una buena costumbre de adquirir. |
| 94 | + |
| 95 | +.. _makegames-4-1-1: |
| 96 | + |
| 97 | +4.1.1. Digresión 1: Sprites |
| 98 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 99 | + |
| 100 | +La otra razón por la cual crear una clase por cada objeto son los sprites. Cada imagen que se renderiza en tu juego será un objeto, |
| 101 | +por lo que en principio, la clase de cada objeto debería heredar la clase:class:`Sprite <pygame.sprite.Sprite>`. Esta es una |
| 102 | +característica muy útil de Python: la herencia de clases. |
| 103 | +Ahora, la clase ``Ball`` tiene todas las funciones que vienen con la clase ``Sprite``, y cualquier instancia del objeto de la clase |
| 104 | +``Ball`` será registrada por Pygame como un sprite. Mientras que con el texto y el fondo, que no se mueven, está bien hacer un blit |
| 105 | +del objeto sobre el fondo, Pygame maneja los objetos sprites de manera diferente, lo cual verás cuando miremos el código completo del |
| 106 | +programa. |
| 107 | + |
| 108 | +Básicamente, creas tanto un objeto pelota y un objeto sprite para la pelota, y luego llamás a la función update de la pelota en el |
| 109 | +objeto de sprite, actualizando así el sprite. Los sprites también te dan formas sofisticadas de determinar si dos objetos han |
| 110 | +colisionado. Normalmente, podrías simplemente comprobar en el bucle principal para ver si sus rectángulos se superponen, pero eso |
| 111 | +implicaría mucho código, lo cual sería una pérdida de tiempo porque la clase ``Sprite`` proporciona dos funciones (``spritecollide`` |
| 112 | +y ``groupcollide``) para hacer esto por vos. |
| 113 | + |
| 114 | +.. _makegames-4-1-2: |
| 115 | + |
| 116 | + |
| 117 | +4.1.2. Digresión 2: Física de vectores |
| 118 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 119 | + |
| 120 | +Aparte de la estructura de la clase ``Ball``, lo notable de este código es la física de vectores utilizada para calcular el |
| 121 | +movimiento de la pelota. En cualquier juego que involucre movimiento angular, no se llegará muy lejos a menos que se esté |
| 122 | +cómodo con la trigonometría, así que simplemente introduciré los conceptos básicos que necesitás saber para entender la |
| 123 | +función ``calcnewpos``. |
| 124 | + |
| 125 | +Para empezar, notarás que la pelota tiene un atributo llamado ``vector``, que está compuesto por ``angle`` y ``z``. El |
| 126 | +ángulo estpa medido en radianes y dará la dirección en la que la pelota se mueve. Z es la velocidad a la que se mueve la |
| 127 | +pelota. Entonces, usando este vector, podemos determinar la dirección y velocidad de la pelota, y por lo tanto, cuánto se |
| 128 | +moverá en los ejes x e y: |
| 129 | + |
| 130 | +..image::tom_radians.png |
| 131 | + |
| 132 | +El diagrama anterior ilustra las matemáticas básicas detrás de los vectores. En el diagrama de la izquierda, se puede ver el |
| 133 | +movimiento proyectado de la pelota representado por una línea azul. La longitud de esa línea (z) representa su velocidad, y el |
| 134 | +ángulo es la dirección en la que se moverá. El ángulo para el movimiento de la pelota siempre se tomará desde el eje x a la |
| 135 | +derecha, y se mide en sentido horario desde esa línea, como se muestra en el diagrama. |
| 136 | + |
| 137 | +A partir del ángulo y la velocidad de la pelota, podemos lograr calcular cuánto se ha movido a lo largo de los ejes x e y. |
| 138 | +Necesitamos hacer esto porque Pygame en sí no admite vectores, y solo podemos mover la pelota moviendo su rectángulo a lo largo |
| 139 | +de los dos ejes. Por lo tanto, necesitamos:firstterm:`resolve` (resolver) el ángulo y la velocidad en su movimiento en el eje |
| 140 | +x (dx) y en el eje y (dy). Esto es un asunto sencillo de trigonometría y se puede hacer con las fórmulas que se muestran en el |
| 141 | +diagrama. |
| 142 | + |
| 143 | +Si has estudiado trigonometría elemental antes, nada de esto debería ser nuevo para vos. Pero en caso que seas olvidadizo, acá |
| 144 | +hay algunas fórmulas útiles para recordar, que te ayudarán a visualizar los ángulos (a mi me resulta más fácil visualizar los |
| 145 | +ángulos en grados que en radianes. |
| 146 | + |
| 147 | +..image::tom_formulae.png |
| 148 | + |