... أو كيف تقتل الوقت مع جهاز ipad ولا شيء أكثر ...تحية!
عن ماذا تتحدث
لسوء الحظ ، لا تحل الأجهزة اللوحية محل أجهزة الكمبيوتر بعد. ولكن ركوب / رحلة أمر حيوي. لذلك ، بحثت عن الأشياء التي تندرج تحت ipad ، واليوم سأفعل اللعبة على Pythonista.
ماذا سنفعل؟
أبسط البرامج ، مثل البلورات (نعم ، نعم ، تلك التي تلعبها في المترو). تتريس ، ثعبان ، ملء - أي قادم جديد ، لديه القليل من الفهم ، سيكتب لهم في غضون 30 دقيقة. تحت cutscene - لقطات ، تعليمي ، رمز.
فيما يلي بعض لقطات الشاشة التي أفسدتها:
تنصلهذه المقالة ليست مخصصة للمبتدئين فقط (ولكن معرفة بيثون) ولن تسمح لك بإنشاء عالم من الدبابات في عشر دقائق أو أي تطبيق جاهز بشكل عام ، لكن المؤلف لا يضمن رمزًا جميلًا وصحيحًا تمامًا من وجهة نظر برمجة الدين (على الرغم من أنه يحاول) . وأيضًا سرق شيء من أمثلة بيثونيستا ووثائقه.
سيتم إعطاء كل رمز في النهاية.تعرف على الرسومات في بيثونيستا
وارداتfrom scene import * import random
قم بإنشاء مشهد فورًا:
class Game(Scene): def setup(self): self.background_color = "green" run(Game(), LANDSCAPE)
حسنًا ، لنبدأ على الفور. يجب أن يكون لديك شاشة خضراء. لنقم ببعض الأشياء الرائعة عن طريق إضافة طريقة التحديث (التي يسميها النظام نفسه) إلى فئة اللعبة ، وتغيير لون الخلفية إليها.
class Game(Scene):
الآن شاشتنا تتغير بسلاسة من الأصفر إلى الأبيض والعكس.

الآن إنشاء بعض الكائنات. نقوم أيضًا بإنشائه في طريقة الإعداد:
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"
قمنا بتعيين السطر (mypath) ، وقمنا بإنشاء ShapeNode عليه ، وقمنا بتوجيهه إلى لون ، ثم حددنا أحد الوالدين (بشكل أساسي نفس الشيء - حدد أحد الوالدين عند الإنشاء ، على سبيل المثال ShapeNode (* ... ، parent = self) أو self.add_child (الكائنات)).
حسنًا ، في Game.update () نقوم بتغيير موضع الكائن (tuple) ، ويكون بالبكسل ويتم حسابه من الركن الأيسر السفلي.
لاحظ أننا لسنا بحاجة إلى إعادة رسم المشهد (رغم أنه ممكن). الكائنات والعُقد التي يكون والدها هو المشهد (أو بعض الكائنات التابعة لها) يتم إعادة رسمها بنفسها
آخر شيء سنتطرق إليه في هذا القسم هو touch_began (وكذلك touch_moved و touch_ended). من السهل التخمين ، فالطريقة تكتشف النقرات على الشاشة. لنجربها:
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

في كل مرة تنقر فوق الشاشة ، نقوم بإنشاء دائرة ، ويكون مكان النقر فوق touch.location.
على استعداد لكتابة ثعبان
آلية اللعبةالأفعى عبارة عن مجموعة من المربعات ، عندما يتحرك الرأس - تنتقل القطعة الثانية إلى مكان الأولى ، والثالثة إلى مكان الثانية ، وما إلى ذلك. لمعرفة تقاطع الرأس مع الذيل ، سنقارن به مع كل قطعة من الذيل.
نزيل كل الشفرة المكتوبة ، لأننا نريد أن نفعل ذلك بشكل أو بآخر بشكل جميل (لكن ، بالطبع ، سنقوم بالدراجات).
أولاً ، دعنا ننشئ فئة 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)
أعتقد أن كل شيء تافه هنا. داخل الفصل نفسه ، أنشأنا عقدة ، ووصفنا أيضًا بعض الطرق التي تجعل كودنا أكثر قابلية للقراءة.
holivaryأنا شخصياً أفضل أولاً إنشاء فصول منخفضة المستوى تحتوي على الكثير من الأساليب والخصائص المكررة ، ولكن بعد ذلك تصبح الشفرة في المستوى الأعلى جميلة جدًا ويمكن قراءتها.
PhyObj هو أدنى مستوى كائن في لعبتنا ، في الواقع هو كائن مادي مجردة.
وصف الثعبان
نحتاج الآن إلى وصف الثعبان ، ولأنه يتكون من قطع ، فسنقوم أولاً بوصف واحد:
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 Game(Scene): def setup(self): self.tile = Tile(self, (40, 40)) self.tile.setpos(100, 100)
كان يجب أن يتحول إلى:

ولكن على سبيل المثال ، لماذا نحتاج إلى الهامش:
class Game(Scene): def setup(self): tile1 = Tile(self, (40, 40)) tile1.setpos(100, 100) tile2 = Tile(self, (40, 40)) tile2.setpos(140, 100)

حسنا ، الآن من هذه القطع تحتاج إلى لصق الثعبان. نحتاج إلى طريقة تهيئة وطريقة نقل.
أولاً ، قم بإنشاء التهيئة:
class Snake: def __init__(self, length, width, initpos, parent): self.width = width
دعنا نحاول رسمها على الفور:
class Game(Scene): def setup(self): self.snake = Snake(10, 40, (200, 200), self)

حسنًا ، أضف طريقة النقل.
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)
أولاً ، نقوم بنقل الأخير إلى الأخير قبل الأخير ، ثم الأخير قبل الأخير ... ثم الثاني إلى الأول. والأول في (س ، ص).
في الواقع ، ثعبان لدينا يتحرك بشكل صحيح. لنجرب:
class Game(self):
إذا تمكنت من الرؤية ، فزحفت كما ينبغي. والحقيقة هي أن التحديث يسمى في كثير من الأحيان (وبالمناسبة ، اختياريا مع الفاصل الزمني نفسه) ، لذلك نحن بحاجة إلى النظر في مقدار الوقت الذي انقضى منذ آخر مكالمة والانتظار حتى "تتراكم" بما فيه الكفاية.
باختصار ، نحن نفعل:
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 صواب إذا مر وقت أطول من t. الآن سوف يتحرك الثعبان كل 0.3 ثانية. هل نجحت؟

إدارة
تحتاج الآن إلى التحكم ، أي منعطف في الاتجاهات الأربعة:

class Game(Scene):
والآن نحن بحاجة إلى القيام بمعالجة touch_began لفهم المنطقة التي قام المستخدم بدسها. في الواقع ، اتضح أنها ليست مثيرة للاهتمام كما اعتقدت ، لذلك يمكنك ببساطة نسخ:
class Game(Scene):
حسنا ، حاول أن تحول الآن

تم إعداد الآلية الرئيسية ، فلا يزال هناك إجراء فحص للاصطدامات والتفاح.
الذيل الاصطدام
لنبدأ بالتحقق وإضافة طريقة find_collisions إلى الثعبان
class Snake:
الآن يمكننا الحصول على زوج من الخلايا التي تتقاطع. أرغب في تلوينها باللون الأحمر ، أضف طريقة القالب إلى Tile:
class Tile(PhyObj):
إضافة التحقق لتحديث وتغيير الإعداد:
class Game(Scene):
ماذا حدث لي:

يبقى لصنع التفاح.
الاستطالة والتفاح
سنطيل الأفعى ، ولجعلها تبدو جميلة ، سيتم إضافة رابط جديد وفقًا للاثنتين الأخيرتين ، وهما:

أضف الطرق إلى الثعبان:
class Snake:
find_dir يجد الاتجاه الذي يتم توجيه طرف ذيل بطلتنا إليه. الزينة ، من السهل أن تخمن ، تضيف خلية. أضف طريقة ثعبان أخرى إلى اللعبة:
class Game(Scene):
هناك حاجة إلى السطر الأخير بحيث ينتظر الأفعى قليلاً ، ويكون لدى المستخدم وقت لمعرفة أنه تمت إضافة القطعة.
لمعرفة ما إذا كان هناك شيء ما يتقاطع مع الثعبان ، أضف طريقة التقاطع إليه.
class Snake:
الصيحة ، يبقى فقط لإنشاء تفاحة. في الواقع ، نحن نصف التفاح دفعة واحدة:
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)
هناك حاجة إلى مثل هذه العشوائية الغريبة لتناسب عين الثور لدينا على الشبكة. عندها لن يكون من الضروري البحث عن المسافة بين الكمامة والتفاح ومقارنتها باير بير. فقط على ifas. دعنا نذهب إلى التحديث وإضافة خطوط بسيطة للغاية إلى نهاية هذه الوظيفة:
class Game(Scene):
حسنًا ، يبدو أن كل شيء على ما يبدو ، والآن تطول الأفعى إذا ضربت تفاحة وماتت إذا ضربت نفسها.

علاوة
يمكنك جعل المؤثرات الصوتية:
import sound class Game(Scene):
قم بحركة سلسة:
class Game(Scene):
وبعبارة أخرى ، قمنا بتغيير منطق الموقف PhyObj. في السابق ، ركزنا على موضع عنصر الرسوم ، والآن يوجد حقل منفصل للموضع المنطقي (أي ، الحقل المستخدم لمنطق اللعبة) ، وموضع عنصر الرسوم أصبح الآن مجانيًا ويمكن تغييره بطريقته الخاصة. وهي ، باستخدام Action ، نترك لها تيار متوازي ، حيث تتحرك.
تحول ثعبان ناعم مثل:

وأخيراً ، ملصق بطول الثعبان:
class Game(Scene):

أيها الأصدقاء ، شكرًا لك على اهتمامك! إذا كان هناك شيء غير واضح ، اسأل. وإذا كان الأمر ممتعًا - سأستمر ، فلا يزال هناك شيء أقوله (لكن هذه المقالة طويلة بالفعل).
كل رمز ثعبان 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)
كريستال كود WhiteBlackGoose طبعةإنه أمر مضحك أنه بعد أن صنعته ، وجدت شيئًا مشابهًا جدًا في أمثلة بيثونيستا نفسها. ولكن لدي المزيد من الميزات :)
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
مظاهرة عمل البلورات: