[Unity学习教程] Python “贪吃蛇”游戏,在不停改进中学习pygame编程

[复制链接]
查看731 | 回复0 | 2023-8-23 11:38:05 | 显示全部楼层 |阅读模式 来自 中国北京

目次
前言
改进过程一
增长提示信息
原版资助择要
pygame.draw
pygame.font
class Rect
class Surface
改进过程二
增长体现得分
改进过程三
增长配景景乐
增长提示音效
音乐切换
静音切换
mixer.music.play 留意事项
原版资助择要
pygame.mixer
pygame.mixer.Sound
改进过程四
增长WASD方向键
增长退失变乱简直认
小结
pygame编程框架
完整代码


前言

在上一篇博文里,从讯飞星火中获取到游戏“贪吃蛇”的代码,运行结果如上图。代码并不完整,当蛇死了就退出游戏,不能多次玩这个游戏。
前文链接:
讯飞星火、文心一言和通义千问同时编“贪吃蛇”游戏,谁会胜出?
代码如下:
  1. import pygame
  2. import sys
  3. import random
  4. # 定义颜色
  5. WHITE = (255, 255, 255)
  6. GREEN = (0, 255, 0)
  7. RED  = (255, 0, 0)
  8. GREY = (211, 211, 211)  # 淡灰色
  9. def init():
  10.     global screen, screen_size
  11.     global snake_pos, food_pos, snake_speed
  12.     # 初始化pygame
  13.     pygame.init()
  14.     # 设置屏幕大小
  15.     screen_size = (640, 480)
  16.     screen = pygame.display.set_mode(screen_size)
  17.     # 设置游戏标题
  18.     pygame.display.set_caption("贪吃蛇")
  19.     # 蛇的初始位置
  20.     snake_pos = [[100, 100], [80, 100], [60, 100]]
  21.     # 食物的初始位置
  22.     food_pos = [300, 300]
  23.     # 蛇的初始速度
  24.     snake_speed = [20, 0]
  25. def repaint():
  26.     # 绘制游戏界面
  27.     screen.fill(WHITE)
  28.     # 定义线段的端点坐标
  29.     x,y = (-1,640,640,-1)*16, []
  30.     for i in range(36):
  31.         for _ in range(2):
  32.             y.append(19+i*20)
  33.     # 使用pygame.draw.lines()函数绘制线段
  34.     points = list(zip(x,y))
  35.     pygame.draw.lines(screen, GREY, False, points, 1) # 线宽为1
  36.     points = list(zip(y,x))
  37.     pygame.draw.lines(screen, GREY, False, points, 1)   
  38.     # 重画蛇和食物
  39.     for pos in snake_pos:
  40.         pygame.draw.rect(screen, GREEN, pygame.Rect(pos[0], pos[1], 20, 20))
  41.     pygame.draw.rect(screen, RED, pygame.Rect(food_pos[0], food_pos[1], 20, 20))
  42.     pygame.display.flip()
  43. def game_quit():
  44.     pygame.quit()
  45.     sys.exit()
  46. def main():
  47.     global screen, screen_size
  48.     global snake_pos, food_pos, snake_speed
  49.    
  50.     # 主循环
  51.     while True:
  52.         # 处理游戏事件
  53.         for event in pygame.event.get():
  54.             if event.type == pygame.QUIT:
  55.                 game_quit()
  56.             elif event.type == pygame.KEYDOWN:
  57.                 if event.key == pygame.K_UP:
  58.                     snake_speed = [0, -20]
  59.                 elif event.key == pygame.K_DOWN:
  60.                     snake_speed = [0, 20]
  61.                 elif event.key == pygame.K_LEFT:
  62.                     snake_speed = [-20, 0]
  63.                 elif event.key == pygame.K_RIGHT:
  64.                     snake_speed = [20, 0]
  65.         # 更新蛇的位置
  66.         snake_pos.insert(0, [snake_pos[0][0] + snake_speed[0], snake_pos[0][1] + snake_speed[1]])
  67.         # 检查蛇头是否碰到食物
  68.         if snake_pos[0] == food_pos:
  69.             food_pos = [random.randrange(1, screen_size[0] // 20) * 20, random.randrange(1, screen_size[1] // 20) * 20]
  70.         else:
  71.             snake_pos.pop()
  72.         # 检查蛇头是否碰到墙壁或者蛇身
  73.         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:]:
  74.             game_quit()
  75.             '''
  76.             此处可增加与用户的交互,如:
  77.             if askyesno('title','again?'):
  78.                 init() # Yes to Play again
  79.             else:
  80.                 game_quit() # No to Exit
  81.             '''
  82.         # 重画界面及蛇和食物
  83.         repaint()
  84.         # 控制游戏速度
  85.         pygame.time.Clock().tick(10)
  86. if __name__ == "__main__":
  87.     init()
  88.     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, 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()

原版资助择要

pygame.draw

NAME
    pygame.draw - pygame module for drawing shapes
FUNCTIONS
    aaline(...)
        aaline(surface, color, start_pos, end_pos) -> Rect
        aaline(surface, color, start_pos, end_pos, blend=1) -> Rect
        draw a straight antialiased line
    
    aalines(...)
        aalines(surface, color, closed, points) -> Rect
        aalines(surface, color, closed, points, blend=1) -> Rect
        draw multiple contiguous straight antialiased line segments
    
    arc(...)
        arc(surface, color, rect, start_angle, stop_angle) -> Rect
        arc(surface, color, rect, start_angle, stop_angle, width=1) -> Rect
        draw an elliptical arc
    
    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
        draw a circle
    
    ellipse(...)
        ellipse(surface, color, rect) -> Rect
        ellipse(surface, color, rect, width=0) -> Rect
        draw an ellipse
    
    line(...)
        line(surface, color, start_pos, end_pos) -> Rect
        line(surface, color, start_pos, end_pos, width=1) -> Rect
        draw a straight line
    
    lines(...)
        lines(surface, color, closed, points) -> Rect
        lines(surface, color, closed, points, width=1) -> Rect
        draw multiple contiguous straight line segments
    
    polygon(...)
        polygon(surface, color, points) -> Rect
        polygon(surface, color, points, width=0) -> Rect
        draw a polygon
    
    rect(...)
        rect(surface, color, 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
        draw a rectangle

pygame.font

NAME
    pygame.font - pygame module for loading and rendering fonts
CLASSES
    builtins.object
        Font
    
    class Font(builtins.object)
     |  Font(file_path=None, size=12) -> Font
     |  Font(file_path, size) -> Font
     |  Font(pathlib.Path, size) -> Font
     |  Font(object, size) -> Font
     |  create a new Font object from a file
     |  
     |  Methods defined here:
     |  
     |  __init__(self, /, *args, **kwargs)
     |      Initialize self.  See help(type(self)) for accurate signature.
     |  
     |  get_ascent(...)
     |      get_ascent() -> int
     |      get the ascent of the font
     |  
     |  get_bold(...)
     |      get_bold() -> bool
     |      check if text will be rendered bold
     |  
     |  get_descent(...)
     |      get_descent() -> int
     |      get the descent of the font
     |  
     |  get_height(...)
     |      get_height() -> int
     |      get the height of the font
     |  
     |  get_italic(...)
     |      get_italic() -> bool
     |      check if the text will be rendered italic
     |  
     |  get_linesize(...)
     |      get_linesize() -> int
     |      get the line space of the font text
     |  
     |  get_strikethrough(...)
     |      get_strikethrough() -> bool
     |      check if text will be rendered with a strikethrough
     |  
     |  get_underline(...)
     |      get_underline() -> bool
     |      check if text will be rendered with an underline
     |  
     |  metrics(...)
     |      metrics(text) -> list
     |      gets the metrics for each character in the passed string
     |  
     |  render(...)
     |      render(text, antialias, color, background=None) -> Surface
     |      draw text on a new Surface
     |  
     |  set_bold(...)
     |      set_bold(bool) -> None
     |      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(...)
     |      size(text) -> (width, height)
     |      determine the amount of space needed to render text
     |  
     |  ----------------------------------------------------------------------
     |  Data descriptors defined here:
     |  
     |  bold
     |      bold -> bool
     |      Gets or sets whether the font should be rendered in (faked) bold.
     |  
     |  italic
     |      italic -> bool
     |      Gets or sets whether the font should be rendered in (faked) italics.
     |  
     |  strikethrough
     |      strikethrough -> bool
     |      Gets or sets whether the font should be rendered with a strikethrough.
     |  
     |  underline
     |      underline -> bool
     |      Gets or sets whether the font should be rendered with an underline.

class Rect

Help on class Rect in module pygame.rect:
class Rect(builtins.object)
 |  Rect(left, top, width, height) -> Rect
 |  Rect((left, top), (width, height)) -> Rect
 |  Rect(object) -> Rect
 |  pygame object for storing rectangular coordinates
 |  
 |  Methods defined here:
 |  
 |  clamp(...)
 |      clamp(Rect) -> Rect
 |      moves the rectangle inside another
 |  
 |  clamp_ip(...)
 |      clamp_ip(Rect) -> None
 |      moves the rectangle inside another, in place
 |  
 |  clip(...)
 |      clip(Rect) -> Rect
 |      crops a rectangle inside another
 |  
 |  clipline(...)
 |      clipline(x1, y1, x2, y2) -> ((cx1, cy1), (cx2, cy2))
 |      clipline(x1, y1, x2, y2) -> ()
 |      clipline((x1, y1), (x2, y2)) -> ((cx1, cy1), (cx2, cy2))
 |      clipline((x1, y1), (x2, y2)) -> ()
 |      clipline((x1, y1, x2, y2)) -> ((cx1, cy1), (cx2, cy2))
 |      clipline((x1, y1, x2, y2)) -> ()
 |      clipline(((x1, y1), (x2, y2))) -> ((cx1, cy1), (cx2, cy2))
 |      clipline(((x1, y1), (x2, y2))) -> ()
 |      crops a line inside a rectangle
 |  
 |  collidedict(...)
 |      collidedict(dict) -> (key, value)
 |      collidedict(dict) -> None
 |      collidedict(dict, use_values=0) -> (key, value)
 |      collidedict(dict, use_values=0) -> None
 |      test if one rectangle in a dictionary intersects
 |  
 |  collidedictall(...)
 |      collidedictall(dict) -> [(key, value), ...]
 |      collidedictall(dict, use_values=0) -> [(key, value), ...]
 |      test if all rectangles in a dictionary intersect
 |  
 |  collidelist(...)
 |      collidelist(list) -> index
 |      test if one rectangle in a list intersects
 |  
 |  collidelistall(...)
 |      collidelistall(list) -> indices
 |      test if all rectangles in a list intersect
 |  
 |  collideobjects(...)
 |      collideobjects(rect_list) -> object
 |      collideobjects(obj_list, key=func) -> object
 |      test if any object in a list intersects
 |  
 |  collideobjectsall(...)
 |      collideobjectsall(rect_list) -> objects
 |      collideobjectsall(obj_list, key=func) -> objects
 |      test if all objects in a list intersect
 |  
 |  collidepoint(...)
 |      collidepoint(x, y) -> bool
 |      collidepoint((x,y)) -> bool
 |      test if a point is inside a rectangle
 |  
 |  colliderect(...)
 |      colliderect(Rect) -> bool
 |      test if two rectangles overlap
 |  
 |  contains(...)
 |      contains(Rect) -> bool
 |      test if one rectangle is inside another
 |  
 |  copy(...)
 |      copy() -> Rect
 |      copy the rectangle
 |  
 |  fit(...)
 |      fit(Rect) -> Rect
 |      resize and move a rectangle with aspect ratio
 |  
 |  inflate(...)
 |      inflate(x, y) -> Rect
 |      grow or shrink the rectangle size
 |  
 |  inflate_ip(...)
 |      inflate_ip(x, y) -> None
 |      grow or shrink the rectangle size, in place
 |  
 |  move(...)
 |      move(x, y) -> Rect
 |      moves the rectangle
 |  
 |  move_ip(...)
 |      move_ip(x, y) -> None
 |      moves the rectangle, in place
 |  
 |  normalize(...)
 |      normalize() -> None
 |      correct negative sizes
 |  
 |  scale_by(...)
 |      scale_by(scalar) -> Rect
 |      scale_by(scalex, scaley) -> Rect
 |      scale the rectangle by given a multiplier
 |  
 |  scale_by_ip(...)
 |      scale_by_ip(scalar) -> None
 |      scale_by_ip(scalex, scaley) -> None
 |      grow or shrink the rectangle size, in place
 |  
 |  union(...)
 |      union(Rect) -> Rect
 |      joins two rectangles into one
 |  
 |  union_ip(...)
 |      union_ip(Rect) -> None
 |      joins two rectangles into one, in place
 |  
 |  unionall(...)
 |      unionall(Rect_sequence) -> Rect
 |      the union of many rectangles
 |  
 |  unionall_ip(...)
 |      unionall_ip(Rect_sequence) -> None
 |      the union of many rectangles, in place
 |  
 |  update(...)
 |      update(left, top, width, height) -> None
 |      update((left, top), (width, height)) -> None
 |      update(object) -> None
 |      sets the position and size of the rectangle

class Surface

class Surface(builtins.object)
 |  Surface((width, height), flags=0, depth=0, masks=None) -> Surface
 |  Surface((width, height), flags=0, Surface) -> Surface
 |  pygame object for representing images
 |  
 |  Methods defined here:
 |  
 |  blit(...)
 |      blit(source, dest, area=None, special_flags=0) -> Rect
 |      draw one image onto another
 |  
 |  blits(...)
 |      blits(blit_sequence=((source, dest), ...), doreturn=1) -> [Rect, ...] or None
 |      blits(((source, dest, area), ...)) -> [Rect, ...]
 |      blits(((source, dest, area, special_flags), ...)) -> [Rect, ...]
 |      draw many images onto another
 |  
 |  convert(...)
 |      convert(Surface=None) -> Surface
 |      convert(depth, flags=0) -> Surface
 |      convert(masks, flags=0) -> Surface
 |      change the pixel format of an image
 |  
 |  convert_alpha(...)
 |      convert_alpha(Surface) -> Surface
 |      convert_alpha() -> Surface
 |      change the pixel format of an image including per pixel alphas
 |  
 |  copy(...)
 |      copy() -> Surface
 |      create a new copy of a Surface
 |  
 |  fill(...)
 |      fill(color, rect=None, special_flags=0) -> Rect
 |      fill Surface with a solid color
 |  
 |  get_abs_offset(...)
 |      get_abs_offset() -> (x, y)
 |      find the absolute position of a child subsurface inside its top level parent
 |  
 |  get_abs_parent(...)
 |      get_abs_parent() -> Surface
 |      find the top level parent of a subsurface
 |  
 |  get_alpha(...)
 |      get_alpha() -> int_value
 |      get the current Surface transparency value
 |  
 |  get_at(...)
 |      get_at((x, y)) -> Color
 |      get the color value at a single pixel
 |  
 |  get_at_mapped(...)
 |      get_at_mapped((x, y)) -> Color
 |      get the mapped color value at a single pixel
 |  
 |  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 the current clipping area of the Surface
 |  
 |  get_colorkey(...)
 |      get_colorkey() -> RGB or None
 |      Get the current transparent colorkey
 |  
 |  get_flags(...)
 |      get_flags() -> int
 |      get the additional flags used for the Surface
 |  
 |  get_height(...)
 |      get_height() -> height
 |      get the height of the Surface
 |  
 |  get_locked(...)
 |      get_locked() -> bool
 |      test if the Surface is current locked
 |  
 |  get_locks(...)
 |      get_locks() -> tuple
 |      Gets the locks for the Surface
 |  
 |  get_losses(...)
 |      get_losses() -> (R, G, B, A)
 |      the significant bits used to convert between a color and a mapped integer
 |  
 |  get_masks(...)
 |      get_masks() -> (R, G, B, A)
 |      the bitmasks needed to convert between a color and a mapped integer
 |  
 |  get_offset(...)
 |      get_offset() -> (x, y)
 |      find the position of a child subsurface inside a parent
 |  
 |  get_palette(...)
 |      get_palette() -> [RGB, RGB, RGB, ...]
 |      get the color index palette for an 8-bit Surface
 |  
 |  get_palette_at(...)
 |      get_palette_at(index) -> 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() -> Surface
 |      returns a copy of the surface with the RGB channels pre-multiplied by the alpha channel.
 |  
 |  scroll(...)
 |      scroll(dx=0, dy=0) -> None
 |      Shift the surface image in place
 |  
 |  set_alpha(...)
 |      set_alpha(value, flags=0) -> None
 |      set_alpha(None) -> None
 |      set the alpha value for the full Surface image
 |  
 |  set_at(...)
 |      set_at((x, y), Color) -> None
 |      set the color value for a single pixel
 |  
 |  set_clip(...)
 |      set_clip(rect) -> None
 |      set_clip(None) -> None
 |      set the current clipping area of the Surface
 |  
 |  set_colorkey(...)
 |      set_colorkey(Color, flags=0) -> None
 |      set_colorkey(None) -> None
 |      Set the transparent colorkey
 |  
 |  set_masks(...)
 |      set_masks((r,g,b,a)) -> None
 |      set the bitmasks needed to convert between a color and a mapped integer
 |  
 |  set_palette(...)
 |      set_palette([RGB, RGB, RGB, ...]) -> None
 |      set the color palette for an 8-bit Surface
 |  
 |  set_palette_at(...)
 |      set_palette_at(index, RGB) -> None
 |      set the color for a single index in an 8-bit Surface palette
 |  
 |  set_shifts(...)
 |      set_shifts((r,g,b,a)) -> None
 |      sets the bit shifts needed to convert between a color and a mapped integer
 |  
 |  subsurface(...)
 |      subsurface(Rect) -> Surface
 |      create a new surface that references its parent
 |  
 |  unlock(...)
 |      unlock() -> None
 |      unlock the Surface memory from pixel access
 |  
 |  unmap_rgb(...)
 |      unmap_rgb(mapped_int) -> Color
 |      convert a mapped integer color value into a 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: 空格键
完整代码如下:
  1. import pygame
  2. import sys
  3. import random
  4. # 定义颜色
  5. BLACK = (0, 0, 0)
  6. WHITE = (255, 255, 255)
  7. RED   = (255, 0, 0)
  8. GREEN = (0, 255, 0)
  9. BLUE  = (0, 0, 255)
  10. GREY  = (220, 220, 220)  # 淡灰色
  11. DARK  = (120, 120, 120)  # 深灰色
  12. def init():
  13.     global screen, screen_size
  14.     global snake_pos, food_pos, snake_speed
  15.     # 初始化pygame
  16.     pygame.init()
  17.     # 设置屏幕大小
  18.     screen_size = (640, 480)
  19.     screen = pygame.display.set_mode(screen_size)
  20.     # 设置游戏标题
  21.     pygame.display.set_caption("贪吃蛇")
  22.     # 蛇的初始位置
  23.     snake_pos = [[100, 100], [80, 100], [60, 100]]
  24.     # 食物的初始位置
  25.     food_pos = [300, 300]
  26.     # 蛇的初始速度
  27.     snake_speed = [20, 0]
  28. def show_msg(msg, color = BLUE):
  29.     x = screen.get_rect().centerx
  30.     y = screen.get_rect().centery - 50
  31.     font = pygame.font.Font(None, 36)
  32.     text = font.render(msg, True, color)
  33.     text_rect = text.get_rect()
  34.     text_rect.centerx = x
  35.     text_rect.centery = y
  36.     rectangle1 = pygame.Rect(x-155, y-25, 320, 60)
  37.     rectangle2 = pygame.Rect(x-160, y-30, 320, 60)
  38.     pygame.draw.rect(screen, DARK, rectangle1)
  39.     pygame.draw.rect(screen, GREY, rectangle2)
  40.     pygame.draw.rect(screen, BLACK, rectangle2, 2) # 边框宽为2
  41.     screen.blit(text, text_rect)
  42.     pygame.display.flip()
  43. def repaint():
  44.     # 绘制游戏界面
  45.     screen.fill(WHITE)
  46.     # 定义线段的端点坐标
  47.     x,y = (-1,640,640,-1)*16, []
  48.     for i in range(36):
  49.         for _ in range(2):
  50.             y.append(19+i*20)
  51.     # 使用pygame.draw.lines()函数绘制线段
  52.     points = list(zip(x,y))
  53.     pygame.draw.lines(screen, GREY, False, points, 1) # 线宽为1
  54.     points = list(zip(y,x))
  55.     pygame.draw.lines(screen, GREY, False, points, 1)   
  56.     # 重画蛇和食物
  57.     for pos in snake_pos:
  58.         pygame.draw.rect(screen, GREEN, pygame.Rect(pos[0], pos[1], 20, 20))
  59.     pygame.draw.rect(screen, RED, pygame.Rect(food_pos[0], food_pos[1], 20, 20))
  60.     pygame.display.flip()
  61. def game_quit():
  62.     pygame.quit()
  63.     sys.exit()
  64. def main():
  65.     global screen, screen_size
  66.     global snake_pos, food_pos, snake_speed
  67.     is_running = False
  68.     is_paused = False
  69.     is_dead = False
  70.     repaint()
  71.     show_msg("Press any key to start ...")
  72.     # 主循环
  73.     while True:
  74.         # 处理游戏事件
  75.         for event in pygame.event.get():
  76.             if event.type == pygame.QUIT:
  77.                 game_quit()
  78.             elif event.type == pygame.KEYDOWN:
  79.                 if event.key == pygame.K_UP:
  80.                     snake_speed = [0, -20]
  81.                 elif event.key == pygame.K_DOWN:
  82.                     snake_speed = [0, 20]
  83.                 elif event.key == pygame.K_LEFT:
  84.                     snake_speed = [-20, 0]
  85.                 elif event.key == pygame.K_RIGHT:
  86.                     snake_speed = [20, 0]
  87.                 elif event.key == pygame.K_y:
  88.                     if is_dead:
  89.                         init()
  90.                         is_dead = False
  91.                     is_running = True
  92.                 elif event.key == pygame.K_n:
  93.                     if is_dead: game_quit()
  94.                     else: is_running = True
  95.                 elif event.key == pygame.K_ESCAPE:
  96.                     if is_running:
  97.                         show_msg(">>> Paused <<<")
  98.                         is_paused = not is_paused
  99.                 else: # 任意键进入开始状态
  100.                     is_running = True
  101.         if not is_running: continue
  102.         if is_paused and is_running: continue
  103.         # 更新蛇的位置
  104.         snake_pos.insert(0, [snake_pos[0][0] + snake_speed[0], snake_pos[0][1] + snake_speed[1]])
  105.         # 检查蛇头是否碰到食物
  106.         if snake_pos[0] == food_pos:
  107.             food_pos = [random.randrange(1, screen_size[0] // 20) * 20, random.randrange(1, screen_size[1] // 20) * 20]
  108.         else:
  109.             snake_pos.pop()
  110.         # 检查蛇头是否碰到墙壁或者蛇身
  111.         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:]:
  112.             show_msg("Dead! Again? (Y or N)")
  113.             is_running = False
  114.             is_dead = True
  115.             continue
  116.         # 重画界面及蛇和食物
  117.         repaint()
  118.         # 控制游戏速度
  119.         pygame.time.Clock().tick(10)
  120. if __name__ == "__main__":
  121.     init()
  122.     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会有额外加分,规则可以自己定,例如:
        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
 
完整代码如下: 
  1. import pygame
  2. import sys
  3. import random
  4. # 定义颜色
  5. BLACK = (0, 0, 0)
  6. WHITE = (255, 255, 255)
  7. RED   = (255, 0, 0)
  8. GREEN = (0, 255, 0)
  9. BLUE  = (0, 0, 255)
  10. GREY  = (220, 220, 220)  # 淡灰色
  11. DARK  = (120, 120, 120)  # 深灰色
  12. def init():
  13.     global screen, screen_size, scores
  14.     global snake_pos, food_pos, snake_speed
  15.     # 初始化pygame
  16.     scores = 0
  17.     pygame.init()
  18.     # 设置屏幕大小
  19.     screen_size = (640, 480)
  20.     screen = pygame.display.set_mode(screen_size)
  21.     # 设置游戏标题
  22.     pygame.display.set_caption("贪吃蛇")
  23.     # 蛇的初始位置
  24.     snake_pos = [[100, 100], [80, 100], [60, 100]]
  25.     # 食物的初始位置
  26.     food_pos = [300, 300]
  27.     # 蛇的初始速度
  28.     snake_speed = [20, 0]
  29. def show_msg(msg, color = BLUE):
  30.     x = screen.get_rect().centerx
  31.     y = screen.get_rect().centery - 50
  32.     font = pygame.font.Font(None, 36)
  33.     text = font.render(msg, True, color)
  34.     text_rect = text.get_rect()
  35.     text_rect.centerx = x
  36.     text_rect.centery = y
  37.     rectangle1 = pygame.Rect(x-155, y-25, 320, 60)
  38.     rectangle2 = pygame.Rect(x-160, y-30, 320, 60)
  39.     pygame.draw.rect(screen, DARK, rectangle1)
  40.     pygame.draw.rect(screen, GREY, rectangle2)
  41.     pygame.draw.rect(screen, BLACK, rectangle2, 2) # 边框宽为2
  42.     screen.blit(text, text_rect)
  43.     pygame.display.flip()
  44. def repaint():
  45.     # 绘制游戏界面
  46.     screen.fill(WHITE)
  47.     # 定义线段的端点坐标
  48.     x,y = (-1,640,640,-1)*16, []
  49.     for i in range(36):
  50.         for _ in range(2):
  51.             y.append(19+i*20)
  52.     # 使用pygame.draw.lines()函数绘制线段
  53.     points = list(zip(x,y))
  54.     pygame.draw.lines(screen, GREY, False, points, 1) # 线宽为1
  55.     points = list(zip(y,x))
  56.     pygame.draw.lines(screen, GREY, False, points, 1)   
  57.     # 重画蛇和食物
  58.     for pos in snake_pos:
  59.         pygame.draw.rect(screen, GREEN, pygame.Rect(pos[0], pos[1], 20, 20))
  60.     pygame.draw.rect(screen, RED, pygame.Rect(food_pos[0], food_pos[1], 20, 20))
  61.     pygame.display.flip()
  62.     show_msg_at(22, 6, f'Scores: {scores}')
  63.     show_msg_at(410, 6, f'Snake coordinate: ({1+snake_pos[0][0]//20:2}, {1+snake_pos[0][1]//20:2})')
  64. def show_msg_at(x, y, msg):
  65.     font = pygame.font.SysFont('Consolas', 14)
  66.     text = font.render(msg, True, BLACK)
  67.     text_rect = text.get_rect()
  68.     text_rect.x, text_rect.y = x, y
  69.     screen.blit(text, text_rect)
  70.     pygame.display.flip()
  71. def game_quit():
  72.     pygame.quit()
  73.     sys.exit()
  74. def main():
  75.     global screen, screen_size, scores
  76.     global snake_pos, food_pos, snake_speed
  77.     is_running = False
  78.     is_paused = False
  79.     is_dead = False
  80.     repaint()
  81.     show_msg("Press any key to start ...")
  82.     # 主循环
  83.     while True:
  84.         # 处理游戏事件
  85.         for event in pygame.event.get():
  86.             if event.type == pygame.QUIT:
  87.                 game_quit()
  88.             elif event.type == pygame.KEYDOWN:
  89.                 if event.key == pygame.K_UP:
  90.                     snake_speed = [0, -20]
  91.                 elif event.key == pygame.K_DOWN:
  92.                     snake_speed = [0, 20]
  93.                 elif event.key == pygame.K_LEFT:
  94.                     snake_speed = [-20, 0]
  95.                 elif event.key == pygame.K_RIGHT:
  96.                     snake_speed = [20, 0]
  97.                 elif event.key == pygame.K_y:
  98.                     if is_dead:
  99.                         init()
  100.                         is_dead = False
  101.                     is_running = True
  102.                 elif event.key == pygame.K_n:
  103.                     if is_dead: game_quit()
  104.                     else: is_running = True
  105.                 elif event.key == pygame.K_SPACE:
  106.                     if is_dead: continue
  107.                     if is_paused: is_paused = False
  108.                     is_running = True
  109.                 elif event.key == pygame.K_ESCAPE:
  110.                     if is_running:
  111.                         show_msg(">>> Paused <<<")
  112.                         is_paused = not is_paused
  113.                 else: # 任意键进入开始状态
  114.                     if is_dead: continue
  115.                     is_running = True
  116.         if not is_running: continue
  117.         if is_paused and is_running: continue
  118.         # 更新蛇的位置
  119.         snake_pos.insert(0, [snake_pos[0][0] + snake_speed[0], snake_pos[0][1] + snake_speed[1]])
  120.         # 检查蛇头是否碰到食物
  121.         if snake_pos[0] == food_pos:
  122.             scores += 10 + len(snake_pos) // 5
  123.             if not 1 < snake_pos[0][0]//20 < 30 or not 1 < snake_pos[0][1]//20 < 22:
  124.                 scores += 5​​​​​​​ 
  125.             food_pos = [random.randrange(1, screen_size[0] // 20) * 20, random.randrange(1, screen_size[1] // 20) * 20]
  126.         else:
  127.             snake_pos.pop()
  128.         # 检查蛇头是否碰到墙壁或者蛇身
  129.         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:]:
  130.             show_msg("Dead! Again? (Y or N)")
  131.             is_running = False
  132.             is_dead = True
  133.             continue
  134.         # 重画界面及蛇和食物
  135.         repaint()
  136.         # 控制游戏速度
  137.         pygame.time.Clock().tick(10)
  138. if __name__ == "__main__":
  139.     init()
  140.     main()
复制代码
改进过程三

增长配景景乐

def play_music(mp3, volume = 1, loops = -1):
    # 初始化pygame的mixer模块
    pygame.mixer.init()
    # 加载音乐文件
    pygame.mixer.music.load(mp3)
    # 控制音量 volume = 0~1,1为最高音量
    pygame.mixer.music.set_volume(volume)
    # 播放音乐 loops = -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 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()
mixer.music.play 留意事项

1. pygame.mixer.music.play() 只能播放pygame支持的音频格式,包罗WAV, MP3等。
2. 如果音频文件未找到或无法读取,pygame.mixer.music.play( ) 会抛出一个非常。利用须要确保音频文件的路径精确,且文件存在。导入os库,用os.path.exists(music_file) 判断文件是否存在。
3. pygame.mixer.music.play() 是一个壅闭函数,在音频播放期间步调将不会实行其他操纵。如果须要在播放同时实行其他操纵,须要在一个单独的线程中调用pygame.mixer.music.play()。
4. 多线程须要导入threading库,例如:
          t = threading.Thread(target=play_music, args=(music_fn,0.8,))
          t.start()

原版资助择要

pygame.mixer

NAME
    pygame.mixer_music - pygame module for controlling streamed audio
FUNCTIONS
    fadeout(...)
        fadeout(time) -> None
        stop music playback after fading out
    
    get_busy(...)
        get_busy() -> bool
        check if the music stream is playing
    
    get_endevent(...)
        get_endevent() -> type
        get the event a channel sends when playback stops
    
    get_pos(...)
        get_pos() -> time
        get the music play time
    
    get_volume(...)
        get_volume() -> value
        get the music volume
    
    load(...)
        load(filename) -> None
        load(fileobj, namehint=) -> None
        Load a music file for playback
    
    pause(...)
        pause() -> None
        temporarily stop music playback
    
    play(...)
        play(loops=0, start=0.0, fade_ms=0) -> None
        Start the playback of the music stream
    
    queue(...)
        queue(filename) -> None
        queue(fileobj, namehint=, loops=0) -> None
        queue a sound file to follow the current
    
    rewind(...)
        rewind() -> None
        restart music
    
    set_endevent(...)
        set_endevent() -> None
        set_endevent(type) -> None
        have the music send an event when playback stops
    
    set_pos(...)
        set_pos(pos) -> None
        set position to play from
    
    set_volume(...)
        set_volume(volume) -> None
        set the music volume
    
    stop(...)
        stop() -> None
        stop the music playback
    
    unload(...)
        unload() -> None
        Unload the currently loaded music to free up resources
    
    unpause(...)
        unpause() -> None
        resume paused music

pygame.mixer.Sound

class Sound(builtins.object)
 |  Sound(filename) -> Sound
 |  Sound(file=filename) -> Sound
 |  Sound(file=pathlib_path) -> Sound
 |  Sound(buffer) -> Sound
 |  Sound(buffer=buffer) -> Sound
 |  Sound(object) -> Sound
 |  Sound(file=object) -> Sound
 |  Sound(array=object) -> Sound
 |  Create a new Sound object from a file or buffer object
 |  
 |  Methods defined here:
 |  
 |  __init__(self, /, *args, **kwargs)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  fadeout(...)
 |      fadeout(time) -> None
 |      stop sound playback after fading out
 |  
 |  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

完整代码:
  1. import pygame
  2. import sys, os
  3. import random
  4. import threading
  5. # 定义颜色
  6. BLACK = (0, 0, 0)
  7. WHITE = (255, 255, 255)
  8. RED   = (255, 0, 0)
  9. GREEN = (0, 255, 0)
  10. BLUE  = (0, 0, 255)
  11. GREY  = (220, 220, 220)  # 淡灰色
  12. DARK  = (120, 120, 120)  # 深灰色
  13. def init():
  14.     global screen, screen_size, scores
  15.     global snake_pos, food_pos, snake_speed
  16.     # 初始化pygame
  17.     scores = 0
  18.     pygame.init()
  19.     # 设置屏幕大小
  20.     screen_size = (640, 480)
  21.     screen = pygame.display.set_mode(screen_size)
  22.     # 设置游戏标题
  23.     pygame.display.set_caption("贪吃蛇")
  24.     # 蛇的初始位置
  25.     snake_pos = [[100, 100], [80, 100], [60, 100]]
  26.     # 食物的初始位置
  27.     food_pos = [300, 300]
  28.     # 蛇的初始速度
  29.     snake_speed = [20, 0]
  30. def play_music(mp3, volume = 1, loops = -1):
  31.     # 初始化pygame的mixer模块
  32.     pygame.mixer.init()
  33.     # 加载音乐文件
  34.     pygame.mixer.music.load(mp3)
  35.     # 控制音量
  36.     pygame.mixer.music.set_volume(volume)
  37.     # 播放音乐
  38.     pygame.mixer.music.play(loops)
  39. def play_sound(wav_no):
  40.     sound_fn = f'sound{wav_no}.wav'
  41.     if os.path.exists(sound_fn):
  42.         alert_sound = pygame.mixer.Sound(sound_fn)
  43.         alert_sound.play()
  44. def show_msg(msg, color = BLUE):
  45.     x = screen.get_rect().centerx
  46.     y = screen.get_rect().centery - 50
  47.     font = pygame.font.Font(None, 36)
  48.     text = font.render(msg, True, color)
  49.     text_rect = text.get_rect()
  50.     text_rect.centerx = x
  51.     text_rect.centery = y
  52.     rectangle1 = pygame.Rect(x-155, y-25, 320, 60)
  53.     rectangle2 = pygame.Rect(x-160, y-30, 320, 60)
  54.     pygame.draw.rect(screen, DARK, rectangle1)
  55.     pygame.draw.rect(screen, GREY, rectangle2)
  56.     pygame.draw.rect(screen, BLACK, rectangle2, 2) # 边框宽为2
  57.     screen.blit(text, text_rect)
  58.     pygame.display.flip()
  59. def repaint():
  60.     # 绘制游戏界面
  61.     screen.fill(WHITE)
  62.     # 定义线段的端点坐标
  63.     x,y = (-1,640,640,-1)*16, []
  64.     for i in range(36):
  65.         for _ in range(2):
  66.             y.append(19+i*20)
  67.     # 使用pygame.draw.lines()函数绘制线段
  68.     points = list(zip(x,y))
  69.     pygame.draw.lines(screen, GREY, False, points, 1) # 线宽为1
  70.     points = list(zip(y,x))
  71.     pygame.draw.lines(screen, GREY, False, points, 1)   
  72.     # 重画蛇和食物
  73.     for pos in snake_pos:
  74.         pygame.draw.rect(screen, GREEN, pygame.Rect(pos[0], pos[1], 20, 20))
  75.     pygame.draw.rect(screen, RED, pygame.Rect(food_pos[0], food_pos[1], 20, 20))
  76.     pygame.display.flip()
  77.     show_msg_at(22, 6, f'Scores: {scores}')
  78.     show_msg_at(410, 6, f'Snake coordinate: ({1+snake_pos[0][0]//20:2}, {1+snake_pos[0][1]//20:2})')
  79. def show_msg_at(x, y, msg):
  80.     font = pygame.font.SysFont('Consolas', 14)
  81.     text = font.render(msg, True, BLACK)
  82.     text_rect = text.get_rect()
  83.     text_rect.x, text_rect.y = x, y
  84.     screen.blit(text, text_rect)
  85.     pygame.display.flip()
  86. def game_quit():
  87.     pygame.quit()
  88.     sys.exit()
  89. def main():
  90.     global screen, screen_size, scores
  91.     global snake_pos, food_pos, snake_speed
  92.     is_running = False
  93.     is_paused = False
  94.     is_dead = False
  95.     is_mute = False
  96.    
  97.     repaint()
  98.     show_msg("Press any key to start ...")
  99.     # 创建一个线程来播放音乐
  100.     music_no = 1
  101.     music_fn = "voice1.mp3"
  102.     if os.path.exists(music_fn):
  103.         t = threading.Thread(target=play_music, args=(music_fn,0.8,))
  104.         t.start()
  105.     # 主循环
  106.     while True:
  107.         # 处理游戏事件
  108.         for event in pygame.event.get():
  109.             if event.type == pygame.QUIT:
  110.                 game_quit()
  111.             elif event.type == pygame.KEYDOWN:
  112.                 if event.key == pygame.K_UP:
  113.                     snake_speed = [0, -20]
  114.                 elif event.key == pygame.K_DOWN:
  115.                     snake_speed = [0, 20]
  116.                 elif event.key == pygame.K_LEFT:
  117.                     snake_speed = [-20, 0]
  118.                 elif event.key == pygame.K_RIGHT:
  119.                     snake_speed = [20, 0]
  120.                 elif event.key == pygame.K_y:
  121.                     if is_dead:
  122.                         init()
  123.                         is_dead = False
  124.                     is_running = True
  125.                 elif event.key == pygame.K_n:
  126.                     if is_dead: game_quit()
  127.                     else: is_running = True
  128.                 elif event.key == pygame.K_SPACE:
  129.                     if is_dead: continue
  130.                     if is_paused: is_paused = False
  131.                     is_running = True
  132.                 elif event.key == pygame.K_ESCAPE:
  133.                     if is_running:
  134.                         show_msg(">>> Paused <<<")
  135.                         is_paused = not is_paused
  136.                         if not is_mute and is_paused: play_sound(1)
  137.                 elif event.key == pygame.K_m and event.mod & pygame.KMOD_CTRL:
  138.                     # Ctrl+M 切换背景音乐
  139.                     is_mute = False
  140.                     music_no = 1 if music_no == 3 else music_no + 1
  141.                     music_fn = f"voice{music_no}.mp3"
  142.                     if os.path.exists(music_fn):
  143.                         t = threading.Thread(target=play_music, args=(music_fn,0.8,))
  144.                         t.start()
  145.                 elif event.key == pygame.K_s and event.mod & pygame.KMOD_CTRL:
  146.                     # Ctrl+S 切换静音状态
  147.                     is_mute = not is_mute
  148.                     if is_mute:
  149.                         pygame.mixer.music.pause()
  150.                     else:
  151.                         pygame.mixer.music.unpause()
  152.                     is_running = True
  153.                 else: # 任意键进入开始状态
  154.                     if is_dead: continue
  155.                     is_running = True
  156.         if not is_running: continue
  157.         if is_paused and is_running: continue
  158.         # 更新蛇的位置
  159.         snake_pos.insert(0, [snake_pos[0][0] + snake_speed[0], snake_pos[0][1] + snake_speed[1]])
  160.         # 检查蛇头是否碰到食物
  161.         if snake_pos[0] == food_pos:
  162.             scores += 10 + len(snake_pos) // 5
  163.             if not 1 < snake_pos[0][0]//20 < 30 or not 1 < snake_pos[0][1]//20 < 22:
  164.                 scores += 5
  165.             if not is_mute: play_sound(2)
  166.             food_pos = [random.randrange(1, screen_size[0] // 20) * 20, random.randrange(1, screen_size[1] // 20) * 20]
  167.         else:
  168.             snake_pos.pop()
  169.         # 检查蛇头是否碰到墙壁或者蛇身
  170.         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:]:
  171.             show_msg("Dead! Again? (Y or N)")
  172.             is_running = False
  173.             is_dead = True
  174.             if not is_mute: play_sound(3)
  175.             continue
  176.         # 重画界面及蛇和食物
  177.         repaint()
  178.         # 控制游戏速度
  179.         pygame.time.Clock().tick(10)
  180. if __name__ == "__main__":
  181.     init()
  182.     main()
复制代码
改进过程四

增长WASD方向键

DOS时代的游戏常常用W、A、S、D四个字母,对应上左下右四个方向键,上面的代码中,快捷键 ctrl+S 换成 strl+Q 克制辩论。
别的,游戏中按了与进步方向相反的键,相当于蛇的自杀行为。为了克制这个bug,引入一个表现蛇移动方向的变量direction:
    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 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]
增长退失变乱简直认

pygame 没有弹窗一类的方法,导入tkinter库,由messagebox来实现:
from tkinter import messagebox
......
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                if messagebox.askyesno("确认", "是否要退出?"):
                    game_quit()
           ......
结果如下:

最后,增长了按停息键时配景音乐也停息的功能;别的修改了一些小错误,估计还会有bug出现,状态变量设置多了感觉逻辑有点乱。

小结

本文以贪吃蛇游戏为例,对pygame编程的一个简单框架举行了深入的学习,包罗对画图、字体、音乐等各个方面操纵的各种方法和函数,学习后在pygame这方面的编程本事有所上进进步。
pygame编程框架

  1. import pygame
  2. import sys
  3. # 初始化Pygame
  4. pygame.init()
  5. # 设置窗口大小
  6. screen_size = (800, 600)
  7. # 创建窗口
  8. screen = pygame.display.set_mode(screen_size)
  9. # 设置窗口标题
  10. pygame.display.set_caption("Pygame Example")
  11. # 主循环
  12. while True:
  13.     # 处理事件
  14.     for event in pygame.event.get():
  15.         if event.type == pygame.QUIT:
  16.             pygame.quit()
  17.             sys.exit()
  18.         elif event.type == ...
  19.             ... // 处理按键事件
  20.     # 填充背景色
  21.     screen.fill((255, 255, 255))
  22.     # 绘制矩形
  23.     pygame.draw.rect(screen, (0, 0, 255), (400, 300, 100, 50))
  24.     # 更新显示
  25.     pygame.display.flip()
复制代码
完整代码

  1. import pygame
  2. from sys import exit as system
  3. from random  import randrange
  4. from os.path import exists
  5. from tkinter import messagebox
  6. from threading import Thread
  7. # 定义颜色
  8. BLACK = (0, 0, 0)
  9. WHITE = (255, 255, 255)
  10. RED   = (255, 0, 0)
  11. GREEN = (0, 255, 0)
  12. BLUE  = (0, 0, 255)
  13. GREY  = (220, 220, 220)  # 淡灰色
  14. DARK  = (120, 120, 120)  # 深灰色
  15. def init():
  16.     global screen, screen_size, scores
  17.     global snake_pos, food_pos, snake_speed
  18.     # 初始化pygame
  19.     scores = 0
  20.     pygame.init()
  21.     # 设置屏幕大小
  22.     screen_size = (640, 480)
  23.     screen = pygame.display.set_mode(screen_size)
  24.     # 设置游戏标题
  25.     pygame.display.set_caption("贪吃蛇")
  26.     # 蛇的初始位置
  27.     snake_pos = [[100, 100], [80, 100], [60, 100]]
  28.     # 食物的初始位置
  29.     food_pos = [300, 300]
  30.     # 蛇的初始速度
  31.     snake_speed = [20, 0]
  32. def play_music(mp3, volume = 1, loops = -1):
  33.     # 初始化pygame的mixer模块
  34.     pygame.mixer.init()
  35.     # 加载音乐文件
  36.     pygame.mixer.music.load(mp3)
  37.     # 控制音量
  38.     pygame.mixer.music.set_volume(volume)
  39.     # 播放音乐
  40.     pygame.mixer.music.play(loops)
  41. def play_sound(wav_no):
  42.     sound_fn = f'sound{wav_no}.wav'
  43.     if exists(sound_fn):
  44.         alert_sound = pygame.mixer.Sound(sound_fn)
  45.         alert_sound.play()
  46. def show_msg(msg, color = BLUE):
  47.     x = screen.get_rect().centerx
  48.     y = screen.get_rect().centery - 50
  49.     font = pygame.font.Font(None, 36)
  50.     text = font.render(msg, True, color)
  51.     text_rect = text.get_rect()
  52.     text_rect.centerx = x
  53.     text_rect.centery = y
  54.     rectangle1 = pygame.Rect(x-155, y-25, 320, 60)
  55.     rectangle2 = pygame.Rect(x-160, y-30, 320, 60)
  56.     pygame.draw.rect(screen, DARK, rectangle1)
  57.     pygame.draw.rect(screen, GREY, rectangle2)
  58.     pygame.draw.rect(screen, BLACK, rectangle2, 2) # 边框宽为2
  59.     screen.blit(text, text_rect)
  60.     pygame.display.flip()
  61. def repaint():
  62.     # 绘制游戏界面
  63.     screen.fill(WHITE)
  64.     # 定义线段的端点坐标
  65.     x,y = (-1,640,640,-1)*16, []
  66.     for i in range(36):
  67.         for _ in range(2):
  68.             y.append(19+i*20)
  69.     # 使用pygame.draw.lines()函数绘制线段
  70.     points = list(zip(x,y))
  71.     pygame.draw.lines(screen, GREY, False, points, 1) # 线宽为1
  72.     points = list(zip(y,x))
  73.     pygame.draw.lines(screen, GREY, False, points, 1)   
  74.     # 重画蛇和食物
  75.     for pos in snake_pos:
  76.         pygame.draw.rect(screen, GREEN, pygame.Rect(pos[0], pos[1], 20, 20))
  77.     pygame.draw.rect(screen, RED, pygame.Rect(food_pos[0], food_pos[1], 20, 20))
  78.     pygame.display.flip()
  79.     show_msg_at(22, 6, f'Scores: {scores}')
  80.     show_msg_at(410, 6, f'Snake coordinate: ({1+snake_pos[0][0]//20:2}, {1+snake_pos[0][1]//20:2})')
  81. def show_msg_at(x, y, msg):
  82.     font = pygame.font.SysFont('Consolas', 14)
  83.     text = font.render(msg, True, BLACK)
  84.     text_rect = text.get_rect()
  85.     text_rect.x, text_rect.y = x, y
  86.     screen.blit(text, text_rect)
  87.     pygame.display.flip()
  88. def game_quit():
  89.     pygame.quit()
  90.     system()
  91. def main():
  92.     global screen, screen_size, scores
  93.     global snake_pos, food_pos, snake_speed
  94.     is_running = False
  95.     is_paused = False
  96.     is_dead = False
  97.     is_mute = False
  98.    
  99.     repaint()
  100.     show_msg("Press any key to start ...")
  101.     # 创建一个线程来播放音乐
  102.     music_no = 1
  103.     music_fn = "voice1.mp3"
  104.     if exists(music_fn):
  105.         t = Thread(target=play_music, args=(music_fn,0.8,))
  106.         t.start()
  107.     # 主循环
  108.     UP, DOWN, LEFT, RIGHT = 1, 2, 3, 4
  109.     direction = RIGHT
  110.     while True:
  111.         # 处理游戏事件
  112.         for event in pygame.event.get():
  113.             if event.type == pygame.QUIT:
  114.                 if messagebox.askyesno("确认", "是否要退出?"):
  115.                     game_quit()
  116.             elif event.type == pygame.KEYDOWN:
  117.                 # 增加 WASD 四键对应 上左下右 方向键
  118.                 if not is_paused:
  119.                     if   (event.key == pygame.K_w or event.key == pygame.K_UP)    and direction != DOWN:
  120.                         direction = UP
  121.                     elif (event.key == pygame.K_s or event.key == pygame.K_DOWN)  and direction != UP:
  122.                         direction = DOWN
  123.                     elif (event.key == pygame.K_a or event.key == pygame.K_LEFT)  and direction != RIGHT:
  124.                         direction = LEFT
  125.                     elif (event.key == pygame.K_f or event.key == pygame.K_RIGHT) and direction != LEFT:
  126.                         direction = RIGHT
  127.                 if event.key == pygame.K_y:
  128.                     if is_dead:
  129.                         init()
  130.                         is_dead = False
  131.                     is_running = True
  132.                 elif event.key == pygame.K_n:
  133.                     if is_dead: game_quit()
  134.                     else: is_running = True
  135.                 elif event.key == pygame.K_SPACE:
  136.                     if is_dead: continue
  137.                     if is_paused:
  138.                         is_paused = False
  139.                         pygame.mixer.music.unpause()
  140.                     is_running = True
  141.                 elif event.key == pygame.K_ESCAPE:
  142.                     if is_running:
  143.                         show_msg(">>> Paused <<<")
  144.                         is_paused = not is_paused
  145.                         if is_paused:
  146.                             pygame.mixer.music.pause()
  147.                             if not is_mute: play_sound(1)
  148.                         else:
  149.                             pygame.mixer.music.unpause()
  150.                 elif event.key == pygame.K_m and event.mod & pygame.KMOD_CTRL:
  151.                     # Ctrl+M 切换背景音乐
  152.                     is_mute = False
  153.                     music_no = 1 if music_no == 3 else music_no + 1
  154.                     music_fn = f"voice{music_no}.mp3"
  155.                     if exists(music_fn):
  156.                         t = Thread(target=play_music, args=(music_fn,0.8,))
  157.                         t.start()
  158.                 elif event.key == pygame.K_q and event.mod & pygame.KMOD_CTRL:
  159.                     # Ctrl+Q 切换静音状态
  160.                     is_mute = not is_mute
  161.                     if is_mute:
  162.                         pygame.mixer.music.pause()
  163.                     else:
  164.                         pygame.mixer.music.unpause()
  165.                     is_running = True
  166.                 else: # 任意键进入开始状态
  167.                     if is_dead: continue
  168.                     is_running = True
  169.         if not is_running: continue
  170.         if is_paused: continue
  171.         if direction   == UP:
  172.             snake_speed = [0, -20]
  173.         elif direction == DOWN:
  174.             snake_speed = [0, 20]
  175.         elif direction == LEFT:
  176.             snake_speed = [-20, 0]
  177.         elif direction == RIGHT:
  178.             snake_speed = [20, 0]
  179.         # 更新蛇的位置
  180.         snake_pos.insert(0, [snake_pos[0][0] + snake_speed[0], snake_pos[0][1] + snake_speed[1]])
  181.         # 检查蛇头是否碰到食物
  182.         if snake_pos[0] == food_pos:
  183.             scores += 10 + len(snake_pos) // 5
  184.             if not 1 < snake_pos[0][0]//20 < 30 or not 1 < snake_pos[0][1]//20 < 22:
  185.                 scores += 5
  186.             if not is_mute: play_sound(2)
  187.             food_pos = [randrange(1, screen_size[0] // 20) * 20, randrange(1, screen_size[1] // 20) * 20]
  188.         else:
  189.             snake_pos.pop()
  190.         # 检查蛇头是否碰到墙壁或者蛇身
  191.         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:]:
  192.             show_msg("Dead! Again? (Y or N)")
  193.             is_running = False
  194.             is_dead = True
  195.             direction = RIGHT
  196.             if not is_mute: play_sound(3)
  197.             continue
  198.         # 重画界面及蛇和食物
  199.         repaint()
  200.         # 控制游戏速度
  201.         pygame.time.Clock().tick(10)
  202. if __name__ == "__main__":
  203.     init()
  204.     main()
复制代码
终极版的源代码及音乐文件列表如下:

下载所在: 
https://download.csdn.net/download/boysoft2002/88231961

来源:https://blog.csdn.net/boysoft2002/article/details/132370232
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则