PyGame:Pythonでのゲームプログラミングの入門書

PyGame:Pythonでのゲームプログラミングの入門書

私が最後の千年紀の後半にコンピュータープログラミングを学び始めたとき、それはコンピューターゲームを書きたいという私の欲求に駆動されました。 Pythonを含め、私が学んだすべての言語とプラットフォームでゲームを書く方法を見つけようとしました。 それがhttps://www.pygame.org/[+ pygame +]を発見し、それを使用してゲームやその他のグラフィカルプログラムを作成する方法を学んだ方法です。 当時、私は本当に「+ pygame +」の入門書が必要でした。

この記事の終わりまでに、次のことができるようになります。

  • 画面にアイテムを描く

  • 効果音と音楽を再生する

  • ユーザー入力を処理する

  • イベントループを実装する

  • ゲームプログラミングが標準の手続き型Pythonプログラミングとどのように異なるかを説明する

この入門書では、https://realpython.com/learning-paths/python3-introduction/[Pythonプログラム作成の基本的な知識](ユーザー定義関数https://realpython.com/courses/python-importsを含む)があることを前提としています。 -101/[imports]、https://realpython.com/courses/mastering-while-loops/[loops]、およびhttps://realpython.com/courses/python-conditional-statements/[conditionals]。 プラットフォームのhttps://realpython.com/read-write-files-python/[ファイルを開く方法]にも精通している必要があります。 オブジェクト指向Pythonの基本的な理解も役立ちます。 `+ pygame +`はPythonのほとんどのバージョンで動作しますが、この記事ではPython 3.6を推奨し使用しています。

この記事のすべてのコードを取得して、次の手順を実行できます。

クローンリポジトリ: 使用するリポジトリをクローンするにはここをクリックして、このチュートリアルでPyGameを使用する方法を学習します。

背景と設定

`+ pygame `はhttps://www.libsdl.org/[SDLライブラリ]のPythonラッパーで、 *Simple DirectMedia Layer* の略です。 SDLは、サウンド、ビデオ、マウス、キーボード、ジョイスティックなど、システムの基盤となるマルチメディアハードウェアコンポーネントへのクロスプラットフォームアクセスを提供します。 ` pygame `は、行き詰まったhttp://www.pygame.org/docs/tut/PygameIntro.html[PySDLプロジェクト]の代わりとして始まりました。 SDLと ` pygame +`の両方のクロスプラットフォームの性質は、それらをサポートするすべてのプラットフォームでゲームとリッチマルチメディアPythonプログラムを書くことができることを意味します!

プラットフォームに + pygame +`をインストールするには、適切なhttps://realpython.com/what-is-pip/[+ pip +`]コマンドを使用します。

$ pip install pygame

ライブラリに付属するサンプルのいずれかをロードすることにより、インストールを確認できます。

$ python3 -m pygame.examples.aliens

ゲームウィンドウが表示されたら、 `+ pygame +`が正しくインストールされています! 問題が発生した場合は、https://www.pygame.org/wiki/GettingStarted [スタートガイド]にすべてのプラットフォームの既知の問題と警告の概要が記載されています。

基本的なPyGameプログラム

詳細を説明する前に、基本的な「+ pygame +」プログラムを見てみましょう。 このプログラムはウィンドウを作成し、背景を白で塗りつぶし、その中央に青い円を描きます。

 1 # Simple pygame program
 2
 3 # Import and initialize the pygame library
 4 import pygame
 5 pygame.init()
 6
 7 # Set up the drawing window
 8 screen = pygame.display.set_mode([500, 500])
 9
10 # Run until the user asks to quit
11 running = True
12 while running:
13
14     # Did the user click the window close button?
15     for event in pygame.event.get():
16         if event.type == pygame.QUIT:
17             running = False
18
19     # Fill the background with white
20     screen.fill((255, 255, 255))
21
22     # Draw a solid blue circle in the center
23     pygame.draw.circle(screen, (0, 0, 255), (250, 250), 75)
24
25     # Flip the display
26     pygame.display.flip()
27
28 # Done! Time to quit.
29 pygame.quit()

このプログラムを実行すると、次のようなウィンドウが表示されます。

このコードをセクションごとに分解してみましょう。

  • * 4行目と5行目では、 `+ pygame `ライブラリをインポートして初期化します。 これらの行がなければ、 ` pygame +`はありません。

  • *行8 *はプログラムの表示ウィンドウを設定します。 作成するウィンドウの幅と高さを指定するリストまたはタプルを提供します。 このプログラムはリストを使用して、各辺に500ピクセルの正方形のウィンドウを作成します。

  • * 11行目と12行目は、*ゲームループ*を設定して、プログラムが終了するタイミングを制御します。 このチュートリアルの後半で、ゲームループについて説明します。

  • *行15〜17 *は、ゲームループ内で*イベント*をスキャンして処理します。 イベントにも少し遅れて行きます。 この場合、処理されるイベントは `+ pygame.QUIT +`のみです。これは、ユーザーがウィンドウの閉じるボタンをクリックしたときに発生します。

  • *行20 *はウィンドウを単色で塗りつぶします。 `+ screen.fill()`は、色のRGB値を指定するリストまたはタプルのいずれかを受け入れます。 `(255、255、255)+`が提供されたため、ウィンドウは白で塗りつぶされます。

  • *行23 *は、次のパラメーターを使用してウィンドウに円を描きます。

  • * + screen +:*描画するウィンドウ

  • * (0、0、255):* RGBカラー値を含むタプル

  • * (250、250):*円の中心座標を指定するタプル

  • * + 75 +:*ピクセルで描く円の半径

  • *行26 *は、画面の表示内容を更新します。 この呼び出しがないと、ウィンドウには何も表示されません!

  • * 29行目は `+ pygame +`を終了します。 これは、ループが終了した後にのみ発生します。

それが「Hello、World」の「+ pygame +」バージョンです。次に、このコードの背後にある概念をもう少し詳しく見てみましょう。

PyGameの概念

「+ pygame +」とSDLライブラリは異なるプラットフォームやデバイス間で移植可能であるため、両方ともさまざまなハードウェアの現実の抽象化を定義して作業する必要があります。 これらの概念と抽象化を理解すると、独自のゲームを設計および開発するのに役立ちます。

初期化とモジュール

`+ pygame `ライブラリはhttps://www.pygame.org/docs/ref/pygame.html [いくつかのPythonコンストラクトで構成されています]で、いくつかの異なる*モジュール*が含まれています。 これらのモジュールは、システム上の特定のハードウェアへの抽象アクセスと、そのハードウェアを使用するための統一された方法を提供します。 たとえば、「 display 」はビデオディスプレイへの均一なアクセスを許可し、「 joystick +」はジョイスティックの抽象的な制御を許可します。

上記の例で `+ pygame `ライブラリをインポートした後、最初にしたことは、 ` pygame.init()`を使用してhttps://www.pygame.org/docs/tut/ImportInit.html[PyGameを初期化]したことです。 。 この関数は、含まれるすべての ` pygame `モジュールのhttps://www.pygame.org/docs/ref/pygame.html#pygame.init [個別の ` init()+`関数を呼び出します]。 これらのモジュールは特定のハードウェアの抽象化であるため、Linux、Windows、およびMacで同じコードを使用するには、この初期化手順が必要です。

ディスプレイと表面

モジュールに加えて、 + pygame +`には、ハードウェアに依存しない概念をカプセル化するいくつかのPython *クラス*も含まれています。 これらの1つはhttps://www.pygame.org/docs/ref/surface.html [+ Surface `]であり、最も基本的には、描画可能な長方形の領域を定義します。 ` Surface `オブジェクトは、 ` pygame `の多くのコンテキストで使用されます。 後で、画像を ` Surface +`に読み込んで画面に表示する方法について説明します。

+ pygame +`では、すべてがユーザーが作成したhttps://www.pygame.org/docs/ref/display.html [+ display `]で表示されます。これはウィンドウまたはフルスクリーンにすることができます。 ディスプレイは、https://www.pygame.org/docs/ref/display.html#pygame.display.set_mode [` .set_mode()`]を使用して作成され、可視部分を表す ` Surface `を返します。ウィンドウの。 https://www.pygame.org/docs/ref/draw.html#pygame.draw.circle [` pygame.draw.circle()`]のような描画関数に渡すのはこの ` Surface `です、およびhttps://www.pygame.org/docs/ref/display.html#pygame.display.flip [` pygame.display.flip()を呼び出すと、その「+ Surface 」の内容がディスプレイにプッシュされます) `]。

画像とRects

基本的な + pygame +`プログラムは、ディスプレイの `+ Surface +`に直接シェイプを描画しましたが、ディスク上の画像を操作することもできます。 https://www.pygame.org/docs/ref/image.html [+ image `モジュール]を使用すると、https://www.pygame.org/docs/ref/image.html#pygame.imageにアクセスできます。さまざまな一般的な形式のload [load]およびhttps://www.pygame.org/docs/ref/image.html#pygame.image.save[save]画像。 画像は ` Surface +`オブジェクトにロードされ、さまざまな方法で操作および表示できます。

前述のように、「+ Surface 」オブジェクトは、画像やウィンドウなど、「 pygame 」の他の多くのオブジェクトと同様に、長方形で表されます。 長方形は非常に頻繁に使用されるため、それらを処理するためだけにhttps://www.pygame.org/docs/ref/rect.html [特別な ` Rect `クラス]があります。 ゲーム内で ` Rect +`オブジェクトと画像を使用して、プレイヤーと敵を描き、それらの衝突を管理します。

さて、それで十分な理論です。 ゲームをデザインして書きましょう!

基本的なゲームデザイン

コードの記述を開始する前に、何らかのデザインを用意しておくことをお勧めします。 これはチュートリアルゲームなので、基本的なゲームプレイもデザインしましょう。

  • ゲームの目標は、入ってくる障害物を避けることです:

  • プレーヤーは画面の左側から開始します。

  • 障害物は右からランダムに入り、直線で左に移動します。

  • プレイヤーは、障害物を避けるために、左、右、上、または下に移動できます。

  • プレーヤーは画面から移動できません。

  • プレイヤーが障害物に当たったとき、またはユーザーがウィンドウを閉じると、ゲームは終了します。

彼がソフトウェアプロジェクトについて説明していたとき、https://devblogs.microsoft.com/oldnewthing/?p = 27543 [私の以前の同僚]は、「あなたは自分が何をしているかわからなくなるまであなたは何をするのかわからない」と言っていました。します。」そのことを念頭に置いて、このチュートリアルではカバーされないいくつかのことを以下に示します。

  • 複数の生命はありません

  • スコアキーピングなし

  • プレイヤーの攻撃能力なし

  • 前進レベルなし

  • ボスキャラクターなし

これらの機能や他の機能を自分のプログラムに追加してみてください。

始めましょう!

PyGameのインポートと初期化

`+ pygame `をインポートした後、それを初期化する必要もあります。 これにより、 ` pygame +`はその抽象化を特定のハードウェアに接続できます。

 1 # Import the pygame module
 2 import pygame
 3
 4 # Import pygame.locals for easier access to key coordinates
 5 # Updated to conform to flake8 and black standards
 6 from pygame.locals import (
 7     K_UP,
 8     K_DOWN,
 9     K_LEFT,
10     K_RIGHT,
11     K_ESCAPE,
12     KEYDOWN,
13     QUIT,
14 )
15
16 # Initialize pygame
17 pygame.init()

`+ pygame `ライブラリは、モジュールとクラス以外にも多くのものを定義します。 また、キーストローク、マウスの動き、表示属性などのいくつかのhttps://www.pygame.org/docs/ref/locals.html [ローカル定数]を定義します。 これらの定数は、構文 ` pygame。<CONSTANT> `を使用して参照します。 ` pygame.locals `から特定の定数をインポートすることにより、代わりに構文 ` <CONSTANT> +`を使用できます。 これにより、キーストロークが節約され、全体的な読みやすさが向上します。

ディスプレイのセットアップ

今、あなたは何かを引き出す必要があります! 全体的なキャンバスとなるhttps://www.pygame.org/docs/ref/display.html[screen]を作成します。

 1 # Import the pygame module
 2 import pygame
 3
 4 # Import pygame.locals for easier access to key coordinates
 5 # Updated to conform to flake8 and black standards
 6 from pygame.locals import (
 7     K_UP,
 8     K_DOWN,
 9     K_LEFT,
10     K_RIGHT,
11     K_ESCAPE,
12     KEYDOWN,
13     QUIT,
14 )
15
16 # Initialize pygame
17 pygame.init()
18
19 # Define constants for the screen width and height
20 SCREEN_WIDTH = 800
21 SCREEN_HEIGHT = 600
22
23 # Create the screen object
24 # The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT
25 screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

`+ pygame.display.set_mode()`を呼び出し、希望の幅と高さのタプルまたはリストを渡すことで、使用する画面を作成します。 この場合、20行目と21行目の定数「 SCREEN_WIDTH 」と「 SCREEN_HEIGHT 」で定義されているように、ウィンドウは800x600です。 これは、ウィンドウの内側の寸法を表す ` Surface +`を返します。 これは制御可能なウィンドウの一部であり、OSはウィンドウの境界線とタイトルバーを制御します。

このプログラムを今すぐ実行すると、ウィンドウが短時間表示され、プログラムが終了するとすぐに消えます。 まばたきしないでください。 次のセクションでは、メインゲームループに焦点を当て、正しい入力が与えられた場合にのみプログラムが終了するようにします。

ゲームループのセットアップ

PongからFortniteまでのすべてのゲームは、http://www.informit.com/articles/article.aspx?p = 2167437&seqNum = 2 [game loop]を使用してゲームプレイを制御します。 ゲームループは4つの非常に重要なことを行います。

  1. ユーザー入力を処理します

  2. すべてのゲームオブジェクトの状態を更新します

  3. ディスプレイとオーディオ出力を更新します

  4. ゲームの速度を維持します

ゲームループのすべてのサイクルは*フレーム*と呼ばれ、各サイクルをより速く実行できるほど、ゲームはより速く実行されます。 フレームは、ゲームを終了する何らかの条件が満たされるまで発生し続けます。 デザインには、ゲームループを終了できる2つの条件があります。

  1. プレイヤーは障害物と衝突します。 (衝突検出については後で説明します。)

  2. プレーヤーはウィンドウを閉じます。

ゲームループが最初に行うことは、ユーザー入力を処理して、プレーヤーが画面内を移動できるようにすることです。 したがって、さまざまな入力をキャプチャして処理する方法が必要です。 これは、 `+ pygame +`イベントシステムを使用して行います。

イベントの処理

キーを押したり、マウスを動かしたり、ジョイスティックを動かしたりすることは、ユーザーが入力できる方法の一部です。 すべてのユーザー入力により、https://www.pygame.org/docs/ref/event.html [event]が生成されます。 イベントはいつでも発生する可能性があり、多くの場合(常にではないが)プログラムの外部で発生します。 `+ pygame +`のすべてのイベントはイベントキューに配置され、その後アクセスして操作できます。 イベントを処理することは、*それらを処理する*と呼ばれ、そうするためのコードは*イベントハンドラ*と呼ばれます。

`+ pygame `のすべてのイベントには、イベント *type* が関連付けられています。 ゲームの場合、注目するイベントの種類はキー入力とウィンドウのクローズです。 Keypressイベントのイベントタイプは「 KEYDOWN 」で、ウィンドウクロージャイベントのタイプは「 QUIT 」です。 異なるイベントタイプには、他のデータが関連付けられている場合があります。 たとえば、 ` KEYDOWN `イベントタイプには、どのキーが押されたかを示す ` key +`という変数もあります。

キュー内のすべてのアクティブなイベントのリストにアクセスするには、 `+ pygame.event.get()+`を呼び出します。 次に、このリストをループして、各イベントタイプを検査し、それに応じて応答します。

27 # Variable to keep the main loop running
28 running = True
29
30 # Main loop
31 while running:
32     # Look at every event in the queue
33     for event in pygame.event.get():
34         # Did the user hit a key?
35         if event.type == KEYDOWN:
36             # Was it the Escape key? If so, stop the loop.
37             if event.key == K_ESCAPE:
38                 running = False
39
40         # Did the user click the window close button? If so, stop the loop.
41         elif event.type == QUIT:
42             running = False

このゲームループを詳しく見てみましょう。

  • *行28 *は、ゲームループの制御変数を設定します。 ループとゲームを終了するには、 `+ running = False +`を設定します。 ゲームループは29行目から始まります。

  • *行31 *は、イベントハンドラーを開始し、現在イベントキューにあるすべてのイベントを調べます。 イベントがない場合、リストは空になり、ハンドラーは何もしません。

  • *行35〜38 *は、現在の `+ event.type `が ` KEYDOWN `イベントかどうかを確認します。 そうである場合、プログラムは ` event.key `属性を見て、どのキーが押されたかをチェックします。 キーが[ key_ESCAPE ]で示される[.keys]#Esc#キーの場合、「 running = False +」を設定してゲームループを終了します。

  • *41行目と42行目は、 `+ QUIT +`と呼ばれるイベントタイプに対して同様のチェックを行います。 このイベントは、ユーザーがウィンドウの閉じるボタンをクリックしたときにのみ発生します。 ユーザーは、他のオペレーティングシステムアクションを使用してウィンドウを閉じることもできます。

これらの行を前のコードに追加して実行すると、空白または黒い画面のウィンドウが表示されます。

ウィンドウは、[。keys]#Esc#キーを押すか、ウィンドウを閉じて「+ QUIT +」イベントをトリガーするまで消えません。

画面に描画する

サンプルプログラムでは、2つのコマンドを使用して画面に描画しました。

* + screen.fill()+ *は背景を塗りつぶします
  1. + pygame.draw.circle()+ は円を描く

次に、画面に描画する3番目の方法、「+ Surface +」を使用する方法について学習します。

https://www.pygame.org/docs/ref/surface.html [+ Surface +]は、空白の紙のように描画できる長方形のオブジェクトであることを思い出してください。 `+ screen `オブジェクトは ` Surface `であり、表示画面とは別に独自の ` Surface +`オブジェクトを作成できます。 その仕組みを見てみましょう。

44 # Fill the screen with white
45 screen.fill((255, 255, 255))
46
47 # Create a surface and pass in a tuple containing its length and width
48 surf = pygame.Surface((50, 50))
49
50 # Give the surface a color to separate it from the background
51 surf.fill((0, 0, 0))
52 rect = surf.get_rect()

45行目に画面が白でいっぱいになった後、48行目に新しい「+ Surface 」が作成されます。 この「 Surface 」は幅50ピクセル、高さ50ピクセルで、「 surf 」に割り当てられます。 この時点で、あなたはそれを ` screen `のように扱います。 51行目では、黒で塗りつぶしています。 ` .get_rect()`を使用して、基礎となる ` Rect `にアクセスすることもできます。 これは、後で使用するために「 rect +」として保存されます。

`+ .blit()`と ` .flip()+`を使用する

新しい「+ Surface 」を作成するだけでは、画面に表示するのに十分ではありません。 それを行うには、https://www.pygame.org/docs/ref/surface.html#pygame.Surface.blit [blit]から別の ` Surface `に ` Surface `を追加する必要があります。 「 blit 」という用語は「ブロック転送」を表し、「。blit()」は、ある「 Surface 」の内容を別の「 Surface 」にコピーする方法です。 ある ` Surface `から別の ` Surface `にのみ ` .blit()`を使用できますが、画面は別の ` Surface `であるため、問題はありません。 画面に「 surf +」を描画する方法は次のとおりです。

54 # This line says "Draw surf onto the screen at the center"
55 screen.blit(surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
56 pygame.display.flip()

55行目の `+ .blit()+`呼び出しは2つの引数を取ります。

  1. 描画する + Surface +

  2. ソース `+ Surface +`で描画する場所

座標 `(SCREEN_WIDTH/2、SCREEN_HEIGHT/2)`は、プログラムに画面の正確な中央に `+ surf +`を配置するように指示しますが、そのようには見えません。

画像が中心から外れて見える理由は、 `+ .blit()`が指定された場所に ` surf `の*左上隅*を置くためです。 「 surf 」を中央に配置する場合は、数学を実行して左上に移動する必要があります。 これを行うには、画面の幅と高さから「 surf 」の幅と高さを差し引き、それぞれを2で割って中心を特定し、それらの数値を引数として「 screen.blit()+」に渡します。 :

54 # Put the center of surf at the center of the display
55 surf_center = (
56     (SCREEN_WIDTH-surf.get_width())/2,
57     (SCREEN_HEIGHT-surf.get_height())/2
58 )
59
60 # Draw surf at the new coordinates
61 screen.blit(surf, surf_center)
62 pygame.display.flip()

+ blit()+への呼び出しの後にhttps://www.pygame.org/docs/ref/display.html#pygame.display.flip [ + pygame.display.flip()+ `]への呼び出しに注意してください。 `。 これにより、最後のフリップ以降に描画されたすべてのもので画面全体が更新されます。 `+ .flip()+`の呼び出しがなければ、何も表示されません。

スプライト

ゲームデザインでは、プレーヤーは左から始まり、障害物は右から入ります。 すべての障害物を `+ Surface +`オブジェクトで表現してすべてを簡単に描画できますが、それらをどこに描画するかをどのように知るのでしょうか? 障害物がプレーヤーと衝突したかどうかをどのように確認しますか? 障害物が画面から飛び出すとどうなりますか? 移動する背景画像を描きたい場合はどうしますか? 画像をアニメーション化する場合はどうしますか? https://en.wikipedia.org/wiki/Sprite_%28computer_graphics%29 [スプライト]を使用すると、これらの状況すべてに対処できます。

プログラミング用語では、*スプライト*は画面上の何かの2D表現です。 本質的に、それは写真です。 + pygame +`はhttps://www.pygame.org/docs/ref/sprite.html [+ Sprite `クラス]を提供します。これは、表示したいゲームオブジェクトの1つまたは複数のグラフィック表現を保持するように設計されています画面上。 それを使用するには、 ` Sprite +`を拡張する新しいクラスを作成します。 これにより、組み込みメソッドを使用できます。

プレイヤー

現在のゲームで `+ Sprite +`オブジェクトを使用してプレーヤーを定義する方法を次に示します。 行18の後にこのコードを挿入します。

20 # Define a Player object by extending pygame.sprite.Sprite
21 # The surface drawn on the screen is now an attribute of 'player'
22 class Player(pygame.sprite.Sprite):
23     def __init__(self):
24         super(Player, self).__init__()
25         self.surf = pygame.Surface((75, 25))
26         self.surf.fill((255, 255, 255))
27         self.rect = self.surf.get_rect()

最初に、22行目で `+ pygame.sprite.Sprite `を拡張して ` Player `を定義します。 次に、「 . init ()」は「 .super()」を使用して、「 Sprite 」の「 . init ()+」メソッドを呼び出します。 これが必要な理由の詳細については、https://realpython.com/python-super/[Python super()によるクラスのスーパーチャージ]を参照してください。

次に、 `+ .surf `を定義および初期化して、表示する画像を保持します。これは現在、白いボックスです。 また、後でプレーヤーを描画するために使用する「 .rect +」を定義および初期化します。 この新しいクラスを使用するには、新しいオブジェクトを作成し、描画コードも変更する必要があります。 以下のコードブロックを展開して、すべて一緒に表示します。

このコードを実行します。 画面のほぼ中央に白い長方形が表示されます。

59行目を `+ screen.blit(player.surf、player.rect)+`に変更するとどうなると思いますか? 試してみてください。

55 # Fill the screen with black
56 screen.fill((0, 0, 0))
57
58 # Draw the player on the screen
59 screen.blit(player.surf, player.rect)
60
61 # Update the display
62 pygame.display.flip()

`+ Rect `を ` .blit()+`に渡すと、左上隅の座標を使用してサーフェスが描画されます。 これを後で使用して、プレーヤーを動かします!

ユーザー入力

これまで、「+ pygame +」を設定し、画面にオブジェクトを描画する方法を学びました。 今、本当の楽しみが始まります! キーボードを使用してプレーヤーを制御可能にします。

前に、 `+ pygame.event.get()`がイベントキュー内のイベントのリストを返し、 ` KEYDOWN `イベントタイプをスキャンすることを見ました。 キープレスを読む方法はそれだけではありません。 ` pygame `は、キュー内の現在のすべての ` KEYDOWN `イベントを含むhttps://realpython.com/python-dicts/[dictionary]を返す ` pygame.event.get_pressed()+`も提供します。

イベント処理ループの直後にこれをゲームループに入れます。 これは、すべてのフレームの先頭で押されたキーを含む辞書を返します。

54 # Get the set of keys pressed and check for user input
55 pressed_keys = pygame.key.get_pressed()

次に、 `+ Player +`でその辞書を受け入れるメソッドを記述します。 これにより、押されたキーに基づいてスプライトの動作が定義されます。 これは次のようなものです。

29 # Move the sprite based on user keypresses
30 def update(self, pressed_keys):
31     if pressed_keys[K_UP]:
32         self.rect.move_ip(0, -5)
33     if pressed_keys[K_DOWN]:
34         self.rect.move_ip(0, 5)
35     if pressed_keys[K_LEFT]:
36         self.rect.move_ip(-5, 0)
37     if pressed_keys[K_RIGHT]:
38         self.rect.move_ip(5, 0)

「+ K_UP 」、「 K_DOWN 」、「 K_LEFT 」、および「 K_RIGHT 」は、キーボードの矢印キーに対応しています。 そのキーの辞書エントリが「 True 」の場合、そのキーは押されていないので、プレイヤーを正しい方向に「 .rect 」に移動します。 ここでは、https://www.pygame.org/docs/ref/rect.html#pygame.Rect.move_ip [ *move in place* ]を表す ` .move_ip()`を使用して、現在の` Rect +`。

次に、フレームごとに `+ .update()`を呼び出して、キー押下に応じてプレーヤーのスプライトを移動できます。 ` .get_pressed()+`の呼び出しの直後にこの呼び出しを追加します。

52 # Main loop
53 while running:
54     # for loop through the event queue
55     for event in pygame.event.get():
56         # Check for KEYDOWN event
57         if event.type == KEYDOWN:
58             # If the Esc key is pressed, then exit the main loop
59             if event.key == K_ESCAPE:
60                 running = False
61         # Check for QUIT event. If QUIT, then set running to false.
62         elif event.type == QUIT:
63             running = False
64
65     # Get all the keys currently pressed
66     pressed_keys = pygame.key.get_pressed()
67
68     # Update the player sprite based on user keypresses
69     player.update(pressed_keys)
70
71     # Fill the screen with black
72     screen.fill((0, 0, 0))

これで、矢印キーを使用してプレーヤーの四角形を画面上で移動できます。

次の2つの小さな問題に気付くかもしれません。

  1. プレーヤーの長方形は、キーが押されていると非常に速く移動できます。 それについては後で取り組みます。

  2. プレーヤーの四角形は画面から移動できます。 それを今解決しましょう。

プレーヤーを画面上に保持するには、「+ rect 」が画面外に移動するかどうかを検出するためのロジックを追加する必要があります。 それには、 ` rect +`座標が画面の境界を越えて移動したかどうかを確認します。 その場合、プログラムをエッジに戻すように指示します。

25 # Move the sprite based on user keypresses
26 def update(self, pressed_keys):
27     if pressed_keys[K_UP]:
28         self.rect.move_ip(0, -5)
29     if pressed_keys[K_DOWN]:
30         self.rect.move_ip(0, 5)
31     if pressed_keys[K_LEFT]:
32         self.rect.move_ip(-5, 0)
33     if pressed_keys[K_RIGHT]:
34         self.rect.move_ip(5, 0)
35
36     # Keep player on the screen
37     if self.rect.left < 0:
38         self.rect.left = 0
39     if self.rect.right > SCREEN_WIDTH:
40         self.rect.right = SCREEN_WIDTH
41     if self.rect.top <= 0:
42         self.rect.top = 0
43     if self.rect.bottom >= SCREEN_HEIGHT:
44         self.rect.bottom = SCREEN_HEIGHT

ここでは、「。move()」を使用する代わりに、「。top +」、「。bottom 」、「。left 」、または「 .right +」の対応する座標を直接変更するだけです。 これをテストすると、プレイヤーの四角形が画面から移動できなくなります。

敵を追加しましょう!

敵のいないゲームとは? 既に学んだテクニックを使用して基本的な敵のクラスを作成し、プレイヤーが回避するためにそれらの多くを作成します。 まず、 `+ random +`ライブラリをインポートします:

 4 # Import random for random numbers
 5 import random

次に、 `+ Player `に使用したのと同じパターンに従って、 ` Enemy +`という新しいスプライトクラスを作成します。

55 # Define the enemy object by extending pygame.sprite.Sprite
56 # The surface you draw on the screen is now an attribute of 'enemy'
57 class Enemy(pygame.sprite.Sprite):
58     def __init__(self):
59         super(Enemy, self).__init__()
60         self.surf = pygame.Surface((20, 10))
61         self.surf.fill((255, 255, 255))
62         self.rect = self.surf.get_rect(
63             center=(
64                 random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
65                 random.randint(0, SCREEN_HEIGHT),
66             )
67         )
68         self.speed = random.randint(5, 20)
69
70     # Move the sprite based on speed
71     # Remove the sprite when it passes the left edge of the screen
72     def update(self):
73         self.rect.move_ip(-self.speed, 0)
74         if self.rect.right < 0:
75             self.kill()

`+ Enemy `と ` Player +`の間には4つの顕著な違いがあります。

  1. *62行目から67行目で、画面の右端に沿ったランダムな場所になるように「+ rect 」を更新します。 長方形の中心は画面から少し外れています。 右端から20〜100ピクセル離れた位置、および上端と下端の間のどこかにあります。 .* 68行目では、「。speed +」を5〜20の乱数として定義します。 これは、この敵がプレイヤーに向かって移動する速度を指定します。

  2. * 73行目から76行目*では、 `+ .update()`を定義します。 敵は自動的に移動するため、引数は必要ありません。 代わりに、 ` .update()`は、作成時に定義された ` .speed +`で敵を画面の左側に移動します。

  3. * 74行目で、敵が画面外に移動したかどうかを確認します。 `+ Enemy `が画面から完全に消え、表示されている間だけ消えないようにするには、 ` .rect `の右側が画面の左側を過ぎていることを確認します。 敵が画面の外に出たら、 ` .kill()+`を呼び出して、それ以上処理されないようにします。

それで、 `+ .kill()+`は何をしますか? これを理解するには、*スプライトグループ*について知る必要があります。

スプライトグループ

+ pygame +`が提供する別の非常に便利なクラスはhttps://www.pygame.org/docs/ref/sprite.html#pygame.sprite.Group[+Sprite Group + `]です。 これは、 `+ Sprite `オブジェクトのグループを保持するオブジェクトです。 なぜそれを使用するのですか? 代わりにリスト内の ` Sprite `オブジェクトを追跡することはできませんか? できますが、 ` Group `を使用する利点は、公開するメソッドにあります。 これらのメソッドは、 ` Enemy `が ` Player +`と衝突したかどうかを検出するのに役立ちます。これにより、更新がはるかに簡単になります。

スプライトグループを作成する方法を見てみましょう。 2つの異なる「+ Group +」オブジェクトを作成します。

  1. 最初の `+ Group `はゲーム内のすべての ` Sprite +`を保持します。

  2. 2番目の `+ Group `は、 ` Enemy +`オブジェクトのみを保持します。

コードでは次のようになります。

82 # Create the 'player'
83 player = Player()
84
85 # Create groups to hold enemy sprites and all sprites
86 # - enemies is used for collision detection and position updates
87 # - all_sprites is used for rendering
88 enemies = pygame.sprite.Group()
89 all_sprites = pygame.sprite.Group()
90 all_sprites.add(player)
91
92 # Variable to keep the main loop running
93 running = True

`+ .kill()`を呼び出すと、それが属するすべての ` Group `から ` Sprite `が削除されます。 これにより、 ` Sprite +`への参照も削除され、Pythonのガベージコレクターが必要に応じてメモリを再利用できるようになります。

`+ all_sprites `グループができたので、オブジェクトの描画方法を変更できます。 ` Player `で ` .blit()`を呼び出す代わりに、 ` all_sprites +`のすべてを反復処理できます。

117 # Fill the screen with black
118 screen.fill((0, 0, 0))
119
120 # Draw all sprites
121 for entity in all_sprites:
122     screen.blit(entity.surf, entity.rect)
123
124 # Flip everything to the display
125 pygame.display.flip()

これで、「+ all_sprites +」に入力されたものは、敵であろうとプレイヤーであろうと、すべてのフレームで描画されます。

問題が1つだけあります。敵はいません! ゲームの開始時に敵の束を作成できますが、数秒後にすべての敵が画面を離れるとすぐにゲームは退屈になります。 代わりに、ゲームの進行中に敵の安定した供給を維持する方法を検討しましょう。

カスタムイベント

この設計では、敵が一定間隔で出現する必要があります。 これは、設定された間隔で、次の2つのことを行う必要があることを意味します。

  1. 新しい `+ Enemy +`を作成します。

  2. それを `+ all_sprites `と ` enemies +`に追加します。

ランダムイベントを処理するコードが既にあります。 イベントループは、フレームごとに発生するランダムイベントを探して適切に処理するように設計されています。 幸いなことに、「+ pygame +」は、定義したイベントタイプのみの使用を制限しません。 必要に応じて処理する独自のイベントを定義できます。

数秒ごとに生成されるカスタムイベントを作成する方法を見てみましょう。 カスタムイベントに名前を付けて作成できます。

78 # Create the screen object
79 # The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT
80 screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
81
82 # Create a custom event for adding a new enemy
83 ADDENEMY = pygame.USEREVENT + 1
84 pygame.time.set_timer(ADDENEMY, 250)
85
86 # Instantiate player. Right now, this is just a rectangle.
87 player = Player()

`+ pygame `はイベントを整数として内部的に定義するため、一意の整数で新しいイベントを定義する必要があります。 「 pygame 」が予約する最後のイベントは「 USEREVENT 」と呼ばれるため、83行目で「 ADDENEMY = pygame.USEREVENT + 1+」と定義すると一意になります。

次に、この新しいイベントをゲーム全体で定期的にイベントキューに挿入する必要があります。 それがhttps://www.pygame.org/docs/ref/time.html [+ time +]モジュールの出番です。 行84は、250ミリ秒ごとに、つまり1秒間に4回、新しい「+ ADDENEMY 」イベントを発生させます。 タイマーは1つしか必要ないため、ゲームループの外で ` .set_timer()+`を呼び出しますが、ゲーム全体で起動します。

新しいイベントを処理するコードを追加します。

100 # Main loop
101 while running:
102     # Look at every event in the queue
103     for event in pygame.event.get():
104         # Did the user hit a key?
105         if event.type == KEYDOWN:
106             # Was it the Escape key? If so, stop the loop.
107             if event.key == K_ESCAPE:
108                 running = False
109
110         # Did the user click the window close button? If so, stop the loop.
111         elif event.type == QUIT:
112             running = False
113
114         # Add a new enemy?
115         elif event.type == ADDENEMY:
116             # Create the new enemy and add it to sprite groups
117             new_enemy = Enemy()
118             enemies.add(new_enemy)
119             all_sprites.add(new_enemy)
120
121     # Get the set of keys pressed and check for user input
122     pressed_keys = pygame.key.get_pressed()
123     player.update(pressed_keys)
124
125     # Update enemy position
126     enemies.update()

イベントハンドラーは、115行目に新しい「+ ADDENEMY 」イベントを検出するたびに、「 Enemy 」を作成し、「 enemies 」と「 all_sprites 」に追加します。 ` Enemy `は ` all_sprites `にあるため、フレームごとに描画されます。 また、126行目で ` enemies.update()`を呼び出す必要があります。これにより、 ` enemies +`のすべてが更新され、適切に移動するようになります。

ただし、「敵」だけのグループがあるのはそれだけではありません。

衝突検出

ゲームの設計では、敵がプレイヤーと衝突するたびにゲームを終了する必要があります。 衝突のチェックは、ゲームプログラミングの基本的な手法であり、通常、2つのスプライトが互いにオーバーラップするかどうかを判断するために、重要な数学が必要です。

これは、「+ pygame 」のようなフレームワークが便利な場所です! 衝突検出コードの記述は退屈ですが、 ` pygame +`にはhttps://www.pygame.org/docs/ref/sprite.html#pygame.sprite.collide_rect [衝突検出方法]がたくさんあり、使用できます。

このチュートリアルでは、https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollideany [+ .spritecollideany()+]と呼ばれるメソッドを使用します。これは「スプライトが衝突します。」このメソッドは、パラメーターとして「+ Sprite 」と「 Group 」を受け入れます。 ` Group `内のすべてのオブジェクトを調べ、 ` .rect `が ` Sprite `の ` .rect `と交差するかどうかを確認します。 もしそうなら、それは ` True `を返します。 そうでなければ、 ` False `を返します。 単一の「 player 」が「+グループ」の「敵」のいずれかと衝突するかどうかを確認する必要があるため、これはこのゲームに最適です。

コードでは次のようになります。

130 # Draw all sprites
131 for entity in all_sprites:
132     screen.blit(entity.surf, entity.rect)
133
134 # Check if any enemies have collided with the player
135 if pygame.sprite.spritecollideany(player, enemies):
136     # If so, then remove the player and stop the loop
137     player.kill()
138     running = False

135行目は、「+ player 」が「 enemies 」のオブジェクトのいずれかと衝突したかどうかをテストします。 その場合、 ` player.kill()`が呼び出され、所属するすべてのグループから削除されます。 レンダリングされるオブジェクトは「 all_sprites 」のみであるため、「 player 」はレンダリングされなくなります。 プレイヤーが殺されたら、ゲームも終了する必要があるので、 ` running = False +`を設定して、138行目のゲームループから抜け出します。

この時点で、ゲームの基本的な要素が準備できました。

Pygame window、width = 790、height = 613

それでは、少しドレスアップしてプレイしやすくし、いくつかの高度な機能を追加して目立たせましょう。

スプライト画像

さて、あなたはゲームを持っていますが、正直に言ってください…​それはちょっといです。 プレイヤーと敵は、黒い背景に白いブロックだけです。 Pongが新しくなったとき、それは最先端でしたが、もはやそれをカットしていません。 それらの退屈な白い長方形を、ゲームを実際のゲームのように感じさせるクールな画像に置き換えましょう。

先ほど、 `+ image `モジュールの助けを借りて、ディスク上の画像を ` Surface +`にロードできることを学びました。 このチュートリアルでは、プレーヤー用の小さなジェットと敵用のミサイルを作成しました。 このアートの使用、独自の描画、または使用するhttps://realpython.com/pygame-a-primer/#note-on-sources [無料ゲームアートアセット]のダウンロードを歓迎します。 以下のリンクをクリックして、このチュートリアルで使用するアートをダウンロードできます。

*サンプルコード:*このチュートリアルで使用されているhttps://realpython.com/bonus/pygame-sample-code/[PyGameサンプルプロジェクトのソースコードをダウンロードするには、ここをクリックしてください]。

オブジェクトコンストラクターの変更

画像を使用してプレーヤーと敵のスプライトを表現する前に、コンストラクターにいくつかの変更を加える必要があります。 以下のコードは、以前に使用されたコードを置き換えます。

 7 # Import pygame.locals for easier access to key coordinates
 8 # Updated to conform to flake8 and black standards
 9 # from pygame.locals import *
10 from pygame.locals import (
11     RLEACCEL,
12     K_UP,
13     K_DOWN,
14     K_LEFT,
15     K_RIGHT,
16     K_ESCAPE,
17     KEYDOWN,
18     QUIT,
19 )
20
21 # Define constants for the screen width and height
22 SCREEN_WIDTH = 800
23 SCREEN_HEIGHT = 600
24
25
26 # Define the Player object by extending pygame.sprite.Sprite
27 # Instead of a surface, use an image for a better-looking sprite
28 class Player(pygame.sprite.Sprite):
29     def __init__(self):
30         super(Player, self).__init__()
31         self.image = pygame.image.load("jet.png").convert()
32         self.image.set_colorkey((255, 255, 255), RLEACCEL)
33         self.rect = self.image.get_rect()

31行目を少し展開しましょう。 `+ pygame.image.load()`はディスクから画像をロードします。 ファイルへのパスを渡します。 それは ` Surface `を返し、 ` .convert()`呼び出しは ` Surface `を最適化し、将来の ` .blit()+`呼び出しを高速化します。

行32では、 `+ .set_colorkey()`を使用して、 ` pygame `が透明としてレンダリングされる色を示しています。 この場合、ジェット画像の背景色であるため、白を選択します。 https://www.pygame.org/docs/ref/surface.html#pygame.Surface.set_colorkey[RLEACCEL]定数は、非加速ディスプレイ上で ` pygame `がより速くレンダリングするのに役立つオプションのパラメーターです。 これは、11行目の ` pygame.locals +`インポートステートメントに追加されます。

他に何も変える必要はありません。 現在、画像がペイントされていることを除いて、画像はまだ「+ Surface +」です。 それでも同じように使用します。

`+ Enemy +`に対する同様の変更は次のようになります。

59 # Define the enemy object by extending pygame.sprite.Sprite
60 # Instead of a surface, use an image for a better-looking sprite
61 class Enemy(pygame.sprite.Sprite):
62     def __init__(self):
63         super(Enemy, self).__init__()
64         self.surf = pygame.image.load("missile.png").convert()
65         self.surf.set_colorkey((255, 255, 255), RLEACCEL)
66         # The starting position is randomly generated, as is the speed
67         self.rect = self.surf.get_rect(
68             center=(
69                 random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
70                 random.randint(0, SCREEN_HEIGHT),
71             )
72         )
73         self.speed = random.randint(5, 20)

プログラムを実行すると、これは以前と同じゲームであることを示すはずです。ただし、画像付きの素晴らしいグラフィックス*スキン*を追加したことを除きます。 しかし、なぜプレイヤーと敵のスプライトの見栄えを良くするのにとどまらないのですか? 空を飛ぶジェット機の印象を与えるために、いくつかの雲を通過させましょう。

背景画像を追加する

バックグラウンドクラウドの場合、 `+ Player `および ` Enemy +`の場合と同じ原則を使用します。

  1. `+ Cloud +`クラスを作成します。

  2. クラウドの画像を追加します。

  3. `+ cloud `を画面の左側に移動するメソッド ` .update()+`を作成します。

  4. カスタムイベントとハンドラーを作成して、設定した時間間隔で新しい `+ cloud +`オブジェクトを作成します。

  5. 新しく作成した `+ cloud `オブジェクトを、 ` clouds `という新しい ` Group +`に追加します。

  6. ゲームループで `+ clouds +`を更新して描画します。

「+ Cloud +」は次のようになります。

 83 # Define the cloud object by extending pygame.sprite.Sprite
 84 # Use an image for a better-looking sprite
 85 class Cloud(pygame.sprite.Sprite):
 86     def __init__(self):
 87         super(Cloud, self).__init__()
 88         self.surf = pygame.image.load("cloud.png").convert()
 89         self.surf.set_colorkey((0, 0, 0), RLEACCEL)
 90         # The starting position is randomly generated
 91         self.rect = self.surf.get_rect(
 92             center=(
 93                 random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
 94                 random.randint(0, SCREEN_HEIGHT),
 95             )
 96
 97     # Move the cloud based on a constant speed
 98     # Remove the cloud when it passes the left edge of the screen
 99     def update(self):
100         self.rect.move_ip(-5, 0)
101         if self.rect.right < 0:
102             self.kill()

それはすべて非常に馴染みがあるはずです。 「敵」とほとんど同じです。

特定の間隔で雲を表示するには、新しい敵の作成に使用したものと同様のイベント作成コードを使用します。 敵の作成イベントのすぐ下に配置します。

116 # Create custom events for adding a new enemy and a cloud
117 ADDENEMY = pygame.USEREVENT + 1
118 pygame.time.set_timer(ADDENEMY, 250)
119 ADDCLOUD = pygame.USEREVENT + 2
120 pygame.time.set_timer(ADDCLOUD, 1000)

これは、次の `+ cloud +`を作成する前に1000ミリ秒、または1秒待機することを意味します。

次に、新しく作成された各 `+ cloud `を保持するために、新しい ` Group +`を作成します。

125 # Create groups to hold enemy sprites, cloud sprites, and all sprites
126 # - enemies is used for collision detection and position updates
127 # - clouds is used for position updates
128 # - all_sprites is used for rendering
129 enemies = pygame.sprite.Group()
130 clouds = pygame.sprite.Group()
131 all_sprites = pygame.sprite.Group()
132 all_sprites.add(player)

次に、イベントハンドラーに新しい `+ ADDCLOUD +`イベントのハンドラーを追加します。

137 # Main loop
138 while running:
139     # Look at every event in the queue
140     for event in pygame.event.get():
141         # Did the user hit a key?
142         if event.type == KEYDOWN:
143             # Was it the Escape key? If so, then stop the loop.
144             if event.key == K_ESCAPE:
145                 running = False
146
147         # Did the user click the window close button? If so, stop the loop.
148         elif event.type == QUIT:
149             running = False
150
151         # Add a new enemy?
152         elif event.type == ADDENEMY:
153             # Create the new enemy and add it to sprite groups
154             new_enemy = Enemy()
155             enemies.add(new_enemy)
156             all_sprites.add(new_enemy)
157
158         # Add a new cloud?
159         elif event.type == ADDCLOUD:
160             # Create the new cloud and add it to sprite groups
161             new_cloud = Cloud()
162             clouds.add(new_cloud)
163             all_sprites.add(new_cloud)

最後に、フレームごとに `+ clouds +`が更新されることを確認します。

167 # Update the position of enemies and clouds
168 enemies.update()
169 clouds.update()
170
171 # Fill the screen with sky blue
172 screen.fill((135, 206, 250))

172行目は、元の `+ screen.fill()+`を更新して、心地よい空色で画面を塗りつぶします。 この色を別の色に変更できます。 紫の空、ネオングリーンの有毒な荒れ地、または赤の火星の表面を備えたエイリアンの世界が欲しいかもしれません!

それぞれの新しい「+ Cloud 」と「 Enemy 」が「 clouds 」と「 enemies 」と同様に「 all_sprites +」に追加されることに注意してください。 これは、各グループが個別の目的に使用されるために行われます。

  • *レンダリング*は `+ all_sprites +`を使用して行われます。

  • *位置の更新*は、 `+ clouds `と ` enemies +`を使用して行われます。

  • *衝突検出*は `+ enemies +`を使用して行われます。

複数のグループを作成して、他のスプライトの動きや動作に影響を与えることなく、スプライトの動きや動作を変更できるようにします。

ゲームスピード

ゲームのテスト中に、敵が少し速く動くことに気づいたかもしれません。 そうでない場合は、この時点で異なるマシンが異なる結果を見るため、大丈夫です。

これは、プロセッサと環境が許す限りゲームループがフレームを処理するためです。 すべてのスプライトはフレームごとに1回移動するため、毎秒数百回移動できます。 1秒間に処理されるフレームの数は*フレームレート*と呼ばれ、これを正しくすることは、プレイ可能なゲームと忘れられるゲームの違いです。

通常、可能な限り高いフレームレートが必要ですが、このゲームでは、ゲームをプレイ可能にするために少し遅くする必要があります。 幸い、モジュール + time +`にはhttps://www.pygame.org/docs/ref/time.html#pygame.time.Clock [+ Clock +`]が含まれており、これはまさにこの目的のために設計されています。

「+ Clock 」を使用して再生可能なフレームレートを確立するには、わずか2行のコードが必要です。 最初は、ゲームループが始まる前に新しい ` Clock +`を作成します。

106 # Setup the clock for a decent framerate
107 clock = pygame.time.Clock()

2番目は、プログラムがフレームの終わりに到達したことを `+ pygame `に通知するために ` .tick()+`を呼び出します。

188 # Flip everything to the display
189 pygame.display.flip()
190
191 # Ensure program maintains a rate of 30 frames per second
192 clock.tick(30)

`+ .tick()`に渡される引数は、目的のフレームレートを確立します。 これを行うために、 ` .tick()`は、目的のフレームレートに基づいて、各フレームにかかるミリ秒数を計算します。 次に、その数値を、最後に ` .tick()`が呼び出されてから経過したミリ秒数と比較します。 十分な時間が経過していない場合、 ` .tick()+`は指定されたフレームレートを決して超えないように処理を遅延させます。

フレームレートを小さくすると、各フレームの計算時間が長くなりますが、フレームレートを大きくすると、よりスムーズな(そしておそらくより高速な)ゲームプレイが提供されます。

この番号をいじって、自分にとって何が一番いいかを確かめてください!

音響効果

これまで、ゲームプレイとゲームの視覚的側面に焦点を当ててきました。 それでは、ゲームに聴覚的なフレーバーを与えることも検討してみましょう。 + pygame +`は、すべてのサウンド関連のアクティビティを処理するhttps://www.pygame.org/docs/ref/mixer.html [+ mixer +`]を提供します。 このモジュールのクラスとメソッドを使用して、さまざまなアクションのバックグラウンドミュージックとサウンドエフェクトを提供します。

「+ mixer 」という名前は、モジュールがさまざまなサウンドをまとまりのある全体にミックスするという事実を指します。 https://www.pygame.org/docs/ref/music.html [` music `]サブモジュールを使用すると、https://en.wikipediaなどのさまざまな形式の個々のサウンドファイルをストリーミングできます。 .org/wiki/MP3 [MP3]、https://en.wikipedia.org/wiki/Ogg [Ogg]、およびhttps://en.wikipedia.org/wiki/Module_file[Mod]。 ` Sound `を使用して、Oggまたはhttps://en.wikipedia.org/wiki/WAV[uncompressed WAV]形式のいずれかで、再生する単一のサウンド効果を保持することもできます。 すべての再生はバックグラウンドで行われるため、 ` Sound +`を再生すると、サウンドが再生されるとすぐにメソッドが戻ります。

注意: https://www.pygame.org/docs/ref/music.html [+ pygame + documentation]には、MP3のサポートが制限されており、サポートされていない形式はシステムクラッシュを引き起こす可能性があると記載されています。 この記事で参照されているサウンドはテスト済みです。ゲームをリリースする前にサウンドを徹底的にテストすることをお勧めします。

ほとんどの「+ pygame 」と同様に、「 mixer 」の使用は初期化ステップから始まります。 幸いなことに、これはすでに ` pygame.init()`によって処理されています。 デフォルトを変更したい場合にのみ ` pygame.mixer.init()+`を呼び出す必要があります:

106 # Setup for sounds. Defaults are good.
107 pygame.mixer.init()
108
109 # Initialize pygame
110 pygame.init()
111
112 # Set up the clock for a decent framerate
113 clock = pygame.time.Clock()

`+ pygame.mixer.init()`はhttps://www.pygame.org/docs/ref/mixer.html#pygame.mixer.init [引数の数]を受け入れますが、ほとんどの場合デフォルトで問題ありません。 デフォルトを変更したい場合、 ` pygame.init()`を呼び出す前に ` pygame.mixer.init()+`を呼び出す必要があることに注意してください。 そうしないと、変更に関係なくデフォルトが有効になります。

システムの初期化後、サウンドとバックグラウンドミュージックのセットアップを取得できます。

135 # Load and play background music
136 # Sound source: http://ccmixter.org/files/Apoxode/59262
137 # License: https://creativecommons.org/licenses/by/3.0/
138 pygame.mixer.music.load("Apoxode_-_Electric_1.mp3")
139 pygame.mixer.music.play(loops=-1)
140
141 # Load all sound files
142 # Sound sources: Jon Fincher
143 move_up_sound = pygame.mixer.Sound("Rising_putter.ogg")
144 move_down_sound = pygame.mixer.Sound("Falling_putter.ogg")
145 collision_sound = pygame.mixer.Sound("Collision.ogg")

*行138および139 *は、バックグラウンドサウンドクリップをロードし、再生を開始します。 名前付きパラメーター `+ loops = -1 +`を設定することで、サウンドクリップがループし、終了しないように指示できます。

  • 143〜145行目では、さまざまな効果音に使用する3つのサウンドを読み込みます。 最初の2つは、プレイヤーが上下に移動するときに再生される上昇音と下降音です。 最後は、衝突が発生するたびに使用される音です。 `+ Enemy +`が作成されたときのサウンドや、ゲームが終了したときの最終的なサウンドなど、他のサウンドも追加できます。

それでは、音響効果をどのように使用しますか? 特定のイベントが発生したときに各サウンドを再生します。 たとえば、船が上に移動するとき、 `+ move_up_sound `を再生します。 したがって、そのイベントを処理するたびに、 ` .play()`への呼び出しを追加します。 設計では、それは次の呼び出しを ` Player `の ` .update()+`に追加することを意味します:

26 # Define the Player object by extending pygame.sprite.Sprite
27 # Instead of a surface, use an image for a better-looking sprite
28 class Player(pygame.sprite.Sprite):
29     def __init__(self):
30         super(Player, self).__init__()
31         self.surf = pygame.image.load("jet.png").convert()
32         self.surf.set_colorkey((255, 255, 255), RLEACCEL)
33         self.rect = self.surf.get_rect()
34
35     # Move the sprite based on keypresses
36     def update(self, pressed_keys):
37         if pressed_keys[K_UP]:
38             self.rect.move_ip(0, -5)
39             move_up_sound.play()
40         if pressed_keys[K_DOWN]:
41             self.rect.move_ip(0, 5)
42             move_down_sound.play()

プレーヤーと敵の衝突の場合、衝突が検出されたときの音を再生します。

201 # Check if any enemies have collided with the player
202 if pygame.sprite.spritecollideany(player, enemies):
203     # If so, then remove the player
204     player.kill()
205
206     # Stop any moving sounds and play the collision sound
207     move_up_sound.stop()
208     move_down_sound.stop()
209     collision_sound.play()
210
211     # Stop the loop
212     running = False

ここでは、衝突時にプレイヤーが動いていないため、他の効果音を最初に停止します。 次に、衝突音を再生し、そこから実行を続けます。

最後に、ゲームが終了すると、すべてのサウンドが停止します。 これは、衝突によりゲームが終了した場合でも、ユーザーが手動で終了した場合でも同様です。 これを行うには、ループの後、プログラムの最後に次の行を追加します。

220 # All done! Stop and quit the mixer.
221 pygame.mixer.music.stop()
222 pygame.mixer.quit()

技術的には、プログラムはこの直後に終了するため、これらの最後の数行は必要ありません。 ただし、後でゲームにイントロ画面または終了画面を追加することにした場合、ゲームの終了後に実行されるコードが増える可能性があります。

それでおしまい! もう一度テストすると、次のようなものが表示されるはずです。

Pygame window、width = 791、height = 612

ソースに関する注意

バックグラウンドミュージックが読み込まれたときに、136〜137行目にコメントがあり、音楽のソースとCreative Commonsライセンスへのリンクがリストされていることに気付いたかもしれません。 これは、そのサウンドの作成者がそれを必要としたために行われました。 ライセンス要件には、サウンドを使用するために、適切な帰属とライセンスへのリンクの両方を提供する必要があると記載されていました。

役立つコンテンツを検索できる音楽、サウンド、アートのソースは次のとおりです。

ゲームを作成し、アート、音楽、または他のソースからのコードなどのダウンロードしたコンテンツを使用するときは、それらのソースのライセンス条項を遵守していることを確認してください。

結論

このチュートリアルを通して、「+ pygame +」を使用したゲームプログラミングが標準の手続き型プログラミングとどのように異なるかを学びました。 また、次の方法も学びました。

  • イベントループを実装する

  • 画面にアイテムを描く

  • 効果音と音楽を再生する

  • ユーザー入力を処理する

これを行うために、 + display ++ mixer +、および + music ++ time ++ image ++ event +、および を含む + pygame + `モジュールのサブセットを使用しました+ key + `モジュール。 また、「+ Rect 」、「 Surface 」、「 Sound 」、「 Sprite 」など、いくつかの「 pygame 」クラスを使用しました。 しかし、これらは ` pygame `ができることの表面をひっかくだけです! 利用可能なモジュールとクラスの完全なリストについては、https://www.pygame.org/docs/[公式の ` pygame +`ドキュメント]をご覧ください。

以下のリンクをクリックすると、この記事のすべてのコード、グラフィック、およびサウンドファイルを見つけることができます。

クローンリポジトリ: 使用するリポジトリをクローンするにはここをクリックして、このチュートリアルでPyGameを使用する方法を学習します。

以下にもコメントを残してください。 ハッピーパイソン!