Package pilas :: Package actores :: Module actor
[hide private]
[frames] | no frames]

Source Code for Module pilas.actores.actor

  1  # -*- encoding: utf-8 -*- 
  2  # Pilas engine - A video game framework. 
  3  # 
  4  # Copyright 2010 - Hugo Ruscitti 
  5  # License: LGPLv3 (see http://www.gnu.org/licenses/lgpl.html) 
  6  # 
  7  # Website - http://www.pilas-engine.com.ar 
  8   
  9  import pilas 
 10  from pilas import utils 
 11  from pilas.estudiante import Estudiante 
 12   
 13  IZQUIERDA = ["izquierda"] 
 14  DERECHA = ["derecha"] 
 15  ARRIBA = ["arriba", "superior"] 
 16  CENTRO = ["centro", "centrado", "medio", "arriba"] 
 17  ABAJO = ["abajo", "inferior", "debajo"] 
18 19 20 -class Actor(object, Estudiante):
21 """Representa un objeto visible en pantalla, algo que se ve y tiene 22 posicion. 23 24 .. image:: images/actores/actor.png 25 26 Un objeto Actor se tiene que crear siempre indicando una imagen. Si no 27 se especifica una imagen, se verá una pila de color gris cómo la que 28 está mas arriba. 29 30 Una forma de crear el actor con una imagen es: 31 32 >>> protagonista = Actor("protagonista_de_frente.png") 33 34 incluso, es equivalente hacer lo siguiente: 35 36 >>> imagen = pilas.imagenes.cargar("protagonista_de_frente.png") 37 >>> protagonista = Actor(imagen) 38 39 Luego, una vez que ha sido ejecutada la sentencia aparecerá 40 el nuevo actor para que puedas manipularlo. Por ejemplo 41 alterando sus propiedades: 42 43 >>> protagonista.x = 100 44 >>> protagonista.escala = 2 45 >>> protagonista.rotacion = 30 46 47 Estas propiedades también se pueden manipular mediante 48 interpolaciones. Por ejemplo, para aumentar el tamaño del 49 personaje de 1 a 5 en 7 segundos: 50 51 >>> protagonista.escala = 1 52 >>> protagonista.escala = [5], 7 53 54 Si quieres que el actor sea invisible, un truco es crearlo 55 con la imagen ``invisible.png``: 56 57 >>> invisible = pilas.actores.Actor('invisible.png') 58 """ 59
60 - def __init__(self, imagen="sin_imagen.png", x=0, y=0):
61 """ Constructor del Actor. 62 63 :param imagen: Ruta de la imagen del Actor. 64 :type imagen: string 65 :param x: Posición horizontal del Actor. 66 :type x: int 67 :param y: Posición vertical del Actor. 68 :type y: int 69 """ 70 71 if not pilas.mundo: 72 mensaje = "Tiene que invocar a la funcion ``pilas.iniciar()`` " \ 73 "para comenzar." 74 print mensaje 75 raise Exception(mensaje) 76 77 Estudiante.__init__(self) 78 self._actor = pilas.mundo.motor.obtener_actor(imagen, x=x, y=y) 79 self.centro = ('centro', 'centro') 80 81 self.id = "" 82 83 self.x = x 84 self.y = y 85 self.transparencia = 0 86 87 # Define en que escena se encuentra el actor. 88 self.escena = None 89 90 # Define el nivel de lejanía respecto del observador. 91 self.z = 0 92 self._espejado = False 93 self.radio_de_colision = 10 94 pilas.actores.utils.insertar_como_nuevo_actor(self) 95 self._transparencia = 0 96 self.anexados = [] 97 self._vivo = True 98 99 # Velocidades horizontal y vertical 100 self._vx = 0 101 self._vy = 0 102 self._dx = self.x 103 self._dy = self.y
104
105 - def definir_centro(self, (x, y)):
106 """ Define en que posición estará el centro del Actor. 107 108 Se puede definir la posición mediante unas coordenadas numéricas o 109 mediante texto. 110 111 La forma de definirlo mediante coordenadas numéricas seria así: 112 113 >>> mi_actor.definir_centro((10,50)) 114 115 La otra forma de definirlo mediante texto sería: 116 117 >>> mi_actor.definir_centro(('centro','derecha')) 118 119 :param x: Coordenadas horizontal en la que se establecerá el centro del Actor. 120 :type x: int 121 :param y: Coordenadas vertical en la que se establecerá el centro del Actor. 122 :type y: int 123 """ 124 if type(x) == str: 125 if x not in IZQUIERDA + CENTRO + DERECHA: 126 raise Exception("No puedes definir '%s' como eje horizontal." %(x)) 127 x = self._interpretar_y_convertir_posicion(x, self.obtener_ancho()) 128 if type(y) == str: 129 if y not in ARRIBA + CENTRO + ABAJO: 130 raise Exception("No puedes definir '%s' como eje vertical." %(y)) 131 y = self._interpretar_y_convertir_posicion(y, self.obtener_alto()) 132 133 self._centro = (x, y) 134 self._actor.definir_centro(x, y)
135
136 - def _interpretar_y_convertir_posicion(self, posicion, maximo_valor):
137 if posicion in IZQUIERDA + ARRIBA: 138 return 0 139 elif posicion in CENTRO: 140 return int(maximo_valor / 2.0) 141 elif posicion in DERECHA + ABAJO: 142 return maximo_valor 143 else: 144 raise Exception("El valor '%s' no corresponde a una posicion, use numeros o valores como 'izquierda', 'arriba' etc." %(posicion))
145
146 - def obtener_centro(self):
147 """ Obtiene las coordenadas del centro del Actor. """ 148 return self._centro
149 150 centro = property(obtener_centro, definir_centro, doc=""" 151 Cambia la posición del punto (x, y) dentro de actor. 152 153 Inicialmente, cuando tomamos un actor y definimos sus 154 atributos (x, y). Ese punto, será el que representa el centro 155 del personaje. 156 157 Eso hace que las rotaciones sean siempre sobre el centro 158 del personajes, igual que los cambios de escala y la posición. 159 160 En algunas ocasiones, queremos que el punto (x, y) sea otra 161 parte del actor. Por ejemplo sus pies. En esos casos 162 es útil definir el centro del actor. 163 164 Por ejemplo, si queremos mover el centro del actor podemos 165 usar sentencias cómo estas: 166 167 >>> actor.centro = ("izquierda", "abajo") 168 >>> actor.centro = ("centro", "arriba") 169 170 Pulsa la tecla **F8** para ver el centro del los actores 171 dentro de pilas. Es aconsejable pulsar la tecla **+** para 172 que el punto del modo **F8** se vea bien. 173 """) 174
175 - def definir_posicion(self, x, y):
176 """ Define la posición del Actor en el mundo. 177 178 :param x: Posición horizontal del Actor en el mundo. 179 :type x: int 180 :param y: Posición vertical del Actor en el mundo. 181 :type y: int 182 """ 183 self._actor.definir_posicion(x, y)
184
185 - def obtener_posicion(self):
186 """ Obtiene la posición del Actor en el mundo. """ 187 return self._actor.obtener_posicion()
188
189 - def dibujar(self, aplicacion):
190 """ Metodo interno para el dibujado del Actor en pantalla. """ 191 self._actor.dibujar(aplicacion)
192
193 - def get_x(self):
194 x, y = self.obtener_posicion() 195 return x
196 197 @pilas.utils.interpolable
198 - def set_x(self, x):
199 self.definir_posicion(x, self.y)
200
201 - def get_z(self):
202 return self._z
203 204 @pilas.utils.interpolable
205 - def set_z(self, z):
208 209 @pilas.utils.interpolable
210 - def set_y(self, y):
211 self.definir_posicion(self.x, y)
212
213 - def get_y(self):
214 x, y = self.obtener_posicion() 215 return y
216 217 @pilas.utils.interpolable
218 - def set_scale(self, s):
219 if s < 0.001: 220 s = 0.001 221 222 ultima_escala = self.obtener_escala() 223 224 # Se hace la siguiente regla de 3 simple: 225 # 226 # ultima_escala self.radio_de_colision 227 # s ? 228 229 self.definir_escala(s) 230 self.radio_de_colision = (s * self.radio_de_colision) / max(ultima_escala, 0.0001)
231 232 @pilas.utils.interpolable
233 - def set_scale_x(self, s):
234 if s < 0.001: 235 s = 0.001 236 self._actor.definir_escala_x(s)
237 238 @pilas.utils.interpolable
239 - def set_scale_y(self, s):
240 if s < 0.001: 241 s = 0.001 242 self._actor.definir_escala_y(s)
243
244 - def get_scale(self):
245 return self.obtener_escala()
246
247 - def get_scale_x(self):
248 return self._actor._escala_x
249
250 - def get_scale_y(self):
251 return self._actor._escala_y
252
253 - def get_rotation(self):
254 return self.obtener_rotacion()
255 256 @pilas.utils.interpolable
257 - def set_rotation(self, x):
258 self.definir_rotacion(x)
259
260 - def get_espejado(self):
261 return self._espejado
262
263 - def set_espejado(self, nuevo_valor):
264 if self._espejado != nuevo_valor: 265 self._espejado = nuevo_valor 266 self._actor.set_espejado(nuevo_valor)
267 268 @pilas.utils.interpolable
269 - def set_transparencia(self, nuevo_valor):
270 self._transparencia = nuevo_valor 271 self.definir_transparencia(nuevo_valor)
272
273 - def get_transparencia(self):
274 return self._transparencia
275
276 - def get_imagen(self):
277 return self.obtener_imagen()
278
279 - def set_imagen(self, imagen):
280 if isinstance(imagen, str): 281 imagen = pilas.imagenes.cargar(imagen) 282 283 self.definir_imagen(imagen)
284
285 - def get_fijo(self):
286 return self._actor.fijo
287
288 - def set_fijo(self, fijo):
289 self._actor.fijo = fijo
290
291 - def get_vx(self):
292 return self._vx
293
294 - def get_vy(self):
295 return self._vy
296 297 espejado = property(get_espejado, set_espejado, doc="Indica si se tiene que invertir horizonaltamente la imagen del actor.") 298 z = property(get_z, set_z, doc="Define lejania respecto del observador.") 299 x = property(get_x, set_x, doc="Define la posición horizontal.") 300 y = property(get_y, set_y, doc="Define la posición vertical.") 301 vx = property(get_vx, None, doc="Obtiene la velocidad horizontal del actor.") 302 vy = property(get_vy, None, doc="Obtiene la velocidad vertical del actor.") 303 rotacion = property(get_rotation, set_rotation, doc="Angulo de rotación (en grados, de 0 a 360)") 304 escala = property(get_scale, set_scale, doc="Escala de tamaño, 1 es normal, 2 al doble de tamaño etc...)") 305 escala_x = property(get_scale_x, set_scale_x, doc="Escala de tamaño horizontal, 1 es normal, 2 al doble de tamaño etc...)") 306 escala_y = property(get_scale_y, set_scale_y, doc="Escala de tamaño vertical, 1 es normal, 2 al doble de tamaño etc...)") 307 transparencia = property(get_transparencia, set_transparencia, doc="Define el nivel de transparencia, 0 indica opaco y 100 la maxima transparencia.") 308 imagen = property(get_imagen, set_imagen, doc="Define la imagen a mostrar.") 309 fijo = property(get_fijo, set_fijo, doc="Indica si el actor debe ser independiente a la camara.") 310
311 - def eliminar(self):
312 """Elimina el actor de la lista de actores que se imprimen en pantalla.""" 313 self._eliminar_anexados() 314 self.destruir()
315
316 - def destruir(self):
317 """Elimina a un actor pero de manera inmediata.""" 318 self._vivo = False 319 self.eliminar_habilidades() 320 self.eliminar_comportamientos() 321 # Solo permite eliminar el actor si está en su escena. 322 if self in pilas.escena_actual().actores: 323 pilas.escena_actual().actores.remove(self)
324
325 - def actualizar(self):
326 """Actualiza el estado del actor. 327 328 Este metodo se llama una vez por frame, y generalmente se suele 329 usar para implementar el comportamiento del actor. 330 331 Si estás haciendo una subclase de Actor, es aconsejable que re-definas 332 este método.""" 333 pass
334
335 - def pre_actualizar(self):
336 """Actualiza comportamiento y habilidades antes de la actualización. 337 También actualiza la velocidad horizontal y vertical que lleva el actor. 338 """ 339 self.actualizar_comportamientos() 340 self.actualizar_habilidades() 341 self.__actualizar_velocidad()
342
343 - def __actualizar_velocidad(self):
344 """ Calcula la velocidad horizontal y vertical del actor. """ 345 346 if (self._dx != self.x): 347 self._vx = abs(self._dx - self.x) 348 self._dx = self.x 349 else: 350 self._vx = 0 351 352 if (self._dy != self.y): 353 self._vy = abs(self._dy - self.y) 354 self._dy = self.y 355 else: 356 self._vy = 0
357
358 - def __cmp__(self, otro_actor):
359 """Compara dos actores para determinar cual esta mas cerca de la camara. 360 361 Este metodo se utiliza para ordenar los actores antes de imprimirlos 362 en pantalla. De modo tal que un usuario pueda seleccionar que 363 actores se ven mas arriba de otros cambiando los valores de 364 los atributos `z`.""" 365 366 if otro_actor.z >= self.z: 367 return 1 368 else: 369 return -1
370
371 - def get_izquierda(self):
372 return self.x - (self.centro[0] * self.escala)
373 374 @pilas.utils.interpolable
375 - def set_izquierda(self, x):
376 self.x = x + (self.centro[0] * self.escala)
377 378 izquierda = property(get_izquierda, set_izquierda, doc="Establece el " \ 379 "espacio entre la izquierda del actor y el centro " \ 380 "de coordenadas del mundo.") 381
382 - def get_derecha(self):
383 return self.izquierda + self.obtener_ancho() * self.escala
384 385 @pilas.utils.interpolable
386 - def set_derecha(self, x):
387 self.set_izquierda(x - self.ancho)
388 389 derecha = property(get_derecha, set_derecha, doc="Establece el " \ 390 "espacio entre la derecha del actor y el centro " \ 391 "de coordenadas del mundo.") 392
393 - def get_abajo(self):
394 return self.get_arriba() - self.alto * self.escala
395 396 @pilas.utils.interpolable
397 - def set_abajo(self, y):
398 self.set_arriba(y + self.alto)
399 400 abajo = property(get_abajo, set_abajo, doc="Establece el " \ 401 "espacio entre la parte inferior del actor y el " \ 402 "centro de coordenadas del mundo.") 403
404 - def get_arriba(self):
405 return self.y + (self.centro[1] * self.escala)
406 407 @pilas.utils.interpolable
408 - def set_arriba(self, y):
409 self.y = y - (self.centro[1] * self.escala)
410 411 arriba = property(get_arriba, set_arriba, doc="Establece el " \ 412 "espacio entre la parte superior del actor y el " \ 413 "centro de coordenadas del mundo.") 414
415 - def colisiona_con_un_punto(self, x, y):
416 """Determina si un punto colisiona con el area del actor. 417 418 Todos los actores tienen un area rectangular, pulsa la 419 tecla **F10** para ver el area de colision. 420 421 :param x: Posición horizontal del punto. 422 :type x: int 423 :param y: Posición vertical del punto. 424 :type y: int 425 """ 426 return self.izquierda <= x <= self.derecha and self.abajo <= y <= self.arriba
427
428 - def distancia_con(self, otro_actor):
429 """Determina la distancia con el ``otro_actor`` 430 431 :param otro_actor: El otro actor para ver la distancia 432 :type otro_actor: pilas.actores.Actor 433 434 """ 435 return utils.distancia_entre_dos_actores(self, otro_actor)
436
437 - def actor_mas_cercano(self):
438 """Retorna otro actor mas cercano a este actor""" 439 return utils.actor_mas_cercano_al_actor(self)
440
441 - def distancia_al_punto(self, x, y):
442 """Determina la distancia desde el centro del actor hasta el punto 443 determinado 444 445 Todos los actores tienen un area rectangular, pulsa la 446 tecla **F10** para ver el area de colision. 447 448 :param x: Posición horizontal del punto. 449 :type x: int 450 :param y: Posición vertical del punto. 451 :type y: int 452 """ 453 return utils.distancia_entre_dos_puntos((self.x, self.y), (x, y))
454
455 - def colisiona_con(self, otro_actor):
456 """Determina si este actor colisiona con ``otro_actor`` 457 458 :param otro_actor: El otro actor para verificar si colisiona. 459 :type otro_actor: pilas.actores.Actor 460 461 """ 462 return utils.colisionan(self, otro_actor)
463
464 - def obtener_rotacion(self):
465 return self._actor.obtener_rotacion()
466
467 - def definir_rotacion(self, r):
468 r = r % 360 469 self._actor.definir_rotacion(r)
470
471 - def definir_color(self, c):
472 self._actor.definir_color(c)
473
474 - def obtener_imagen(self):
475 """ Obtinene la imagen del Actor. """ 476 return self._actor.obtener_imagen()
477
478 - def definir_imagen(self, imagen):
479 """ Define la imagen del Actor y establece el centro del mismo a 480 ('centro,'centro'). 481 482 :param imagen: Ruta de la imagen del Actor. 483 :type imagen: string 484 """ 485 self._actor.definir_imagen(imagen) 486 self.centro = ('centro', 'centro')
487
488 - def duplicar(self, **kv):
489 """ Duplica un Actor. 490 491 :return: `Actor`. 492 """ 493 duplicado = self.__class__() 494 495 for clave in kv: 496 setattr(duplicado, clave, kv[clave]) 497 498 return duplicado
499
500 - def obtener_ancho(self):
501 return self.imagen.ancho()
502
503 - def obtener_alto(self):
504 return self.imagen.alto()
505 506 ancho = property(obtener_ancho, doc="Obtiene el ancho del Actor.") 507 alto = property(obtener_alto, doc="Obtiene el alto del Actor.") 508
509 - def __mul__(self, cantidad):
510 if type(cantidad) is not int or cantidad < 1: 511 raise TypeError("Solo puede multiplicar por numeros enteros " \ 512 "mayores a 1.") 513 514 grupo = pilas.atajos.fabricar(self.__class__, cantidad - 1) 515 grupo.append(self) 516 return grupo
517
518 - def __str__(self):
519 return "<%s en (%d, %d)>" % (self.__class__.__name__, self.x, self.y)
520
521 - def obtener_escala(self):
522 return self._actor.obtener_escala()
523
524 - def definir_escala(self, escala):
525 self._actor.definir_escala(escala)
526
527 - def definir_transparencia(self, valor):
528 self._actor.definir_transparencia(valor)
529
530 - def imitar(self, otro_actor_o_figura):
531 """ Hace que un Actor copie la posición y la rotación de otro Actor o 532 Figura fisica. 533 534 Por ejemplo: 535 536 >>> circulo_dinamico = pilas.fisica.Circulo(10, 200, 50) 537 >>> mi_actor.imitar(circulo_dinamico) 538 539 :param otro_actor_o_figura: Actor o Figura física a imitar. 540 :type otro_actor_o_figura: `Actor`, `Figura` 541 """ 542 self.aprender(pilas.habilidades.Imitar, otro_actor_o_figura)
543
544 - def decir(self, mensaje, autoeliminar=True):
545 """Emite un mensaje usando un globo similar al de los comics. 546 547 :param mensaje: Texto a mostrar en el mensaje. 548 :type mensaje: string 549 :param autoeliminar: Establece si se eliminará el globo al cabo de unos segundos. 550 :type autoeliminar: boolean 551 """ 552 nuevo_actor = pilas.actores.Globo(mensaje, self.x, self.y, autoeliminar=autoeliminar) 553 nuevo_actor.aprender(pilas.habilidades.Imitar, self, False) 554 nuevo_actor.z = self.z - 1 555 self.anexar(nuevo_actor) 556 pilas.atajos.leer(mensaje)
557
558 - def anexar(self, otro_actor):
559 """ Agrega un Actor a la lista de actores anexados al Actor actual. 560 Cuando se elimina un Actor, se eliminan los actores anexados. 561 562 :param otro_actor: Actor a anexar. 563 :type otro_actor: `Actor` 564 """ 565 self.anexados.append(otro_actor)
566
567 - def _eliminar_anexados(self):
568 for x in self.anexados: 569 x.eliminar()
570
572 """Indica si el actor está fuera del area visible de la pantalla. 573 574 :return: boolean""" 575 if self.fijo: 576 return False 577 izquierda, derecha, arriba, abajo = self.escena.camara.obtener_area_visible() 578 return self.derecha < izquierda or self.izquierda > derecha or self.abajo > arriba or self.arriba < abajo
579
580 - def es_fondo(self):
581 """ Comprueba si el actor es un fondo del juego. 582 583 :return: boolean""" 584 return False
585