pilas.actores.actor

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):
    206          self._z = z
    207          pilas.actores.utils.ordenar_actores_por_valor_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
    571 -    def esta_fuera_de_la_pantalla(self):
    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