Eine Schlange auf das iPad schreiben (Pythonista)

... oder wie man die Zeit mit einem iPad tötet und sonst nichts ...

Hallo!

Worüber sprichst du?


Leider ersetzen Tablets Computer noch nicht. Aber eine Fahrt / ein Flug ist wichtig. Deshalb habe ich nach den IDes gesucht, die sich unter dem iPad befinden, und heute werde ich das Spiel auf Pythonista machen.

Was machen wir?


Die einfachsten Programme wie Kristalle (ja, ja, genau die, die Sie in der U-Bahn spielen). Tetris, Schlange, Füllung - jeder Neuling, der ein wenig Verständnis hat, wird sie in 30 Minuten schreiben. Unter der Zwischensequenz - Screenshots, Tutorial, Code.

Hier sind ein paar Screenshots von dem, was ich vermasselt habe:

Viele Screenshots











Haftungsausschluss
Dieser Artikel richtet sich nicht nur ausschließlich an Anfänger (die Python kennen) und ermöglicht es Ihnen nicht, in zehn Minuten eine Panzerwelt oder eine fertige Anwendung im Allgemeinen zu erstellen. Der Autor garantiert jedoch keinen absolut schönen und korrekten Code aus Sicht der Programmierreligion (obwohl er es versucht). . Und auch etwas wird aus Pythonista-Beispielen und -Dokumentationen gestohlen.

Der gesamte Code wird am Ende angegeben.

Lernen Sie die Grafiken in Pythonista kennen


Importieren
from scene import * import random 


Erstellen Sie sofort eine Szene:

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

Fangen wir gleich an. Sie sollten einen grünen Bildschirm haben. Lassen Sie uns ein paar coole Sachen machen, indem wir die Update-Methode (die das System selbst aufruft) zur Game-Klasse hinzufügen und die Hintergrundfarbe ändern.

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

Jetzt wechselt unser Bildschirm reibungslos von gelb nach weiß und umgekehrt.



Erstellen Sie nun ein Objekt. Wir erstellen es auch in der Setup-Methode:

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

Wir haben die Linie (mypath) festgelegt, einen ShapeNode darauf erstellt, die Farbe angezeigt und dann das übergeordnete Element angegeben (im Wesentlichen dasselbe - geben Sie das übergeordnete Element beim Erstellen an, d. H. ShapeNode (* ..., parent = self) oder self.add_child (obj)).

Nun, in Game.update () ändern wir die Position des Objekts (Tupel), und es ist in Pixel und wird von der unteren linken Ecke aus gezählt.
Beachten Sie, dass wir die Szene nicht neu zeichnen müssen (obwohl dies möglich ist). Objekte und Knoten, deren übergeordnetes Element die Szene ist (oder einige ihrer untergeordneten Objekte), werden selbst neu gezeichnet
Das Letzte, was wir in diesem Abschnitt durchgehen werden, ist touch_began (sowie touch_moved und touch_ended). Es ist leicht zu erraten, dass die Methode Klicks auf dem Bildschirm abfängt. Probieren wir es aus:

 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 



Jedes Mal, wenn Sie auf den Bildschirm klicken, erstellen wir einen Kreis. Der Ort des Klicks ist touch.location.

Bereit, eine Schlange zu schreiben


Spielmechanismus
Eine Schlange ist eine Anordnung von Quadraten, wenn sich der Kopf bewegt - das zweite Stück bewegt sich an die Stelle des ersten, das dritte - an die Stelle des zweiten usw. Um den Schnittpunkt des Kopfes mit dem Schwanz herauszufinden, werden wir ihn mit jedem Stück Schwanz vergleichen

Wir entfernen den gesamten geschriebenen Code, weil wir ihn mehr oder weniger schön machen wollen (aber natürlich werden wir Fahrräder machen).

Zuerst erstellen wir die PhyObj-Klasse:

 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) 

Ich denke, hier ist alles trivial. Innerhalb der Klasse selbst haben wir einen Knoten erstellt und einige Methoden beschrieben, mit denen unser Code besser lesbar wird.

Holivary
Persönlich ziehe ich es vor, zuerst Klassen auf niedriger Ebene zu erstellen, die viele doppelte Methoden und Eigenschaften haben, aber dann wird der Code in den Klassen auf höherer Ebene sehr schön und lesbar.

PhyObj ist das Objekt der untersten Ebene in unserem Spiel, tatsächlich ist es ein abstraktes physisches Objekt.

Beschreibung der Schlange


Jetzt müssen wir die Schlange beschreiben, und da sie aus Teilen besteht, werden wir zuerst eine beschreiben:

 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" 

Im Konstruktor rufen wir die übergeordnete Methode der Klasse auf und geben uns Form und Farbe. Der Rand wird benötigt, damit die Quadrate nicht zusammenkleben und eine Art Netz bilden.

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

Es hätte sich herausstellen sollen:



Aber warum brauchen wir zum Beispiel eine Marge:

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



Nun, aus diesen Stücken müssen Sie die Schlange kleben. Wir brauchen eine Initialisierungsmethode und eine Verschiebungsmethode.

Erstellen Sie zunächst die Initialisierung:

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

Versuchen wir es gleich zu zeichnen:

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



Fügen Sie die Verschiebungsmethode hinzu.

 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) 

Zuerst bewegen wir den letzten zum vorletzten, dann den vorletzten zum vorletzten ... dann den zweiten zum ersten. Und der erste auf (x, y).

Eigentlich bewegt sich unsere Schlange gerade gut. Versuchen wir mal:

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

Wenn du es geschafft hast zu sehen, dann kroch sie weg, wie es sollte. Tatsache ist, dass das Update sehr oft aufgerufen wird (und übrigens optional mit demselben Intervall). Daher müssen wir berücksichtigen, wie viel Zeit seit dem letzten Aufruf vergangen ist, und warten, bis es sich genug angesammelt hat.

Kurz gesagt, wir tun:

 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 gibt True zurück, wenn mehr Zeit als t vergangen ist. Jetzt bewegt sich die Schlange alle 0,3 Sekunden. Hat es geklappt?



Management


Jetzt müssen Sie die Kontrolle übernehmen, dh eine Drehung in alle vier Richtungen:



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

Und jetzt müssen wir touch_began verarbeiten, um zu verstehen, in welchen Bereich der Benutzer gestochen hat. Eigentlich stellte sich heraus, dass es nicht so interessant war, wie ich dachte, also können Sie hier einfach kopieren:

 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) 

Versuchen Sie es jetzt



Der Hauptmechanismus ist ausgearbeitet, es bleibt eine Überprüfung auf Kollisionen und Äpfel.

Schwanzkollision


Beginnen wir mit der Überprüfung und fügen der Schlange die Methode find_collisions hinzu

 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 

Jetzt können wir ein Paar Zellen erhalten, die sich schneiden. Ich möchte sie rot färben und die Würfelmethode zu Tile hinzufügen:

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

Fügen Sie eine Überprüfung hinzu, um das Setup zu aktualisieren und zu ändern:

 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) 

Was ist mit mir passiert:



Es bleibt Äpfel zu machen.

Dehnung und Äpfel


Wir werden die Schlange verlängern und damit sie schön aussieht, wird ein neuer Link gemäß den letzten beiden hinzugefügt, das heißt:



Fügen Sie der Schlange die Methoden hinzu:

 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 findet die Richtung, in die die Schwanzspitze unserer Heldin gerichtet ist. Anhängen, es ist leicht zu erraten, fügt eine Zelle hinzu. Füge dem Spiel eine weitere snake_lengthen-Methode hinzu:

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

Die letzte Zeile wird benötigt, damit die Schlange ein wenig wartet und der Benutzer Zeit hat, um zu sehen, dass das Stück hinzugefügt wurde.

Um herauszufinden, ob sich etwas mit der Schlange schneidet, fügen Sie die Schnittmethode hinzu.

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

Hurra, es bleibt nur ein Apfel zu kreieren. Eigentlich beschreiben wir den Apfel auf einmal:

 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) 

Solch eine seltsame Zufälligkeit ist erforderlich, um unser Bullauge auf das Gitter zu richten. Dann ist es nicht notwendig, den Abstand zwischen der Schnauze und dem Apfel zu suchen und seinen Scheiterhaufen zu vergleichen. Nur auf ifas. Gehen wir zum Aktualisieren und fügen am Ende dieser Funktion sehr einfache Zeilen hinzu:

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

Nun, alles scheint zu sein, jetzt verlängert sich die Schlange, wenn sie auf einen Apfel trifft, und stirbt, wenn sie sich selbst trifft.



Bonus


Sie können Soundeffekte erstellen:

 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) 

Machen Sie eine sanfte Bewegung:

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

Mit anderen Worten, wir haben die Logik der Position PhyObj geändert. Früher haben wir uns auf die Position des grafischen Elements konzentriert, und jetzt gibt es ein separates Feld für die logische Position (dh das für die Spielelogik verwendete), und die Position des grafischen Elements ist jetzt frei und kann auf seine eigene Weise geändert werden. Mit Action hinterlassen wir ihr nämlich einen parallelen Strom, in dem sie sich bewegt.

Eine so glatte Schlange stellte sich heraus:



Und zum Schluss das Etikett mit der Länge der Schlange:

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



Freunde, danke für eure Aufmerksamkeit! Wenn etwas nicht klar ist, fragen Sie. Und wenn es interessant sein wird - ich werde weitermachen, gibt es noch etwas zu erzählen (aber dieser Artikel ist schon ziemlich lang).

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


Crystal Code WhiteBlackGoose Edition
Es ist lustig, dass ich, nachdem ich es gemacht habe, in den Beispielen von Pythonista selbst etwas SEHR Ähnliches gefunden habe. Aber ich habe ein bisschen mehr Funktionen :)

 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) 


Demonstration der Arbeit von Kristallen:

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


All Articles