這是有關使用 Python 3 和 Pygame 製作遊戲的五部分教程系列中的第四部分。在第三部分中,我們深入研究了 Breakout 的核心,學習如何處理事件,了解了主要的 Breakout 類,並了解如何移動不同的遊戲物件。
在這一部分中,我們將了解如何偵測碰撞以及當球撞擊各種物體(如槳、磚塊、牆壁、天花板和地板)時會發生什麼。最後,我們將回顧遊戲 UI 的重要主題,特別是如何使用我們自己的自訂按鈕建立選單。
在遊戲中,事物會互相碰撞。突破也不例外。大多數情況下是球撞到東西了。 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
清單中時,選單可見;當它們被刪除時,它就被隱藏了。就如此容易。
可以建立一個具有自己的表面的嵌套選單,該表面呈現按鈕等子元件,然後只需新增/刪除該選單元件,但這個簡單的選單不需要它。
在這一部分中,我們介紹了碰撞檢測以及當球撞擊各種物體(例如槳、磚塊、牆壁、天花板和地板)時會發生什麼。此外,我們還創建了自己的選單,其中包含根據命令隱藏和顯示的自訂按鈕。
在本系列的最後一部分中,我們將研究最終遊戲,密切注意得分和生命、音效和音樂。
#然後,我們將開發一套複雜的特效系統,為遊戲增添趣味。最後,我們將討論未來的方向和潛在的改進。
以上是使用 Python 3 和 Pygame 創建互動遊戲:第 4 部分的詳細內容。更多資訊請關注PHP中文網其他相關文章!