Python “贪吃蛇”游戏,在不断改进中学习pygame编程

1ae28d15992f405bb7fd51daa60c3dc2.gif

目录

前言

改进过程一

增加提示信息

原版帮助摘要

pygame.draw

pygame.font

class Rect

class Surface

改进过程二

增加显示得分

改进过程三

增加背景景乐

增加提示音效

音乐切换

静音切换

mixer.music.play 注意事项

原版帮助摘要

pygame.mixer

pygame.mixer.Sound

小结

pygame编程框架


前言

在上一篇博文里,从讯飞星火中获取到游戏“贪吃蛇”的代码,运行效果如上图。代码并不完整,当蛇死了就退出游戏,不能多次玩这个游戏。

前文链接:

讯飞星火、文心一言和通义千问同时编“贪吃蛇”游戏,谁会胜出?

代码如下:

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

改进过程一

增加提示信息

之前没有仔细学过pygame的编程,刚好拿这例程练练手,在不断改进中学习pygame编程。

原代码一执行,蛇就开始游动了,就从这里入手。开始前,增加显示“按任意键开始...”的提示。

蛇死后,提醒是否重来?Yes to play again, No to quit game.

同时,在游戏中允许按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) -> なし
     |      enable fake rendering of bold text
     |  
     |  set_italic(...)
     |      set_italic(bool) -> None
     |      enable fake rendering of italic text
     |  
     |  set_script(...)
     |      set_script(str) -> None
     |      set the script code for text shaping
     |  
     |  set_strikethrough(...)
     |      set_strikethrough(bool) -> None
     |      control if text is rendered with a strikethrough
     |  
     |  set_underline(...)
     |      set_underline(bool) -> None
     |      control if text is rendered with an underline
     |  
     |  size(...)
     | サイズ(テキスト) -> (幅、高さ)
     | テキストをレンダリングするために必要なスペースの量を決定する
     |  
     | -------------------------------------------------- --------------------
     | ここで定義されるデータ記述子:
     |  
     | 太字
     | 太字 -> ブール値
     | フォントを(偽の)太字で表示するかどうかを取得または設定します。
     |  
     | 斜体
     | イタリック体 -> ブール文字
     | フォントを(偽の)斜体でレンダリングするかどうかを取得または設定します。
     |  
     | 取り消し線
     | 取り消し線 -> ブール値
     | フォントに取り消し線を付けてレンダリングするかどうかを取得または設定します。
     |  
     | 下線
     | 下線 -> ブール値
     | フォントに下線を付けて表示するかどうかを取得または設定します。


クラス 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
 |      get the bit depth of the Surface pixel format
 |  
 |  get_blendmode(...)
 |      Return the surface's SDL 2 blend mode
 |  
 |  get_bounding_rect(...)
 |      get_bounding_rect(min_alpha = 1) -> Rect
 |      find the smallest rect containing data
 |  
 |  get_buffer(...)
 |      get_buffer() -> BufferProxy
 |      acquires a buffer object for the pixels of the Surface.
 |  
 |  get_bytesize(...)
 |      get_bytesize() -> int
 |      get the bytes used per Surface pixel
 |  
 |  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
 |      get the color for a single entry in a palette
 |  
 |  get_parent(...)
 |      get_parent() -> Surface
 |      find the parent of a subsurface
 |  
 |  get_pitch(...)
 |      get_pitch() -> int
 |      get the number of bytes used per Surface row
 |  
 |  get_rect(...)
 |      get_rect(**kwargs) -> Rect
 |      get the rectangular area of the Surface
 |  
 |  get_shifts(...)
 |      get_shifts() -> (R, G, B, A)
 |      the bit shifts needed to convert between a color and a mapped integer
 |  
 |  get_size(...)
 |      get_size() -> (width, height)
 |      get the dimensions of the Surface
 |  
 |  get_view(...)
 |      get_view(<kind>='2') -> BufferProxy
 |      return a buffer view of the Surface's pixels.
 |  
 |  get_width(...)
 |      get_width() -> width
 |      get the width of the Surface
 |  
 |  lock(...)
 |      lock() -> None
 |      lock the Surface memory for pixel access
 |  
 |  map_rgb(...)
 |      map_rgb(Color) -> mapped_int
 |      convert a color into a mapped color value
 |  
 |  mustlock(...)
 |      mustlock() -> bool
 |      test if the Surface requires locking
 |  
 | 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()

改进过程二

增加显示得分

每吃到一个食物+10分,蛇长和食物靠近边界会有额外加分;顺带显示出蛇的坐标位置。

函数show_msg_at(),比show_msg增加x,y坐标,把信息显示到指定的位置:

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

另外新增一个全局变量 scores,当碰到食物时加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 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()

静音切换

快捷键 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() -> seconds
 |      get the length of the Sound
 |  
 |  get_num_channels(...)
 |      get_num_channels() -> count
 |      count how many times this Sound is playing
 |  
 |  get_raw(...)
 |      get_raw() -> bytes
 |      return a bytestring copy of the Sound samples.
 |  
 |  get_volume(...)
 |      get_volume() -> value
 |      get the playback volume
 |  
 |  play(...)
 |      play(loops=0, maxtime=0, fade_ms=0) -> Channel
 |      begin sound playback
 |  
 |  set_volume(...)
 |      set_volume(value) -> None
 |      set the playback volume for this Sound
 |  
 |  stop(...)
 |      stop() -> None
 |      stop sound playback


完整代码:

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

改进过程四

增加WASD方向键

DOS时代的游戏经常用W、A、S、D四个字母,对应上左下右四个方向键

另外,游戏中按了与前进方向相反的键,相当于蛇的自杀行为。为了避免这个bug,引入一个表示蛇移动方向的变量direction:

    そうでない場合 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を一時停止する機能を追加しました、また、いくつかの小さなエラーが修正され、バグがあることが予想され、あまりにも多くすぎるとロジックが少し混乱します状態変数。


まとめ

本文以贪吃蛇游戏为例,对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

おすすめ

転載: blog.csdn.net/boysoft2002/article/details/132370232