Python 3およびPygameでゲームを作成する:パート4

画像






これは、Python 3とPygameを使用してゲームを作成する5部構成のチュートリアルの4番目です。 第3部では、ブレイクアウトの中心を掘り下げ、イベントの処理方法を学び、メインのブレイクアウトクラスに精通し、異なるゲームオブジェクトを移動する方法を見ました。



(チュートリアルの残り: firstsecondthirdfive 。)



このパートでは、衝突を認識する方法と、ボールがさまざまなオブジェクト(ラケット、レンガ、壁、天井、床)に当たったときの動作を学習します。 最後に、ユーザーインターフェイスの重要なトピック、特に独自のボタンからメニューを作成する方法について説明します。



衝突認識



ゲームでは、オブジェクトは互いに衝突し、ブレイクアウトも例外ではありません。 基本的に、ボールはオブジェクトと衝突します。 handle_ball_collisions()



メソッドには、 intersect()



と呼ばれる組み込み関数があります。この関数は、ボールがオブジェクトに当たったかどうか、およびオブジェクトに当たった場所をチェックするために使用されます。 ボールがオブジェクトと衝突しない場合、「left」、「right」、「top」、「bottom」またはNoneを返します。



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



関数を使用しています。



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





床との衝突



ラケットがボールをその途中で通過する(またはボールが横からラケットに当たる)と、ボールは落下し続けてから床に当たります。 この時点で、プレーヤーは自分の命を失い、ゲームが続行できるようにボールが再作成されます。 プレーヤーの寿命が尽きるとゲームは終了します。



 #    if self.ball.top > c.screen_height: self.lives -= 1 if self.lives == 0: self.game_over = True else: self.create_ball()
      
      





天井と壁との衝突



ボールが壁や天井に当たると、ボールは跳ね返ります。



 #    if self.ball.top < 0: self.ball.speed = (s[0], -s[1]) #    if self.ball.left < 0 or self.ball.right > c.screen_width: self.ball.speed = (-s[0], s[1])
      
      





レンガとの衝突



ボールがブリックに当たったとき、これがブレイクアウトゲームのメインイベントです-ブリックが消え、プレイヤーがポイントを受け取り、ボールが跳ね返り、さらにいくつかのイベントが発生します(サウンド効果、場合によっては特殊効果)。これについては後で説明します。



ボールがレンガに当たったことを判断するために、コードはレンガがボールと交差するかどうかを確認します。



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





ゲームメニュープログラミング



ほとんどのゲームには何らかのUIがあります。 Breakoutには、「PLAY」と「QUIT」の2つのボタンがあるシンプルなメニューがあります。 メニューはゲームの開始時に表示され、プレイヤーが「PLAY」をクリックすると消えます。 ボタンとメニューがどのように実装されるか、またそれらがゲームにどのように統合されるかを見てみましょう。



ボタンの作成



Pygameには組み込みのUIライブラリはありません。 サードパーティの拡張機能がありますが、メニュー用に独自のボタンを作成することにしました。 ボタンは、通常、ハイライト、押されたの3つの状態を持つゲームオブジェクトです。 通常の状態は、マウスがボタンの上にないときであり、強調表示された状態は、マウスがボタンの上にあるが、マウスの左ボタンがまだ押されていないときです。 押された状態は、マウスがボタンの上にあり、プレーヤーがマウスの左ボタンを押したときです。



ボタンは、背景色とテキストが上部に表示される長方形として実装されます。 ボタンは、ボタンがクリックされると呼び出されるon_click関数(デフォルトでは空のラムダ関数)も受け取ります。



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



関数が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」の2つのボタンを持つメニューを作成します。 これには2つの組み込み関数on_play()



on_quit()



があり、対応するボタンに渡されます。 各ボタンは、 menu_buttons



フィールドと同様に、 objects



リスト(レンダリング用)に追加されます。



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



関数がon_play()



、ボタンがobjects



リストから削除され、レンダリングされなくなります。 さらに、ゲームの開始をトリガーするブールフィールド( is_game_running



およびstart_level



がTrueになります。



QUITボタンを押すと、 is_game_running



False



(実際にゲームを一時停止)にgame_over



れ、 game_over



Trueにgame_over



れ、ゲームがgame_over



ます。



 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



リストにある場合、メニューが表示されます。 それらが削除されると、非表示になります。 すべてが非常に簡単です。



サブコンポーネント(ボタンおよびその他のオブジェクト)をレンダリングする独自のサーフェスを備えた組み込みメニューを作成し、これらのメニューコンポーネントを単純に追加/削除できますが、このような単純なメニューには必要ありません。



まとめると



このパートでは、衝突認識と、ボールが異なるオブジェクト(ラケット、レンガ、壁、床、天井)と衝突したときに何が起こるかを調べました。 また、独自のボタンを持つメニューを作成しました。これらのボタンは、コマンドで非表示にしたり表示したりできます。



シリーズの最後の部分では、ゲームの完了、ポイントとライフの追跡、効果音、音楽について検討します。



次に、ゲームにいくつかのスパイスを追加する特殊効果の複雑なシステムを開発します。 最後に、さらなる開発と可能な改善について説明します。



All Articles