Escribir una serpiente en ipad (pythonista)

... o cómo matar el tiempo con un ipad y nada más ...

Hola

De que estas hablando


Desafortunadamente, las tabletas aún no reemplazan a las computadoras. Pero tener un viaje / vuelo es vital. Por lo tanto, busqué qué ides están debajo del ipad, y en realidad hoy haré el juego en Pythonista.

Que haremos


Los programas más simples, como los cristales (sí, sí, los mismos que juegas en el metro). Tetris, serpiente, relleno: cualquier recién llegado que tenga un poco de comprensión los escribirá en 30 minutos. Debajo de la escena: capturas de pantalla, tutorial, código.

Aquí hay algunas capturas de pantalla de lo que he estropeado:

Muchas capturas de pantalla











Descargo de responsabilidad
Este artículo no solo es exclusivo para principiantes (sino que conoce Python) y no le permitirá crear World of Tanks en diez minutos o cualquier aplicación preparada en general, sino que el autor no garantiza un código absolutamente hermoso y correcto desde el punto de vista de la programación de religión (aunque lo intenta) . Y también algo es robado de ejemplos y documentación de pythonista.

Todo el código se dará al final.

Conozca los gráficos en pythonista


Importar
from scene import * import random 


Inmediatamente crea una escena:

 class Game(Scene): def setup(self): self.background_color = "green" run(Game(), LANDSCAPE) 

Bueno, comencemos de inmediato. Deberías tener una pantalla verde. Hagamos algunas cosas interesantes agregando el método de actualización (que el propio sistema llama) a la clase Game y cambiando el color de fondo.

 class Game(Scene): #    def update(self): self.background_color = (1.0, 1.0, (math.sin(self.t) + 1) / 2) 

Ahora nuestra pantalla cambia suavemente de amarillo a blanco y viceversa.



Ahora crea algún objeto. También lo creamos en el método de configuración:

 class Game(Scene): def setup(self): self.background_color = "white" mypath = ui.Path.rect(0, 0, 50, 50) self.obj = ShapeNode(mypath) self.obj.color = "purple" #      html,  #FF00FF.   tuple,   (1.0, 0.0, 1.0).       alpha,    self.add_child(self.obj) def update(self): self.obj.position = (500 + 200 * math.sin(self.t), 500 + 200 * math.cos(self.t)) 

Establecimos la línea (mypath), creamos un ShapeNode en él, lo apuntamos a un color y luego especificamos un padre (esencialmente lo mismo: especifique un padre al crear, es decir, ShapeNode (* ..., parent = self) o self.add_child (obj)).

Bueno, en Game.update () cambiamos la posición del objeto (tupla), y está en píxeles y se considera desde la esquina inferior izquierda.
Tenga en cuenta que no necesitamos volver a dibujar la escena (aunque es posible). Los objetos y nodos cuyo padre es la escena (o algunos de sus objetos hijos) se vuelven a dibujar
Lo último que veremos en esta sección es touch_began (así como touch_moved y touch_ended). Es fácil de adivinar, el método captura clics en la pantalla. Probémoslo:

 class Game(Scene): def touch_began(self, touch): mypath = ui.Path.oval(0, 0, 50, 50) obj = ShapeNode(mypath) obj.color = (0.5, 0.5, 1.0) self.add_child(obj) obj.position = touch.location 



Cada vez que hace clic en la pantalla, creamos un círculo, el lugar del clic es touch.location.

Listo para escribir una serpiente


Mecanismo de juego
Una serpiente es un conjunto de cuadrados, cuando la cabeza se mueve - la segunda pieza se mueve al lugar de la primera, la tercera - al lugar de la segunda, etc. Para averiguar la intersección de la cabeza con la cola, la compararemos con cada pieza de cola

Eliminamos todo el código escrito, porque queremos hacerlo más o menos bellamente (pero, por supuesto, haremos bicicletas).

Primero, creemos la clase PhyObj:

 class PhyObj: def __init__(self, path, color, parent): self.graph_obj = ShapeNode(path, parent=parent) self.parent = parent self.graph_obj.color = color def setpos(self, x, y): self.graph_obj.position = (x, y) def getpos(self): return self.graph_obj.position def move(self, x, y): self.graph_obj.position += (x, y) 

Creo que todo es trivial aquí. Dentro de la clase en sí, creamos un nodo y también describimos algunos métodos que harán que nuestro código sea más legible.

Holivario
Personalmente, prefiero crear primero clases de bajo nivel que tengan muchos métodos y propiedades duplicados, pero luego el código en los de nivel superior se vuelve muy hermoso y legible.

PhyObj es el objeto de nivel más bajo en nuestro juego, de hecho es un objeto físico abstracto.

Descripción de la serpiente


Ahora tenemos que describir la serpiente, y dado que consta de piezas, primero describiremos una:

 class Tile(PhyObj): def __init__(self, parent, size, margin=4): super().__init__(ui.Path.rect(0, 0, size[0] - margin, size[1] - margin), "#66FF66", parent) def die(self): self.graph_obj.color = "red" 

En el constructor, llamamos al método padre de la clase y nos damos forma y color. se necesita un margen para que los cuadrados no se peguen y creen algún tipo de malla.

 class Game(Scene): def setup(self): self.tile = Tile(self, (40, 40)) self.tile.setpos(100, 100) 

Debería haber resultado:



Pero, por ejemplo, ¿por qué necesitamos margen?

 class Game(Scene): def setup(self): tile1 = Tile(self, (40, 40)) tile1.setpos(100, 100) tile2 = Tile(self, (40, 40)) tile2.setpos(140, 100) 



Bueno, ahora de estas piezas necesitas pegar la serpiente. Necesitamos un método de inicialización y un método de movimiento.

Primero, cree la inicialización:

 class Snake: def __init__(self, length, width, initpos, parent): self.width = width #     self.tiles = [Tile(parent, (width, width)) for i in range(length)] #        for i, tile in enumerate(self.tiles): tile.setpos(initpos[0] + i * self.width, initpos[1]) #        

Intentemos dibujarlo de inmediato:

 class Game(Scene): def setup(self): self.snake = Snake(10, 40, (200, 200), self) 



Bueno, agregue el método de movimiento.

 class Snake: def move(self, x, y): for i in range(len(self.tiles) - 1, 0, -1): self.tiles[i].setpos(*self.tiles[i - 1].getpos()) self.tiles[0].move(x * self.width, y * self.width) 

Primero movemos el último al penúltimo, luego el penúltimo al penúltimo ... luego el segundo al primero. Y el primero en (x, y).

En realidad, nuestra serpiente se mueve bien recta. Probemos

 class Game(self): # <...> def update(self): self.snake.move(0, 1) 

Si lograste ver, entonces ella se arrastró como debería. El hecho es que la actualización se llama con mucha frecuencia (y, por cierto, opcionalmente con el mismo intervalo), por lo que debemos considerar cuánto tiempo ha pasado desde la última llamada y esperar hasta que se "acumule" lo suficiente.

En resumen, hacemos:

 class Game(Scene): def time_reset(self): self.last_time = self.t def time_gone(self, t): if self.t - self.last_time > t: res = True self.time_reset() else: res = False return res def setup(self): self.snake = Snake(10, 40, (200, 200), self) self.time_reset() def update(self): if self.time_gone(0.3): self.snake.move(0, 1) 

time_gone devuelve True si ha pasado más tiempo que t. Ahora la serpiente se moverá cada 0.3 segundos. ¿Funcionó?



Gestión


Ahora necesita hacer el control, es decir, un giro en las cuatro direcciones:



 class Game(Scene): # <...> def setup(self): # <...> self.dir = (0, 1) #     def update(self): if self.time_gone(0.3): self.snake.move(*self.dir) 

Y ahora tenemos que hacer un procesamiento touch_began para comprender en qué área se asomó el usuario. En realidad, resultó no ser tan interesante como pensaba, así que aquí puedes simplemente copiar:

 class Game(Scene): # <...> def touch_began(self, touch): ws = touch.location[0] / self.size.w hs = touch.location[1] / self.size.h aws = 1 - ws if ws > hs and aws > hs: self.dir = (0, -1) elif ws > hs and aws <= hs: self.dir = (1, 0) elif ws <= hs and aws > hs: self.dir = (-1, 0) else: self.dir = (0, 1) 

Bueno, intenta girar ahora



El mecanismo principal está resuelto, queda por hacer una verificación de colisiones y manzanas.

Colisión de cola


Comencemos por verificar y agregar el método find_collisions a la serpiente

 class Snake: # <...> def find_collisions(self): for i in range(1, len(self.tiles)): if self.tiles[i].getpos() == self.tiles[0].getpos(): return self.tiles[i], self.tiles[0] return False 

Ahora podemos obtener un par de celdas que se cruzan. Me gustaría colorearlos de rojo, agregar el método de troquel a Tile:

 class Tile(PhyObj): # <...> def die(self): self.graph_obj.color = "red" 

Agregue una marca de verificación para actualizar y cambiar la configuración:

 class Game(Scene): # <...> def setup(self): self.snake = Snake(30, 40, (200, 200), self) #   self.time_reset() self.dir = (0, 1) self.game_on = True #   ? def update(self): if not self.game_on: #  ,  return col = self.snake.find_collisions() #  ? if col: for tile in col: tile.die() #     self.game_on = False #   if self.time_gone(0.3): self.snake.move(*self.dir) 

Lo que me paso



Queda por hacer manzanas.

Alargamiento y manzanas


Vamos a alargar la serpiente, y para que se vea hermosa, se agregará un nuevo enlace de acuerdo con los dos últimos, es decir:



Agregue los métodos a la serpiente:

 class Snake: # <...> def find_dir(self, x1, y1, x2, y2): if x1 == x2 and y1 > y2: return (0, 1) elif x1 == x2 and y1 < y2: return (0, -1) elif y1 == y2 and x1 > x2: return (1, 0) elif y1 == y2 and x1 < x2: return (-1, 0) else: assert False, "Error!" def append(self): if len(self.tiles) > 1: lastdir = self.find_dir(*self.tiles[-1].getpos(), *self.tiles[-2].getpos()) else: lastdir = (-self.parent.dir[0], -self.parent.dir[1]) self.tiles.append(Tile(self.parent, (self.width, self.width))) x_prev, y_prev = self.tiles[-2].getpos() self.tiles[-1].setpos(x_prev + lastdir[0] * self.width, y_prev + lastdir[1] * self.width) 

find_dir encuentra la dirección en la que se dirige la punta de la cola de nuestra heroína. agregar, es fácil de adivinar, agrega una celda. Agregue otro método snake_lengthen al juego:

 class Game(Scene): # <...> def snake_lengthen(self): self.snake.append() self.time_reset() 

Se necesita la última línea para que la serpiente espere un poco, y el usuario logró ver que se agregó la pieza.

Para saber si algo se cruza con la serpiente, agregue el método de intersección.

 class Snake: # <...> def getpos(self): return self.tiles[0].getpos() def intersect(self, x, y): return self.getpos() == (x, y) 

Hurra, solo queda crear una manzana. En realidad, describimos la manzana de una vez:

 class Apple(PhyObj): def __init__(self, width, size, parent): super().__init__(ui.Path.oval(0, 0, size[0], size[1]), "#55AAFF", parent) self.parent = parent self.width = width self.dislocate() def dislocate(self): a = random.randint(2, int(self.parent.size.w / self.width) - 2) b = random.randint(2, int(self.parent.size.h / self.width) - 2) self.setpos(a * self.width, b * self.width) 

Se necesita una aleatoriedad tan extraña para encajar en nuestra cuadrícula. Entonces no será necesario buscar la distancia entre el hocico y la manzana y comparar su pira-pyr. Solo en ifas. Vamos a actualizar y agregar líneas muy simples al final de esta función:

 class Game(Scene): # <...> def setup(self): self.apple = Apple(40, (50, 50), self) # 40 -  ,       Snake() # <...> def update(self): # <...> if self.snake.intersect(*self.apple.getpos()): self.snake_lengthen() self.apple.dislocate() 

Bueno, todo parece ser, ahora la serpiente se alarga si golpea una manzana y muere si se golpea a sí misma.



Bono


Puedes hacer efectos de sonido:

 import sound class Game(Scene): # <...> def snake_lengthen(self): self.snake.append() self.time_reset() sound.play_effect('arcade:Powerup_1', 0.25, 0.8) 

Haz un movimiento suave:

 class Game(Scene): # <...> def setup(self): self.game_on = False self.GLOBAL_TIMING = 0.2 self.GLOBAL_WIDTH = 40 self.apple = Apple(self.GLOBAL_WIDTH, (50, 50), self) self.snake = Snake(10, self.GLOBAL_WIDTH, (200, 200), self) self.time_reset() self.dir = (0, 1) self.game_on = True class Snake: # <...> def move(self, x, y): for i in range(len(self.tiles) - 1, 0, -1): self.tiles[i].setpos(*self.tiles[i - 1].getpos(), self.parent.GLOBAL_TIMING) self.tiles[0].move(x * self.width, y * self.width, self.parent.GLOBAL_TIMING) class PhyObj: def __init__(self, path, color, parent): self.graph_obj = ShapeNode(path, parent=parent) self.parent = parent self.graph_obj.color = color self.pos = self.graph_obj.position def setpos(self, x, y, t=0.0): self.pos = (x, y) self.graph_obj.run_action(Action.move_to(x, y, t)) #    x, y    t def getpos(self): return self.pos def move(self, x, y, t=0.0): self.pos = (self.pos[0] + x, self.pos[1] + y) self.graph_obj.run_action(Action.move_by(x, y, t)) 

En otras palabras, cambiamos la lógica de la posición PhyObj. Anteriormente, nos centramos en la posición del elemento gráfico, y ahora hay un campo separado para la posición lógica (es decir, el que se usa para la lógica del juego), y la posición del elemento gráfico ahora es libre y se puede cambiar de alguna manera a su manera. Es decir, usando Acción, le dejamos una corriente paralela, donde se mueve.

Una serpiente tan suave resultó:



Y finalmente, una etiqueta con una longitud de serpiente:

 class Game(Scene): # <...> def setup(self): self.game_on = False self.GLOBAL_TIMING = 0.2 self.GLOBAL_WIDTH = 40 self.apple = Apple(self.GLOBAL_WIDTH, (50, 50), self) self.snake = Snake(30, self.GLOBAL_WIDTH, (200, 200), self) self.time_reset() self.dir = (0, 1) self.label = LabelNode("", font=("Chalkduster", 20), parent=self, position=(self.size.w / 2, self.size.h - 100)) #    self.update_labels() self.game_on = True def update_labels(self): self.label.text = "Length: " + str(len(self.snake.tiles)) def update(self): if not self.game_on: return col = self.snake.find_collisions() if col: for tile in col: tile.die() self.game_on = False if self.time_gone(self.GLOBAL_TIMING): self.snake.move(*self.dir) if self.snake.intersect(*self.apple.getpos()): self.snake_lengthen() self.apple.dislocate() self.update_labels() #  



Amigos, gracias por su atención! Si algo no está claro, pregunte. Y si será interesante, continuaré, todavía hay algo que contar (pero este artículo ya es bastante largo).

Todo el código de la serpiente
 from scene import * import random import math import sound class PhyObj: def __init__(self, path, color, parent): self.graph_obj = ShapeNode(path, parent=parent) self.parent = parent self.graph_obj.color = color self.pos = self.graph_obj.position def setpos(self, x, y, t=0.0): self.pos = (x, y) self.graph_obj.run_action(Action.move_to(x, y, t)) def getpos(self): return self.pos def move(self, x, y, t=0.0): self.pos = (self.pos[0] + x, self.pos[1] + y) self.graph_obj.run_action(Action.move_by(x, y, t)) class Tile(PhyObj): def __init__(self, parent, size, margin=4): super().__init__(ui.Path.rect(0, 0, size[0] - margin, size[1] - margin), "#66FF66", parent) def die(self): self.graph_obj.color = "red" class Snake: def __init__(self, length, width, initpos, parent): self.width = width self.tiles = [Tile(parent, (width, width)) for i in range(length)] for i, tile in enumerate(self.tiles): tile.setpos(initpos[0] + i * self.width, initpos[1]) self.parent = parent def move(self, x, y): for i in range(len(self.tiles) - 1, 0, -1): self.tiles[i].setpos(*self.tiles[i - 1].getpos(), self.parent.GLOBAL_TIMING) self.tiles[0].move(x * self.width, y * self.width, self.parent.GLOBAL_TIMING) def find_collisions(self): for i in range(1, len(self.tiles)): if self.tiles[i].getpos() == self.tiles[0].getpos(): return self.tiles[i], self.tiles[0] return False def find_dir(self, x1, y1, x2, y2): if x1 == x2 and y1 > y2: return (0, 1) elif x1 == x2 and y1 < y2: return (0, -1) elif y1 == y2 and x1 > x2: return (1, 0) elif y1 == y2 and x1 < x2: return (-1, 0) else: assert False, "Error!" def append(self): if len(self.tiles) > 1: lastdir = self.find_dir(*self.tiles[-1].getpos(), *self.tiles[-2].getpos()) else: lastdir = (-self.parent.dir[0], -self.parent.dir[1]) self.tiles.append(Tile(self.parent, (self.width, self.width))) x_prev, y_prev = self.tiles[-2].getpos() self.tiles[-1].setpos(x_prev + lastdir[0] * self.width, y_prev + lastdir[1] * self.width) def getpos(self): return self.tiles[0].getpos() def intersect(self, x, y): return self.getpos() == (x, y) class Apple(PhyObj): def __init__(self, width, size, parent): super().__init__(ui.Path.oval(0, 0, size[0], size[1]), "#55AAFF", parent) self.parent = parent self.width = width self.dislocate() def dislocate(self): a = random.randint(2, int(self.parent.size.w / self.width) - 2) b = random.randint(2, int(self.parent.size.h / self.width) - 2) self.setpos(a * self.width, b * self.width) class Game(Scene): def snake_lengthen(self): self.snake.append() self.time_reset() sound.play_effect('arcade:Powerup_1', 0.25, 0.8) def time_reset(self): self.last_time = self.t def time_gone(self, t): if self.t - self.last_time > t: res = True self.time_reset() else: res = False return res def setup(self): self.game_on = False self.GLOBAL_TIMING = 0.2 self.GLOBAL_WIDTH = 40 self.apple = Apple(self.GLOBAL_WIDTH, (50, 50), self) self.snake = Snake(30, self.GLOBAL_WIDTH, (200, 200), self) self.time_reset() self.dir = (0, 1) self.label = LabelNode("", font=("Chalkduster", 20), parent=self, position=(self.size.w / 2, self.size.h - 100)) self.update_labels() self.game_on = True def update_labels(self): self.label.text = "Length: " + str(len(self.snake.tiles)) def update(self): if not self.game_on: return col = self.snake.find_collisions() if col: for tile in col: tile.die() self.game_on = False if self.time_gone(self.GLOBAL_TIMING): self.snake.move(*self.dir) if self.snake.intersect(*self.apple.getpos()): self.snake_lengthen() self.apple.dislocate() self.update_labels() def touch_began(self, touch): ws = touch.location[0] / self.size.w hs = touch.location[1] / self.size.h aws = 1 - ws if ws > hs and aws > hs: self.dir = (0, -1) elif ws > hs and aws <= hs: self.dir = (1, 0) elif ws <= hs and aws > hs: self.dir = (-1, 0) else: self.dir = (0, 1) run(Game(), LANDSCAPE) 


Código de cristal WhiteBlackGoose edition
Es curioso que después de haberlo hecho, encontré algo MUY similar en los ejemplos del propio pythonista. Pero tengo un poco más de características :)

 from scene import * from math import pi from random import uniform as rnd, choice, randint import sys import random A = Action sys.setrecursionlimit(1000000) colors = ['pzl:Green5', "pzl:Red5", "pzl:Blue5"] + ["pzl:Purple5", "pzl:Button2"] + ["plf:Item_CoinGold"] global inited inited = False class Explosion (Node): def __init__(self, brick, *args, **kwargs): Node.__init__(self, *args, **kwargs) self.position = brick.position for dx, dy in ((-1, -1), (1, -1), (-1, 1), (1, 1)): p = SpriteNode(brick.texture, scale=0.5, parent=self) p.position = brick.size.w/4 * dx, brick.size.h/4 * dy p.size = brick.size d = 0.6 r = 30 p.run_action(A.move_to(rnd(-r, r), rnd(-r, r), d)) p.run_action(A.scale_to(0, d)) p.run_action(A.rotate_to(rnd(-pi/2, pi/2), d)) self.run_action(A.sequence(A.wait(d), A.remove())) class Brick (SpriteNode): def __init__(self, brick_type, *args, **kwargs): img = colors[brick_type] SpriteNode.__init__(self, img, *args, **kwargs) self.brick_type = brick_type self.is_on = True self.lf = True self.enabled = True def destroy(self): self.remove_from_parent() self.is_on = False def mark(self): self.lf = False def demark(self): self.lf = True class Game(Scene): def brickgetpos(self, i, j): return (self.Woff + j * self.W, self.Hoff + i * self.H) def brick(self, ty, i, j): b = Brick(ty, size=(self.W, self.H), position=self.brickgetpos(i, j), parent=self.game_node) b.rotation = random.random() return b def random_brick_type(self): if random.random() < 0.992: return random.randint(0, 3) else: if random.random() < 0.8: return 5 else: return 4 def setup(self): FONT = ('Chalkduster', 20) self.score_label = LabelNode('Score: 0', font=FONT, position=(self.size.w/2-100, self.size.h-40), parent=self) self.score = 0 self.last_score_label = LabelNode('Delta: +0', font=FONT, position=(self.size.w/2-300, self.size.h-40), parent=self) self.last_score = 0 #self.avg_label = LabelNode('Speed: +0/s', font=FONT, position=(self.size.w/2+100, self.size.h-40), parent=self) #self.max_label = LabelNode('Peak: +0/s', font=FONT, position=(self.size.w/2+300, self.size.h-40), parent=self) #self.max_speed = 0 self.game_time = 120 self.timel = LabelNode('Time: ' + str(self.game_time) + "s", font=FONT, position=(self.size.w/2+300, self.size.h-40), parent=self) self.gems = [0 for i in colors] self.effect_node = EffectNode(parent=self) self.game_node = Node(parent=self.effect_node) self.l = [0 for i in colors] self.lt = [0 for i in colors] for i in range(len(colors)): R = 50 if i == 6 else 35 self.l[i] = Brick(i, size=(R, R), position=(40, self.size.h-100-i*40), parent=self.game_node) self.lt[i] = LabelNode(": 0", font=FONT, position=(self.l[i].position[0] + 40, self.l[i].position[1]), parent=self) self.WB = 30 self.HB = 30 self.W = 900 // self.WB self.H = 900 // self.HB self.colcount = 4 self.Woff = (int(self.size.w) - self.W * self.WB + self.W) // 2 self.Hoff = self.H + 10 self.net = [[self.brick(self.random_brick_type(), i, j) for i in range(self.HB)] for j in range(self.WB)] #self.touch_moved = self.touch_began self.start_time = self.t self.game_on = True global inited inited = True def demark(self): for bricks in self.net: for brick in bricks: brick.demark() def howfar(self, x, y): alt = 0 for i in range(y): if not self.net[x][i].is_on: alt += 1 return alt def update(self): global inited if not inited: return self.game_on = self.t - self.start_time < self.game_time if self.game_on: self.timel.text = "Time: " + str(round(self.game_time - (self.t - self.start_time))) + "s" else: self.timel.text = "Game over" #if speed > self.max_speed: # self.max_speed = speed # self.max_label.text = "Peak: +" + str(round(self.max_speed)) + "/s" def gravity(self, x, y): alt = self.howfar(x, y) if alt == 0: return self.net[x][y].destroy() self.net[x][y - alt] = self.brick(self.net[x][y].brick_type, y, x) self.net[x][y - alt].position = self.net[x][y].position self.net[x][y - alt].rotation = self.net[x][y].rotation self.net[x][y - alt].enabled = False self.net[x][y - alt].run_action(A.sequence(A.move_to(*self.brickgetpos(y - alt, x), 0.2 * alt ** 0.5, TIMING_EASE_IN_2), A.call(lambda: self.enable_cell(x, y - alt)))) def enable_cell(self, x, y): self.net[x][y].enabled = True def fall(self): for x in range(self.WB): for y in range(self.HB): if self.net[x][y].is_on: self.gravity(x, y) def update_scores(self): self.score += self.last_score self.score_label.text = "Score: " + str(self.score) self.last_score_label.text = "Delta: +" + str(self.last_score) self.last_score = 0 def update_cells(self): for i in range(self.WB): for j in range(self.HB): if not self.net[i][j].is_on: self.net[i][j] = self.brick(self.random_brick_type(), j + self.HB, i) self.net[i][j].enabled = True self.net[i][j].run_action(A.sequence(A.move_to(*self.brickgetpos(j, i), 0.2 * self.HB ** 0.5, TIMING_EASE_IN_2), A.call(lambda: self.enable_cell(i, j)))) def inbounds(self, x, y): return (x >= 0) and (y >= 0) and (x < self.WB) and (y < self.HB) def bomb(self, x, y, radius): score = 0 bc = 0 for i in range(round(4 * radius ** 2)): rad = random.random() * radius ang = random.random() * 2 * pi xp, yp = x + sin(ang) * rad, y + cos(ang) * rad xp, yp = int(xp), int(yp) if self.inbounds(xp, yp): score += self.explode(xp, yp) self.fall() self.give_score(round(score / 1.7), self.brickgetpos(y, x)) def laser(self, x, y): score = 0 coords = [] for i in range(self.HB): for j in range(-1, 1 + 1, 1): coords.append((x + j, i)) for i in range(self.WB): coords.append((i, y)) for i in range(-self.HB, self.HB): coords.append((x + i, y + i)) for i in range(-self.WB, self.WB): coords.append((x - i, y + i)) bc = 0 for x, y in coords: if not self.inbounds(x, y): continue score += self.explode(x, y) self.fall() self.give_score(score, self.brickgetpos(y, x)) def getty(self, x, y): if not self.inbounds(x, y) or not self.net[x][y].is_on: return -1 else: return self.net[x][y].brick_type def popupt(self, text, position_, font_=("Arial", 30), color_="white"): label = LabelNode(text, font=font_, color=color_, parent=self, position=position_) label.run_action(A.sequence(A.wait(1), A.call(label.remove_from_parent))) def give_score(self, count, xy): self.last_score = int(count ** 2.5) size = 10 if self.last_score > 50000: size = 60 elif self.last_score > 20000: size = 40 elif self.last_score > 10000: size = 30 elif self.last_score > 5000: size = 25 elif self.last_score > 2000: size = 20 elif self.last_score > 1000: size = 15 if self.last_score > 0: self.popupt("+" + str(self.last_score), xy, font_=("Chalkduster", int(size * 1.5))) self.update_scores() def touch_began(self, touch): if not self.game_on: return x, y = touch.location x, y = x + self.W / 2, y + self.H / 2 W, H = get_screen_size() x, y = x, y x, y = int(x), int(y) x, y = x - self.Woff, y - self.Hoff x, y = x // self.W, y // self.H if not self.inbounds(x, y): return count = self.react(self.net[x][y].brick_type, x, y, True) self.demark() if self.getty(x, y) in [0, 1, 2, 3]: if count >= 2: self.react(self.net[x][y].brick_type, x, y) self.fall() self.give_score(count, touch.location) elif self.getty(x, y) == 4: self.bomb(x, y, 5 * count) elif self.getty(x, y) == 5: self.explode(x, y) self.fall() self.update_cells() def explode(self, x, y): if self.net[x][y].is_on: self.net[x][y].destroy() self.gems[self.net[x][y].brick_type] += 1 s = str(self.gems[self.net[x][y].brick_type]) self.lt[self.net[x][y].brick_type].text = " " * len(s) + ": " + s self.game_node.add_child(Explosion(self.net[x][y])) return True else: return False def react(self, col, x, y, ignore=False): if self.inbounds(x, y) and self.net[x][y].brick_type == col and self.net[x][y].is_on and self.net[x][y].lf and self.net[x][y].enabled: if not ignore: self.explode(x, y) else: self.net[x][y].mark() r = 1 r += self.react(col, x + 1, y + 0, ignore) r += self.react(col, x - 1, y - 0, ignore) r += self.react(col, x + 0, y + 1, ignore) r += self.react(col, x - 0, y - 1, ignore) return r else: return 0 def destroy_brick(self, x, y): self.net[x][y].destroy() run(Game(), LANDSCAPE, show_fps=True) 


Demostración del trabajo de los cristales:

Source: https://habr.com/ru/post/464805/


All Articles