martes, 29 de mayo de 2012

Proyecto de Simulación - Air Hockey

Descripción
El hockey de aire (también llamado hockey de mesa, aerohockey o tejo) es un deporte en el cual dos personas compiten para anotar puntos en la portería contraria.


En particular pensamos en el proyecto para simulación de colisiones, la velocidad del disco de hockey disminuyendo con la poca fricción de la mesa, y otros efectos físicos que mencionaremos más adelante.

Por lo tanto las reglas del juego y el gameplay en sí no son en realidad importantes, aunque implementamos unas ligeras funcionalidades para incrementar la puntuación y anotar puntos.

Teoría(Física)
En este proyecto, tenemos tres efectos de física importantes que deseamos simular:
  • Colisiones. El disco choca con una pared-limitante, o con el mazo.
  • Fricción. El disco y los mazos(ambos pueden ser controlados con el mouse) deben de disminuir su velocidad una cantidad considerable al dejarlos moverse.
  • Fuerza. La velocidad y dirección del disco debe ser resultado de una "fuerza" aplicada con el mazo. 
Para comprender esto, necesitamos saber como funcionan en mecánica todos estos fenómenos.
Colisiones

En cuanto a las colisiones, nos referimos a un tipo específico, las colisiones elásticas. En física, estas son colisiones entre dos o más cuerpos en las que éstos no sufren deformaciones permanentes durante el impacto. En una colisión elástica se conservan tanto el momento lineal como la energía cinética del sistema, y no hay intercambio de masa entre los cuerpos, que se separan después del choque.




Ahora en nuestro caso, dos cuerpos de dos dimensiones chocaran(el disco y el mazo), y la velocidad total de cada uno debe ser separada en dos velocidades perpendiculares: una tangente a la normal común de la superficie de los cuerpos colisionando al punto de contacto, y la otra junto a la linea de colisión. Este gif obtenido de Wikipedia, explica perfectamente este fenómeno:
Fricción


Fricción es la fuerza resistiva generada por el movimiento de dos superficies que hacen contacto una con la otra. Es el producto indirecto de una de las cuatro fuerzas fundamentales conocidas. La fricción de un sistema es imposible de predeterminar estríctamente desde principios teóricos. Matemáticamente, la expresión para la fricción incluye una sola constante que incorpora todos los factores que puedan causarla, el coeficiente de fricción (COF). La ecuación es simplemente escrita  fx = μxF, donde fdefine la forma y la medida de la fricción, y F es la fuerza normal ejercida por ambas superficies, una sobre la otra.

Todos los coeficientes de fricción son magnitudes escalares sin dimensiones. La fuerza fundamental responsable por la mayoría de la fricción es también la que permite la formación de los enlaces químicos, la fuerza electroestática. En una consideración inicial, podría decirse que la gravedad es la causa inicial de la fricción, debido a la fuerza que ejerce la gravedad hacia la tierra es la fuente de la variable F.




En el hockey de mesa, es importante señalar que la fricción que estamos considerando, no es la del disco con la mesa, o el mazo con la mesa. Debido a que prácticamente el disco flota de un lado a otro con la mínima fuerza, la única fricción que estamos tomando en cuenta es la del aire que las mesas de hockey utilizan normalmente. Esta fuerza en contra del movimiento es bastante pequeña, pero existe, de otra forma el disco continuaría moviéndose indefinidamente.


Herramientas
  • Python-Pygame
    • Pygame es un set de módulos de Python diseñado para escribir juegos. Pygame permite crear juegos y programas de multimedia en el lenguaje python. Es bastante portable y corre en prácticamente cualquier plataforma y sistema operativo.
  • ¿Por qué Pygame?
    • Al inicio no considerábamos Pygame como una opción, debido a que como la mayoría de personas, solamente por el nombre o por ver algunos ejemplos, podemos darnos a la idea de que Pygame es una librería para hacer juegos, y nada más. Pero buscando en Internet ,encontramos ejemplos bastantes interesantes de como aplicar fenómenos físicos a objetos dibujados por Pygame, lo cual prácticamente cumplía nuestras expectativas, lo que hizo que lo tomáramos en serio.
Física en Pygame


Para entender un poco el potencial de Pygame, se explicará a continuación algunas de las funciones que utilizamos para poder añadir un poco de realidad a la simulación.


Limites y Rebotes


Creando una simulación como la nuestra, deseamos que tenga un limite, en este caso sería la mesa, para evitar simular una región infinita. El ejemplo que utilizamos para esto, es una pared virtual en la cual las partículas rebotan.


Lo primero que nuestra función de rebote debe tener sería verificar si una partícula paso un cierto límite. Los cuatro limites en una ventana serían entonces:
  • x = 0 (pared izquierda)
  • x = ancho (pared derecha)
  • x = 0 (parte superior)
  • x = alto (parte inferior)
Ya que la simulación tiene pasos de tiempo discretos, es difícil verificar una partícula en el punto exacto en el que toca el límite, pero es posible verificar si ha viajado un poco después del límite. Si la velocidad de la partícula es baja, entonces la partícula tiene poca probabilidad de haber pasado muy lejos del limite(la distancia máxima que habrá excedido después del límite es entonces, igual a la velocidad. En este caso ignoraremos éste fenómeno, y nos concentraremos en simplemente reflectar el ángulo de la partícula lo más precisamente posible.

Entonces cuando una partícula excede el límite, calculamos cuanto fue lo que excedió. Por ejemplo, sí el valor de x de la partícula es mayor al ancho de la ventana menos el tamaño de la partícula, ha superado el límite derecho, entonces calculamos el excedente:


d = self.x - (width - self.size)

Y reflejamos la posición en el límite (rebotando) poniendo las coordenadas x de la siguiente manera:


self.x = (width - self.size) - d

Esto puede ser simplificado para que no necesitemos la variable d, de la siguiente manera:


self.x = 2*(width - self.size) - self.x


La parte más importante de rebotar una partícula, es reflejar el ángulo en el límite, que es donde nuestros vectores empiezan a ser útiles(aunque es más útil cuando los límites no son lineares). Un limite vertical tiene un ángulo de 0, mientras que uno horizontal tiene un ángulo de pi. Entonces reflejamos verticalmente partículas que reboten, restando su ángulo actual de 0, y por pi en el caso de rebotes horizontales.

La función de rebote entonces debe quedarnos así:


def bounce(self):

    if self.x > width - self.size:
       self.x = 2*(width - self.size) - self.x
       self.angle = - self.angle

   elif self.x < self.size:
        self.x = 2*self.size - self.x
        self.angle = - self.angle

    if self.y > height - self.size:
        self.y = 2*(height - self.size) - self.y
        self.angle = math.pi - self.angle

    elif self.y < self.size:
        self.y = 2*self.size - self.y
        self.angle = math.pi - self.angle



Ahora podemos llamar a la función rebote, después de cada movimiento, pero antes de mostrarlo en pantalla para poder comprobar si hay un rebote en las partículas.


Movimiento


Casi todas las simulaciones tienen valores que cambian con el tiempo. Un valor que cambia bastante seguido en nuestra simulación, es la posición de un objeto(el disco y el mazo), en el caso de que este en movimiento.
Para nuestra simulación hicimos 2 cosas a estos objetos:
  1. Dar a los objetos velocidad y dirección
  2. Usar trigonometría básica para convertir los vectores en movimiento.
Como se mencionó anteriormente, nuestra simulación es en tiempo discreto, lo que significa que dividimos el tiempo en pasos individuales. En cada paso actualizamos la simulación un poco para después mostrar la nueva situación. Entonces seguimos actualizando la simulación hasta que el usuario salga.

La forma más simple de representar el movimiento es crear dos atributos, dx y dy. Entonces cada paso por el ciclo principal del programa, añadir dx a x y dy a y. Entonces, por ejemplo, si una particula tiene dx = 2 y dy = 1, seguira una diagonal desde la izquierda a la derecha, y desde arriba hacia abajo. Éste es un método simple y correcto para la mayoría de las situaciones. Pero cuando necesitamos trabajar en la interacción entre dos objetos, las cosas se vuelven más complejas.

El otro método es entonces crear atributos para representar la rapidez y dirección (velocidad). Esto requiere un poco más de trabajo inicial, pero hace todo más simple. El método es bueno también para crear objetos con velocidad constante pero dirección variada. Entonces para darle velocidad a nuestra partícula, necesitamos darle dos atributos:

self.speed = 0.01
self.angle = 0

Ya que haremos cálculos de trigonometría, haremos uso del módulo math de python, para el uso de funciones como sin y cos para calcular senos y cosenos.
Ahora debemos agregar una función move a nuestro objeto. La función debería entonces cambiar las coordenadas x, y de la partícula basada en la velocidad y el ángulo de movimiento. Entonces podemos calcular el cambio en x y y, como muestra el diagrama:

The mathematics of movement

Es mas simple considerar un ángulo de 0 cuando apunta hacia arriba, a pesar del hecho de que el eje y realmente apunta hacia abajo en gráficos de computadora.

Para calcular el cambio en x y y, usamos algunas funciones trigonométricas de nivel de secundaria como se puede observar:

def move(self):

self.x += math.sin(self.angle) * self.speed

self.y -= math.cos(self.angle) * self.speed

Otro punto a tomar en cuenta es que los ángulos están todos en radiantes. Entonces debemos de tomar en cuenta que 1 radian es 180°/pi. Entonces si queremos que una partícula se mueva hacia adelante(izquierda o derecha), el ángulo debería ser pi/2 = 90°. En python podemos hacer eso usando math también, de la siguiente manera:

self.angle = math.pi/2
De esta forma podemos llamar la función move dentro del ciclo principal del programa, antes de llamar la función que dibuja los elementos
Colisiones


Crear partículas con movimiento, y que tengan un limite es bastante interesante, pero no es suficiente. En el hockey de mesa un punto importante son las colisiones entre el disco y el mazo, por lo cual es imprescindible agregar ese tipo de interacción al programa. Entonces lo que necesitábamos hacer era:
  • Checar si dos partículas han chocado
  • Hacer que dichas partículas reboten
Las cosas se empiezan a poner difíciles cuando tenemos múltiples partículas interactuando unas con otras. De hecho, es matemáticamente imposible resolver las ecuaciones que describen tres o más objetos que interactuan. En nuestra simulación se hicieron simplificaciones, que aunque son un tanto imprecisas, representan una razonable aproximación a la realidad.

Primero para probar si dos partículas se sobreponen unas a otras, necesitamos comparar cada partícula con las demás. Podemos hacer éso con un simple for anidado, justo después de moverlos y checar si han tocado un límite, pero antes de dibujarlos en la pantalla:

for i, particle in enumerate(my_particles):
    particle.move()
    particle.bounce()
    for particle2 in my_particles[i+1:]:
        collide(particle, particle2)
    particle.display()

El segundo for anidado no es un completo, es decir no iteramos todas las partículas. Si tuviéramos dos ciclos completos, compararíamos cada partícula con las demás dos veces cada una(además de compararla consigo mismo). En cambio, comparamos cada partícula con cada partícula con un índice superior que ella en el arreglo. Entonces necesitamos saber el índice de la partícula actual, el cual podemos conseguir usando enumerate. Usamos el valor de i para cortar el arreglo desde i+1 hasta el final para construir el segundo for.
Ahora podemos definir la función colisión. La primera cosa que la función debe hacer es checar si dos partículas han colisionado. Esto es muy simple debido a que las partículas son ambos círculos en el caso del hockey de mesa, pero contienen un sprite superpuesto. Entonces para verificar la colisión, medimos la distancia entre ellas, utilizando math.hypot(), y probamos si el valor es menor que sus radios combinados.
def collide(p1, p2):
    dx = p1.x - p2.x
    dy = p1.y - p2.y
    distance = math.hypot(dx, dy)
    if distance < p1.size + p2.size:
        print 'Chocaron!'

Hasta esta parte sería suficiente con imprimir algo en caso de que esto sea cierto.
Como mencionamos en la teoría, cuando dos partículas circulares chocan, hacen contacto en un punto infinitamente pequeño. El ángulo de este punto es la tangente de la partícula en este punto. Entonces el ángulo de salida es perpendicular a la linea que se une el centro de las dos partículas.
Entonces podemos tratar la colisión como si las partículas estuvieran rebotando con una superficie plana con el ángulo de la tangente. Podemos encontrar dicho ángulo con la siguiente fórmula:
tangent = math.atan2(dy, dx)
Para reflejar el ángulo de las partículas en la superficie, restamos el ángulo actual menos dos veces la tangente, para lo cual necesitamos conocer el ángulo en el que nuestra partícula viajaba, el mismo que usamos en el movimiento.


p1.angle = 2*tangent - p1.angle
p2.angle = 2*tangent - p2.angle



Entonces necesitamos cambiar las velocidades de las dos partículas, debido a que transfieren las energías uno a otro. Podemos hacer esto con una simple tupla.


(p1.speed, p2.speed) = (p2.speed, p1.speed)


Entonces podemos agregar una elasticidad para reducir la energía de ambas partículas después de la colisión.


p1.speed *= elasticity
p2.speed *= elasticity
Código


#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Referencias:
# Imagen de fondo obtenida de https://engineering.purdue.edu/477grp6/images/lrprice/air_hockey_concept.png
# 
# Código basado en el tutorial http://razonartificial.com/tutoriales-pygame/
# Código basado en el tutorial http://www.petercollingridge.co.uk/book/export/html/6
# Código basado en el tutorial http://www.petercollingridge.co.uk/pygame-physics-simulation/mouse-interactions

#modulos
import sys, pygame, random, math
from pygame.locals import *

#Constantes
width = 800
height = 600
handles = []
pucks = []
nHandles = 2
npocks = 1
puntuacion = [0,0]#jugador A, jugador B

#Clases
class Puck(pygame.sprite.Sprite):
        def __init__(self, x, y, id_):
            self.inicial = [x,y]
     self.anterior = [0, 0]
     self.cambio = [0.0, 0.0, 0.0]#X, Y y Contador
     self.id_ = id_
            pygame.sprite.Sprite.__init__(self)
            self.image = load_image("images/ball.png", True)
            self.diametro = width*0.07
            self.image = pygame.transform.scale(self.image, (int(self.diametro) ,int(self.diametro) ))
            self.rect = self.image.get_rect()
            self.rect.centerx = x
            self.rect.centery = y
            self.speed = [0.8, 0.8]
     self.coeficiente_arrastre = 0.001
            self.acceleration =  [0, 0]
     self.velocidad_juego = 4

        def actualizar(self, time, bandera):
            if bandera == self.id_:
  if self.cambio[2] == 15: 
      self.cambio[2] = self.cambio[1] = self.cambio[0] = 0

                pos = pygame.mouse.get_pos() 
                self.rect.centerx = pos[0]
                self.rect.centery = pos[1]
  
  self.cambio[2] = self.cambio[2]+1
  self.speed[0] = (self.cambio[0] + (float(pos[0] - self.anterior[0]))/(time*16) )
  self.speed[0]/self.cambio[2]
  self.speed[1] = (self.cambio[1] + (float(pos[1] - self.anterior[1]))/(time*16) )
  self.speed[1]/self.cambio[2]

                self.anterior[0] += self.speed[0] * time
                self.anterior[1] += self.speed[1] * time
  
            else:
                self.cambio[2] = self.cambio[1] = self.cambio[0] = 0
                self.anterior[0] = self.rect.centerx
                self.anterior[1] = self.rect.centery

  self.speed[0] = self.speed[0]*(1-self.coeficiente_arrastre)
  self.speed[1] = self.speed[1]*(1-self.coeficiente_arrastre)
                self.rect.centerx += self.speed[0] * time
                self.rect.centery += self.speed[1] * time

                if (self.rect.left <= 0 or self.rect.right >= width) and (self.rect.bottom <= height*(1.0/3) or self.rect.top > height*(2.0/3) ):
                    self.speed[0] = - self.speed[0]
                    self.rect.centerx += self.speed[0] * time
  elif (self.rect.right < 0 or self.rect.left > width) and not (self.rect.bottom <= height*(1.0/3) or self.rect.top > height*(2.0/3) ):

                    if self.rect.centerx <= 0:
          puntuacion[0] = puntuacion[0] + 1
                    if self.rect.centerx > width:
          puntuacion[1] = puntuacion[1] + 1

      self.rect.centerx = self.inicial[0]
      self.rect.centery = self.inicial[1]
      self.speed[0] = self.speed[1] = 0

                if self.rect.top <= 0 or self.rect.bottom >= height:
                    self.speed[1] = -self.speed[1]
                    self.rect.centery += self.speed[1] * time

                for i in range(len(handles)):
                    if  math.hypot((handles[i].rect.centerx - self.rect.centerx), (handles[i].rect.centery - self.rect.centery) ) <= (self.diametro):

                        angulo = math.atan2(self.rect.centery - handles[i].rect.centery, self.rect.centerx-handles[i].rect.centerx )
   angulo_invertido = math.atan2(-self.speed[1], -self.speed[0])
   angulo_reflexion = angulo_invertido + (angulo * angulo_invertido)*2
   self.speed[1] = math.sin(angulo_reflexion)
   self.speed[0] = math.cos(angulo_reflexion)

                for i in range(len(pucks)):
                    if math.hypot((pucks[i].rect.centerx - self.rect.centerx), (pucks[i].rect.centery - self.rect.centery) ) <= (self.diametro) and pucks[i].id_ != self.id_ :
                        angulo = math.atan2(self.rect.centery - pucks[i].rect.centery, self.rect.centerx-pucks[i].rect.centerx )
   angulo_invertido = math.atan2(-self.speed[1],-self.speed[0])
   angulo_reflexion = angulo_invertido + (angulo * angulo_invertido)*2
   self.speed[1] = math.sin(angulo_reflexion)
   self.speed[0] = math.cos(angulo_reflexion)


class Handle(pygame.sprite.Sprite):
        def __init__(self, x, y, id_):
            self.inicial = [float(x), float(y)]
     self.anterior = [0.0, 0.0]
     self.cambio = [0.0, 0.0, 0.0]#X, Y y Contador
            self.id_ = id_
            pygame.sprite.Sprite.__init__(self)
            self.image = load_image("images/handle.png", True)
            self.diametro = width*0.15
            self.image = pygame.transform.scale(self.image, (int(self.diametro) ,int(self.diametro) ))
            self.rect = self.image.get_rect()
            self.rect.centerx = float(x)
            self.rect.centery = float(y)
            self.speed = [0.2, 0.2]
     self.coeficiente_arrastre = 0.012
            self.acceleration =  [0, 0]

        def inteligencia(self, time):
            if pucks[0].rect.centerx <= width:
                if pucks[0].rect.centerx < self.rect.centerx :
                    self.rect.centerx += 5
  else:
                    self.rect.centerx -= 5

                if pucks[0].rect.centery < self.rect.centery :
                    self.rect.centery += 5
  else:
                    self.rect.centery -= 5      
     else:
                if pucks[0].rect.centerx < self.rect.centerx :
                    self.rect.centerx += 5
  else:
                    self.rect.centerx -= 5

                if pucks[0].rect.centery < self.rect.centery :
                    self.rect.centery += 5
  else:
                    self.rect.centery -= 5

 
     if self.rect.left <= 0:
                self.rect.centerx = width/2
     if self.rect.right >= width/2:
                self.rect.centerx = width/2

     if self.rect.top <= 0:
  self.rect.centery = 0 
            if self.rect.bottom >= height:
  self.rect.centery = height


        def actualizar(self, time, bandera):
            if bandera == self.id_:
  if self.cambio[2] == 15: 
      self.cambio[2] = self.cambio[1] = self.cambio[0] = 0
                pos = pygame.mouse.get_pos() 
                self.rect.centerx = pos[0]
                self.rect.centery = pos[1]
  
  self.cambio[2] = self.cambio[2]+1
  self.speed[0] = (self.cambio[0] + (float(pos[0] - self.anterior[0]))/(time*16) )
  self.speed[0]/self.cambio[2]
  self.speed[1] = (self.cambio[1] + (float(pos[1] - self.anterior[1]))/(time*16) )
  self.speed[1]/self.cambio[2]

                self.anterior[0] += self.speed[0] * time
                self.anterior[1] += self.speed[1] * time
  
            else:
                self.cambio[2] = self.cambio[1] = self.cambio[0] = 0
                self.anterior[0] = self.rect.centerx
                self.anterior[1] = self.rect.centery

  self.speed[0] = self.speed[0]*(1-self.coeficiente_arrastre)
  self.speed[1] = self.speed[1]*(1-self.coeficiente_arrastre)
                self.rect.centerx += self.speed[0] * time
                self.rect.centery += self.speed[1] * time

                if self.rect.left <= 0 or  self.rect.right >= width:
                    self.speed[0] = - self.speed[0]
                    self.rect.centerx += self.speed[0] * time

                if self.rect.top <= 0 or self.rect.bottom >= height:
                    self.speed[1] = -self.speed[1]
                    self.rect.centery += self.speed[1] * time


     if self.rect.left <= width/2:
                self.rect.centerx = width/2

                for i in range(len(handles)):
                    if  math.hypot((handles[i].rect.centerx - self.rect.centerx), (handles[i].rect.centery - self.rect.centery) ) <= (self.diametro/2) and handles[i].id_ != self.id_ :

                        angulo = math.atan2(self.rect.centery - handles[i].rect.centery, self.rect.centerx-handles[i].rect.centerx )
   angulo_invertido = math.atan2(-self.speed[1], -self.speed[0])
   angulo_reflexion = angulo_invertido + (angulo * angulo_invertido)*2
   self.speed[1] = math.sin(angulo_reflexion)
   self.speed[0] = math.cos(angulo_reflexion)

                for i in range(len(pucks)):
                    if math.hypot((pucks[i].rect.centerx - self.rect.centerx), (pucks[i].rect.centery - self.rect.centery) ) <= (self.diametro/2) :
                        angulo = math.atan2(self.rect.centery - pucks[i].rect.centery, self.rect.centerx-pucks[i].rect.centerx )
   angulo_invertido = math.atan2(-self.speed[1],-self.speed[0])
   angulo_reflexion = angulo_invertido + (angulo * angulo_invertido)*2
   self.speed[1] = math.sin(angulo_reflexion)
   self.speed[0] = math.cos(angulo_reflexion)

#Funciones

def select_handle(vector):
    pos = pygame.mouse.get_pos()
    for i in range(len(vector)):
        if  math.hypot(int(vector[i].rect.centerx-pos[0]), int(vector[i].rect.centery-pos[1]) ) <= (vector[i].diametro/2):
            return vector[i].id_
    return -1

##########################################
# Carga la imagen y la convierte al formato
# optimizado de pygame
##########################################
def load_image(filename, transparent=False):
            try: image = pygame.image.load(filename)
            except pygame.error, message:
                    raise SystemExit, message
            image = image.convert()
            if transparent:
                    color = image.get_at((0,0))
                    image.set_colorkey(color, RLEACCEL)
            return image

def main():
    screen = pygame.display.set_mode((width, height))
    pygame.display.set_caption("Clase de simulación")

    background_image = load_image("images/background.png")
    fuente = pygame.font.Font(None, 40)
    fuente_a = pygame.font.Font(None, 80)
    fuente_b = pygame.font.Font(None, 80)

    puntos_a = fuente_a.render('0', True, (255, 255, 255), (159, 182, 205))
    puntos_a_r = puntos_a.get_rect()

    puntos_b = fuente_b.render('0', True, (255, 255, 255), (159, 182, 205))
    puntos_b_r = puntos_b.get_rect()

    principal = fuente.render('', True, (255, 255, 255), (159, 182, 205))
    principal_r = principal.get_rect()

    principal_r.centerx = 400
    principal_r.centery = 160

    puntos_a_r.centerx = 100
    puntos_a_r.centery = 90

    puntos_b_r.centerx = 700
    puntos_b_r.centery = 90

    clock = pygame.time.Clock() 
    screen = pygame.display.set_mode((width, height))
    bandera = -1

#    for i in range(nHandles):
    handles.append( Handle(random.randrange(0,width), random.randrange(0,height), 0))
    handles.append( Handle(random.randrange(0,width), random.randrange(0,height), 1))

    for i in range(nHandles, nHandles+npocks):
     pucks.append( Puck(random.randrange(0,width), random.randrange(0,height), i) )

    while True:
        time = clock.tick(60)
        for event in pygame.event.get():
            if event.type == QUIT:
                sys.exit(0)
            if event.type == pygame.MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()
                bandera = select_handle(handles)
  if bandera < 0: 
   bandera = select_handle(pucks)
            elif event.type == pygame.MOUSEBUTTONUP:
                bandera = -1

        # En esta parte se ponen las cosas que se redibujan

#        for i in handles:
#            i.actualizar(time, bandera)
#            screen.blit(i.image, i.rect)

        screen.blit(background_image, (0, 0))
        handles[0].inteligencia(time)
        handles[1].actualizar(time, bandera)

        for i in pucks:
            i.actualizar(time, bandera)
            screen.blit(i.image, i.rect)

 puntos_a = fuente_a.render(str(puntuacion[0]), True, (255, 255, 255), (159, 182, 205))
 puntos_b = fuente_a.render(str(puntuacion[1]), True, (255, 255, 255), (159, 182, 205))

        screen.blit(handles[1].image, handles[1].rect)
        screen.blit(handles[0].image, handles[0].rect)
 screen.blit(puntos_a, puntos_a_r)
 screen.blit(puntos_b, puntos_b_r)
 screen.blit(principal, principal_r)

        #-------------------> Aqui se termian de redibujar los componentes
        pygame.display.flip()
        #-------------------> Aqui termina el while 

    return 0


if __name__ == '__main__':
    pygame.init()
    main()


Referencias:

No hay comentarios:

Publicar un comentario