Python の「スネーク」ゲーム、継続的な改善で pygame プログラミングを学ぶ

1ae28d15992f405bb7fd51daa60c3dc2.gif

目次

序文

改善プロセス1

プロンプト情報を追加する

オリジナルのヘルプの概要

pygame.draw

pygame.font

クラス Rect

クラス サーフェス

改善プロセス2

表示スコアを上げる

改善プロセス3

バックグラウンドミュージックを追加する

プロンプト効果音を追加する

音楽スイッチ

ミュートスイッチ

ミキサー.ミュージック.プレイノート

オリジナルのヘルプの概要

pygame.mixer

pygame.mixer.Sound

まとめ

pygameプログラミングフレームワーク


序文

前回のブログ投稿で、Xunfei Spark からゲーム「Snake」のコードを入手しました。実行時のエフェクトは上記のとおりです。コードは不完全で、ヘビが死ぬとゲームが終了し、ゲームを複数回プレイすることはできません。

前のリンク:

Xunfei Xinghuo、Wenxin Yiyan、Tongyi Qianwen が同時に「Snake Snake」ゲームを編集しました。誰が勝ちますか?

コードは以下のように表示されます:

import pygame
import sys
import random
 
# 定义颜色
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED  = (255, 0, 0)
GREY = (211, 211, 211)  # 淡灰色
 
def init():
    global screen, screen_size
    global snake_pos, food_pos, snake_speed
 
    # 初始化pygame
    pygame.init()
 
    # 设置屏幕大小
    screen_size = (640, 480)
    screen = pygame.display.set_mode(screen_size)
 
    # 设置游戏标题
    pygame.display.set_caption("贪吃蛇")
 
    # 蛇的初始位置
    snake_pos = [[100, 100], [80, 100], [60, 100]]
 
    # 食物的初始位置
    food_pos = [300, 300]
 
    # 蛇的初始速度
    snake_speed = [20, 0]
 
def repaint():
    # 绘制游戏界面
    screen.fill(WHITE)
 
    # 定义线段的端点坐标
    x,y = (-1,640,640,-1)*16, []
    for i in range(36):
        for _ in range(2):
            y.append(19+i*20)
 
    # 使用pygame.draw.lines()函数绘制线段
    points = list(zip(x,y))
    pygame.draw.lines(screen, GREY, False, points, 1) # 线宽为1
    points = list(zip(y,x))
    pygame.draw.lines(screen, GREY, False, points, 1)   
 
    # 重画蛇和食物
    for pos in snake_pos:
        pygame.draw.rect(screen, GREEN, pygame.Rect(pos[0], pos[1], 20, 20))
    pygame.draw.rect(screen, RED, pygame.Rect(food_pos[0], food_pos[1], 20, 20))
    pygame.display.flip()
 
def game_quit():
    pygame.quit()
    sys.exit()
 
def main():
    global screen, screen_size
    global snake_pos, food_pos, snake_speed
    
    # 主循环
    while True:
        # 处理游戏事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_quit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_UP:
                    snake_speed = [0, -20]
                elif event.key == pygame.K_DOWN:
                    snake_speed = [0, 20]
                elif event.key == pygame.K_LEFT:
                    snake_speed = [-20, 0]
                elif event.key == pygame.K_RIGHT:
                    snake_speed = [20, 0]
 
        # 更新蛇的位置
        snake_pos.insert(0, [snake_pos[0][0] + snake_speed[0], snake_pos[0][1] + snake_speed[1]])
 
        # 检查蛇头是否碰到食物
        if snake_pos[0] == food_pos:
            food_pos = [random.randrange(1, screen_size[0] // 20) * 20, random.randrange(1, screen_size[1] // 20) * 20]
        else:
            snake_pos.pop()
 
        # 检查蛇头是否碰到墙壁或者蛇身
        if snake_pos[0][0] < 0 or snake_pos[0][0] >= screen_size[0] or snake_pos[0][1] < 0 or snake_pos[0][1] >= screen_size[1] or snake_pos[0] in snake_pos[1:]:
            game_quit()
            '''
            此处可增加与用户的交互,如:
            if askyesno('title','again?'):
                init() # Yes to Play again
            else:
                game_quit() # No to Exit
            '''
 
        # 重画界面及蛇和食物
        repaint()
 
        # 控制游戏速度
        pygame.time.Clock().tick(10)
 
if __name__ == "__main__":
 
    init()
    main()
 

改善プロセス1

プロンプト情報を追加する

私はこれまで pygame プログラミングを注意深く勉強したことがなかったので、継続的な改善を通じて pygame プログラミングを練習し学習するためにこのルーチンを実行しました。

元のコードを実行するとすぐにヘビが泳ぎ始めるので、ここから始めましょう。開始する前に、「Press any key to start...」と表示されるプロンプトを追加します。

ヘビが死んだ後、リマインダーは戻ってきますか? もう一度プレイする場合は「はい」、ゲームを終了する場合は「いいえ」です。

同時に、ESC キーを押してゲーム内でゲームを一時停止し、もう一度押して続行することができます。

pygameには情報ウィンドウをポップアップさせる方法(機能)がないので、DOS時代の情報ウィンドウを表示する方法を使い、複数の長方形のウィンドウを描画してウィンドウをシミュレートし、最後に長方形のボックスにプロンプ​​トテキストを書きました。コードは以下のように表示されます:

def show_msg(msg, color = BLUE):
    x = screen.get_rect().centerx
    y = screen.get_rect().centery - 50
    font = pygame.font.Font(None, 36)
    text = font.render(msg, True、色)
    text_rect = text.get_rect()
    text_rect.centerx = x
    text_rect.centery = y
    Rectangle1 = pygame.Rect(x-155, y-25, 320, 60)
    Rectangle2 = pygame.Rect(x-160, y -30, 320, 60)
    pygame.draw.rect(screen, DARK, Rectangle1)
    pygame.draw.rect(screen, GREY, Rectangle2)
    pygame.draw.rect(screen, BLACK, Rectangle2, 2) # 边框宽以下2
    screen.blit(text, text_rect)
    pygame.display.flip()


オリジナルのヘルプの概要

pygame.draw

名前
    pygame.draw - 図形を描画するための pygame モジュール

関数
    aaline(...)
        aaline(surface, color, start_pos, end_pos) -> Rect
        aaline(surface, color, start_pos, end_pos, Blend=1) -> Rect
        アンチエイリアスをかけた直線を描画
    
    aalines(...)
        aalines( surface、color、closed、points) -> Rect
        aalines(surface、color、closed、points、blend=1) -> Rect
        複数の連続するアンチエイリアス処理された直線セグメントを描画します
    
    arc(...)
        arc(surface, color, rect, start_angle , stop_angle) -> Rect
        arc(surface, color, Rect, start_angle, stop_angle, width=1) -> Rect
        楕円弧を描きます
    
    Circle(...)
        Circle(surface, color, center, radius) -> Rect
        Circle(surface, color, center, radius, width=0,draw_top_right=None,draw_top_left=None,draw_bottom_left=None,draw_bottom_right=None) -> Rect 円を描く ellipse(...) ellipse(surface, color
        ,
    
    Rect
        ) -> Rect
        ellipse(surface, color, rect, width=0) -> Rect
        楕円
    
    線を描画します(...)
        line(surface, color, start_pos, end_pos) -> Rect
        line(surface, color, start_pos, end_pos, width=1) -> Rect
        直線を描画
    
    Lines(...)
        Lines(surface, color, Closed, Points) -> Rect
        Lines(surface, Color, Closed, Points, width=1) -> Rect
        複数の連続した直線を描画直線セグメント
    
    Polygon(...)
        Polygon(面, カラー, ポイント) -> Rect
        ポリゴン(面, カラー, ポイント, width=0) -> Rect 多
        角形を描画します
    
    ect(...)
        Rect(面, カラー, 長方形) - > Rect
        rect(surface, color, rect, width=0, border_radius=0, border_top_left_radius=-1, border_top_right_radius=-1, border_bottom_left_radius=-1, border_bottom_right_radius=-1) -> Rect 長方形を描画し
        ます


pygame.font

名前
    pygame.font - フォントのロードとレンダリングのための pygame モジュール

クラス
    buildins.object
        フォント
    
    クラス Font(builtins.object)
     |  フォント(ファイルパス=なし、サイズ=12) -> フォント
     | フォント(ファイルパス, サイズ) -> フォント
     | フォント(pathlib.Path, サイズ) -> フォント
     | フォント(オブジェクト、サイズ) -> フォント
     | ファイルから新しい Font オブジェクトを作成する
     |  
     | ここで定義されているメソッド:
     |  
     | __init__(self, /, *args, **kwargs)
     | 自分自身を初期化します。正確な署名については、help(type(self)) を参照してください。
     |  
     | get_ascent(...)
     | get_ascent() -> int
     | フォントのアセントを取得する
     |  
     | get_bold(...)
     | get_bold() -> ブール
     | テキストが太字で表示されるかどうかを確認します
     。  
     | get_descent(...)
     | get_descent() -> int
     | フォントのディセントを取得する
     |  
     | get_height(...)
     | get_height() -> int
     | フォントの高さを取得する
     |  
     | get_italic(...)
     | get_italic() -> ブール値
     | テキストが斜体で表示されるかどうかを確認します
     。  
     | get_linesize(...)
     | get_linesize() -> int
     | フォントテキストの行間を取得する
     |  
     | get_取り消し線(...)
     | get_strikethrough() -> ブール
     | テキストが取り消し線付きで表示されるかどうかを確認します
     。  
     | get_underline(...)
     | get_underline() -> ブール
     | テキストが下線付きで表示されるかどうかを確認します
     。  
     | メトリクス(...)
     | メトリクス(テキスト) -> リスト
     | 渡された文字列内の各文字のメトリックを取得します
     。  
     |  レンダリング(...)
     | render(テキスト、アンチエイリアス、カラー、背景=なし) -> サーフェス
     | 新しい表面にテキストを描画する
     |  
     | set_bold(...)
     | set_bold(bool) -> なし
     | 太字テキストの偽のレンダリングを有効にする
     |  
     | set_italic(...)
     | set_italic(bool) -> なし
     | イタリック体のテキストの偽のレンダリングを有効にする
     |  
     | set_script(...)
     | set_script(str) -> なし
     | テキスト整形用のスクリプトコードを設定する
     |  
     | set_strikethrough(...)
     | set_strikethrough(bool) -> なし
     | テキストに取り消し線を付けてレンダリングするかどうかを制御します
     。  
     | set_underline(...)
     | set_underline(bool) -> なし
     | テキストに下線を付けるかどうかを制御します
     。  
     | サイズ(...)
     | サイズ(テキスト) -> (幅、高さ)
     | テキストをレンダリングするために必要なスペースの量を決定する
     |  
     | -------------------------------------------------- --------------------
     | ここで定義されるデータ記述子:
     |  
     | 太字
     | 太字 -> ブール値
     | フォントを(偽の)太字で表示するかどうかを取得または設定します。
     |  
     | 斜体
     | イタリック体 -> ブール文字
     | フォントを(偽の)斜体でレンダリングするかどうかを取得または設定します。
     |  
     | 取り消し線
     | 取り消し線 -> ブール値
     | フォントに取り消し線を付けてレンダリングするかどうかを取得または設定します。
     |  
     | 下線
     | 下線 -> ブール値
     | フォントに下線を付けて表示するかどうかを取得または設定します。


クラス Rect

モジュール pygame.rect のクラス Rect に関するヘルプ:

クラス Rect(builtins.object)
 |  Rect(左、上、幅、高さ) -> Rect
 | Rect((左, 上), (幅, 高さ)) -> 長方形
 | Rect(オブジェクト) -> Rect
 | 直交座標を保存するための pygame オブジェクト
 |  
 | ここで定義されているメソッド:
 |  
 | クランプ(...)
 | クランプ(長方形) -> 長方形
 | 四角形を別の四角形の中に移動します
 |  
 | クランプ IP(...)
 | クランプ_ip(Rect) -> なし
 |
 |  四角形を別の四角形の内側に移動します。
 | クリップ(...)
 | クリップ(長方形) -> 長方形
 | 四角形の内側を別の四角形でトリミングします
 。  
 | クリップライン(...)
 | クリップライン(x1, y1, x2, y2) -> ((cx1, cy1), (cx2, cy2))
 | クリップライン(x1, y1, x2, y2) -> ()
 | クリップライン((x1, y1), (x2, y2)) -> ((cx1, cy1), (cx2, cy2))
 | クリップライン((x1, y1), (x2, y2)) -> ()
 | クリップライン((x1, y1, x2, y2)) -> ((cx1, cy1), (cx2, cy2))
 | クリップライン((x1, y1, x2, y2)) -> ()
 | クリップライン(((x1, y1), (x2, y2))) -> ((cx1, cy1), (cx2, cy2))
 | クリップライン(((x1, y1), (x2, y2))) -> ()
 | 長方形内の線をトリミングします
 。  
 | 衝突(...)
 | collidedict(dict) -> (キー, 値)
 | collidedict(dict) -> なし
 | collidedict(dict, use_values=0) -> (キー, 値)
 | collidedict(dict, use_values=0) -> なし
 | 辞書内の 1 つの四角形が交差するかどうかをテストします
 。  
 | collidedictall(...)
 | collidedictall(dict) -> [(キー, 値), ...]
 | collidedictall(dict, use_values=0) -> [(キー, 値), ...]
 | 辞書内のすべての四角形が交差するかどうかをテストします
 。  
 | 衝突リスト(...)
 | 衝突リスト(リスト) -> インデックス
 | リスト内の 1 つの四角形が交差するかどうかをテストする
 |  
 | collidelistall(...)
 | collidelistall(リスト) -> インデックス
 | リスト内のすべての四角形が交差するかどうかをテストします
 。  
 | 衝突オブジェクト(...)
 | collideobjects(rect_list) -> オブジェクト
 | collideobjects(obj_list, key=func) -> オブジェクト
 | リスト内のオブジェクトが交差するかどうかをテストする
 |  
 | collideobjectsall(...)
 | collideobjectsall(rect_list) -> オブジェクト
 | collideobjectsall(obj_list, key=func) -> オブジェクト
 | リスト内のすべてのオブジェクトが交差するかどうかをテストします
 。  
 | 衝突点(...)
 | 衝突点(x, y) -> ブール値
 | 衝突点((x,y)) -> ブール値
 | 点が四角形の内側にあるかどうかをテストする
 |  
 | 衝突(...)
 | colliderect(Rect) -> bool
 | 2 つの長方形が重なっているかどうかをテストする
 |  
 | を含む(...)
 | contains(Rect) -> ブール値
 | ある四角形が別の四角形の中にあるかどうかをテストする
 |  
 | コピー(...)
 | copy() -> Rect
 | 四角形をコピーする
 |  
 | フィット(...)
 | fit(Rect) -> Rect
 | アスペクト比を指定して長方形をサイズ変更および移動する
 |  
 | インフレート(...)
 | inflate(x, y) -> Rect
 | 長方形のサイズを拡大または縮小する
 |  
 | inflate_ip(...)
 | inflate_ip(x, y) -> なし
 | 四角形のサイズをその場で拡大または縮小する
 |  
 | 移動(...)
 | move(x, y) -> 長方形
 | 四角形を移動します
 |  
 | move_ip(...)
 | move_ip(x, y) -> なし
 |
 |  四角形をその場に移動します。
 | 正規化(...)
 | Normalize() -> なし
 | 正しい負のサイズ
 |  
 | スケールバイ(...)
 | scale_by(スカラー) -> Rect
 | scale_by(scalex,scaley) -> Rect
 | 指定された乗数で四角形をスケールします
 |  
 | スケールによるip(...)
 | scale_by_ip(スカラー) -> なし
 | scale_by_ip(scalex、scaley) -> なし
 | 四角形のサイズをその場で拡大または縮小する
 |  
 | ユニオン(...)
 | Union(Rect) -> Rect
 | 2 つの長方形を 1 つに結合します
 |  
 | Union_ip(...)
 | Union_ip(Rect) -> なし
 | 2 つの長方形をその場で 1 つに結合します
 |  
 | ユニオンオール(...)
 | Unionall(Rect_sequence) -> Rect
 | 多くの長方形の結合
 |  
 | ユニオンオール IP(...)
 | Unionall_ip(Rect_sequence) -> なし
 | 多くの四角形を所定の位置で結合
 |  
 | 更新(...)
 | update(左、上、幅、高さ) -> なし
 | update((左, 上), (幅, 高さ)) -> なし
 | update(オブジェクト) -> なし
 | 長方形の位置とサイズを設定します


クラス サーフェス

クラス Surface(builtins.object)
 | 表面((幅、高さ)、フラグ=0、深さ=0、マスク=なし) -> 表面
 | 表面((幅, 高さ), flags=0, 表面) -> 表面
 | 画像を表現するための pygame オブジェクト
 |  
 | ここで定義されているメソッド:
 |  
 |  ブリット(...)
 | blit(source, dest, area=None,special_flags=0) -> Rect
 | ある画像を別の画像の上に描画する
 |  
 | ブリッツ(...)
 | blits(blit_sequence=((source, dest), ...), doreturn=1) -> [Rect, ...] または None
 | blits(((ソース、宛先、エリア), ...)) -> [Rect, ...]
 | blits((((ソース、宛先、エリア、special_flags), ...)) -> [Rect, ...]
 | たくさんの画像を別の画像に描画する
 |  
 | 変換(...)
 | 変換(表面=なし) -> 表面
 | 変換(深さ, フラグ=0) -> 表面
 | 変換(マスク、フラグ=0) -> 表面
 | 画像のピクセル形式を変更する
 |  
 | 変換アルファ(...)
 | Convert_alpha(表面) -> 表面
 | Convert_alpha() -> 表面
 | ピクセルごとのアルファを含む画像のピクセル形式を変更する
 |  
 | コピー(...)
 | copy() -> サーフェス
 | Surface の新しいコピーを作成する
 |  
 | 塗りつぶし(...)
 | fill(color,rect=None,special_flags=0) -> Rect
 | 表面を単色で塗りつぶす
 |  
 | get_abs_offset(...)
 | get_abs_offset() -> (x, y)
 | 最上位の親内の子のサブサーフェスの絶対位置を見つけます
 。  
 | get_abs_parent(...)
 | get_abs_parent() -> 表面
 | サブサーフェスの最上位の親を見つける
 |  
 | get_alpha(...)
 | get_alpha() -> int_value
 | 現在のサーフェスの透明度の値を取得する
 |  
 | get_at(...)
 | get_at((x, y)) -> 色
 | 単一ピクセルの色の値を取得する
 |  
 | get_at_mapped(...)
 | get_at_mapped((x, y)) -> 色
 | 単一ピクセルでマップされたカラー値を取得する
 |  
 | get_bitsize(...)
 | get_bitsize() -> int
 | Surface ピクセル フォーマットのビット深度を取得する
 |  
 | get_blendmode(...)
 | サーフェスの SDL 2 ブレンド モードを返す
 |  
 | get_bounding_rect(...)
 | get_bounding_rect(min_alpha = 1) -> Rect
 | データを含む最小の四角形を見つける
 |  
 | get_buffer(...)
 | get_buffer() -> BufferProxy
 | Surface のピクセルのバッファ オブジェクトを取得します。
 |  
 | get_bytesize(...)
 | get_bytesize() -> int
 | Surface ピクセルごとに使用されるバイトを取得します
 。  
 | get_clip(...)
 | get_clip() -> Rect
 | サーフェスの現在のクリッピング領域を取得する
 |  
 | get_colorkey(...)
 | get_colorkey() -> RGB またはなし
 | 現在の透明なカラーキーを取得します
 。  
 | get_flags(...)
 | get_flags() -> int
 | Surface に使用される追加のフラグを取得します
 。  
 | get_height(...)
 | get_height() -> 高さ
 | 表面の高さを取得する
 |  
 | get_locked(...)
 | get_locked() -> ブール
 | Surface が現在ロックされているかどうかをテストします
 。  
 | get_locks(...)
 | get_locks() -> タプル
 | Surface のロックを取得します
 |  
 | get_losses(...)
 | get_losses() -> (R、G、B、A)
 | 色とマップされた整数の間の変換に使用される有効ビット
 |  
 | get_masks(...)
 | get_masks() -> (R、G、B、A)
 | 色とマップされた整数の間の変換に必要なビットマスク
 |  
 | get_offset(...)
 | get_offset() -> (x, y)
 | 親の内部の子のサブサーフェスの位置を見つける
 |  
 | get_palette(...)
 | get_palette() -> [RGB, RGB, RGB, ...]
 | 8 ビット Surface のカラー インデックス パレットを取得する
 |  
 | get_palette_at(...)
 | get_palette_at(インデックス) -> RGB
 | パレット内の 1 つのエントリの色を取得する
 |  
 | get_parent(...)
 | get_parent() -> サーフェス
 | サブサーフェスの親を見つける
 |  
 | get_pitch(...)
 | get_pitch() -> int
 | Surface 行ごとに使用されるバイト数を取得する
 |  
 |  get_rect(...)
 | get_rect(**kwargs) -> Rect
 | 表面の長方形の領域を取得する
 |  
 | get_shifts(...)
 | get_shifts() -> (R、G、B、A)
 | 色とマップされた整数の間の変換に必要なビット シフト
 |  
 | get_size(...)
 | get_size() -> (幅、高さ)
 | 表面の寸法を取得する
 |  
 | get_view(...)
 | get_view(<kind>='2') -> BufferProxy
 | Surface のピクセルのバッファー ビューを返します。
 |  
 | get_width(...)
 | get_width() -> 幅
 | サーフェスの幅を取得する
 |  
 | ロック(...)
 | lock() -> なし
 | ピクセル アクセスのために Surface メモリをロックする
 |  
 | マップ_rgb(...)
 | マップ_rgb(カラー) -> マップされた_int
 | 色をマッピングされた色の値に変換する
 |  
 | マストロック(...)
 | マストロック() -> ブール
 | Surface にロックが必要かどうかをテストする
 |  
 | premul_alpha(...)
 | premul_alpha() -> 表面
 | アルファ チャネルが事前に乗算された RGB チャネルを含むサーフェスのコピーを返します。
 |  
 | スクロール(...)
 | スクロール(dx=0, dy=0) -> なし
 | 表面画像をその場で移動
 |  
 | set_alpha(...)
 | set_alpha(値, flags=0) -> なし
 | set_alpha(なし) -> なし
 | 完全な Surface 画像のアルファ値を設定します
 。  
 | set_at(...)
 | set_at((x, y), カラー) -> なし
 | 単一ピクセルの色の値を設定する
 |  
 | set_clip(...)
 | set_clip(rect) -> なし
 | set_clip(なし) -> なし
 | サーフェスの現在のクリッピング領域を設定します
 |  
 | set_colorkey(...)
 | set_colorkey(色, flags=0) -> なし
 | set_colorkey(なし) -> なし
 | 透明なカラーキーを設定する
 |  
 | set_masks(...)
 | set_masks((r,g,b,a)) -> なし
 | 色とマップされた整数の間の変換に必要なビットマスクを設定します
 。  
 | set_palette(...)
 | set_palette([RGB, RGB, RGB, ...]) -> なし
 | 8 ビット Surface のカラー パレットを設定する
 |  
 | set_palette_at(...)
 | set_palette_at(index, RGB) -> なし
 | 8 ビット Surface パレットの単一インデックスの色を設定する
 |  
 | set_shifts(...)
 | set_shifts((r,g,b,a)) -> なし
 | 色とマップされた整数の間の変換に必要なビット シフトを設定します
 。  
 | 表面下(...)
 | サブサーフェス(Rect) -> サーフェス
 | 親を参照する新しいサーフェスを作成する
 |  
 | ロック解除(...)
 | ロック解除() -> なし
 | Surface メモリのピクセル アクセスのロックを解除する
 |  
 | unmap_rgb(...)
 | unmap_rgb(mapped_int) -> カラー
 | マップされた整数のカラー値を Color に変換します。


さらに、3 つの状態変数が追加され、初期状態は次のようになります。

    is_running = False
    is_paused = False
    is_dead = False

4 ボタンの識別を追加しました。

is_dead時、ゲームを再起動するか終了するかを判断する

pygame.K_y: 文字 Y/y
pygame.K_n: 文字 N/n

一時停止して再開する

pygame.K_ESCAPE: Escキー
pygame.K_SPACE: スペースバー

完全なコードは次のとおりです。

import pygame
import sys
import random

# 定义颜色
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED   = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE  = (0, 0, 255)
GREY  = (220, 220, 220)  # 淡灰色
DARK  = (120, 120, 120)  # 深灰色

def init():
    global screen, screen_size
    global snake_pos, food_pos, snake_speed

    # 初始化pygame
    pygame.init()

    # 设置屏幕大小
    screen_size = (640, 480)
    screen = pygame.display.set_mode(screen_size)

    # 设置游戏标题
    pygame.display.set_caption("贪吃蛇")

    # 蛇的初始位置
    snake_pos = [[100, 100], [80, 100], [60, 100]]

    # 食物的初始位置
    food_pos = [300, 300]

    # 蛇的初始速度
    snake_speed = [20, 0]


def show_msg(msg, color = BLUE):
    x = screen.get_rect().centerx
    y = screen.get_rect().centery - 50
    font = pygame.font.Font(None, 36)
    text = font.render(msg, True, color)
    text_rect = text.get_rect()
    text_rect.centerx = x
    text_rect.centery = y
    rectangle1 = pygame.Rect(x-155, y-25, 320, 60)
    rectangle2 = pygame.Rect(x-160, y-30, 320, 60)
    pygame.draw.rect(screen, DARK, rectangle1)
    pygame.draw.rect(screen, GREY, rectangle2)
    pygame.draw.rect(screen, BLACK, rectangle2, 2) # 边框宽为2
    screen.blit(text, text_rect)
    pygame.display.flip()

def repaint():
    # 绘制游戏界面
    screen.fill(WHITE)

    # 定义线段的端点坐标
    x,y = (-1,640,640,-1)*16, []
    for i in range(36):
        for _ in range(2):
            y.append(19+i*20)

    # 使用pygame.draw.lines()函数绘制线段
    points = list(zip(x,y))
    pygame.draw.lines(screen, GREY, False, points, 1) # 线宽为1
    points = list(zip(y,x))
    pygame.draw.lines(screen, GREY, False, points, 1)   

    # 重画蛇和食物
    for pos in snake_pos:
        pygame.draw.rect(screen, GREEN, pygame.Rect(pos[0], pos[1], 20, 20))
    pygame.draw.rect(screen, RED, pygame.Rect(food_pos[0], food_pos[1], 20, 20))
    pygame.display.flip()

def game_quit():
    pygame.quit()
    sys.exit()

def main():
    global screen, screen_size
    global snake_pos, food_pos, snake_speed

    is_running = False
    is_paused = False
    is_dead = False

    repaint()
    show_msg("Press any key to start ...")

    # 主循环
    while True:
        # 处理游戏事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_quit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_UP:
                    snake_speed = [0, -20]
                elif event.key == pygame.K_DOWN:
                    snake_speed = [0, 20]
                elif event.key == pygame.K_LEFT:
                    snake_speed = [-20, 0]
                elif event.key == pygame.K_RIGHT:
                    snake_speed = [20, 0]
                elif event.key == pygame.K_y:
                    if is_dead:
                        init()
                        is_dead = False
                    is_running = True
                elif event.key == pygame.K_n:
                    if is_dead: game_quit()
                    else: is_running = True
                elif event.key == pygame.K_ESCAPE:
                    if is_running:
                        show_msg(">>> Paused <<<")
                        is_paused = not is_paused
                else: # 任意键进入开始状态
                    is_running = True

        if not is_running: continue
        if is_paused and is_running: continue

        # 更新蛇的位置
        snake_pos.insert(0, [snake_pos[0][0] + snake_speed[0], snake_pos[0][1] + snake_speed[1]])

        # 检查蛇头是否碰到食物
        if snake_pos[0] == food_pos:
            food_pos = [random.randrange(1, screen_size[0] // 20) * 20, random.randrange(1, screen_size[1] // 20) * 20]
        else:
            snake_pos.pop()

        # 检查蛇头是否碰到墙壁或者蛇身
        if snake_pos[0][0] < 0 or snake_pos[0][0] >= screen_size[0] or snake_pos[0][1] < 0 or snake_pos[0][1] >= screen_size[1] or snake_pos[0] in snake_pos[1:]:
            show_msg("Dead! Again? (Y or N)")
            is_running = False
            is_dead = True
            continue

        # 重画界面及蛇和食物
        repaint()

        # 控制游戏速度
        pygame.time.Clock().tick(10)

if __name__ == "__main__":

    init()
    main()

改善プロセス2

表示スコアを上げる

餌を一個食べるごとに+10点、蛇の長さと餌が境界線に近いほど加点される、ちなみに蛇の座標位置が表示される。

関数 show_msg_at() は、show_msg と比較して x 座標と y 座標を増加させ、指定された位置に情報を表示します。

def show_msg_at( x, y, msg):
    フォント = pygame。font.SysFont ('Consolas', 14) # 系统字库を使用します
    text = font.render(msg, True, BLACK)
    text_rect = text.get_rect()
   text_rect.x, text_rect.y = x, y
    screen.blit(text, text_rect)
    pygame.display.flip()

また、新たなグローバル変数スコアが追加され、餌に遭遇すると10点が加算され、ヘビの長さが5を超え、餌と境界線の距離が3未満の場合はさらに加点されます。たとえば次のように自分で設定できます。

        ifsnake_pos[0] == food_pos:
           スコア += 10 + len(snake_pos) // 5
            そうでない場合 1 <snake_pos[0][0]//20 < 30 または 1<snake_pos[0][1]// 20 < 22:
                スコア += 5
 

完全なコードは次のとおりです。 

import pygame
import sys
import random

# 定义颜色
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED   = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE  = (0, 0, 255)
GREY  = (220, 220, 220)  # 淡灰色
DARK  = (120, 120, 120)  # 深灰色

def init():
    global screen, screen_size, scores
    global snake_pos, food_pos, snake_speed

    # 初始化pygame
    scores = 0
    pygame.init()

    # 设置屏幕大小
    screen_size = (640, 480)
    screen = pygame.display.set_mode(screen_size)

    # 设置游戏标题
    pygame.display.set_caption("贪吃蛇")

    # 蛇的初始位置
    snake_pos = [[100, 100], [80, 100], [60, 100]]

    # 食物的初始位置
    food_pos = [300, 300]

    # 蛇的初始速度
    snake_speed = [20, 0]


def show_msg(msg, color = BLUE):
    x = screen.get_rect().centerx
    y = screen.get_rect().centery - 50
    font = pygame.font.Font(None, 36)
    text = font.render(msg, True, color)
    text_rect = text.get_rect()
    text_rect.centerx = x
    text_rect.centery = y
    rectangle1 = pygame.Rect(x-155, y-25, 320, 60)
    rectangle2 = pygame.Rect(x-160, y-30, 320, 60)
    pygame.draw.rect(screen, DARK, rectangle1)
    pygame.draw.rect(screen, GREY, rectangle2)
    pygame.draw.rect(screen, BLACK, rectangle2, 2) # 边框宽为2
    screen.blit(text, text_rect)
    pygame.display.flip()

def repaint():
    # 绘制游戏界面
    screen.fill(WHITE)

    # 定义线段的端点坐标
    x,y = (-1,640,640,-1)*16, []
    for i in range(36):
        for _ in range(2):
            y.append(19+i*20)

    # 使用pygame.draw.lines()函数绘制线段
    points = list(zip(x,y))
    pygame.draw.lines(screen, GREY, False, points, 1) # 线宽为1
    points = list(zip(y,x))
    pygame.draw.lines(screen, GREY, False, points, 1)   

    # 重画蛇和食物
    for pos in snake_pos:
        pygame.draw.rect(screen, GREEN, pygame.Rect(pos[0], pos[1], 20, 20))
    pygame.draw.rect(screen, RED, pygame.Rect(food_pos[0], food_pos[1], 20, 20))
    pygame.display.flip()
    show_msg_at(22, 6, f'Scores: {scores}')
    show_msg_at(410, 6, f'Snake coordinate: ({1+snake_pos[0][0]//20:2}, {1+snake_pos[0][1]//20:2})')

def show_msg_at(x, y, msg):
    font = pygame.font.SysFont('Consolas', 14)
    text = font.render(msg, True, BLACK)
    text_rect = text.get_rect()
    text_rect.x, text_rect.y = x, y
    screen.blit(text, text_rect)
    pygame.display.flip()

def game_quit():
    pygame.quit()
    sys.exit()

def main():
    global screen, screen_size, scores
    global snake_pos, food_pos, snake_speed

    is_running = False
    is_paused = False
    is_dead = False

    repaint()
    show_msg("Press any key to start ...")

    # 主循环
    while True:
        # 处理游戏事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_quit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_UP:
                    snake_speed = [0, -20]
                elif event.key == pygame.K_DOWN:
                    snake_speed = [0, 20]
                elif event.key == pygame.K_LEFT:
                    snake_speed = [-20, 0]
                elif event.key == pygame.K_RIGHT:
                    snake_speed = [20, 0]
                elif event.key == pygame.K_y:
                    if is_dead:
                        init()
                        is_dead = False
                    is_running = True
                elif event.key == pygame.K_n:
                    if is_dead: game_quit()
                    else: is_running = True
                elif event.key == pygame.K_SPACE:
                    if is_dead: continue
                    if is_paused: is_paused = False
                    is_running = True
                elif event.key == pygame.K_ESCAPE:
                    if is_running:
                        show_msg(">>> Paused <<<")
                        is_paused = not is_paused
                else: # 任意键进入开始状态
                    if is_dead: continue
                    is_running = True

        if not is_running: continue
        if is_paused and is_running: continue

        # 更新蛇的位置
        snake_pos.insert(0, [snake_pos[0][0] + snake_speed[0], snake_pos[0][1] + snake_speed[1]])

        # 检查蛇头是否碰到食物
        if snake_pos[0] == food_pos:
            scores += 10 + len(snake_pos) // 5
            if not 1 < snake_pos[0][0]//20 < 30 or not 1 < snake_pos[0][1]//20 < 22:
                scores += 5​​​​​​​ 
            food_pos = [random.randrange(1, screen_size[0] // 20) * 20, random.randrange(1, screen_size[1] // 20) * 20]
        else:
            snake_pos.pop()

        # 检查蛇头是否碰到墙壁或者蛇身
        if snake_pos[0][0] < 0 or snake_pos[0][0] >= screen_size[0] or snake_pos[0][1] < 0 or snake_pos[0][1] >= screen_size[1] or snake_pos[0] in snake_pos[1:]:
            show_msg("Dead! Again? (Y or N)")
            is_running = False
            is_dead = True
            continue

        # 重画界面及蛇和食物
        repaint()

        # 控制游戏速度
        pygame.time.Clock().tick(10)

if __name__ == "__main__":

    init()
    main()

改善プロセス3

バックグラウンドミュージックを追加する

def play_music(mp3, volume = 1,loops = -1):
    # pygameのミキサーモジュールを初期化します
    pygame.mixer .init()
    # 音楽ファイルをロードします
    pygame.mixer.music .load(mp3)
    # 音量を制御します volume = 0~1 、1 が最高の音量です
    pygame.mixer.music .set_volume(volume)
    # 音楽をループ再生 = -1 はループ再生
    pygame.mixer.music .play(loops)

プロンプト効果音を追加する

def play_sound(wav_no):
    sound_fn = f'sound{wav_no}.wav'
    if os.path.exists(sound_fn):
        alert_sound = pygame.mixer.Sound (sound_fn)
        alert_sound.play()

音楽スイッチ

ショートカットキーCtrl+M 

    elif イベント.キー == pygame. K_mとevent.mod & pygame.KMOD_CTRL :
        # Ctrl+M 切换背景音乐
        is_mute = False
        music_no = 1 if music_no == 3 else music_no + 1
        music_fn = f"voice{music_no}.mp3"
        if os.path.exists( music_fn):
            t = threading.Thread(target=play_music, args=(music_fn,0.8,))
            t.start()

ミュートスイッチ

ショートカットキーCtrl+S

    elif イベント.キー == pygame. K_sとevent.mod & pygame.KMOD_CTRL :
        # Ctrl+S 切换静音状態态
        is_mute = not is_mute
        if is_mute:
            pygame.mixer.music .pause()
        else:
            pygame.mixer.music .unpause()

ミキサー.ミュージック.プレイノート

1. pygame.mixer.music.play() は、WAV、MP3 など、pygame でサポートされているオーディオ形式のみを再生できます。

2. オーディオ ファイルが見つからない場合、または読み取れない場合、pygame.mixer.music.play( ) は例外をスローします。オーディオ ファイルのパスが正しく、ファイルが存在することを確認する必要があります。OS ライブラリをインポートし、os.path.exists(music_file) を使用してファイルが存在するかどうかを確認します。

3. pygame.mixer.music.play() はブロック関数であり、プログラムはオーディオ再生中に他の操作を実行しません。再生中に他の操作を実行する必要がある場合は、別のスレッドで pygame.mixer.music.play() を呼び出す必要があります。

4. マルチスレッドでは、スレッド ライブラリをインポートする必要があります。例:

          t = threading.Thread(target=play_music, args=(music_fn,0.8,))
          t.start()


オリジナルのヘルプの概要

pygame.mixer

名前
    pygame.mixer_music - ストリーミングオーディオを制御するための pygame モジュール

関数
    fadeout(...)
        fadeout(time) ->
        なし フェードアウト後に音楽再生を停止する
    
    get_busy(...)
        get_busy() ->
        音楽ストリームが再生されているかどうかを bool チェック
    
    get_endevent(...)
        get_endevent() -> type
        再生停止時にチャンネルが送信するイベントを取得
    
    get_pos(...)
        get_pos() -> time
        音楽の再生時間を取得
    
    get_volume(...)
        get_volume() -> value
        音楽の音量を取得
    
    load(...)
        load (filename) -> なし
        load(fileobj, namehint=) -> なし
        再生する音楽ファイルをロードします
    
    一時停止(...)
        一時停止() ->
        なし 音楽再生を一時停止します。
    
    play(...)
        play(loops=0, start=0.0, fade_ms=0) -> なし
        音楽ストリームキューの再生を開始します
    
    (.. .)
        queue(filename) -> なし
        queue(fileobj, namehint=,loops=0) -> なし     rewind(...)
        に続くサウンド ファイルをキューに入れます。         rewind() -> なし         restart music     set_endevent(...) )         set_endevent() -> なし         set_endevent(type) -> なし         再生停止時に音楽にイベントを送信させる     set_pos(...)         set_pos(pos) -> なし
    



    




    


set_volume (...)         set_volume(volume) -> なし         音楽         の音量を設定     stop(...)         stop() -> なし         音楽の再生を停止します     unload(...)         unload() -> なし         現在ロードされている音楽をアンロードしてリソースを解放します    unpause (...)         unpause() -> なし 一時         停止した音楽を再開します
    
   


    



    



    



pygame.mixer.Sound

クラスサウンド(builtins.object)
 |  サウンド(ファイル名) -> サウンド
 | サウンド(ファイル=ファイル名) -> サウンド
 | サウンド(file=pathlib_path) -> サウンド
 | サウンド(バッファ) -> サウンド
 | サウンド(バッファ=バッファ) -> サウンド
 | 音(オブジェクト) -> 音
 | サウンド(ファイル=オブジェクト) -> サウンド
 | サウンド(配列=オブジェクト) -> サウンド
 | ファイルまたはバッファ オブジェクトから新しい Sound オブジェクトを作成します
 。  
 | ここで定義されているメソッド:
 |  
 | __init__(self, /, *args, **kwargs)
 | 自分自身を初期化します。正確な署名については、help(type(self)) を参照してください。
 |  
 | フェードアウト(...)
 | フェードアウト(時間) -> なし
 | フェードアウト後にサウンドの再生を停止する
 |  
 | get_length(...)
 | get_length() -> 秒
 | サウンドの長さを取得する
 |  
 | get_num_channels(...)
 | get_num_channels() -> カウント
 | このサウンドが何回再生されているか数えてください
 |  
 | get_raw(...)
 | get_raw() -> バイト
 | サウンド サンプルのバイト文字列のコピーを返します。
 |  
 | get_volume(...)
 | get_volume() -> 値
 | 再生音量を取得する
 |  
 | 遊ぶ(...)
 | play(loops=0, maxtime=0, fade_ms=0) -> チャンネル
 | サウンドの再生を開始します
 |  
 | set_volume(...)
 | set_volume(値) -> なし
 | このサウンドの再生音量を設定します
 |  
 | 停止(...)
 | stop() -> なし
 | サウンドの再生を停止する


完全なコード:

import pygame
import sys, os
import random
import threading

# 定义颜色
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED   = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE  = (0, 0, 255)
GREY  = (220, 220, 220)  # 淡灰色
DARK  = (120, 120, 120)  # 深灰色

def init():
    global screen, screen_size, scores
    global snake_pos, food_pos, snake_speed

    # 初始化pygame
    scores = 0
    pygame.init()

    # 设置屏幕大小
    screen_size = (640, 480)
    screen = pygame.display.set_mode(screen_size)

    # 设置游戏标题
    pygame.display.set_caption("贪吃蛇")

    # 蛇的初始位置
    snake_pos = [[100, 100], [80, 100], [60, 100]]

    # 食物的初始位置
    food_pos = [300, 300]

    # 蛇的初始速度
    snake_speed = [20, 0]

def play_music(mp3, volume = 1, loops = -1):
    # 初始化pygame的mixer模块
    pygame.mixer.init()
    # 加载音乐文件
    pygame.mixer.music.load(mp3)
    # 控制音量
    pygame.mixer.music.set_volume(volume)
    # 播放音乐
    pygame.mixer.music.play(loops)

def play_sound(wav_no):
    sound_fn = f'sound{wav_no}.wav'
    if os.path.exists(sound_fn):
        alert_sound = pygame.mixer.Sound(sound_fn)
        alert_sound.play()

def show_msg(msg, color = BLUE):
    x = screen.get_rect().centerx
    y = screen.get_rect().centery - 50
    font = pygame.font.Font(None, 36)
    text = font.render(msg, True, color)
    text_rect = text.get_rect()
    text_rect.centerx = x
    text_rect.centery = y
    rectangle1 = pygame.Rect(x-155, y-25, 320, 60)
    rectangle2 = pygame.Rect(x-160, y-30, 320, 60)
    pygame.draw.rect(screen, DARK, rectangle1)
    pygame.draw.rect(screen, GREY, rectangle2)
    pygame.draw.rect(screen, BLACK, rectangle2, 2) # 边框宽为2
    screen.blit(text, text_rect)
    pygame.display.flip()

def repaint():
    # 绘制游戏界面
    screen.fill(WHITE)

    # 定义线段的端点坐标
    x,y = (-1,640,640,-1)*16, []
    for i in range(36):
        for _ in range(2):
            y.append(19+i*20)

    # 使用pygame.draw.lines()函数绘制线段
    points = list(zip(x,y))
    pygame.draw.lines(screen, GREY, False, points, 1) # 线宽为1
    points = list(zip(y,x))
    pygame.draw.lines(screen, GREY, False, points, 1)   

    # 重画蛇和食物
    for pos in snake_pos:
        pygame.draw.rect(screen, GREEN, pygame.Rect(pos[0], pos[1], 20, 20))
    pygame.draw.rect(screen, RED, pygame.Rect(food_pos[0], food_pos[1], 20, 20))
    pygame.display.flip()
    show_msg_at(22, 6, f'Scores: {scores}')
    show_msg_at(410, 6, f'Snake coordinate: ({1+snake_pos[0][0]//20:2}, {1+snake_pos[0][1]//20:2})')

def show_msg_at(x, y, msg):
    font = pygame.font.SysFont('Consolas', 14)
    text = font.render(msg, True, BLACK)
    text_rect = text.get_rect()
    text_rect.x, text_rect.y = x, y
    screen.blit(text, text_rect)
    pygame.display.flip()

def game_quit():
    pygame.quit()
    sys.exit()

def main():
    global screen, screen_size, scores
    global snake_pos, food_pos, snake_speed

    is_running = False
    is_paused = False
    is_dead = False
    is_mute = False
    
    repaint()
    show_msg("Press any key to start ...")

    # 创建一个线程来播放音乐
    music_no = 1
    music_fn = "voice1.mp3"
    if os.path.exists(music_fn):
        t = threading.Thread(target=play_music, args=(music_fn,0.8,))
        t.start()

    # 主循环
    while True:
        # 处理游戏事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_quit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_UP:
                    snake_speed = [0, -20]
                elif event.key == pygame.K_DOWN:
                    snake_speed = [0, 20]
                elif event.key == pygame.K_LEFT:
                    snake_speed = [-20, 0]
                elif event.key == pygame.K_RIGHT:
                    snake_speed = [20, 0]
                elif event.key == pygame.K_y:
                    if is_dead:
                        init()
                        is_dead = False
                    is_running = True
                elif event.key == pygame.K_n:
                    if is_dead: game_quit()
                    else: is_running = True
                elif event.key == pygame.K_SPACE:
                    if is_dead: continue
                    if is_paused: is_paused = False
                    is_running = True
                elif event.key == pygame.K_ESCAPE:
                    if is_running:
                        show_msg(">>> Paused <<<")
                        is_paused = not is_paused
                        if not is_mute and is_paused: play_sound(1)
                elif event.key == pygame.K_m and event.mod & pygame.KMOD_CTRL:
                    # Ctrl+M 切换背景音乐
                    is_mute = False
                    music_no = 1 if music_no == 3 else music_no + 1
                    music_fn = f"voice{music_no}.mp3"
                    if os.path.exists(music_fn):
                        t = threading.Thread(target=play_music, args=(music_fn,0.8,))
                        t.start()
                elif event.key == pygame.K_s and event.mod & pygame.KMOD_CTRL:
                    # Ctrl+S 切换静音状态
                    is_mute = not is_mute
                    if is_mute:
                        pygame.mixer.music.pause()
                    else:
                        pygame.mixer.music.unpause()
                    is_running = True
                else: # 任意键进入开始状态
                    if is_dead: continue
                    is_running = True

        if not is_running: continue
        if is_paused and is_running: continue

        # 更新蛇的位置
        snake_pos.insert(0, [snake_pos[0][0] + snake_speed[0], snake_pos[0][1] + snake_speed[1]])

        # 检查蛇头是否碰到食物
        if snake_pos[0] == food_pos:
            scores += 10 + len(snake_pos) // 5
            if not 1 < snake_pos[0][0]//20 < 30 or not 1 < snake_pos[0][1]//20 < 22:
                scores += 5
            if not is_mute: play_sound(2)
            food_pos = [random.randrange(1, screen_size[0] // 20) * 20, random.randrange(1, screen_size[1] // 20) * 20]
        else:
            snake_pos.pop()

        # 检查蛇头是否碰到墙壁或者蛇身
        if snake_pos[0][0] < 0 or snake_pos[0][0] >= screen_size[0] or snake_pos[0][1] < 0 or snake_pos[0][1] >= screen_size[1] or snake_pos[0] in snake_pos[1:]:
            show_msg("Dead! Again? (Y or N)")
            is_running = False
            is_dead = True
            if not is_mute: play_sound(3)
            continue

        # 重画界面及蛇和食物
        repaint()

        # 控制游戏速度
        pygame.time.Clock().tick(10)

if __name__ == "__main__":

    init()
    main()

改善プロセス4

WASD矢印キーを追加

DOS 時代のゲームでは、上、左、下、右の 4 つの矢印キーに対応する、W、A、S、D の 4 つの文字がよく使用されます。

また、ゲーム内で順方向と逆のキーを押すことは、ヘビの自殺行為に相当します。このバグを回避するために、ヘビの移動方向を示す変数方向が導入されています。

    そうでない場合 is_paused:
        if (event.key == pygame.K_w またはevent.key == pygame.K_UP) および方向 != DOWN:
            方向 = UP
        elif (event.key == pygame.K_s またはevent.key == pygame .K_DOWN) および方向 != UP:
            方向 = DOWN
        elif (event.key == pygame.K_a またはevent.key == pygame.K_LEFT) および方向 != RIGHT:
            方向 = LEFT
        elif (event.key == pygame. K_f またはevent.key == pygame.K_RIGHT) および方向 != LEFT:
            方向 = RIGHT

動きをキーストロークから切り離す:

        if 方向 == UP:
            snake_speed = [0, -20]
        elif 方向 == DOWN:
            snake_speed = [0, 20]
        elif 方向 == LEFT:
            snake_speed = [-20, 0]
        elif 方向 == RIGHT:
            snake_speed = [ 20、0]

終了イベントの確認を追加

pygame にはポップアップ ウィンドウのようなメソッドはなく、tkinter ライブラリをインポートし、メッセージボックスで実装します。

from tkinter import messagebox
......
        pygame.event.get() のイベント用:
            ifevent.type == pygame.QUIT:
                if messagebox.askyesno("確認", "終了しますか?"):
                    game_quit( )
           …

最後に、一時停止ボタンを押したときにBGMを一時停止する機能を追加しました、また、いくつかの小さなエラーが修正され、バグがあることが予想され、あまりにも多くすぎるとロジックが少し混乱します状態変数。


まとめ

この記事では、Snake ゲームを例として、描画、フォント、音楽、その他の操作面のさまざまなメソッドと関数を含む、pygame プログラミングの単純なフレームワークを詳しく学習します。学習後、pygame でのプログラミング能力は非常に優れています。強みが向上します。

pygameプログラミングフレームワーク

import pygame
import sys

# 初始化Pygame
pygame.init()

# 设置窗口大小
screen_size = (800, 600)

# 创建窗口
screen = pygame.display.set_mode(screen_size)

# 设置窗口标题
pygame.display.set_caption("Pygame Example")

# 主循环
while True:
    # 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == ...
            ... // 处理按键事件

    # 填充背景色
    screen.fill((255, 255, 255))

    # 绘制矩形
    pygame.draw.rect(screen, (0, 0, 255), (400, 300, 100, 50))

    # 更新显示
    pygame.display.flip()

完全なコード

import pygame
from sys import exit as system
from random  import randrange
from os.path import exists
from tkinter import messagebox
from threading import Thread

# 定义颜色
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED   = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE  = (0, 0, 255)
GREY  = (220, 220, 220)  # 淡灰色
DARK  = (120, 120, 120)  # 深灰色

def init():
    global screen, screen_size, scores
    global snake_pos, food_pos, snake_speed

    # 初始化pygame
    scores = 0
    pygame.init()

    # 设置屏幕大小
    screen_size = (640, 480)
    screen = pygame.display.set_mode(screen_size)

    # 设置游戏标题
    pygame.display.set_caption("贪吃蛇")

    # 蛇的初始位置
    snake_pos = [[100, 100], [80, 100], [60, 100]]

    # 食物的初始位置
    food_pos = [300, 300]

    # 蛇的初始速度
    snake_speed = [20, 0]

def play_music(mp3, volume = 1, loops = -1):
    # 初始化pygame的mixer模块
    pygame.mixer.init()
    # 加载音乐文件
    pygame.mixer.music.load(mp3)
    # 控制音量
    pygame.mixer.music.set_volume(volume)
    # 播放音乐
    pygame.mixer.music.play(loops)

def play_sound(wav_no):
    sound_fn = f'sound{wav_no}.wav'
    if exists(sound_fn):
        alert_sound = pygame.mixer.Sound(sound_fn)
        alert_sound.play()

def show_msg(msg, color = BLUE):
    x = screen.get_rect().centerx
    y = screen.get_rect().centery - 50
    font = pygame.font.Font(None, 36)
    text = font.render(msg, True, color)
    text_rect = text.get_rect()
    text_rect.centerx = x
    text_rect.centery = y
    rectangle1 = pygame.Rect(x-155, y-25, 320, 60)
    rectangle2 = pygame.Rect(x-160, y-30, 320, 60)
    pygame.draw.rect(screen, DARK, rectangle1)
    pygame.draw.rect(screen, GREY, rectangle2)
    pygame.draw.rect(screen, BLACK, rectangle2, 2) # 边框宽为2
    screen.blit(text, text_rect)
    pygame.display.flip()

def repaint():
    # 绘制游戏界面
    screen.fill(WHITE)

    # 定义线段的端点坐标
    x,y = (-1,640,640,-1)*16, []
    for i in range(36):
        for _ in range(2):
            y.append(19+i*20)

    # 使用pygame.draw.lines()函数绘制线段
    points = list(zip(x,y))
    pygame.draw.lines(screen, GREY, False, points, 1) # 线宽为1
    points = list(zip(y,x))
    pygame.draw.lines(screen, GREY, False, points, 1)   

    # 重画蛇和食物
    for pos in snake_pos:
        pygame.draw.rect(screen, GREEN, pygame.Rect(pos[0], pos[1], 20, 20))
    pygame.draw.rect(screen, RED, pygame.Rect(food_pos[0], food_pos[1], 20, 20))
    pygame.display.flip()
    show_msg_at(22, 6, f'Scores: {scores}')
    show_msg_at(410, 6, f'Snake coordinate: ({1+snake_pos[0][0]//20:2}, {1+snake_pos[0][1]//20:2})')

def show_msg_at(x, y, msg):
    font = pygame.font.SysFont('Consolas', 14)
    text = font.render(msg, True, BLACK)
    text_rect = text.get_rect()
    text_rect.x, text_rect.y = x, y
    screen.blit(text, text_rect)
    pygame.display.flip()

def game_quit():
    pygame.quit()
    system()

def main():
    global screen, screen_size, scores
    global snake_pos, food_pos, snake_speed

    is_running = False
    is_paused = False
    is_dead = False
    is_mute = False
    
    repaint()
    show_msg("Press any key to start ...")

    # 创建一个线程来播放音乐
    music_no = 1
    music_fn = "voice1.mp3"
    if exists(music_fn):
        t = Thread(target=play_music, args=(music_fn,0.8,))
        t.start()

    # 主循环
    UP, DOWN, LEFT, RIGHT = 1, 2, 3, 4
    direction = RIGHT
    while True:
        # 处理游戏事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                if messagebox.askyesno("确认", "是否要退出?"):
                    game_quit()
            elif event.type == pygame.KEYDOWN:
                # 增加 WASD 四键对应 上左下右 方向键
                if not is_paused:
                    if   (event.key == pygame.K_w or event.key == pygame.K_UP)    and direction != DOWN:
                        direction = UP
                    elif (event.key == pygame.K_s or event.key == pygame.K_DOWN)  and direction != UP:
                        direction = DOWN
                    elif (event.key == pygame.K_a or event.key == pygame.K_LEFT)  and direction != RIGHT:
                        direction = LEFT
                    elif (event.key == pygame.K_f or event.key == pygame.K_RIGHT) and direction != LEFT:
                        direction = RIGHT
                if event.key == pygame.K_y:
                    if is_dead:
                        init()
                        is_dead = False
                    is_running = True
                elif event.key == pygame.K_n:
                    if is_dead: game_quit()
                    else: is_running = True
                elif event.key == pygame.K_SPACE:
                    if is_dead: continue
                    if is_paused:
                        is_paused = False
                        pygame.mixer.music.unpause()
                    is_running = True
                elif event.key == pygame.K_ESCAPE:
                    if is_running:
                        show_msg(">>> Paused <<<")
                        is_paused = not is_paused
                        if is_paused:
                            pygame.mixer.music.pause()
                            if not is_mute: play_sound(1)
                        else:
                            pygame.mixer.music.unpause()
                elif event.key == pygame.K_m and event.mod & pygame.KMOD_CTRL:
                    # Ctrl+M 切换背景音乐
                    is_mute = False
                    music_no = 1 if music_no == 3 else music_no + 1
                    music_fn = f"voice{music_no}.mp3"
                    if exists(music_fn):
                        t = Thread(target=play_music, args=(music_fn,0.8,))
                        t.start()
                elif event.key == pygame.K_q and event.mod & pygame.KMOD_CTRL:
                    # Ctrl+Q 切换静音状态
                    is_mute = not is_mute
                    if is_mute:
                        pygame.mixer.music.pause()
                    else:
                        pygame.mixer.music.unpause()
                    is_running = True
                else: # 任意键进入开始状态
                    if is_dead: continue
                    is_running = True

        if not is_running: continue
        if is_paused: continue

        if direction   == UP:
            snake_speed = [0, -20]
        elif direction == DOWN:
            snake_speed = [0, 20]
        elif direction == LEFT:
            snake_speed = [-20, 0]
        elif direction == RIGHT:
            snake_speed = [20, 0]

        # 更新蛇的位置
        snake_pos.insert(0, [snake_pos[0][0] + snake_speed[0], snake_pos[0][1] + snake_speed[1]])

        # 检查蛇头是否碰到食物
        if snake_pos[0] == food_pos:
            scores += 10 + len(snake_pos) // 5
            if not 1 < snake_pos[0][0]//20 < 30 or not 1 < snake_pos[0][1]//20 < 22:
                scores += 5
            if not is_mute: play_sound(2)
            food_pos = [randrange(1, screen_size[0] // 20) * 20, randrange(1, screen_size[1] // 20) * 20]
        else:
            snake_pos.pop()

        # 检查蛇头是否碰到墙壁或者蛇身
        if snake_pos[0][0] < 0 or snake_pos[0][0] >= screen_size[0] or snake_pos[0][1] < 0 or snake_pos[0][1] >= screen_size[1] or snake_pos[0] in snake_pos[1:]:
            show_msg("Dead! Again? (Y or N)")
            is_running = False
            is_dead = True
            direction = RIGHT
            if not is_mute: play_sound(3)
            continue

        # 重画界面及蛇和食物
        repaint()

        # 控制游戏速度
        pygame.time.Clock().tick(10)

if __name__ == "__main__":

    init()
    main()

最終版のソースコードと音楽ファイルリストは以下の通りです。

ダウンロードリンク: 

https://download.csdn.net/download/boysoft2002/88231961

Guess you like

Origin blog.csdn.net/boysoft2002/article/details/132370232