Rumah > Artikel > pembangunan bahagian belakang > Mencipta permainan interaktif dengan Python 3 dan Pygame: Bahagian 4
Ini adalah yang keempat dalam siri tutorial lima bahagian tentang membuat permainan dengan Python 3 dan Pygame. Dalam Bahagian Tiga, kami menyelidiki teras Breakout, mempelajari cara mengendalikan acara, melihat kelas Breakout utama dan melihat cara mengalihkan objek permainan yang berbeza.
Dalam bahagian ini, kita akan belajar cara mengesan perlanggaran dan apa yang berlaku apabila bola terkena pelbagai objek seperti dayung, bata, dinding, siling dan lantai. Akhir sekali, kami akan menyemak topik penting UI permainan, khususnya cara membuat menu dengan butang tersuai kami sendiri.
Dalam permainan, perkara bertembung antara satu sama lain. Terobosan tidak terkecuali. Selalunya ia adalah bola mengenai sesuatu. Kaedah handle_ball_collision()
mempunyai fungsi bersarang bernama intersect()
, yang digunakan untuk menguji sama ada bola mengenai objek dan Kedudukan objek dalam objek. Jika bola terlepas objek, kembalikan "Kiri", "Kanan", "Atas", "Bawah", atau "Tiada". handle_ball_collisions()
方法有一个嵌套函数,名为 intersect()
,用于测试球是否击中物体以及击中物体的位置。如果球没有击中物体,则返回“左”、“右”、“上”、“下”或“无”。
def handle_ball_collisions(self): def intersect(obj, ball): edges = dict( left=Rect(obj.left, obj.top, 1, obj.height), right=Rect(obj.right, obj.top, 1, obj.height), top=Rect(obj.left, obj.top, obj.width, 1), bottom=Rect(obj.left, obj.bottom, obj.width, 1)) collisions = set(edge for edge, rect in edges.items() if ball.bounds.colliderect(rect)) if not collisions: return None if len(collisions) == 1: return list(collisions)[0] if 'top' in collisions: if ball.centery >= obj.top: return 'top' if ball.centerx < obj.left: return 'left' else: return 'right' if 'bottom' in collisions: if ball.centery >= obj.bottom: return 'bottom' if ball.centerx < obj.left: return 'left' else: return 'right'
当球击中球拍时,它会弹开。如果它击中桨的顶部,它将弹回来,但保持相同的水平速度分量。
但是,如果它击中桨的一侧,它将弹到另一侧(左或右)并继续向下运动,直到击中地板。该代码使用了 intersect 函数()。
# Hit paddle s = self.ball.speed edge = intersect(self.paddle, self.ball) if edge is not None: self.sound_effects['paddle_hit'].play() if edge == 'top': speed_x = s[0] speed_y = -s[1] if self.paddle.moving_left: speed_x -= 1 elif self.paddle.moving_left: speed_x += 1 self.ball.speed = speed_x, speed_y elif edge in ('left', 'right'): self.ball.speed = (-s[0], s[1])
当球拍在下降过程中错过球(或者球击中球拍的侧面)时,球将继续下落并最终击中地板。此时,玩家失去了生命,并且球被重新创建,因此游戏可以继续。当玩家生命耗尽时,游戏结束。
# Hit floor if self.ball.top > c.screen_height: self.lives -= 1 if self.lives == 0: self.game_over = True else: self.create_ball()
当球撞到墙壁或天花板时,它只会弹回来。
# Hit ceiling if self.ball.top < 0: self.ball.speed = (s[0], -s[1]) # Hit wall if self.ball.left < 0 or self.ball.right > c.screen_width: self.ball.speed = (-s[0], s[1])
当球击中砖块时,这是《Breakout》中的一个重大事件 - 砖块消失,玩家获得一分,球反弹回来,并且会发生一些其他事情(声音效果,可能还有特殊效果),我稍后再讨论。
为了确定砖块是否被击中,代码会检查是否有任何砖块与球相交:
# Hit brick for brick in self.bricks: edge = intersect(brick, self.ball) if not edge: continue self.bricks.remove(brick) self.objects.remove(brick) self.score += self.points_per_brick if edge in ('top', 'bottom'): self.ball.speed = (s[0], -s[1]) else: self.ball.speed = (-s[0], s[1])
大多数游戏都有一些用户界面。 Breakout 有一个简单的菜单,其中有两个按钮,分别是“PLAY”和“QUIT”。该菜单在游戏开始时出现,并在玩家单击“开始”时消失。让我们看看按钮和菜单是如何实现的以及它们如何与游戏集成。
Pygame 没有内置的 UI 库。有第三方扩展,但我决定为菜单构建自己的按钮。按钮是具有三种状态的游戏对象:正常、悬停和按下。正常状态是鼠标不在按钮上时,悬停状态是鼠标在按钮上但未按下鼠标左键时。按下状态是指鼠标位于按钮上方且玩家按下鼠标左键时。
该按钮被实现为一个矩形,其上显示有背景颜色和文本。该按钮还接收一个 on_click 函数(默认为 noop lambda 函数),该函数在单击按钮时被调用。
import pygame from game_object import GameObject from text_object import TextObject import config as c class Button(GameObject): def __init__(self, x, y, w, h, text, on_click=lambda x: None, padding=0): super().__init__(x, y, w, h) self.state = 'normal' self.on_click = on_click self.text = TextObject(x + padding, y + padding, lambda: text, c.button_text_color, c.font_name, c.font_size) def draw(self, surface): pygame.draw.rect(surface, self.back_color, self.bounds) self.text.draw(surface)
按钮处理自己的鼠标事件并根据这些事件更改其内部状态。当按钮处于按下状态时,收到 MOUSEBUTTONUP
事件,表示玩家点击了按钮,并调用 on_click()
函数。
def handle_mouse_event(self, type, pos): if type == pygame.MOUSEMOTION: self.handle_mouse_move(pos) elif type == pygame.MOUSEBUTTONDOWN: self.handle_mouse_down(pos) elif type == pygame.MOUSEBUTTONUP: self.handle_mouse_up(pos) def handle_mouse_move(self, pos): if self.bounds.collidepoint(pos): if self.state != 'pressed': self.state = 'hover' else: self.state = 'normal' def handle_mouse_down(self, pos): if self.bounds.collidepoint(pos): self.state = 'pressed' def handle_mouse_up(self, pos): if self.state == 'pressed': self.on_click(self) self.state = 'hover'
用于绘制背景矩形的 back_color
属性始终返回与按钮当前状态匹配的颜色,因此玩家可以清楚地看到按钮处于活动状态:
@property def back_color(self): return dict(normal=c.button_normal_back_color, hover=c.button_hover_back_color, pressed=c.button_pressed_back_color)[self.state]
create_menu()
函数创建一个带有两个按钮的菜单,其中包含文本“PLAY”和“QUIT”。它有两个嵌套函数,名为 on_play()
和 on_quit()
,它提供给相应的按钮。每个按钮都添加到 objects
列表(待绘制)以及 menu_buttons
字段。
def create_menu(self): for i, (text, handler) in enumerate((('PLAY', on_play), ('QUIT', on_quit))): b = Button(c.menu_offset_x, c.menu_offset_y + (c.menu_button_h + 5) * i, c.menu_button_w, c.menu_button_h, text, handler, padding=5) self.objects.append(b) self.menu_buttons.append(b) self.mouse_handlers.append(b.handle_mouse_event)
当点击 PLAY 按钮时,会调用 on_play() ,这会从 objects
列表中删除按钮,这样就不再绘制它们了。此外,触发游戏开始的布尔字段 is_game_running
和 start_level
设置为 True。
当点击退出按钮时,is_game_running
设置为 False
(有效暂停游戏)并且 game_over
设置为 True,触发最终游戏序列。
def on_play(button): for b in self.menu_buttons: self.objects.remove(b) self.is_game_running = True self.start_level = True def on_quit(button): self.game_over = True self.is_game_running = False
显示和隐藏菜单是隐式的。当按钮位于 objects
rrreee
MOUSEBUTTONUP
diterima, menunjukkan bahawa pemain mengklik butang tersebut dan fungsi on_click()
dipanggil. 🎜
rrreee
🎜Harta back_color
yang digunakan untuk melukis segi empat tepat latar belakang sentiasa mengembalikan warna yang sepadan dengan keadaan semasa butang, jadi pemain dapat melihat dengan jelas bahawa butang itu aktif: 🎜
rrreee
create_menu()
mencipta menu dengan dua butang yang mengandungi teks "MAIN" dan "BERHENTI". Ia mempunyai dua fungsi bersarang bernama on_play()
dan on_quit()
yang diberikan kepada butang yang sepadan. Setiap butang ditambahkan pada senarai objek
(untuk dilukis) dan medan menu_buttons
. 🎜
rrreee
🎜Apabila butang PLAY diklik, on_play() dipanggil, yang mengalih keluar butang daripada senarai objek
supaya ia tidak lagi dilukis. Selain itu, medan boolean is_game_running
dan start_level
yang mencetuskan permulaan permainan ditetapkan kepada True. 🎜
🎜Apabila butang keluar diklik, is_game_running
ditetapkan kepada False
(menjeda permainan dengan berkesan) dan game_over
Tetapkan kepada True untuk mencetuskan urutan permainan terakhir. 🎜
rrreee
objek
apabila ia dialih keluar, ia disembunyikan. Ia semudah itu. 🎜Anda boleh membuat menu bersarang dengan permukaannya sendiri yang menjadikan subkomponen seperti butang dan kemudian hanya menambah/mengalih keluar komponen menu itu, tetapi menu ringkas ini tidak memerlukannya.
Dalam bahagian ini kami merangkumi pengesanan perlanggaran dan perkara yang berlaku apabila bola mengenai pelbagai objek seperti dayung, bata, dinding, siling dan lantai. Selain itu, kami mencipta menu kami sendiri dengan butang tersuai yang menyembunyikan dan menunjukkan berdasarkan arahan.
Dalam bahagian akhir siri ini, kita akan melihat perlawanan akhir, memberi perhatian yang teliti kepada skor dan kehidupan, kesan bunyi dan muzik.
Kemudian kami akan membangunkan sistem kesan khas yang kompleks untuk menambah minat kepada permainan. Akhir sekali, kami membincangkan hala tuju masa hadapan dan potensi penambahbaikan.
Atas ialah kandungan terperinci Mencipta permainan interaktif dengan Python 3 dan Pygame: Bahagian 4. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!