一觉醒来,Python也可以做游戏了,Pygame游戏优秀样例合集

目录

0.开篇废话(允许跳过)

1.今日样例

2.开始制作游戏

2.1 俄罗斯方块(Tetris)

2.2 扫雷(Minesweeper)

2.3 贪吃蛇(Snake)

2.4 飞机大战(Airplane War)


0.开篇废话(允许跳过)

接下来,系列“游戏制作,但是使用Python,还能离线玩?干!”会为您介绍使用Python完成游戏创作的相关内容,但是,请注意:Python不能用于开发过度精细3D游戏,如想尝试3D游戏,请移步:虚幻引擎5 - Unreal Engine

请认准系列名称,都在部分0.中,学习前请确认,以免造成内容不连续

今天的内容为  Pygame 优秀样例

1.今日样例

1.俄罗斯方块(Tetris)
2.扫雷(Minesweeper)
3.贪吃蛇(Snake)
4.飞机大战(Airplane War)

2.开始制作游戏

优秀样例分享不会过多的解释原理,我将会介绍4个游戏的制作方法

2.1 俄罗斯方块(Tetris)

  1. 导入Pygame,创建游戏窗口并初始化Pygame库
  2. 加载游戏素材,包括背景图、方块图片、音乐、素材不同造型等(也可以使用Pygame绘画)
  3. 定义游戏状态,包括方块和游戏区域
  4. 实现方块下落和旋转功能
  5. 实现消除行和计分功能
  6. 实现游戏结束判定
  7. 实现主循环并调用游戏函数

 代码:

import pygame
import random

"""
10 x 20 square grid
shapes: S, Z, I, O, J, L, T
represented in order by 0 - 6
"""

pygame.font.init()

# GLOBALS VARS
s_width = 800
s_height = 700
play_width = 300  # meaning 300 // 10 = 30 width per block
play_height = 600  # meaning 600 // 20 = 20 height per blo ck
block_size = 30

top_left_x = (s_width - play_width) // 2
top_left_y = s_height - play_height


# SHAPE FORMATS

S = [['.....',
      '.....',
      '..00.',
      '.00..',
      '.....'],
     ['.....',
      '..0..',
      '..00.',
      '...0.',
      '.....']]

Z = [['.....',
      '.....',
      '.00..',
      '..00.',
      '.....'],
     ['.....',
      '..0..',
      '.00..',
      '.0...',
      '.....']]

I = [['..0..',
      '..0..',
      '..0..',
      '..0..',
      '.....'],
     ['.....',
      '0000.',
      '.....',
      '.....',
      '.....']]

O = [['.....',
      '.....',
      '.00..',
      '.00..',
      '.....']]

J = [['.....',
      '.0...',
      '.000.',
      '.....',
      '.....'],
     ['.....',
      '..00.',
      '..0..',
      '..0..',
      '.....'],
     ['.....',
      '.....',
      '.000.',
      '...0.',
      '.....'],
     ['.....',
      '..0..',
      '..0..',
      '.00..',
      '.....']]

L = [['.....',
      '...0.',
      '.000.',
      '.....',
      '.....'],
     ['.....',
      '..0..',
      '..0..',
      '..00.',
      '.....'],
     ['.....',
      '.....',
      '.000.',
      '.0...',
      '.....'],
     ['.....',
      '.00..',
      '..0..',
      '..0..',
      '.....']]

T = [['.....',
      '..0..',
      '.000.',
      '.....',
      '.....'],
     ['.....',
      '..0..',
      '..00.',
      '..0..',
      '.....'],
     ['.....',
      '.....',
      '.000.',
      '..0..',
      '.....'],
     ['.....',
      '..0..',
      '.00..',
      '..0..',
      '.....']]

shapes = [S, Z, I, O, J, L, T]
shape_colors = [(0, 255, 0), (255, 0, 0), (0, 255, 255), (255, 255, 0), (255, 165, 0), (0, 0, 255), (128, 0, 128)]
# index 0 - 6 represent shape


class Piece(object):
    rows = 20  # y
    columns = 10  # x

    def __init__(self, column, row, shape):
        self.x = column
        self.y = row
        self.shape = shape
        self.color = shape_colors[shapes.index(shape)]
        self.rotation = 0  # number from 0-3


def create_grid(locked_positions={}):
    grid = [[(0,0,0) for x in range(10)] for x in range(20)]

    for i in range(len(grid)):
        for j in range(len(grid[i])):
            if (j,i) in locked_positions:
                c = locked_positions[(j,i)]
                grid[i][j] = c
    return grid


def convert_shape_format(shape):
    positions = []
    format = shape.shape[shape.rotation % len(shape.shape)]

    for i, line in enumerate(format):
        row = list(line)
        for j, column in enumerate(row):
            if column == '0':
                positions.append((shape.x + j, shape.y + i))

    for i, pos in enumerate(positions):
        positions[i] = (pos[0] - 2, pos[1] - 4)

    return positions


def valid_space(shape, grid):
    accepted_positions = [[(j, i) for j in range(10) if grid[i][j] == (0,0,0)] for i in range(20)]
    accepted_positions = [j for sub in accepted_positions for j in sub]
    formatted = convert_shape_format(shape)

    for pos in formatted:
        if pos not in accepted_positions:
            if pos[1] > -1:
                return False

    return True


def check_lost(positions):
    for pos in positions:
        x, y = pos
        if y < 1:
            return True
    return False


def get_shape():
    global shapes, shape_colors

    return Piece(5, 0, random.choice(shapes))


def draw_text_middle(text, size, color, surface):
    font = pygame.font.SysFont('comicsans', size, bold=True)
    label = font.render(text, 1, color)

    surface.blit(label, (top_left_x + play_width/2 - (label.get_width() / 2), top_left_y + play_height/2 - label.get_height()/2))


def draw_grid(surface, row, col):
    sx = top_left_x
    sy = top_left_y
    for i in range(row):
        pygame.draw.line(surface, (128,128,128), (sx, sy+ i*30), (sx + play_width, sy + i * 30))  # horizontal lines
        for j in range(col):
            pygame.draw.line(surface, (128,128,128), (sx + j * 30, sy), (sx + j * 30, sy + play_height))  # vertical lines


def clear_rows(grid, locked):
    # need to see if row is clear the shift every other row above down one

    inc = 0
    for i in range(len(grid)-1,-1,-1):
        row = grid[i]
        if (0, 0, 0) not in row:
            inc += 1
            # add positions to remove from locked
            ind = i
            for j in range(len(row)):
                try:
                    del locked[(j, i)]
                except:
                    continue
    if inc > 0:
        for key in sorted(list(locked), key=lambda x: x[1])[::-1]:
            x, y = key
            if y < ind:
                newKey = (x, y + inc)
                locked[newKey] = locked.pop(key)


def draw_next_shape(shape, surface):
    font = pygame.font.SysFont('comicsans', 30)
    label = font.render('Next Shape', 1, (255,255,255))

    sx = top_left_x + play_width + 50
    sy = top_left_y + play_height/2 - 100
    format = shape.shape[shape.rotation % len(shape.shape)]

    for i, line in enumerate(format):
        row = list(line)
        for j, column in enumerate(row):
            if column == '0':
                pygame.draw.rect(surface, shape.color, (sx + j*30, sy + i*30, 30, 30), 0)

    surface.blit(label, (sx + 10, sy- 30))


def draw_window(surface):
    surface.fill((0,0,0))
    # Tetris Title
    font = pygame.font.SysFont('comicsans', 60)
    label = font.render('TETRIS', 1, (255,255,255))

    surface.blit(label, (top_left_x + play_width / 2 - (label.get_width() / 2), 30))

    for i in range(len(grid)):
        for j in range(len(grid[i])):
            pygame.draw.rect(surface, grid[i][j], (top_left_x + j* 30, top_left_y + i * 30, 30, 30), 0)

    # draw grid and border
    draw_grid(surface, 20, 10)
    pygame.draw.rect(surface, (255, 0, 0), (top_left_x, top_left_y, play_width, play_height), 5)
    # pygame.display.update()


def main():
    global grid

    locked_positions = {}  # (x,y):(255,0,0)
    grid = create_grid(locked_positions)

    change_piece = False
    run = True
    current_piece = get_shape()
    next_piece = get_shape()
    clock = pygame.time.Clock()
    fall_time = 0
    level_time = 0
    fall_speed = 0.27
    score = 0
    
    while run:

        grid = create_grid(locked_positions)
        fall_time += clock.get_rawtime()
        level_time += clock.get_rawtime()
        clock.tick()

        if level_time/1000 > 4:
            level_time = 0
            if fall_speed > 0.15:
                fall_speed -= 0.005
            

        # PIECE FALLING CODE
        if fall_time/1000 >= fall_speed:
            fall_time = 0
            current_piece.y += 1
            if not (valid_space(current_piece, grid)) and current_piece.y > 0:
                current_piece.y -= 1
                change_piece = True

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
                pygame.display.quit()
                quit()

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    current_piece.x -= 1
                    if not valid_space(current_piece, grid):
                        current_piece.x += 1

                elif event.key == pygame.K_RIGHT:
                    current_piece.x += 1
                    if not valid_space(current_piece, grid):
                        current_piece.x -= 1
                elif event.key == pygame.K_UP:
                    # rotate shape
                    current_piece.rotation = current_piece.rotation + 1 % len(current_piece.shape)
                    if not valid_space(current_piece, grid):
                        current_piece.rotation = current_piece.rotation - 1 % len(current_piece.shape)

                if event.key == pygame.K_DOWN:
                    # move shape down
                    current_piece.y += 1
                    if not valid_space(current_piece, grid):
                        current_piece.y -= 1

                '''if event.key == pygame.K_SPACE:
                    while valid_space(current_piece, grid):
                        current_piece.y += 1
                    current_piece.y -= 1
                    print(convert_shape_format(current_piece))'''  # todo fix

        shape_pos = convert_shape_format(current_piece)

        # add piece to the grid for drawing
        for i in range(len(shape_pos)):
            x, y = shape_pos[i]
            if y > -1:
                grid[y][x] = current_piece.color

        # IF PIECE HIT GROUND
        if change_piece:
            for pos in shape_pos:
                p = (pos[0], pos[1])
                locked_positions[p] = current_piece.color
            current_piece = next_piece
            next_piece = get_shape()
            change_piece = False

            # call four times to check for multiple clear rows
            if clear_rows(grid, locked_positions):
                score += 10

        draw_window(win)
        draw_next_shape(next_piece, win)
        pygame.display.update()

        # Check if user lost
        if check_lost(locked_positions):
            run = False

    draw_text_middle("You Lost", 40, (255,255,255), win)
    pygame.display.update()
    pygame.time.delay(2000)


def main_menu():
    run = True
    while run:
        win.fill((0,0,0))
        draw_text_middle('Press any key to begin.', 60, (255, 255, 255), win)
        pygame.display.update()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False

            if event.type == pygame.KEYDOWN:
                main()
    pygame.quit()


win = pygame.display.set_mode((s_width, s_height))
pygame.display.set_caption('Tetris')

main_menu()  # start game





2.2 扫雷(Minesweeper)

  1. 创建游戏窗口并初始化Pygame库
  2. 加载游戏素材,包括背景图和方格图片
  3. 定义游戏状态,包括方格、地雷和游戏区域
  4. 实现方格揭开、插旗、判定是否为地雷功能
  5. 实现游戏胜利判定
  6. 实现游戏失败判定
  7. 实现主循环并调用游戏函数

代码: 

​
import random, pygame, sys
from pygame.locals import *

# set constants
FPS = 30
WINDOWWIDTH = 800
WINDOWHEIGHT = 900
BOXSIZE = 30
GAPSIZE = 5
FIELDWIDTH = 20
FIELDHEIGHT = 20
XMARGIN = int((WINDOWWIDTH-(FIELDWIDTH*(BOXSIZE+GAPSIZE)))/2)
YMARGIN = XMARGIN
MINESTOTAL = 60

# assertions
assert MINESTOTAL < FIELDHEIGHT*FIELDWIDTH, 'More mines than boxes'
assert BOXSIZE^2 * (FIELDHEIGHT*FIELDWIDTH) < WINDOWHEIGHT*WINDOWWIDTH, 'Boxes will not fit on screen'
assert BOXSIZE/2 > 5, 'Bounding errors when drawing rectangle, cannot use half-5 in drawMinesNumbers'

# assign colors 
LIGHTGRAY = (225, 225, 225)
DARKGRAY = (160, 160, 160)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 128, 0)

# set up major colors
BGCOLOR = WHITE
FIELDCOLOR = BLACK
BOXCOLOR_COV = DARKGRAY # covered box color
BOXCOLOR_REV = LIGHTGRAY # revealed box color
MINECOLOR = BLACK
TEXTCOLOR_1 = BLUE
TEXTCOLOR_2 = RED
TEXTCOLOR_3 = BLACK
HILITECOLOR = GREEN
RESETBGCOLOR = LIGHTGRAY
MINEMARK_COV = RED

# set up font 
FONTTYPE = 'Courier New'
FONTSIZE = 20

def main():

    # initialize global variables & pygame module, set caption
    global FPSCLOCK, DISPLAYSURFACE, BASICFONT, RESET_SURF, RESET_RECT, SHOW_SURF, SHOW_RECT
    pygame.init()
    pygame.display.set_caption('Minesweeper')
    FPSCLOCK = pygame.time.Clock()
    DISPLAYSURFACE = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
    BASICFONT = pygame.font.SysFont(FONTTYPE, FONTSIZE)

    # obtain reset & show objects and rects
    RESET_SURF, RESET_RECT = drawButton('RESET', TEXTCOLOR_3, RESETBGCOLOR, WINDOWWIDTH/2, WINDOWHEIGHT-120)
    SHOW_SURF, SHOW_RECT = drawButton('SHOW ALL', TEXTCOLOR_3, RESETBGCOLOR, WINDOWWIDTH/2, WINDOWHEIGHT-95)

    # stores XY of mouse events
    mouse_x = 0
    mouse_y = 0

    # set up data structures and lists
    mineField, zeroListXY, revealedBoxes, markedMines = gameSetup()

    # set background color
    DISPLAYSURFACE.fill(BGCOLOR)

    # main game loop
    while True:

        # check for quit function
        checkForKeyPress()

        # initialize input booleans
        mouseClicked = False
        spacePressed = False

        # draw field
        DISPLAYSURFACE.fill(BGCOLOR)
        pygame.draw.rect(DISPLAYSURFACE, FIELDCOLOR, (XMARGIN-5, YMARGIN-5, (BOXSIZE+GAPSIZE)*FIELDWIDTH+5, (BOXSIZE+GAPSIZE)*FIELDHEIGHT+5))
        drawField()
        drawMinesNumbers(mineField)        

        # event handling loop
        for event in pygame.event.get(): 
            if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
                terminate()
            elif event.type == MOUSEMOTION:
                mouse_x, mouse_y = event.pos
            elif event.type == MOUSEBUTTONDOWN:
                mouse_x, mouse_y = event.pos
                mouseClicked = True
            elif event.type == KEYDOWN:
                if event.key == K_SPACE:
                    spacePressed = True
            elif event.type == KEYUP:
                if event.key == K_SPACE:
                    spacePressed = False

        # draw covers
        drawCovers(revealedBoxes, markedMines)

        # mine marker tip
        tipFont = pygame.font.SysFont(FONTTYPE, 16) ## not using BASICFONT - too big
        drawText('Tip: Highlight a box and press space (rather than click the mouse)', tipFont, TEXTCOLOR_3, DISPLAYSURFACE, WINDOWWIDTH/2, WINDOWHEIGHT-60)
        drawText('to mark areas that you think contain mines.', tipFont, TEXTCOLOR_3, DISPLAYSURFACE, WINDOWWIDTH/2, WINDOWHEIGHT-40)
            
        # determine boxes at clicked areas
        box_x, box_y = getBoxAtPixel(mouse_x, mouse_y)

        # mouse not over a box in field
        if (box_x, box_y) == (None, None):

            # check if reset box is clicked
            if RESET_RECT.collidepoint(mouse_x, mouse_y):
                highlightButton(RESET_RECT)
                if mouseClicked: 
                    mineField, zeroListXY, revealedBoxes, markedMines = gameSetup()

            # check if show box is clicked
            if SHOW_RECT.collidepoint(mouse_x, mouse_y):
                highlightButton(SHOW_RECT)
                if mouseClicked:
                    revealedBoxes = blankRevealedBoxData(True)

        # mouse currently over box in field
        else:

            # highlight unrevealed box
            if not revealedBoxes[box_x][box_y]: 
                highlightBox(box_x, box_y)

                # mark mines
                if spacePressed:
                    markedMines.append([box_x, box_y])
                    
                # reveal clicked boxes
                if mouseClicked:
                    revealedBoxes[box_x][box_y] = True

                    # when 0 is revealed, show relevant boxes
                    if mineField[box_x][box_y] == '[0]':
                        showNumbers(revealedBoxes, mineField, box_x, box_y, zeroListXY)

                    # when mine is revealed, show mines
                    if mineField[box_x][box_y] == '[X]':
                        showMines(revealedBoxes, mineField, box_x, box_y)
                        gameOverAnimation(mineField, revealedBoxes, markedMines, 'LOSS')
                        mineField, zeroListXY, revealedBoxes, markedMines = gameSetup()

        # check if player has won 
        if gameWon(revealedBoxes, mineField):
            gameOverAnimation(mineField, revealedBoxes, markedMines, 'WIN')
            mineField, zeroListXY, revealedBoxes, markedMines = gameSetup()
            
        # redraw screen, wait clock tick
        pygame.display.update()
        FPSCLOCK.tick(FPS)
    
def blankField():

   # creates blank FIELDWIDTH x FIELDHEIGHT data structure

    field = []
    for x in range(FIELDWIDTH):
        field.append([]) 
        for y in range(FIELDHEIGHT):
            field[x].append('[ ]')
    return field

def placeMines(field): 

    # places mines in FIELDWIDTH x FIELDHEIGHT data structure
    # requires blank field as input

    mineCount = 0
    xy = [] 
    while mineCount < MINESTOTAL: 
        x = random.randint(0,FIELDWIDTH-1)
        y = random.randint(0,FIELDHEIGHT-1)
        xy.append([x,y]) 
        if xy.count([x,y]) > 1: 
            xy.remove([x,y]) 
        else: 
            field[x][y] = '[X]' 
            mineCount += 1

def isThereMine(field, x, y): 

    # checks if mine is located at specific box on field

    return field[x][y] == '[X]'  

def placeNumbers(field): 

    # places numbers in FIELDWIDTH x FIELDHEIGHT data structure
    # requires field with mines as input

    for x in range(FIELDWIDTH):
        for y in range(FIELDHEIGHT):
            if not isThereMine(field, x, y):
                count = 0
                if x != 0: 
                    if isThereMine(field, x-1, y):
                        count += 1
                    if y != 0: 
                        if isThereMine(field, x-1, y-1):
                            count += 1
                    if y != FIELDHEIGHT-1: 
                        if isThereMine(field, x-1, y+1):
                            count += 1
                if x != FIELDWIDTH-1: 
                    if isThereMine(field, x+1, y):
                        count += 1
                    if y != 0: 
                        if isThereMine(field, x+1, y-1):
                            count += 1
                    if y != FIELDHEIGHT-1: 
                        if isThereMine(field, x+1, y+1):
                            count += 1
                if y != 0: 
                    if isThereMine(field, x, y-1):
                        count += 1
                if y != FIELDHEIGHT-1: 
                    if isThereMine(field, x, y+1):
                        count += 1
                field[x][y] = '[%s]' %(count)

def blankRevealedBoxData(val):

    # returns FIELDWIDTH x FIELDHEIGHT data structure different from the field data structure
    # each item in data structure is boolean (val) to show whether box at those fieldwidth & fieldheight coordinates should be revealed

    revealedBoxes = []
    for i in range(FIELDWIDTH):
        revealedBoxes.append([val] * FIELDHEIGHT)
    return revealedBoxes

def gameSetup():

    # set up mine field data structure, list of all zeros for recursion, and revealed box boolean data structure

    mineField = blankField()
    placeMines(mineField)
    placeNumbers(mineField)
    zeroListXY = []
    markedMines = []
    revealedBoxes = blankRevealedBoxData(False)

    return mineField, zeroListXY, revealedBoxes, markedMines

def drawField():

    # draws field GUI and reset button

    for box_x in range(FIELDWIDTH):
        for box_y in range(FIELDHEIGHT):
            left, top = getLeftTopXY(box_x, box_y)
            pygame.draw.rect(DISPLAYSURFACE, BOXCOLOR_REV, (left, top, BOXSIZE, BOXSIZE))

    DISPLAYSURFACE.blit(RESET_SURF, RESET_RECT)
    DISPLAYSURFACE.blit(SHOW_SURF, SHOW_RECT)

def drawMinesNumbers(field):
    
    # draws mines and numbers onto GUI
    # field should have mines and numbers

    half = int(BOXSIZE*0.5) 
    quarter = int(BOXSIZE*0.25)
    eighth = int(BOXSIZE*0.125)
    
    for box_x in range(FIELDWIDTH):
        for box_y in range(FIELDHEIGHT):
            left, top = getLeftTopXY(box_x, box_y)
            center_x, center_y = getCenterXY(box_x, box_y)
            if field[box_x][box_y] == '[X]':
                pygame.draw.circle(DISPLAYSURFACE, MINECOLOR, (left+half, top+half), quarter)
                pygame.draw.circle(DISPLAYSURFACE, WHITE, (left+half, top+half), eighth)
                pygame.draw.line(DISPLAYSURFACE, MINECOLOR, (left+eighth, top+half), (left+half+quarter+eighth, top+half))
                pygame.draw.line(DISPLAYSURFACE, MINECOLOR, (left+half, top+eighth), (left+half, top+half+quarter+eighth))
                pygame.draw.line(DISPLAYSURFACE, MINECOLOR, (left+quarter, top+quarter), (left+half+quarter, top+half+quarter))
                pygame.draw.line(DISPLAYSURFACE, MINECOLOR, (left+quarter, top+half+quarter), (left+half+quarter, top+quarter))
            else: 
                for i in range(1,9):
                    if field[box_x][box_y] == '[' + str(i) + ']':
                        if i in range(1,3):
                            textColor = TEXTCOLOR_1
                        else:
                            textColor = TEXTCOLOR_2
                        drawText(str(i), BASICFONT, textColor, DISPLAYSURFACE, center_x, center_y)

def showNumbers(revealedBoxes, mineField, box_x, box_y, zeroListXY):

    # modifies revealedBox data strucure if chosen box_x & box_y is [0] 
    # show all boxes using recursion
    
    revealedBoxes[box_x][box_y] = True
    revealAdjacentBoxes(revealedBoxes, box_x, box_y)
    for i,j in getAdjacentBoxesXY(mineField, box_x, box_y):
        if mineField[i][j] == '[0]' and [i,j] not in zeroListXY:
            zeroListXY.append([i,j])
            showNumbers(revealedBoxes, mineField, i, j, zeroListXY)

def showMines(revealedBoxes, mineField, box_x, box_y): 

    # modifies revealedBox data strucure if chosen box_x & box_y is [X] 

    for i in range(FIELDWIDTH):
        for j in range(FIELDHEIGHT):
            if mineField[i][j] == '[X]':
                revealedBoxes[i][j] = True
    
def revealAdjacentBoxes(revealedBoxes, box_x, box_y):

    # modifies revealedBoxes data structure so that all adjacent boxes to (box_x, box_y) are set to True

    if box_x != 0: 
        revealedBoxes[box_x-1][box_y] = True
        if box_y != 0: 
            revealedBoxes[box_x-1][box_y-1] = True
        if box_y != FIELDHEIGHT-1: 
            revealedBoxes[box_x-1][box_y+1] = True
    if box_x != FIELDWIDTH-1:
        revealedBoxes[box_x+1][box_y] = True
        if box_y != 0: 
            revealedBoxes[box_x+1][box_y-1] = True
        if box_y != FIELDHEIGHT-1: 
            revealedBoxes[box_x+1][box_y+1] = True
    if box_y != 0: 
        revealedBoxes[box_x][box_y-1] = True
    if box_y != FIELDHEIGHT-1: 
        revealedBoxes[box_x][box_y+1] = True

def getAdjacentBoxesXY(mineField, box_x, box_y):

    # get box XY coordinates for all adjacent boxes to (box_x, box_y)

    adjacentBoxesXY = []

    if box_x != 0:
        adjacentBoxesXY.append([box_x-1,box_y])
        if box_y != 0:
            adjacentBoxesXY.append([box_x-1,box_y-1])
        if box_y != FIELDHEIGHT-1:
            adjacentBoxesXY.append([box_x-1,box_y+1])
    if box_x != FIELDWIDTH-1: 
        adjacentBoxesXY.append([box_x+1,box_y])
        if box_y != 0:
            adjacentBoxesXY.append([box_x+1,box_y-1])
        if box_y != FIELDHEIGHT-1:
            adjacentBoxesXY.append([box_x+1,box_y+1])
    if box_y != 0:
        adjacentBoxesXY.append([box_x,box_y-1])
    if box_y != FIELDHEIGHT-1:
        adjacentBoxesXY.append([box_x,box_y+1])

    return adjacentBoxesXY
    
def drawCovers(revealedBoxes, markedMines):

    # uses revealedBox FIELDWIDTH x FIELDHEIGHT data structure to determine whether to draw box covering mine/number
    # draw red cover instead of gray cover over marked mines

    for box_x in range(FIELDWIDTH):
        for box_y in range(FIELDHEIGHT):
            if not revealedBoxes[box_x][box_y]:
                left, top = getLeftTopXY(box_x, box_y)
                if [box_x, box_y] in markedMines:
                    pygame.draw.rect(DISPLAYSURFACE, MINEMARK_COV, (left, top, BOXSIZE, BOXSIZE))
                else:
                    pygame.draw.rect(DISPLAYSURFACE, BOXCOLOR_COV, (left, top, BOXSIZE, BOXSIZE))

def drawText(text, font, color, surface, x, y):  

    # function to easily draw text and also return object & rect pair

    textobj = font.render(text, True, color)
    textrect = textobj.get_rect()
    textrect.centerx = x
    textrect.centery = y
    surface.blit(textobj, textrect)

def drawButton(text, color, bgcolor, center_x, center_y):

    # similar to drawText but text has bg color and returns obj & rect

    butSurf = BASICFONT.render(text, True, color, bgcolor)
    butRect = butSurf.get_rect()
    butRect.centerx = center_x
    butRect.centery = center_y

    return (butSurf, butRect)

def getLeftTopXY(box_x, box_y):

    # get left & top coordinates for drawing mine boxes

    left = XMARGIN + box_x*(BOXSIZE+GAPSIZE)
    top = YMARGIN + box_y*(BOXSIZE+GAPSIZE)
    return left, top

def getCenterXY(box_x, box_y):

    # get center coordinates for drawing mine boxes
    
    center_x = XMARGIN + BOXSIZE/2 + box_x*(BOXSIZE+GAPSIZE)
    center_y = YMARGIN + BOXSIZE/2 + box_y*(BOXSIZE+GAPSIZE)
    return center_x, center_y

def getBoxAtPixel(x, y):

    # gets coordinates of box at mouse coordinates
    
    for box_x in range(FIELDWIDTH):
        for box_y in range(FIELDHEIGHT):
            left, top = getLeftTopXY(box_x, box_y)
            boxRect = pygame.Rect(left, top, BOXSIZE, BOXSIZE)
            if boxRect.collidepoint(x, y):
                return (box_x, box_y)
    return (None, None)

def highlightBox(box_x, box_y):

    # highlight box when mouse hovers over it
    
    left, top = getLeftTopXY(box_x, box_y)
    pygame.draw.rect(DISPLAYSURFACE, HILITECOLOR, (left, top, BOXSIZE, BOXSIZE), 4)

def highlightButton(butRect):

    # highlight button when mouse hovers over it

    linewidth = 4
    pygame.draw.rect(DISPLAYSURFACE, HILITECOLOR, (butRect.left-linewidth, butRect.top-linewidth, butRect.width+2*linewidth, butRect.height+2*linewidth), linewidth)

def gameWon(revealedBoxes, mineField):

    # check if player has revealed all boxes

    notMineCount = 0

    for box_x in range(FIELDWIDTH):
        for box_y in range(FIELDHEIGHT):
            if revealedBoxes[box_x][box_y] == True:
                if mineField[box_x][box_y] != '[X]':
                    notMineCount += 1

    if notMineCount >= (FIELDWIDTH*FIELDHEIGHT)-MINESTOTAL:
        return True
    else:
        return False

def gameOverAnimation(mineField, revealedBoxes, markedMines, result):

    # makes background flash red (loss) or blue (win)

    origSurf = DISPLAYSURFACE.copy()
    flashSurf = pygame.Surface(DISPLAYSURFACE.get_size())
    flashSurf = flashSurf.convert_alpha()
    animationSpeed = 20

    if result == 'WIN':
        r, g, b = BLUE
    else:
        r, g, b = RED

    for i in range(5):
        for start, end, step in ((0, 255, 1), (255, 0, -1)):
            for alpha in range(start, end, animationSpeed*step): # animation loop
                checkForKeyPress()
                flashSurf.fill((r, g, b, alpha))
                DISPLAYSURFACE.blit(origSurf, (0, 0))
                DISPLAYSURFACE.blit(flashSurf, (0, 0))
                pygame.draw.rect(DISPLAYSURFACE, FIELDCOLOR, (XMARGIN-5, YMARGIN-5, (BOXSIZE+GAPSIZE)*FIELDWIDTH+5, (BOXSIZE+GAPSIZE)*FIELDHEIGHT+5))
                drawField()
                drawMinesNumbers(mineField)
                tipFont = pygame.font.SysFont(FONTTYPE, 16) ## not using BASICFONT - too big
                drawText('Tip: Highlight a box and press space (rather than click the mouse)', tipFont, TEXTCOLOR_3, DISPLAYSURFACE, WINDOWWIDTH/2, WINDOWHEIGHT-60)
                drawText('to mark areas that you think contain mines.', tipFont, TEXTCOLOR_3, DISPLAYSURFACE, WINDOWWIDTH/2, WINDOWHEIGHT-40)
                RESET_SURF, RESET_RECT = drawButton('RESET', TEXTCOLOR_3, RESETBGCOLOR, WINDOWWIDTH/2, WINDOWHEIGHT-120)
                SHOW_SURF, SHOW_RECT = drawButton('SHOW ALL', TEXTCOLOR_3, RESETBGCOLOR, WINDOWWIDTH/2, WINDOWHEIGHT-95)
                drawCovers(revealedBoxes, markedMines)
                pygame.display.update()
                FPSCLOCK.tick(FPS)
  
def terminate():

    # simple function to exit game
    
    pygame.quit()
    sys.exit()

def checkForKeyPress():

    # check if quit or any other key is pressed
    
    if len(pygame.event.get(QUIT)) > 0:
        terminate()
        
    keyUpEvents = pygame.event.get(KEYUP)
    if len(keyUpEvents) == 0:
        return None
    if keyUpEvents[0].key == K_ESCAPE:
        terminate()
    return keyUpEvents[0].key

# run code
if __name__ == '__main__':
    main()

​

2.3 贪吃蛇(Snake)

  1. 创建游戏窗口并初始化Pygame库
  2. 加载游戏素材,包括背景图和蛇和食物的图片
  3. 定义游戏状态,包括蛇、食物和游戏区域
  4. 实现蛇的移动功能
  5. 实现食物的生成和蛇的长度增加功能
  6. 实现游戏结束判定
  7. 实现主循环并调用游戏函数

代码: 

import pygame, sys, time, random


# Difficulty settings
# Easy      ->  10
# Medium    ->  25
# Hard      ->  40
# Harder    ->  60
# Impossible->  120
difficulty = 25

# Window size
frame_size_x = 720
frame_size_y = 480

# Checks for errors encountered
check_errors = pygame.init()
# pygame.init() example output -> (6, 0)
# second number in tuple gives number of errors
if check_errors[1] > 0:
    print(f'[!] Had {check_errors[1]} errors when initialising game, exiting...')
    sys.exit(-1)
else:
    print('[+] Game successfully initialised')


# Initialise game window
pygame.display.set_caption('Snake Eater')
game_window = pygame.display.set_mode((frame_size_x, frame_size_y))


# Colors (R, G, B)
black = pygame.Color(0, 0, 0)
white = pygame.Color(255, 255, 255)
red = pygame.Color(255, 0, 0)
green = pygame.Color(0, 255, 0)
blue = pygame.Color(0, 0, 255)


# FPS (frames per second) controller
fps_controller = pygame.time.Clock()


# Game variables
snake_pos = [100, 50]
snake_body = [[100, 50], [100-10, 50], [100-(2*10), 50]]

food_pos = [random.randrange(1, (frame_size_x//10)) * 10, random.randrange(1, (frame_size_y//10)) * 10]
food_spawn = True

direction = 'RIGHT'
change_to = direction

score = 0


# Game Over
def game_over():
    my_font = pygame.font.SysFont('times new roman', 90)
    game_over_surface = my_font.render('YOU DIED', True, red)
    game_over_rect = game_over_surface.get_rect()
    game_over_rect.midtop = (frame_size_x/2, frame_size_y/4)
    game_window.fill(black)
    game_window.blit(game_over_surface, game_over_rect)
    show_score(0, red, 'times', 20)
    pygame.display.flip()
    time.sleep(3)
    pygame.quit()
    sys.exit()


# Score
def show_score(choice, color, font, size):
    score_font = pygame.font.SysFont(font, size)
    score_surface = score_font.render('Score : ' + str(score), True, color)
    score_rect = score_surface.get_rect()
    if choice == 1:
        score_rect.midtop = (frame_size_x/10, 15)
    else:
        score_rect.midtop = (frame_size_x/2, frame_size_y/1.25)
    game_window.blit(score_surface, score_rect)
    # pygame.display.flip()


# Main logic
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        # Whenever a key is pressed down
        elif event.type == pygame.KEYDOWN:
            # W -> Up; S -> Down; A -> Left; D -> Right
            if event.key == pygame.K_UP or event.key == ord('w'):
                change_to = 'UP'
            if event.key == pygame.K_DOWN or event.key == ord('s'):
                change_to = 'DOWN'
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                change_to = 'LEFT'
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                change_to = 'RIGHT'
            # Esc -> Create event to quit the game
            if event.key == pygame.K_ESCAPE:
                pygame.event.post(pygame.event.Event(pygame.QUIT))

    # Making sure the snake cannot move in the opposite direction instantaneously
    if change_to == 'UP' and direction != 'DOWN':
        direction = 'UP'
    if change_to == 'DOWN' and direction != 'UP':
        direction = 'DOWN'
    if change_to == 'LEFT' and direction != 'RIGHT':
        direction = 'LEFT'
    if change_to == 'RIGHT' and direction != 'LEFT':
        direction = 'RIGHT'

    # Moving the snake
    if direction == 'UP':
        snake_pos[1] -= 10
    if direction == 'DOWN':
        snake_pos[1] += 10
    if direction == 'LEFT':
        snake_pos[0] -= 10
    if direction == 'RIGHT':
        snake_pos[0] += 10

    # Snake body growing mechanism
    snake_body.insert(0, list(snake_pos))
    if snake_pos[0] == food_pos[0] and snake_pos[1] == food_pos[1]:
        score += 1
        food_spawn = False
    else:
        snake_body.pop()

    # Spawning food on the screen
    if not food_spawn:
        food_pos = [random.randrange(1, (frame_size_x//10)) * 10, random.randrange(1, (frame_size_y//10)) * 10]
    food_spawn = True

    # GFX
    game_window.fill(black)
    for pos in snake_body:
        # Snake body
        # .draw.rect(play_surface, color, xy-coordinate)
        # xy-coordinate -> .Rect(x, y, size_x, size_y)
        pygame.draw.rect(game_window, green, pygame.Rect(pos[0], pos[1], 10, 10))

    # Snake food
    pygame.draw.rect(game_window, white, pygame.Rect(food_pos[0], food_pos[1], 10, 10))

    # Game Over conditions
    # Getting out of bounds
    if snake_pos[0] < 0 or snake_pos[0] > frame_size_x-10:
        game_over()
    if snake_pos[1] < 0 or snake_pos[1] > frame_size_y-10:
        game_over()
    # Touching the snake body
    for block in snake_body[1:]:
        if snake_pos[0] == block[0] and snake_pos[1] == block[1]:
            game_over()

    show_score(1, white, 'consolas', 20)
    # Refresh game screen
    pygame.display.update()
    # Refresh rate
    fps_controller.tick(difficulty)

2.4 飞机大战(Airplane War)

  1. 创建游戏窗口并初始化Pygame库
  2. 加载游戏素材,包括背景图和飞机、子弹、敌机的图片
  3. 定义游戏状态,包括飞机、子弹、敌机和游戏区域
  4. 实现飞机射击、敌机移动和被子弹打中后的消失功能
  5. 实现敌机的生成和碰撞判断功能
  6. 实现游戏暂停和结束判定
  7. 实现主循环并调用游戏函数

代码: 

import pygame
import random
import math

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption('飞机大战')
icon = pygame.image.load('ufo.png')
pygame.display.set_icon(icon)
bgImg = pygame.image.load('bg.png')

pygame.mixer.music.load('bg.wav')
pygame.mixer.music.play(-1)
bao_sound = pygame.mixer.Sound('exp.wav')

playerImg = pygame.image.load('player.png')
playerX = 400
playerY = 500
playerStep = 0

score = 0
font = pygame.font.Font('freesansbold.ttf', 32)

def show_score():
    text = f'Score: {score}'
    score_render = font.render(text, True, (255,0,0))
    screen.blit(score_render, (10,10))

is_over = False
over_font = pygame.font.Font('freesansbold.ttf', 64)

def check_is_over():
    if is_over:
        text = "Game Over"
        render = over_font.render(text, True, (255,0,0))
        screen.blit(render, (200,250))

def distance(bx, by, ex, ey):
    a = bx - ex
    b = by - ey
    return math.sqrt(a*a + b*b)

class Enemy():
    def __init__(self):
        self.img = pygame.image.load('enemy.png')
        self.x = random.randint(200, 600)
        self.y = random.randint(50, 250)
        self.step = random.randint(2, 6)

    def reset(self):
        self.x = random.randint(200, 600)
        self.y = random.randint(50, 200)

enemies = []
for i in range(6):
    enemies.append(Enemy())

def show_enemy():
    global is_over
    for e in enemies:
        screen.blit(e.img,(e.x, e.y))
        e.x += e.step
        if(e.x > 736 or e.x < 0):
            e.step *= -1
            e.y += 40
            if e.y > 450:
                is_over = True
                enemies.clear()

class Bullet():
    def __init__(self):
        self.img = pygame.image.load('bullet.png')
        self.x = playerX + 16
        self.y = playerY + 10
        self.step = 10

    def hit(self):
        global score
        for e in enemies:
            if(distance(self.x, self.y, e.x, e.y) < 30):
                bao_sound.play()
                bullets.remove(self)
                e.reset()
                score += 1

bullets = []

def show_bullets():
    for b in bullets:
        screen.blit(b.img, (b.x, b.y))
        b.hit()
        b.y -= b.step
        if b.y < 0:
            bullets.remove(b)

def move_player():
    global playerX
    playerX += playerStep
    if playerX > 736:
        playerX = 736
    if playerX < 0:
        playerX = 0

running = True
while running:
    screen.blit(bgImg,(0,0))
    show_score()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RIGHT:
                playerStep = 5
            elif event.key == pygame.K_LEFT:
                playerStep = -5
            elif event.key == pygame.K_SPACE:
                bullets.append(Bullet())
        if event.type == pygame.KEYUP:
            playerStep = 0

    screen.blit(playerImg, (playerX, playerY))
    move_player()
    show_enemy()
    show_bullets()
    check_is_over()
    pygame.display.update()

这个游戏需要亿点素材,链接:https://raw.githubusercontent.com/liuxinleiup/Pygame_AirplaneWar/main/%E7%B4%A0%E6%9D%90.zip

建议打开Github加速器后使用高速下载器下载,或者使用网盘:

链接:https://pan.baidu.com/s/1WphKeWRPxJ2RSYBv_jP-jA 
提取码:fvyv

注意,下载后解压素材包,目录与 *.py 文件在一起 


以上是本期的全部内容,4个Pygame高质量作品,更多的作品等待你探索,可以去Pygame官网和Github上获取资讯或代码,如果你喜欢文章的话,可以收藏起来,或者分享给更多人,总之,你的支持是我创作的动力。


The End

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值