Escrevendo uma cobra no ipad (pythonista)

... ou como passar o tempo com um ipad e nada mais ...

Oi

Do que você está falando?


Infelizmente, os tablets ainda não substituem os computadores. Mas ter uma carona / voo é vital. Portanto, procurei quais ides estão sob o ipad e hoje, hoje, farei o jogo no Pythonista.

O que vamos fazer?


Os programas mais simples, como os cristais (sim, sim, os mesmos que você toca no metrô). Tetris, cobra, preenchimento - qualquer iniciante, com um pouco de compreensão, os escreverá em 30 minutos. Sob a cena - screenshots, tutorial, código.

Aqui estão algumas capturas de tela do que eu errei:

Muitas capturas de tela











Isenção de responsabilidade
Este artigo não é apenas exclusivo para iniciantes (mas conhece python) e não permitirá que você crie um mundo de tanques em dez minutos ou qualquer aplicativo pronto em geral, mas o autor não garante um código absolutamente bonito e correto do ponto de vista da programação de religião (embora ele tente) . E também algo é roubado de exemplos e documentação pythonista.

Todo o código será fornecido no final.

Conheça os gráficos em pythonista


Importar
from scene import * import random 


Crie imediatamente uma cena:

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

Bem, vamos começar imediatamente. Você deve ter uma tela verde. Vamos fazer algumas coisas legais adicionando o método de atualização (que o próprio sistema chama) à classe Game e alterando a cor do plano de fundo.

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

Agora, nossa tela muda suavemente de amarelo para branco e vice-versa.



Agora crie algum objeto. Também o criamos no método de instalação:

 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)) 

Definimos a linha (mypath), criamos um ShapeNode, mostramos a cor e especificamos o pai (essencialmente a mesma coisa - especifique o pai ao criar, ou seja, ShapeNode (* ..., parent = self) ou self.add_child (obj)).

Bem, em Game.update () alteramos a posição do objeto (tupla), que é em pixels e é contada no canto inferior esquerdo.
Observe que não precisamos redesenhar a cena (embora seja possível). Objetos e nós cujo pai é a cena (ou alguns de seus objetos filhos) são redesenhados
A última coisa que abordaremos nesta seção é touch_began (assim como touch_moved e touch_ended). É fácil adivinhar, o método captura cliques na tela. Vamos tentar:

 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 você clica na tela, criamos um círculo, o local do clique é touch.location.

Pronto para escrever uma cobra


Mecanismo de jogo
Uma cobra é uma matriz de quadrados, quando a cabeça se move - a segunda peça se move para o local da primeira, a terceira - para o local da segunda, etc. Para descobrir a interseção da cabeça com a cauda, ​​comparamos com cada peça da cauda

Removemos todo o código escrito, porque queremos fazê-lo de maneira mais ou menos bonita (mas, é claro, faremos bicicletas).

Primeiro, vamos criar a classe 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) 

Eu acho que tudo é trivial aqui. Dentro da própria classe, criamos um nó e também descrevemos alguns métodos que tornarão nosso código mais legível.

Holivary
Pessoalmente, prefiro primeiro criar classes de baixo nível que possuam muitos métodos e propriedades duplicados, mas depois o código nos de nível superior se torna muito bonito e legível.

PhyObj é o objeto de nível mais baixo do nosso jogo, na verdade, é um objeto físico abstrato.

Descrição de cobra


Agora precisamos descrever a cobra e, como ela consiste em pedaços, descreveremos primeiro uma:

 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" 

No construtor, chamamos o método pai da classe e nos damos forma e cor. É necessária uma margem para que os quadrados não fiquem juntos e criem algum tipo de malha.

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

Deveria ter acontecido:



Mas, por exemplo, por que precisamos de margem:

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



Bem, agora, a partir dessas peças, você precisa colar a cobra. Precisamos de um método de inicialização e um método de movimentação.

Primeiro, crie a inicialização:

 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]) #        

Vamos tentar desenhá-lo imediatamente:

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



Bem, adicione o método de movimentação.

 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) 

Primeiro, movemos o último para o penúltimo, depois o penúltimo para o penúltimo ... depois o segundo para o primeiro. E o primeiro em (x, y).

Na verdade, nossa cobra se move bem. Vamos tentar:

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

Se você conseguiu ver, ela se afastou como deveria. O fato é que a atualização é chamada com muita frequência (e, a propósito, opcionalmente com o mesmo intervalo), portanto, precisamos considerar quanto tempo se passou desde a última chamada e aguardar até que ela se acumule o suficiente.

Em suma, fazemos:

 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 retorna True se tiver passado mais tempo que t. Agora a cobra se moverá a cada 0,3 segundos. Isso deu certo?



Gerência


Agora você precisa fazer o controle, ou seja, uma curva nas quatro direções:



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

E agora precisamos processar o touch_began para entender em que área o usuário entrou. Na verdade, acabou não sendo tão interessante quanto eu pensava, então aqui você pode simplesmente 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) 

Bem, tente virar agora



O principal mecanismo é elaborado, resta fazer uma verificação de colisões e maçãs.

Colisão na cauda


Vamos começar verificando e adicionando o método find_collisions à cobra

 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 

Agora podemos obter um par de células que se cruzam. Gostaria de colori-los de vermelho, adicione o método die ao Tile:

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

Adicione uma verificação para atualizar e alterar a configuração:

 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) 

O que aconteceu comigo:



Resta fazer maçãs.

Alongamento e maçãs


Vamos alongar a cobra e, para torná-la bonita, um novo link será adicionado de acordo com os dois últimos, ou seja:



Adicione os métodos à cobra:

 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) 

O find_dir encontra a direção em que a ponta da cauda da nossa heroína é direcionada. acrescentar, é fácil adivinhar, adiciona uma célula. Adicione outro método snake_lengthen ao jogo:

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

A última linha é necessária para que a cobra espere um pouco e o usuário tenha tempo para ver que a peça foi adicionada.

Para descobrir se algo se cruza com a cobra, adicione o método de interseção a ela.

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

Viva, resta apenas criar uma maçã. Na verdade, descrevemos a maçã de uma só 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) 

Uma aleatoriedade tão estranha é necessária para encaixar nosso alvo na grade. Então não será necessário procurar a distância entre o focinho e a maçã e comparar seu pira-pir. Apenas no ifas. Vamos atualizar e adicionar linhas muito simples ao final desta função:

 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() 

Bem, tudo parece estar, agora a cobra se alonga se atingir uma maçã e morre se atingir a si mesma.



Bônus


Você pode criar efeitos sonoros:

 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) 

Faça um movimento 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)) 

Em outras palavras, mudamos a lógica da posição PhyObj. Anteriormente, nos concentramos na posição do elemento gráfico, e agora há um campo separado para a posição lógica (ou seja, o usado para a lógica do jogo), e a posição do elemento gráfico agora está livre e pode ser alterada de alguma maneira à sua maneira. Ou seja, usando Action, deixamos um fluxo paralelo para onde ela se move.

Uma cobra tão lisa acabou:



E, finalmente, o rótulo com o comprimento da cobra:

 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, obrigado pela atenção! Se algo não estiver claro, pergunte. E se for interessante - continuarei, ainda há algo a dizer (mas este artigo já é bastante longo).

Todo o código de cobra
 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
É engraçado que depois que eu o fiz, encontrei algo MUITO similar nos exemplos do próprio pythonista. Mas eu tenho um pouco mais de recursos :)

 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) 


Demonstração do trabalho de cristais:

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


All Articles